거래량 주가확인지표(VPCI, Volume Price Confirmation Indicator)의 개념과 유래
거래량 주가확인지표(VPCI)는 버프 도르마이어(Buff Dormeier)가 개발한 기술적 지표로, 주가와 거래량 사이의 관계를 분석하여 추세의 강도를 측정한다. VPCI는 거래량 가중 이동평균(VWMA)과 VW-MACD(Volume-Weighted MACD)를 결합하여 계산된다.
VPCI의 기본 아이디어는 주가의 움직임과 거래량 사이에는 밀접한 관련이 있다는 것이다. 만약 주가가 상승하는데 거래량이 감소한다면, 이는 상승 추세가 약화되고 있음을 시사할 수 있다. 반대로 주가가 하락하는데 거래량이 증가한다면, 이는 하락 추세가 가속화될 수 있음을 나타낸다.
VPCI를 계산하기 위해서는 먼저 VWMA와 VW-MACD를 구해야 한다. VWMA는 거래량을 가중치로 사용하여 계산한 이동평균으로, 거래량이 많은 날의 가격에 더 큰 비중을 둔다. VW-MACD는 VWMA를 이용하여 계산한 MACD 지표이다.
VWMA는 거래량을 고려해서 가격의 평균을 구한다. 먼저 각 거래일의 종가에 그 날의 거래량을 곱한다. 그 다음 이 값들을 모두 더한다. 같은 기간 동안의 거래량도 모두 더한다. 그리고 나서 첫 번째 합계를 두 번째 합계로 나누면 거래량을 반영한 가격의 평균이 나온다.
VW-MACD는 거래량을 고려한 MACD다. 우선 단기와 장기 VWMA를 구한다. 보통 12일, 26일을 쓴다. 그 다음 장기 VWMA에서 단기 VWMA를 빼면 VW-MACD가 된다. 여기에 VW-MACD의 9일 지수이동평균을 구하면 이게 Signal Line이 된다.
VPCI는 VW-MACD의 변동폭을 일정 범위로 맞춘 값이다. 먼저 VW-MACD에서 Signal Line을 빼서 VW-MACD Histogram을 구한다. 그 다음 정해진 기간, 보통 63일 동안의 VW-MACD Histogram 최고값과 최저값을 찾는다. 그리고 VW-MACD Histogram을 이 최고값과 최저값의 차이로 나누고 100을 곱하면 VPCI가 나온다. 이렇게 하면 VPCI는 -100에서 100 사이의 값을 갖게 된다.
VPCI 스토캐스틱은 VPCI를 0과 100 사이로 변환한 값이다. 정해진 기간, 주로 14일 동안의 VPCI 최고값과 최저값을 구한다. 그리고 VPCI에서 최저값을 뺀 다음, 최고값과 최저값의 차이로 나눈다. 여기에 100을 곱하면 VPCI 스토캐스틱이 된다. 이렇게 하면 VPCI 스토캐스틱은 0에서 100 사이의 값을 갖는다.
요약하면 VWMA는 거래량을 고려한 가격 평균, VW-MACD는 거래량을 고려한 MACD, VPCI는 VW-MACD의 변동폭을 일정하게 만든 값, VPCI 스토캐스틱은 VPCI를 스토캐스틱 오실레이터처럼 0과 100 사이로 변환한 값이다.
VPCI 스토캐스틱을 활용한 매매 전략
VPCI 스토캐스틱은 VPCI에 스토캐스틱 오실레이터의 개념을 적용한 지표이다. 이는 VPCI의 상대적인 위치를 나타내며, 0에서 1 사이의 값을 갖는다.
VPCI 스토캐스틱을 활용한 매매 전략의 기본 규칙은 다음과 같다:
- VPCI 스토캐스틱이 특정 기준선(예: 0.8) 위로 상승하면 매수 신호로 해석한다.
- VPCI 스토캐스틱이 특정 기준선(예: 0.2) 아래로 하락하면 매도 신호로 해석한다.
- VPCI 스토캐스틱이 매수 기준선과 매도 기준선 사이에 있을 때는 관망한다.
또한 VPCI 스토캐스틱과 주가의 다이버전스도 중요한 신호가 될 수 있다. 예를 들어, 주가는 새로운 고점을 형성하는데 VPCI 스토캐스틱이 이를 확인하지 않는다면(Bearish Divergence), 이는 상승 추세의 약화를 시사할 수 있다.
VPCI 스토캐스틱 매매 전략의 파이썬 코드 예시
다음은 Yahoo Finance에서 VOO ETF의 데이터를 가져와 VPCI 스토캐스틱을 계산하고, 이를 기반으로 매매 신호를 생성하는 파이썬 코드 예시이다.
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# VOO ETF 데이터 가져오기 (최대 기간)
data = yf.download('VOO', start='2010-09-07')
# VWMA 계산 함수
def calc_vwma(data, window):
vwma = data.Close.copy()
for i in range(len(data)):
if i < window:
vwma[i] = np.nan
else:
vwma[i] = ((data.Close[i-window:i] * data.Volume[i-window:i]).sum() / data.Volume[i-window:i].sum())
return vwma
# VW-MACD 계산 함수
def calc_vw_macd(data, short_window, long_window, signal_window):
vwma_short = calc_vwma(data, short_window)
vwma_long = calc_vwma(data, long_window)
vw_macd = vwma_short - vwma_long
vw_macd_signal = vw_macd.ewm(span=signal_window).mean()
vw_macd_histogram = vw_macd - vw_macd_signal
return vw_macd, vw_macd_signal, vw_macd_histogram
# VPCI 계산 함수
def calc_vpci(vw_macd_histogram):
vpci = vw_macd_histogram / (vw_macd_histogram.rolling(window=63).max() - vw_macd_histogram.rolling(window=63).min())
return vpci
# VPCI 스토캐스틱 계산 함수
def calc_vpci_stochastic(vpci, window):
vpci_stochastic = (vpci - vpci.rolling(window=window).min()) / (vpci.rolling(window=window).max() - vpci.rolling(window=window).min())
return vpci_stochastic
# VPCI 스토캐스틱 기반 매매 신호 생성 함수
def generate_signals(data, vpci_stochastic):
signals = pd.DataFrame(index=data.index)
signals['Signal'] = 0
signals['VPCI_Stochastic'] = vpci_stochastic
signals['VPCI_Stochastic_MA'] = vpci_stochastic.rolling(window=3).mean() #VPCI 스토캐스틱의 단기 이동평균
signals['Signal'][1:] = np.where((signals['VPCI_Stochastic'][1:] > 0.8) & (signals['VPCI_Stochastic_MA'].shift(1)[1:] <= 0.8), 1, signals['Signal'][1:])
signals['Signal'][1:] = np.where((signals['VPCI_Stochastic'][1:] < 0.2) & (signals['VPCI_Stochastic_MA'].shift(1)[1:] >= 0.2), -1, signals['Signal'][1:])
signals['Signal'] = signals['Signal'].ffill().fillna(0) # 동일 신호 유지, 결측치는 무거래(0)
return signals
# 백테스팅 함수
def backtest(data, signals, initial_capital):
positions = pd.DataFrame(index=signals.index).fillna(0.0)
portfolio = pd.DataFrame(index=signals.index).fillna(0.0)
positions['VOO'] = signals['Signal']
portfolio['Positions'] = (positions.multiply(data['Adj Close'], axis=0))
portfolio['Cash'] = initial_capital - (positions.diff().multiply(data['Adj Close'], axis=0)).cumsum()
portfolio['Total'] = portfolio['Positions'] + portfolio['Cash']
portfolio['Returns'] = portfolio['Total'].pct_change()
return portfolio
# 매매 신호 생성
short_window = 9
long_window = 26
signal_window = 9
window = 14
vw_macd, vw_macd_signal, vw_macd_histogram = calc_vw_macd(data, short_window, long_window, signal_window)
vpci = calc_vpci(vw_macd_histogram)
vpci_stochastic = calc_vpci_stochastic(vpci, window)
signals = generate_signals(data, vpci_stochastic)
# 백테스팅
initial_capital = 10000
portfolio = backtest(data, signals, initial_capital)
# 결과 출력
print(f'초기 자본: ${initial_capital:,.0f}')
print(f'최종 자산: ${portfolio["Total"][-1]:,.0f}')
print(f'누적 수익률: {portfolio["Returns"].cumsum()[-1]:.2%}')
# 차트 그리기
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
axes[0].plot(data.Close)
axes[0].set_ylabel('Price')
axes[1].plot(vpci, label='VPCI')
axes[1].axhline(y=0, color='gray', linestyle='--')
axes[1].legend()
axes[2].plot(vpci_stochastic, label='VPCI Stochastic')
axes[2].plot(signals['VPCI_Stochastic_MA'], label='VPCI Stochastic MA')
axes[2].axhline(y=0.8, color='green', linestyle='--')
axes[2].axhline(y=0.2, color='red', linestyle='--')
axes[2].legend()
plt.show()
위 코드에서는 calc_vwma
함수를 사용하여 VWMA를 계산하고, calc_vw_macd
함수를 사용하여 VW-MACD를 계산한다. 그리고 calc_vpci
함수에서 VW-MACD의 Histogram 값을 정규화하여 VPCI를 계산하며, calc_vpci_stochastic
함수에서는 VPCI에 스토캐스틱 오실레이터의 개념을 적용하여 VPCI 스토캐스틱을 계산한다.
generate_signals
함수에서는 VPCI 스토캐스틱과 그 이동평균을 기반으로 매매 신호를 생성한다. 0.8 위로 상승하면 매수 신호(1), 0.2 아래로 하락하면 매도 신호(-1)로 처리한다.
backtest
함수에서는 생성된 매매 신호에 따라 매매를 시뮬레이션하고, 포트폴리오의 가치를 추적한다. 마지막으로 주가, VPCI, VPCI 스토캐스틱 차트를 그려 시각적으로 확인할 수 있다.
이 전략은 거래량과 주가의 관계를 활용하여 추세의 강도를 판단하고, 이에 기반하여 매매 신호를 생성한다는 점에서 흥미롭다. 하지만 VPCI 스토캐스틱의 최적 매매 기준선은 시장 상황에 따라 달라질 수 있으므로, 실제 트레이딩에 적용하기 위해서는 더 많은 테스트와 최적화가 필요할 것이다.