이동평균 크로스오버란?
이동평균 크로스오버(Moving Average Crossover)는 자동매매에서 가장 널리 쓰이는 추세추종 전략입니다. 단기 이동평균이 장기 이동평균을 위로 돌파하면 매수, 아래로 돌파하면 매도하는 단순한 원리입니다.
골든크로스(Golden Cross)와 데드크로스(Dead Cross)라는 이름으로도 익숙할 것입니다. 원리는 단순하지만, 실전에서 수익을 내려면 세밀한 조정이 필요합니다.
기본 원리와 시그널
# 이동평균 크로스오버 기본 구현
import pandas as pd
import numpy as np
def ma_crossover_signals(prices, short_window=20, long_window=60):
"""
단기·장기 이동평균 크로스오버 시그널 생성
Returns:
1 = 매수 시그널 (골든크로스)
-1 = 매도 시그널 (데드크로스)
0 = 시그널 없음
"""
df = pd.DataFrame({'close': prices})
df['ma_short'] = df['close'].rolling(short_window).mean()
df['ma_long'] = df['close'].rolling(long_window).mean()
df['signal'] = 0
df.loc[df['ma_short'] > df['ma_long'], 'position'] = 1
df.loc[df['ma_short'] <= df['ma_long'], 'position'] = -1
df['signal'] = df['position'].diff() # 변화 시점 = 크로스오버
return df
SMA vs EMA: 어떤 이동평균을 쓸 것인가
| 구분 | SMA (단순이동평균) | EMA (지수이동평균) |
|---|---|---|
| 계산 방식 | 기간 내 가격의 단순 평균 | 최근 가격에 더 높은 가중치 |
| 반응 속도 | 느림 | 빠름 |
| 노이즈 민감도 | 낮음 | 높음 |
| 추세 전환 감지 | 지연됨 | 빠름 |
| 적합한 시장 | 강한 추세장 | 변동성 큰 시장 |
일반적으로 EMA가 실전에서 더 많이 사용됩니다. 최근 가격 변화에 빠르게 반응하므로 진입·청산 타이밍이 더 유리합니다.
# EMA 크로스오버 구현
def ema_crossover(prices, short_window=12, long_window=26):
df = pd.DataFrame({'close': prices})
df['ema_short'] = df['close'].ewm(span=short_window, adjust=False).mean()
df['ema_long'] = df['close'].ewm(span=long_window, adjust=False).mean()
# 크로스오버 감지
df['cross'] = np.where(df['ema_short'] > df['ema_long'], 1, -1)
df['signal'] = df['cross'].diff()
return df
대표적인 이동평균 조합 5가지
| 조합 | 단기 | 장기 | 특징 |
|---|---|---|---|
| 스캘핑용 | 5 EMA | 20 EMA | 빈번한 시그널, 높은 거래 비용 |
| 단기 스윙 | 10 EMA | 30 EMA | 일~주 단위 트레이딩 |
| MACD 기본 | 12 EMA | 26 EMA | 가장 널리 사용되는 조합 |
| 중기 추세 | 20 SMA | 60 SMA | 노이즈 적음, 큰 추세 포착 |
| 장기 투자 | 50 SMA | 200 SMA | 골든/데드크로스의 원조 |
크로스오버 전략의 치명적 약점: 횡보장
이동평균 크로스오버의 최대 약점은 횡보장(레인지 마켓)입니다. 가격이 좁은 범위에서 오르내리면 단기·장기 이동평균이 수시로 교차하면서 거짓 신호(Whipsaw)가 쏟아집니다.
이 문제를 해결하는 실전 필터 3가지를 소개합니다.
필터 1: ADX로 추세 강도 확인
ADX(Average Directional Index)가 25 이상일 때만 크로스오버 시그널을 따릅니다. ADX가 낮으면 횡보 구간이므로 거래하지 않습니다.
# ADX 필터 적용
def filtered_signal(cross_signal, adx_value, threshold=25):
if adx_value < threshold:
return 0 # 횡보장 → 시그널 무시
return cross_signal
필터 2: 확인 봉(Confirmation Bar)
크로스오버가 발생한 직후 바로 진입하지 않고, 다음 1~2개 봉에서도 크로스 상태가 유지되는지 확인합니다. 거짓 크로스의 상당수는 1~2봉 내에 되돌아갑니다.
필터 3: 거래량 확인
크로스오버 시점의 거래량이 20일 평균 거래량의 1.5배 이상인지 확인합니다. 거래량이 뒷받침되지 않는 크로스오버는 신뢰도가 낮습니다.
실전 백테스트 시 주의사항
- 슬리피지 반영 — 크로스오버 시점의 가격과 실제 체결가는 다릅니다. 최소 0.1~0.3% 슬리피지를 반영하세요
- 수수료 포함 — 시그널이 잦은 조합(5/20 EMA 등)은 수수료만으로 수익이 잠식될 수 있습니다
- 다양한 시장 구간 — 상승장에서만 테스트하면 안 됩니다. 2022년 같은 하락장, 2023년 같은 횡보장 데이터를 반드시 포함하세요
- 파라미터 과적합 주의 — 최적 이동평균 기간을 찾겠다고 1일부터 200일까지 전수 조사하면 과적합에 빠집니다
크로스오버 자동매매 봇 설계 팁
class CrossoverBot:
def __init__(self, short=12, long=26, adx_threshold=25):
self.short = short
self.long = long
self.adx_threshold = adx_threshold
self.position = 0 # 0: 없음, 1: 롱, -1: 숏
def on_candle(self, candle_data):
ema_s = calc_ema(candle_data, self.short)
ema_l = calc_ema(candle_data, self.long)
adx = calc_adx(candle_data, 14)
# ADX 필터
if adx < self.adx_threshold:
return # 횡보장 → 대기
# 골든크로스 → 매수
if ema_s > ema_l and self.position <= 0:
self.buy()
self.position = 1
# 데드크로스 → 매도
elif ema_s < ema_l and self.position >= 0:
self.sell()
self.position = -1
봇을 실전에 배포할 때는 반드시 리스크 관리 로직을 함께 구현해야 합니다. 포지션 사이징, 손절, 일일 손실 한도는 기본입니다.
정리: 크로스오버 전략 체크리스트
- ☑️ SMA/EMA 중 시장에 맞는 타입을 선택했는가
- ☑️ 이동평균 기간 조합이 거래 스타일에 맞는가
- ☑️ ADX 등 추세 필터를 적용했는가
- ☑️ 횡보장에서의 거짓 신호를 처리했는가
- ☑️ 슬리피지와 수수료를 백테스트에 반영했는가
- ☑️ 상승장·하락장·횡보장 모두에서 테스트했는가
- ☑️ 리스크 관리 로직이 포함되어 있는가
이동평균 크로스오버는 가장 오래된 전략 중 하나지만, 필터와 리스크 관리를 결합하면 여전히 실전에서 유효한 전략입니다. 단순함이 곧 강점입니다.
관련 글도 함께 읽어보세요: