파이썬 MACD 자동매매 전략: 시그널 교차·제로라인

MACD란 무엇인가?

MACD(Moving Average Convergence Divergence)는 제럴드 아펠(Gerald Appel)이 1970년대 후반에 개발한 추세 추종형 모멘텀 지표입니다. 두 개의 지수이동평균(EMA) 사이의 관계를 분석하여 추세의 방향, 강도, 전환 시점을 한눈에 파악할 수 있습니다. 퀀트 트레이딩에서 MACD는 RSI와 함께 가장 많이 사용되는 기술적 지표이며, 특히 추세 전환 포착에 탁월한 성능을 보입니다.

MACD는 세 가지 구성 요소로 이루어집니다:

  • MACD 라인: 12일 EMA − 26일 EMA (단기와 장기 이동평균의 차이)
  • 시그널 라인: MACD 라인의 9일 EMA (MACD의 이동평균)
  • 히스토그램: MACD 라인 − 시그널 라인 (두 선의 차이를 막대로 표시)

MACD 라인이 시그널 라인을 위로 교차하면 골든 크로스(매수 신호), 아래로 교차하면 데드 크로스(매도 신호)로 해석합니다. 이 단순한 교차 규칙만으로도 강력한 자동매매 시스템을 구축할 수 있습니다.

파이썬으로 MACD 계산하기

pandasewm() 메서드를 활용하면 MACD를 간결하게 구현할 수 있습니다.

import pandas as pd
import numpy as np

def calculate_macd(prices: pd.Series,
                   fast: int = 12, slow: int = 26,
                   signal: int = 9) -> pd.DataFrame:
    """MACD 지표 계산"""
    ema_fast = prices.ewm(span=fast, adjust=False).mean()
    ema_slow = prices.ewm(span=slow, adjust=False).mean()

    macd_line = ema_fast - ema_slow
    signal_line = macd_line.ewm(span=signal, adjust=False).mean()
    histogram = macd_line - signal_line

    return pd.DataFrame({
        'macd': macd_line,
        'signal': signal_line,
        'histogram': histogram
    })

여기서 adjust=False는 재귀적 EMA 계산 방식을 사용하겠다는 의미입니다. 트레이딩 소프트웨어 대부분이 이 방식을 채택하므로, 다른 플랫폼과 동일한 결과를 얻으려면 반드시 이 옵션을 지정해야 합니다.

MACD 자동매매 전략 4가지

전략 1: 기본 시그널 교차 전략

MACD 라인이 시그널 라인을 교차하는 순간에 매매하는 가장 전통적인 방식입니다. 구현이 간단하고 직관적이지만, 횡보장에서 잦은 거짓 신호(whipsaw)가 발생하는 단점이 있습니다.

def signal_crossover(df: pd.DataFrame) -> pd.DataFrame:
    """MACD-시그널 교차 매매 신호"""
    df = df.copy()
    df['trade_signal'] = 0

    # 골든 크로스: MACD가 시그널 위로 교차
    df.loc[(df['macd'] > df['signal_line']) &
           (df['macd'].shift(1) <= df['signal_line'].shift(1)),
           'trade_signal'] = 1

    # 데드 크로스: MACD가 시그널 아래로 교차
    df.loc[(df['macd'] < df['signal_line']) &
           (df['macd'].shift(1) >= df['signal_line'].shift(1)),
           'trade_signal'] = -1

    return df

전략 2: 제로라인 교차 전략

MACD 라인이 0선(제로라인)을 교차하는 시점에 매매합니다. 시그널 교차보다 신호 발생이 느리지만, 추세 확인 후 진입하므로 신뢰도가 더 높습니다. MACD가 0 위로 올라가면 단기 EMA가 장기 EMA를 상회하기 시작했다는 뜻이므로, 상승 추세의 시작으로 해석할 수 있습니다.

def zero_line_crossover(df: pd.DataFrame) -> pd.DataFrame:
    """MACD 제로라인 교차 매매 신호"""
    df = df.copy()
    df['trade_signal'] = 0

    # MACD가 0 위로 → 상승 추세 시작
    df.loc[(df['macd'] > 0) &
           (df['macd'].shift(1) <= 0), 'trade_signal'] = 1

    # MACD가 0 아래로 → 하락 추세 시작
    df.loc[(df['macd'] < 0) &
           (df['macd'].shift(1) >= 0), 'trade_signal'] = -1

    return df

전략 3: 히스토그램 반전 전략

히스토그램의 기울기 변화를 활용하는 전략입니다. 히스토그램이 감소하다가 증가로 전환되면 모멘텀이 바뀌고 있다는 신호입니다. 시그널 교차보다 1~3일 빠르게 신호를 포착할 수 있어 선행 매매(early entry)에 유리합니다.

def histogram_reversal(df: pd.DataFrame) -> pd.DataFrame:
    """히스토그램 반전 매매 신호"""
    df = df.copy()
    df['trade_signal'] = 0
    df['hist_diff'] = df['histogram'].diff()

    # 히스토그램 음수 영역에서 반전 상승 → 매수
    df.loc[(df['histogram'] < 0) &
           (df['hist_diff'] > 0) &
           (df['hist_diff'].shift(1) <= 0), 'trade_signal'] = 1

    # 히스토그램 양수 영역에서 반전 하락 → 매도
    df.loc[(df['histogram'] > 0) &
           (df['hist_diff'] < 0) &
           (df['hist_diff'].shift(1) >= 0), 'trade_signal'] = -1

    return df

전략 4: MACD 다이버전스 전략

가격은 신고가를 경신하는데 MACD는 전고점을 넘지 못하는 약세 다이버전스, 또는 가격은 신저가인데 MACD는 전저점보다 높은 강세 다이버전스를 포착합니다. 추세 전환의 가장 강력한 선행 신호로 알려져 있지만, 자동화 구현이 상대적으로 복잡합니다.

def detect_divergence(df: pd.DataFrame,
                      lookback: int = 20) -> pd.DataFrame:
    """MACD 다이버전스 탐지"""
    df = df.copy()
    df['trade_signal'] = 0

    for i in range(lookback, len(df)):
        window = df.iloc[i-lookback:i+1]
        price = window['Close']
        macd = window['macd']

        # 강세 다이버전스: 가격 저점 하락 + MACD 저점 상승
        price_low_now = price.iloc[-1] == price.min()
        if price_low_now:
            prev_low_idx = price.iloc[:-1].idxmin()
            if (price.iloc[-1] < price[prev_low_idx] and
                macd.iloc[-1] > macd[prev_low_idx]):
                df.iloc[i, df.columns.get_loc('trade_signal')] = 1

        # 약세 다이버전스: 가격 고점 상승 + MACD 고점 하락
        price_high_now = price.iloc[-1] == price.max()
        if price_high_now:
            prev_high_idx = price.iloc[:-1].idxmax()
            if (price.iloc[-1] > price[prev_high_idx] and
                macd.iloc[-1] < macd[prev_high_idx]):
                df.iloc[i, df.columns.get_loc('trade_signal')] = -1

    return df

통합 백테스팅 엔진

네 가지 MACD 전략을 체계적으로 비교할 수 있는 백테스팅 엔진입니다. 이동평균 크로스오버 백테스팅 가이드의 구조를 확장했습니다.

import yfinance as yf

class MACDBacktester:
    """MACD 전략 백테스팅 엔진"""

    def __init__(self, ticker: str, start: str, end: str,
                 fast: int = 12, slow: int = 26, signal: int = 9):
        self.ticker = ticker
        self.fast = fast
        self.slow = slow
        self.signal_period = signal
        self.df = self._prepare(start, end)

    def _prepare(self, start: str, end: str) -> pd.DataFrame:
        df = yf.download(self.ticker, start=start, end=end)
        macd_df = calculate_macd(
            df['Close'], self.fast, self.slow, self.signal_period
        )
        df['macd'] = macd_df['macd']
        df['signal_line'] = macd_df['signal']
        df['histogram'] = macd_df['histogram']
        return df

    def run(self, strategy_func, capital: float = 10_000_000,
            commission: float = 0.00015,
            stop_loss: float = 0.03) -> dict:
        """백테스팅 실행 (수수료·손절 포함)"""
        df = strategy_func(self.df)
        cash = capital
        shares = 0
        position = 0
        entry = 0
        wins = 0
        losses = 0

        for _, row in df.iterrows():
            price = row['Close']

            # 손절 확인
            if position == 1 and entry > 0:
                if (entry - price) / entry >= stop_loss:
                    cash += shares * price * (1 - commission)
                    losses += 1
                    shares, position = 0, 0
                    continue

            if row['trade_signal'] == 1 and position == 0:
                shares = int(cash // (price * (1 + commission)))
                if shares > 0:
                    cash -= shares * price * (1 + commission)
                    entry = price
                    position = 1

            elif row['trade_signal'] == -1 and position == 1:
                proceeds = shares * price * (1 - commission)
                cash += proceeds
                if price > entry:
                    wins += 1
                else:
                    losses += 1
                shares, position = 0, 0

        final = cash + shares * df.iloc[-1]['Close']
        total_trades = wins + losses
        return {
            'total_return_pct': round((final - capital) / capital * 100, 2),
            'final_value': round(final),
            'trades': total_trades,
            'win_rate': round(wins / total_trades * 100, 1) if total_trades else 0,
            'wins': wins,
            'losses': losses
        }

MACD 파라미터 최적화

기본 설정(12, 26, 9)은 1970년대 주 6일 거래 기준으로 만들어진 값입니다. 현재 주 5일 거래 환경과 종목 특성에 맞게 파라미터를 최적화해야 합니다.

def optimize_macd(ticker: str, start: str, end: str,
                  strategy_func) -> dict:
    """MACD 파라미터 그리드 서치"""
    best = {'return': -float('inf')}

    for fast in [8, 10, 12, 15]:
        for slow in [20, 24, 26, 30]:
            if fast >= slow:
                continue
            for sig in [7, 9, 11, 13]:
                bt = MACDBacktester(ticker, start, end, fast, slow, sig)
                result = bt.run(strategy_func)

                if result['total_return_pct'] > best['return']:
                    best = {
                        'fast': fast, 'slow': slow, 'signal': sig,
                        'return': result['total_return_pct'],
                        'win_rate': result['win_rate'],
                        'trades': result['trades']
                    }
    return best

최적화 시 주의사항:

  • 과적합 방지: 전체 데이터를 학습/검증 세트로 분리(예: 70:30)하여, 학습 구간에서 찾은 파라미터가 검증 구간에서도 유효한지 확인하세요.
  • 안정성 검증: 최적 파라미터 근처의 값들도 비슷한 성과를 보이는지 확인합니다. 특정 조합 하나만 성과가 좋고 주변 값은 급격히 나빠진다면 과적합일 가능성이 높습니다.
  • 거래 비용 포함: 매매 빈도가 높은 파라미터일수록 수수료 영향이 커지므로, 반드시 수수료를 반영한 순수익으로 비교하세요.

전략별 특성 비교

전략 신호 속도 매매 빈도 최적 시장 환경 주요 리스크
시그널 교차 보통 월 3~6회 중기 추세 횡보장 whipsaw
제로라인 교차 느림 월 1~2회 강한 추세 진입 지연
히스토그램 반전 빠름 월 5~10회 단기 스윙 거짓 반전 빈발
다이버전스 선행 분기 2~5회 추세 전환 조기 진입

MACD + 보조 지표 복합 전략

MACD 단독 사용의 한계를 보완하기 위해 다른 지표와 결합하는 것이 실전에서는 필수입니다.

MACD + RSI 복합 필터

RSI를 필터로 추가하면 거짓 신호를 크게 줄일 수 있습니다. MACD 골든 크로스가 발생했을 때 RSI가 50 이상이면 상승 모멘텀이 확인된 것으로 보고 진입하고, RSI가 30 이하의 과매도 상태에서 MACD 골든 크로스가 발생하면 더 강력한 매수 신호로 해석합니다.

def macd_rsi_composite(df: pd.DataFrame) -> pd.DataFrame:
    """MACD + RSI 복합 전략"""
    df = df.copy()
    df['trade_signal'] = 0

    # MACD 골든크로스 + RSI 확인
    golden = ((df['macd'] > df['signal_line']) &
              (df['macd'].shift(1) <= df['signal_line'].shift(1)))

    # RSI 50 이상: 모멘텀 확인 매수
    # RSI 30 이하 + 골든크로스: 강력 매수
    df.loc[golden & (df['rsi'] >= 50), 'trade_signal'] = 1
    df.loc[golden & (df['rsi'] < 30), 'trade_signal'] = 1

    # MACD 데드크로스 + RSI 70 이상: 과열 매도
    dead = ((df['macd'] < df['signal_line']) &
            (df['macd'].shift(1) >= df['signal_line'].shift(1)))
    df.loc[dead & (df['rsi'] >= 70), 'trade_signal'] = -1
    df.loc[dead & (df['rsi'] < 50), 'trade_signal'] = -1

    return df

MACD + 볼린저 밴드 복합 전략

볼린저 밴드와 결합하면 변동성 정보까지 반영할 수 있습니다. 볼린저 밴드 스퀴즈 상태에서 MACD 교차가 발생하면 강력한 브레이크아웃 신호로 해석하고, 밴드 상·하단 근처에서 MACD 다이버전스가 나타나면 최고의 진입 기회로 봅니다.

실전 운영 시 핵심 노하우

1. 시장 국면별 전략 전환

ADX(Average Directional Index)로 추세 강도를 측정하여 전략을 자동 전환할 수 있습니다:

  • ADX > 25 (추세장): 시그널 교차 또는 제로라인 교차 전략 사용
  • ADX < 20 (횡보장): MACD 신호 무시, 평균회귀 전략으로 전환
  • ADX 20~25 (전환 구간): 히스토그램 반전으로 조심스럽게 진입

2. 다중 시간프레임 확인

일봉에서 MACD 매수 신호가 발생해도, 주봉 MACD가 하락 중이라면 큰 하락 추세 안에서의 일시적 반등일 수 있습니다. 상위 시간프레임과 방향이 일치하는 신호만 실행하세요.

3. 포지션 사이징과 리스크 관리

한 번의 거래에 전체 자본의 일정 비율(보통 1~2%)만 위험에 노출하는 고정 비율 리스크 관리를 적용합니다:

def position_size(capital: float, entry: float,
                  stop_price: float, risk_pct: float = 0.02) -> int:
    """고정 비율 리스크 기반 포지션 사이즈 계산"""
    risk_amount = capital * risk_pct
    per_share_risk = abs(entry - stop_price)
    if per_share_risk == 0:
        return 0
    return int(risk_amount / per_share_risk)

마무리: MACD 자동매매 체크리스트

MACD 기반 자동매매 시스템을 실전에 투입하기 전 확인해야 할 핵심 항목입니다:

  • ✅ fast/slow/signal 파라미터를 종목과 시간프레임에 맞게 최적화했는가?
  • ✅ 최소 5년 이상의 백테스팅 데이터로 검증했는가?
  • ✅ 상승장·하락장·횡보장 각각에서 전략 성과를 분리 분석했는가?
  • ✅ RSI나 볼린저 밴드 등 보조 필터를 추가했는가?
  • ✅ 수수료와 슬리피지를 포함한 순수익으로 평가했는가?
  • ✅ 고정 비율 리스크 관리와 손절매 규칙을 설정했는가?
  • ✅ Walk-Forward Analysis로 과적합 여부를 점검했는가?
  • ✅ 실시간 전환 시 API 오류 처리와 모니터링 체계가 있는가?

MACD는 추세의 방향과 모멘텀을 동시에 읽는 강력한 도구입니다. 네 가지 전략을 시장 국면에 따라 적절히 선택하고, 보조 지표와 결합하여 복합 시스템을 구축하면 안정적인 자동매매 성과를 기대할 수 있습니다. 항상 소액으로 실전 테스트를 먼저 진행하고, 전략의 유효성을 지속적으로 모니터링하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux