본문 바로가기
프로그래밍 공부/파이썬 구현

[변동성 돌파 전략 2] 변동성 돌파 전략 백테스트 k 최적화 (그리드 서치)

by 세상만사 네상만사 2022. 1. 24.
반응형

지난 포스팅에 이어 변동성 돌파전략의 변수를 최적화하여 결과값을 도출해보고자 한다.

 

최적화 대상은 target값을 도출하는데 쓰이는 k값이다. 

 

여기서는 먼저 grid search 방법을 활용해서 변수를 최적화해보았다. sklearn과 같은 전문적인 모듈을 사용하지 않고 그냥 간단한 함수를 활용하여 최적화해보았다.

 


import pandas as pd
import numpy as np
import random as rand
from bayes_opt import BayesianOptimization
import matplotlib.pyplot as plt

################ 백테스트 ################

# 백테스트 기본 셋팅

symbol = "BTCUSDT" # 다른 티커도 가능
interval = '1d' # 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
year = 'All' # 2017~2022 중 선택 가능.

# 데이터 읽어오기: 전체 데이터
df = pd.read_csv (f'C:.\\Data\\{symbol[:-4]}-USDT_Data\\{symbol[:-4]}-USDT_Data_{interval}\\{symbol[:-4].upper()}_USDT_{interval}_{year}.csv')
# 전체: _All
# 기간: 연도 불러오면 됨
df = pd.DataFrame(df)
df = df.dropna(how='any')

 

먼저 데이터를 불러오고, 필요한 module을 import한다.

 


def VBS(df, config_data):
    # k값 넣기
    k = config_data['k']

    # k = results[0][0]
    # 목표가 구하기
    df['range'] = df['High'] - df['Low']  # 고저 변동폭
    df['target'] = df['Open'] + df['range'].shift(1) * k  # 목표가 한칸 내려주고 (shift), 이후 k값을 곱한 목표가 계산
    # df = df.iloc[1:-2]

    # 매수 시뮬레이션
    df['ror'] = np.where(df['High'] > df['target'], df['Close'] / df['target'],
                         1)  # high > target인 지점에서 close / target

    # 최종 누적 산출
    df['total'] = df['ror'].cumprod()
    final_cum_ror = (df['total'].iloc[-1].astype(float)) * 100 -100

    # 연간 수익률 (기간 수익률)
    N = ((df.index[-1] - df.index[0])) / 365
    CAGR = (final_cum_ror ** (1 / N))

    # dd값 기록 및 mdd 계산
    array_v = np.array(df['total'])
    dd_list = -(np.maximum.accumulate(array_v) - array_v) / np.maximum.accumulate(array_v)
    peak_lower = np.argmax(np.maximum.accumulate(array_v) - array_v)
        # np.maximum.accumulate(array_v)는 최고값을 계속 갱신한 array
        # 그 array에서 원래 array를 뺀 값들의 array를 새로 생성
        # 뺀 값만 남은 array 중 가장 큰 값의 index를 추출
    peak_upper = np.argmax(array_v[:peak_lower])
        # peak_lower의 index값까지의 array_v값 추출
        # 그 중에 가장 큰 놈 (MDD 계산을 위한 최고값) 의 index 추출
    mdd = round((array_v[peak_lower] - array_v[peak_upper]) / array_v[peak_upper] * 100, 3)

    return final_cum_ror, CAGR, mdd, dd_list,df['total'] # 최종 수익률, 연간 수익률, 최대손실폭 (mdd), 손실 목록, 누적 이익 목록

 

최적화를 위하여 k = config_data['k]로 설정한다.

 


이후 그리드 서치를 진행하고 이에 대한 결과값을 도출한다. k값의 범위는 0.3 ~ 1.21 로 잡았다.

 

### 그리드 서치 통한 최적화

def random_select():
    config_data = {
        'k':round(np.random.uniform(0.3,1.21),3)
    }
    return config_data

# 랜덤하게 추출한 값으로 백테스트 돌리고 결과 추출

results = []

# 1000번 랜덤하게 넣기
for i in range(1000):
    config_data = random_select()
    revenue_total = VBS(df,config_data)[0]
    print("config:{}, result:{}".format(config_data, revenue_total))
    result = []
    result.append(config_data)
    result.append(revenue_total)
    results.append(result)

# 랜덤 추출 결과 중 가장 나은 것들 순으로 정렬
results.sort(key=lambda x:x[1], reverse=True) #2차원 배열 정렬하기

# 결과 도출
max_k = results[0][0]
a = VBS(df,max_k)
print("최종 누적 수익률: ", a[0] ,"%") # 최종 누적 수익률 도출
print("MDD: ", a[2], "%") # mdd 도출
print("최선의 K값: ",max_k)

 

그리드 서치를 진행하면서, 해당 시도별로 k값과 result 값을 출력하도록 하였다. 

이를 출력해보면 다음과 같다.

 

 

바이낸스 1일봉 BTC/USDT 기준, 수익률이 가장 높았던 최적의 k값은 0.552 였으며, 이때의 Mdd는 30.131%였다. 

 

 

다음 포스팅에서는 베이지안 최적화를 통해 최적의 결과값을 찾아보고자 한다.

 

 


전체 코드

 

import pandas as pd
import numpy as np
import random as rand
from bayes_opt import BayesianOptimization
import matplotlib.pyplot as plt

################ 백테스트 ################

# 백테스트 기본 셋팅

symbol = "BTCUSDT" # 다른 티커도 가능
interval = '1d' # 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M
year = 'All' # 2017~2022 중 선택 가능.

# 데이터 읽어오기: 전체 데이터
df = pd.read_csv (f'C:.\\Data\\{symbol[:-4]}-USDT_Data\\{symbol[:-4]}-USDT_Data_{interval}\\{symbol[:-4].upper()}_USDT_{interval}_{year}.csv')
# 전체: _All
# 기간: 연도 불러오면 됨
df = pd.DataFrame(df)
df = df.dropna(how='any')

# 변동성 돌파 전략 위한 로직 설정

def VBS(df, config_data):
    # k값 넣기
    k = config_data['k']

    # k = results[0][0]
    # 목표가 구하기
    df['range'] = df['High'] - df['Low']  # 고저 변동폭
    df['target'] = df['Open'] + df['range'].shift(1) * k  # 목표가 한칸 내려주고 (shift), 이후 k값을 곱한 목표가 계산
    # df = df.iloc[1:-2]

    # 매수 시뮬레이션
    df['ror'] = np.where(df['High'] > df['target'], df['Close'] / df['target'],
                         1)  # high > target인 지점에서 close / target

    # 최종 누적 산출
    df['total'] = df['ror'].cumprod()
    final_cum_ror = (df['total'].iloc[-1].astype(float)) * 100 -100

    # 연간 수익률 (기간 수익률)
    N = ((df.index[-1] - df.index[0])) / 365
    CAGR = (final_cum_ror ** (1 / N))

    # dd값 기록 및 mdd 계산
    array_v = np.array(df['total'])
    dd_list = -(np.maximum.accumulate(array_v) - array_v) / np.maximum.accumulate(array_v)
    peak_lower = np.argmax(np.maximum.accumulate(array_v) - array_v)
        # np.maximum.accumulate(array_v)는 최고값을 계속 갱신한 array
        # 그 array에서 원래 array를 뺀 값들의 array를 새로 생성
        # 뺀 값만 남은 array 중 가장 큰 값의 index를 추출
    peak_upper = np.argmax(array_v[:peak_lower])
        # peak_lower의 index값까지의 array_v값 추출
        # 그 중에 가장 큰 놈 (MDD 계산을 위한 최고값) 의 index 추출
    mdd = round((array_v[peak_lower] - array_v[peak_upper]) / array_v[peak_upper] * 100, 3)

    return final_cum_ror, CAGR, mdd, dd_list,df['total'] # 최종 수익률, 연간 수익률, 최대손실폭 (mdd), 손실 목록, 누적 이익 목록

################ 최적화 ################

### 그리드 서치 통한 최적화

def random_select():
    config_data = {
        'k':round(np.random.uniform(0.3,1.21),3)
    }
    return config_data

# 랜덤하게 추출한 값으로 백테스트 돌리고 결과 추출

results = []

# 1000번 랜덤하게 넣기
for i in range(1000):
    config_data = random_select()
    revenue_total = VBS(df,config_data)[0]
    print("config:{}, result:{}".format(config_data, revenue_total))
    result = []
    result.append(config_data)
    result.append(revenue_total)
    results.append(result)

# 랜덤 추출 결과 중 가장 나은 것들 순으로 정렬
results.sort(key=lambda x:x[1], reverse=True) #2차원 배열 정렬하기

# 결과 도출
max_k = results[0][0]
a = VBS(df,max_k)
print("최종 누적 수익률: ", a[0] ,"%") # 최종 누적 수익률 도출
print("MDD: ", a[2], "%") # mdd 도출
print("최선의 K값: ",max_k)
반응형

댓글