퀀트

23. 래리 윌리엄스의 변동성 돌파 전략

만 기 2023. 1. 13. 10:25

 

래리 윌리엄스 - 변동성 돌파 전략

Volatility Breakout Strategy

 

전략의 기본 원리

하루 동안의 단기 상승 추세에 탑승해서 이익을 실현한다.

시장 상황이 상승 추세인 상황에서, 현재 주가가 이미 오늘 시가에 어제 Range 보다 더 올라가고 있다면, 오늘 하루 상승을 예측함.

  • Range = (어제 고가 - 어제 저가)
  • 매수 : 현재가 > (오늘 시가 + Range * 0.5(=k))
    • k는 조절 가능
  • 매도 : 다음 날 시가

 

 

코스닥 지수에 백테스팅

# 코스닥 지수(KQ11) 전체 (데이터 시작 ~ 현재)
df = fdr.DataReader('KQ11')

# 변동성 돌파 전략 시뮬레이션
def volatility_break_out_strategy(df, k):
    # 함수 내의 변수 df로 복사
    df = df.copy()
    
    # 필요한 데이터 컬럼 추가
    df['변동폭'] = df['High'] - df['Low']
    df['목표가'] = df['Open'] + df['변동폭'].shift(1) * k
    df['다음날시가'] = df['Open'].shift(-1)

    # 맨앞과 맨뒤에 목표가가 Nan인 row 제거
    df = df.dropna()
	  
    # 조건
    cond = df['High'] > df['목표가']   # 특정 순간에 현재가가 목표가보다 높았던 순간이 있었다.
    
    # 조건에 맞는 날짜만 뽑기
    수익률 = df.loc[cond, '다음날시가'] / df.loc[cond, '목표가']
        
    # 슬리피지를 0.002로 고려 0.2%
    수익률 = 수익률 - 0.002
    누적수익률 = 수익률.cumprod()
    기간수익률 = 누적수익률.iloc[-1]
		
    # CAGR 계산 공식
    N = (df.index[-1] - df.index[0]).days / 365    # 투자 날짜
    CAGR = (기간수익률 ** (1/N)) - 1
    
    return CAGR, int(N), 누적수익률
  • df.shift(n) : n의 수 만큼 아래로 내려감. n이 음수일 경우 위로 올라감. 처음 밀린 자리는 NaN값이 들어간다.
    • df['변동폭'].shift(1) : 변동폭 컬럼의 값이 한칸 아래로 밀린 값. 전날의 변동폭 값이 필요하기 때문.
    • df['Open'].shift(-1) : Open 컬럼의 값이 한칸 위로 밀린 값. 다음날의 Open 값이 필요하기 때문
  • 슬리피지 : 주문이 체결될 것이라고 예상한 가격과 실행된 가격의 차이

 

 

상수 k값의 변화에 따른 투자 결과 변화

CAGR_dict = {}

for k in range(1, 21):
  CAGR, invest_year, 누적수익률 = volatility_break_out_strategy(df, k/10)
  CAGR_dict[f'{k/10}'] = CAGR
  print(f'range 가중치 : {k/10}, CAGR 수익률 : {CAGR}, 투자년도 : {invest_year}년')

# 딕셔너리 내림차순 정렬
sorted(CAGR_dict.items(), key=lambda item: item[1], reverse=True)

무조건 CAGR이 높다고 쓰면 과적합이 날 수 있다.

 

 

수익률 그래프

CAGR, invest_year, 누적수익률 = volatility_break_out_strategy(df, 0.5)
print(f'CAGR 수익률 : {CAGR}, 투자년도 : {invest_year}년')

# CAGR 수익률 : 0.22526677329310751, 투자년도 : 22년

print(누적수익률)
# Date
# 2001-01-03      1.073477
# 2001-01-05      1.061213
# 2001-01-08      1.112404
# 2001-01-10      1.038447
# 2001-01-12      1.066388
#                  ...    
# 2021-08-27    173.282374
# 2021-08-31    173.162288
# 2021-09-01    173.491811
# 2021-09-03    173.733170
# 2021-09-14    173.928866
# Length: 1563, dtype: float64

# 그래프
fig = 누적수익률.plot(figsize=(20,10), title=f'변동성 돌파전략 (k=0.5) - 코스닥 지수 - 백테스팅 결과 ({누적수익률.index[0].strftime("%Y-%m-%d")}-{누적수익률.index[-1].strftime("%Y-%m-%d")}) - 누적수익률 {(누적수익률.iloc[-1]-1)*100:.2f}%', fontsize=10)
fig.axes.title.set_size(30)
  • .strftime() : 날짜 형식 변환 메서드
  • %d : 0을 채운 10진수 표기로 날짜를 표시
  • %m : 0을 채운 10진수 표기로 월을 표시
  • %y : 0을 채운 10진수 표기로 2자리 년도
  • %Y : 0을 채운 10진수 표기로 4자리 년도
  • %H : 0을 채운 10진수 표기로 시간 (24시간 표기)
  • %I : 0을 채운 10진수 표기로 시간 (12시간 표기)
  • %M : 0을 채운 10진수 표기로 분
  • 등등