CMO 지표를 활용한 트레이딩 전략(CMO Indicator Trading Strategy)

CMO 지표의 개념과 유래

CMO(Chande Momentum Oscillator)는 Tushar Chande가 개발한 모멘텀 지표로, 가격 변화의 강도를 측정하는 데 사용된다. CMO는 일정 기간 동안 상승일의 가격 변화와 하락일의 가격 변화를 비교하여 모멘텀의 강도를 계산한다.

CMO의 값은 -100부터 +100 사이에서 변동하며, 0을 기준으로 양수와 음수로 나뉜다. 양수는 상승 모멘텀을, 음수는 하락 모멘텀을 나타낸다. CMO의 절대값이 클수록 모멘텀의 강도가 강하다는 것을 의미한다.

CMO 계산 방법

CMO는 다음과 같은 단계를 통해 계산된다:

  1. 일정 기간(일반적으로 20일) 동안의 상승일과 하락일의 가격 변화를 계산한다.
  • 상승일의 가격 변화(Up): 금일 종가 – 전일 종가 (금일 종가 > 전일 종가인 경우)
  • 하락일의 가격 변화(Down): 전일 종가 – 금일 종가 (금일 종가 < 전일 종가인 경우)
  1. 상승일의 가격 변화 합(SU)과 하락일의 가격 변화 합(SD)을 계산한다.
  2. CMO 계산:
  • CMO = (SU – SD) / (SU + SD) * 100

예를 들어, 20일 동안 상승일의 가격 변화 합이 150이고 하락일의 가격 변화 합이 50이라면, CMO = (150 – 50) / (150 + 50) * 100 = 50이 된다.

CMO를 활용한 매매 전략

CMO를 활용한 매매 전략은 다음과 같다:

  1. 과매수/과매도 구간 활용:
  • CMO가 +50 이상인 경우 과매수 구간으로 판단하여 매도 신호로 해석한다.
  • CMO가 -50 이하인 경우 과매도 구간으로 판단하여 매수 신호로 해석한다.
  1. 시그널 라인 교차 전략:
  • CMO와 시그널 라인(예: CMO의 9일 이동평균)을 활용한다.
  • CMO가 시그널 라인을 상향 돌파할 때 매수 신호, 하향 돌파할 때 매도 신호로 해석한다.
  1. 다이버전스 전략:
  • 가격은 신고가를 경신하는데 CMO는 신고가를 경신하지 못하는 베어시 다이버전스가 나타나면 매도 신호로 해석한다.
  • 가격은 신저가를 경신하는데 CMO는 신저가를 경신하지 못하는 불리시 다이버전스가 나타나면 매수 신호로 해석한다.

실제 트레이딩에서는 CMO와 함께 추세 지표, 거래량 지표 등을 함께 활용하여 종합적으로 판단하는 것이 효과적이다.

파이썬을 활용한 CMO 백테스팅

파이썬을 사용하여 CMO 기반의 트레이딩 전략을 백테스팅해보자. 다음 라이브러리를 활용할 것이다:

  • pandas: 데이터 조작 및 분석
  • numpy: 수치 계산
  • matplotlib: 데이터 시각화
  • FinanceDataReader: 주가 데이터 수집

먼저 가상환경을 활성화하고 필요한 라이브러리를 설치한다.

source venv/bin/activate
pip install pandas numpy matplotlib FinanceDataReader

FinanceDataReader를 설치하면 종속 모듈도 함께 설치된다.

다음으로 CMO를 계산하고 매매 신호를 생성하는 코드를 작성해보자.

import FinanceDataReader as fdr
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 데이터 가져오기
symbol = "VOO"
start_date = "1900-01-01"
end_date = "2023-05-01"

try:
    data = fdr.DataReader(symbol, start_date, end_date)
except Exception as e:
    print(f"Error occurred while fetching data: {e}")
    exit(1)

# CMO 계산 함수
def calculate_cmo(data, period=20):
    close = data['Close']
    diff = close.diff()

    up = diff.copy()
    up[up < 0] = 0

    down = -diff.copy()
    down[down < 0] = 0

    cmo = 100 * (up.rolling(window=period).sum() - down.rolling(window=period).sum()) / (up.rolling(window=period).sum() + down.rolling(window=period).sum())

    return cmo

# 매매 신호 생성 함수
def get_signals(data, cmo, upper_threshold=50, lower_threshold=-50):
    signals = pd.DataFrame(index=data.index)
    signals['signal'] = 0

    signals['signal'][cmo > upper_threshold] = -1
    signals['signal'][cmo < lower_threshold] = 1

    signals['positions'] = signals['signal'].diff()

    return signals

# CMO 계산
cmo = calculate_cmo(data)

# 매매 신호 생성
signals = get_signals(data, cmo)

# 백테스팅
initial_capital = 10000
positions = pd.DataFrame(index=signals.index).fillna(0.0)
positions[symbol] = 100 * signals['signal']

portfolio = positions.multiply(data['Adj Close'], axis=0)
pos_diff = positions.diff()

portfolio['holdings'] = (positions.multiply(data['Adj Close'], axis=0)).sum(axis=1)
portfolio['cash'] = initial_capital - (pos_diff.multiply(data['Adj Close'], axis=0)).sum(axis=1).cumsum()

portfolio['total'] = portfolio['cash'] + portfolio['holdings']
portfolio['returns'] = portfolio['total'].pct_change()

# 결과 출력
print(f"Final portfolio value: ${portfolio['total'][-1]:,.2f}")
print(f"Total returns: {100 * (portfolio['total'][-1] / initial_capital - 1):.2f}%")

# 시각화
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 15), gridspec_kw={'height_ratios': [3, 1, 1]})

ax1.plot(data['Close'], label='Close Price')
ax1.set_ylabel('Price')
ax1.set_title('Price Chart')
ax1.legend(loc='upper left')

ax2.plot(cmo, label='CMO')
ax2.axhline(y=50, color='r', linestyle='--', label='Overbought')
ax2.axhline(y=-50, color='g', linestyle='--', label='Oversold')
ax2.set_ylabel('CMO')
ax2.set_title('Chande Momentum Oscillator')
ax2.legend(loc='upper left')

ax3.plot(portfolio['total'], label='Portfolio Value')
ax3.set_ylabel('Portfolio Value')
ax3.set_title('Portfolio Performance')
ax3.legend(loc='upper left')

plt.tight_layout()
plt.show()

위 코드에서는 FinanceDataReader를 사용하여 VOO ETF의 전체 기간 주가 데이터를 가져온다. start_date를 가능한 한 오래전 날짜로 설정하여 모든 데이터를 가져올 수 있도록 한다.

calculate_cmo 함수는 주어진 데이터를 바탕으로 CMO를 계산한다. 먼저 종가의 차이를 계산하고, 양수인 경우 상승일, 음수인 경우 하락일로 구분한다. 그런 다음 상승일과 하락일의 합을 구하고, CMO 공식에 따라 계산한다.

get_signals 함수는 계산된 CMO를 기준으로 매매 신호를 생성한다. CMO가 상단 임계값(예: 50) 이상일 때 매도 신호(-1), 하단 임계값(예: -50) 이하일 때 매수 신호(1)를 생성한다.

백테스팅을 위해 초기 자본금을 10,000달러로 설정하고, 생성된 매매 신호를 바탕으로 포트폴리오 가치를 계산한다. 최종적으로 총 수익률과 포트폴리오 가치를 출력한다.

matplotlib을 사용하여 주가, CMO, 포트폴리오 가치를 시각화한다. CMO 차트에는 과매수/과매도 구간을 나타내는 기준선도 표시한다.

코드 실행 중 발생할 수 있는 오류를 처리하기 위해 try-except 문을 사용하였다. 데이터를 가져오는 과정에서 오류가 발생하면 에러 메시지를 출력하고 프로그램을 종료한다.

Leave a Reply

error: Content is protected !!