[알고리즘 트레이딩] 공매도 없이 ETF로 페어 트레이딩 하기 – KODEX 200 & 인버스

📌 요약

공매도가 어렵거나 제한적인 국내 ETF 환경에서도 가능한 시장 중립적 트레이딩 전략이 있습니다.
KODEX 200과 KODEX 200 인버스를 활용한 Z-score 기반 페어 트레이딩 전략을 소개합니다.


🎯 전략 목표

  • 시장 방향성 상관없이 수익을 추구

  • Z-score 기반 비대칭 전략으로 단기 분산의 평균 회귀를 이용

  • 공매도 없이 한쪽 ETF 매수만으로 대응하는 방식


📈 대상 ETF 소개

ETF설명
KODEX 200 (069500.KS)KOSPI200 지수 추종
KODEX 200 인버스 (114800.KS)KOSPI200 지수 반대 방향 추종 (역방향)

🧠 전략 원리

➤ Spread & Z-score 계산

Spread = log(KODEX200) - log(KODEX 인버스) Z-score = (Spread - 평균) / 표준편차
  • Z-score > 2: KODEX 200 가격이 고평가 vs 인버스
    KODEX 인버스 매수

  • Z-score < -2: KODEX 200이 저평가 vs 인버스
    KODEX 200 매수

  • Z-score ≈ 0: 스프레드가 평균에 근접
    포지션 청산


📌 전략 요약표

조건실행
Z-score > 2✅ KODEX 인버스 매수
Z-score < -2✅ KODEX 200 매수
Z-score ≈ 0 (절댓값 < 0.5)❎ 포지션 청산

🛠 진입 시그널 감지 파이썬 코드 (야후 파이낸스 기반)

import yfinance as yf import numpy as np import pandas as pd import matplotlib.pyplot as plt # 티커 불러오기 ticker_1 = yf.Ticker("069500.KS") # KODEX 200 ticker_2 = yf.Ticker("114800.KS") # KODEX 인버스 # 초단기 1분봉, 최근 5일 df1 = ticker_1.history(period="5d", interval="1m") df2 = ticker_2.history(period="5d", interval="1m") # 종가 정제 df1 = df1[["Close"]].rename(columns={"Close": "KODEX200"}) df2 = df2[["Close"]].rename(columns={"Close": "INVERSE"}) df = pd.concat([df1, df2], axis=1).dropna() # Spread & Z-score 계산 spread = np.log(df["KODEX200"]) - np.log(df["INVERSE"]) zscore = (spread - spread.rolling(60).mean()) / spread.rolling(60).std() df["Z-score"] = zscore # 진입/청산 시그널 df["Signal"] = None df.loc[zscore > 2, "Signal"] = "매수: 인버스" df.loc[zscore < -2, "Signal"] = "매수: KODEX200" df.loc[zscore.abs() < 0.5, "Signal"] = "청산" # 최근 시그널 확인 print(df[["KODEX200", "INVERSE", "Z-score", "Signal"]].tail(20))

📊 시각화 (2x2 플롯)

fig, axs = plt.subplots(2, 2, figsize=(14, 10)) # Z-score axs[0, 0].plot(df.index, df["Z-score"], label="Z-score") axs[0, 0].axhline(2, color='red', linestyle='--') axs[0, 0].axhline(-2, color='blue', linestyle='--') axs[0, 0].axhline(0, color='black', linestyle=':') axs[0, 0].set_title("Z-score") axs[0, 0].legend() axs[0, 0].grid(True) # Spread axs[0, 1].plot(df.index, spread, color='green', label="Spread") axs[0, 1].set_title("Log Spread") axs[0, 1].legend() axs[0, 1].grid(True) # 개별 ETF 가격 axs[1, 0].plot(df.index, df["KODEX200"], color='orange', label="KODEX200") axs[1, 0].set_title("KODEX 200 Price") axs[1, 0].legend() axs[1, 0].grid(True) axs[1, 1].plot(df.index, df["INVERSE"], color='purple', label="INVERSE") axs[1, 1].set_title("KODEX INVERSE Price") axs[1, 1].legend() axs[1, 1].grid(True) plt.tight_layout() plt.show()

🧪 시뮬레이션 & 자동화 도구 제안

  • 진입 알림: Telegram, 이메일, Discord 알림 연동

  • 조건부 자동 주문: 매수/청산 신호 발생 시 모의 거래 실행

  • 백테스트 시뮬레이터: 과거 Z-score 데이터 기반 수익률 테스트

  • 리스크 제어: 최대 허용 손실, 진입 간격 제한, 시간 필터링

댓글

이 블로그의 인기 게시물

전력(kW) 계산하기 (직류, 교류 단상, 교류 삼상)

[PLC] PLC 아날로그 입출력 기본

공압 속도 제어: 미터인 vs 미터아웃

[아두이노] 가변저항(Potential Divider)과 전압분배(Voltage Divider)

제너 다이오드에 저항을 연결하는 이유

PLC 출력 형태

NPN, PNP 트랜지스터 차이점

[전기 기초] 저항의 정격전력(Watt) 표기의 의미

[PLC] 래더 다이어그램과 PLC

3선 결선식 센서의 타입 PNP, NPN