삼중이동평균선의 개념
삼중이동평균선(Triple Moving Average)은 세 개의 이동평균선을 조합하여 사용하는 기술적 분석 지표다. 이는 단기, 중기, 장기 추세를 파악하고 매매 시점을 결정하는 데 사용된다. 일반적으로 단기 이동평균선은 5일 또는 10일, 중기 이동평균선은 20일, 장기 이동평균선은 50일 또는 60일을 사용한다.
삼중이동평균선의 주요 특징은 다음과 같다:
- 단기 이동평균선이 중기와 장기 이동평균선을 상향 돌파할 때는 강한 상승 추세를, 하향 돌파할 때는 강한 하락 추세를 시사한다.
- 세 개의 이동평균선이 같은 방향으로 배열되어 있을 때는 추세의 강도가 강함을 나타낸다.
- 이동평균선들 사이의 간격은 추세의 강도와 관련이 있다. 간격이 벌어지면 추세가 강해지고 있음을 나타내며, 좁아지면 추세가 약해지고 있음을 시사한다.
삼중이동평균선을 활용한 매매 전략
삼중이동평균선을 활용한 매매 전략은 다음과 같다:
- 골든크로스 매매 전략: 단기 이동평균선이 중기와 장기 이동평균선을 상향 돌파(골든크로스)하면 매수하고, 하향 돌파(데드크로스)하면 매도하는 전략이다. 이는 강한 상승 또는 하락 추세에 대한 신호를 포착하여 매매하는 방식이다.
- 이동평균선 배열 전략: 세 개의 이동평균선이 같은 방향으로 배열되어 있을 때 추세를 따르는 매매를 한다. 예를 들어, 단기 > 중기 > 장기 순으로 배열되어 있을 때는 매수 포지션을 취하고, 역순으로 배열되어 있을 때는 매도 포지션을 취한다.
- 이동평균선 간격 전략: 이동평균선들 사이의 간격 변화를 관찰하여 매매 시점을 결정한다. 간격이 벌어질 때 매수하고, 간격이 좁아질 때 매도하는 방식이다. 이는 추세의 강도 변화에 주목하는 전략이다.
- 이동평균선과 다른 지표 조합 전략: 삼중이동평균선을 다른 기술적 지표(예: MACD, RSI 등)와 함께 사용하여 매매 신호의 신뢰성을 높일 수 있다.
삼중이동평균선은 강력한 추세 파악 도구이지만, 모든 시장 상황에 완벽하게 대응할 수는 없다. 특히 추세가 없는 박스권 시장에서는 효과가 제한적일 수 있다. 따라서 투자자는 시장 상황과 자신의 투자 스타일을 고려하여 삼중이동평균선을 활용해야 한다. 또한 실제 매매에서는 거래 비용과 시장 잡음 등의 요인도 함께 고려해야 한다.
이동평균선의 종류
이동평균선은 시계열 데이터의 추세를 파악하는데 널리 사용되는 기술적 분석 도구다. 주가, 거래량, 기타 지표 등 다양한 데이터에 적용될 수 있다. 이동평균선은 과거 일정 기간 동안의 데이터를 평균 내어 산출되며, 크게 단순이동평균, 가중이동평균, 지수이동평균, 그리고 변형된 형태의 이동평균선으로 나눌 수 있다.
단순이동평균(Simple Moving Average, SMA)
가장 기본적인 형태의 이동평균으로, 일정 기간 동안의 가격을 단순히 합산한 후 기간의 수로 나누어 계산한다. 예를 들어, 10일 단순이동평균은 최근 10일간의 종가를 합한 후 10으로 나누어 계산한다. 단순이동평균은 계산이 쉽고 이해하기 쉽다는 장점이 있지만, 모든 가격에 동일한 가중치를 부여하므로 최근 가격 변동을 즉각적으로 반영하지 못한다는 단점이 있다.
계산 방법: 일정 기간 동안의 가격을 모두 더한 후 기간의 수로 나눈다. 예시: 가격 데이터 [10, 12, 11, 13, 14]의 5일 단순 이동평균(SMA5)은 (10 + 12 + 11 + 13 + 14) / 5 = 12이다.
가중이동평균(Weighted Moving Average, WMA)
단순이동평균과 달리 최근 가격에 더 높은 가중치를 부여하여 계산하는 방식이다. 이는 최근 가격 변동을 더 잘 반영할 수 있다는 장점이 있다. 가중치는 일반적으로 선형적으로 할당되며, 최근 가격일수록 더 높은 가중치를 받는다.
계산 방법: 각 가격에 가중치를 부여하여 계산한다. 가중치는 최근 가격일수록 높게 부여된다. 예시: 가격 데이터 [10, 12, 11, 13, 14]의 5일 가중 이동평균(WMA5)은 (10 * 1 + 12 * 2 + 11 * 3 + 13 * 4 + 14 * 5) / (1 + 2 + 3 + 4 + 5) = 12.67이다.
지수이동평균(Exponential Moving Average, EMA)
가중이동평균의 일종으로, 지수함수를 사용하여 최근 가격에 더 높은 가중치를 부여한다. 지수이동평균은 최근 가격 변동에 더욱 민감하게 반응하며, 단순이동평균보다 추세 변화에 더 빠르게 적응한다. 지수이동평균을 계산할 때는 평활 상수(smoothing constant)를 사용하며, 이 값이 클수록 최근 가격에 더 높은 가중치를 부여한다.
계산 방법: 이전 EMA 값과 현재 가격을 가중 평균하여 계산한다. 가중치는 smoothing factor (α)에 의해 결정된다. 예시: 가격 데이터 [10, 12, 11, 13, 14]의 5일 지수 이동평균(EMA5)은 α = 2 / (5 + 1) = 0.3333일 때, 첫 번째 EMA5 값은 SMA5와 동일한 12이고, 두 번째 EMA5 값은 13 * 0.3333 + 12 * (1 – 0.3333) = 12.67이다.
삼각이동평균(Triangular Moving Average, TMA)
단순이동평균을 두 번 적용하여 계산하는 방식이다. 첫 번째 단순이동평균을 계산한 후, 그 결과에 다시 한 번 단순이동평균을 적용한다. 이는 단순이동평균의 지연 효과를 줄이고 스무딩 효과를 높이는 데 도움이 된다.
계산 방법: SMA를 두 번 적용하여 계산한다. 예시: 가격 데이터 [10, 12, 11, 13, 14]의 5일 삼각 이동평균(TMA5)은 첫 번째 SMA5가 12이고, 두 번째 SMA5는 (11 + 12 + 12 + 12 + 13) / 5 = 12이다.
가변이동평균(Variable Moving Average, VMA)
이동평균 기간을 고정하지 않고 시장 상황에 따라 동적으로 조정하는 방식이다. 변동성이 높은 시기에는 이동평균 기간을 짧게, 변동성이 낮은 시기에는 이동평균 기간을 길게 설정하여 시장 변화에 적응할 수 있다.
계산 방법: 가격 변동성에 따라 이동평균의 기간을 조정하여 계산한다. 예시: 변동성이 낮을 때는 [10, 12, 11, 13, 14]의 5일 VMA를 사용하여 12를 얻고, 변동성이 높을 때는 [11, 13, 14]의 3일 VMA를 사용하여 12.67을 얻는다.
Hull 이동평균(Hull Moving Average, HMA)
Alan Hull이 개발한 이동평균으로, 가중이동평균의 지연 효과를 최소화하기 위해 고안되었다. Hull 이동평균은 가중이동평균(WMA)을 사용하여 계산되며, 다음과 같은 단계로 계산된다: a. 단순이동평균 기간의 절반에 해당하는 가중이동평균(WMA)을 계산한다. b. 단순이동평균 기간에 해당하는 가중이동평균(WMA)을 계산한다. c. 첫 번째 가중이동평균의 2배에서 두 번째 가중이동평균을 뺀다.
계산 방법: WMA를 기반으로 두 번 계산하고, 제곱근 WMA를 적용한다. 예시: 가격 데이터 [10, 12, 11, 13, 14]의 5일 헐 이동평균(HMA5)은 WMA2.5가 (11 * 1 + 13 * 2) / (1 + 2) = 12.33, WMA5가 12.67이므로, HMA5는 2 * 12.33 – 12.67 = 12이다. 제곱근 WMA를 적용하면 (12 * 2 + 12.5 * 1) / (2 + 1) = 12.17이다.
Hull 이동평균은 가중이동평균보다 추세 변화에 더 빠르게 반응하며, 단순이동평균보다 스무딩 효과가 높다는 장점이 있다.
이 외에도 다양한 변형 이동평균선이 존재하며, 각각의 특성과 장단점이 있다. 투자자는 자신의 투자 전략과 시장 상황에 맞는 이동평균선을 선택하여 활용할 수 있다. 또한 여러 이동평균선을 조합하여 사용하거나, 다른 기술적 지표와 함께 분석하는 것도 효과적인 방법이 될 수 있다.
파이썬을 활용한 Hull 이동평균 백테스팅
이제 파이썬을 사용하여 헐 이동평균을 기반으로 한 트레이딩 전략을 백테스팅해보자. 다음 라이브러리를 활용할 것이다:
pandas
: 데이터 조작 및 분석numpy
: 수치 계산matplotlib
: 데이터 시각화FinanceDataReader
: 주가 데이터 수집
먼저 가상환경을 활성화하고 필요한 라이브러리를 설치한다.
source venv/bin/activate
pip install pandas numpy matplotlib FinanceDataReader
FinanceDataReader를 설치하면 종속 모듈도 함께 설치된다.
다음으로 헐 이동평균을 계산하고 매매 신호를 생성하는 코드를 작성해보자.
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)
# 헐 이동평균 계산 함수
def hull_moving_average(data, period):
wma1 = data['Close'].rolling(window=period//2).mean()
wma2 = data['Close'].rolling(window=period).mean()
hma = wma1 * 2 - wma2
hma = hma.rolling(window=int(np.sqrt(period))).mean()
return hma
# 매매 신호 생성 함수
def get_signals(data, short_period, medium_period, long_period):
signals = pd.DataFrame(index=data.index)
signals['short_hma'] = hull_moving_average(data, short_period)
signals['medium_hma'] = hull_moving_average(data, medium_period)
signals['long_hma'] = hull_moving_average(data, long_period)
signals['signal'] = 0
signals['signal'][1:] = np.where(
(signals['short_hma'][1:] > signals['medium_hma'][1:]) &
(signals['medium_hma'][1:] > signals['long_hma'][1:]), 1,
np.where(
(signals['short_hma'][1:] < signals['medium_hma'][1:]) &
(signals['medium_hma'][1:] < signals['long_hma'][1:]), -1, 0))
signals['positions'] = signals['signal'].diff()
return signals
# 매매 신호 생성
short_period = 5
medium_period = 20
long_period = 60
signals = get_signals(data, short_period, medium_period, long_period)
# 백테스팅
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) = plt.subplots(2, 1, figsize=(15, 10), gridspec_kw={'height_ratios': [3, 1]})
ax1.plot(data['Close'], label='Close Price')
ax1.plot(signals['short_hma'], label=f'Hull MA ({short_period})')
ax1.plot(signals['medium_hma'], label=f'Hull MA ({medium_period})')
ax1.plot(signals['long_hma'], label=f'Hull MA ({long_period})')
ax1.legend(loc='upper left')
ax1.set_title('Triple Hull Moving Average Strategy')
ax2.plot(portfolio['total'], label='Portfolio Value')
ax2.legend(loc='upper left')
ax2.set_title('Portfolio Performance')
plt.tight_layout()
plt.show()
위 코드에서는 FinanceDataReader를 사용하여 VOO ETF의 전체 기간 주가 데이터를 가져온다. start_date
를 가능한 한 오래전 날짜로 설정하여 모든 데이터를 가져올 수 있도록 한다.
hull_moving_average
함수는 헐 이동평균을 계산한다. 먼저 두 개의 가중 이동평균을 계산하고 그 차이를 구한 후, 제곱근 가중 이동평균을 적용하여 최종 헐 이동평균 값을 산출한다.
get_signals
함수는 단기, 중기, 장기 헐 이동평균을 기반으로 매매 신호를 생성한다. 단기 이동평균이 중기 이동평균을 상향 돌파하고 중기 이동평균이 장기 이동평균을 상향 돌파할 때 매수 신호(1)를, 그 반대의 경우 매도 신호(-1)를 생성한다.
백테스팅을 위해 초기 자본금을 10,000달러로 설정하고, 생성된 매매 신호를 바탕으로 포트폴리오 가치를 계산한다. 최종적으로 총 수익률과 포트폴리오 가치를 출력한다.
matplotlib
을 사용하여 주가, 헐 이동평균선, 포트폴리오 가치를 시각화한다.
코드 실행 중 발생할 수 있는 오류를 처리하기 위해 try-except
문을 사용하였다. 데이터를 가져오는 과정에서 오류가 발생하면 에러 메시지를 출력하고 프로그램을 종료한다.