크립토

[백테스트] 변동성 돌파 전략 백테스트(2) by 파이썬

코대장 2021. 9. 7. 21:44
반응형

[백테스트] 변동성 돌파 전략 백테스트(2) by 파이썬

 

지난 글에서는 강(갓 또는 킹)환국님 영상에 나온 백테스트 를 찍!쩌업! 파이썬으로 구현해보는 첫번째 시간이었는데요. 백테스트를 위한 데이터를 수집하고 데이터를 가공(영상에 나온 조건과 동일하게, 기간 및 OHLCV 맞춤) 해주었죠.

 

이번 글에서는 직!쩌업 파이썬을 통한 로직을 만들어 가 보겠습니다. 들어가기에 앞서, 엑셀을 키고 끌 수 있는 수준의 사용자라면 엑셀로 하는것이 정신 건강에 이로우실 꺼에요. 다만, 파이썬을 사랑하고 코딩을 사랑하는 분들이라면 함께 들어가 보시죠. 고고!

 

1. 백테스트 계산 조건

- 1일레인지 : 전날High - 전날Low
- 1일레인지(%) : 1일레인지 / 오늘시가
- 매수여부 : 오늘고가 > 오늘시가 + k_factor * 1일레인지 => BUY
- 상승장여부 : 오늘시가 > 전 3일치 종가의 평균 => UP
- 매수가 : 오늘시가 + k_factor * 1일레인지
- 매도가 : 다음날시가
- 투입비중(%) = 변동성조절팩터 / 1일레인지(%), Max 50%
- 조정수익률

k_factor = 0.5
f_factor = 2 # 변동성조절팩터
fee = 0.2 / 100 # 거래비용
# 조정수익률(%) 구하는 함수
def benefit(x):
    try:
        benefit = x['투입비중(%)']*(((x['매도가']*(1-fee))/(x['매수가']*(1+fee)))-1)
        return benefit
    except:
        pass
df['1일수익'] = (df['close']/df['open']-1)*100
df['1일레인지'] = df['high'].shift(1) - df['low'].shift(1) #전날 High - Low
df['1일레인지(%)'] = (df['1일레인지'] / df['open'])*100
df['매수여부'] = df.apply(lambda x: "BUY" if x['high'] > x['open'] + k_factor*x['1일레인지'] else "", axis=1 )
df['상승장여부'] = df['open'] > df['close'].shift(1).rolling(window=3).mean()
df['상승장여부'] = df.apply(lambda x: "UP" if x['상승장여부'] else "DOWN", axis=1 )
df['매수가'] = df.apply(lambda x: x['open'] + k_factor*x['1일레인지'] if x['매수여부']=="BUY" and x['상승장여부']=="UP" else "", axis=1)
df['매도'] = df.apply(lambda x: "매도" if x['매수여부']=="BUY" and x['상승장여부']=="UP" else "", axis=1)
df['매도가'] = df['open'].shift(-1)
df['매도가'] = df.apply(lambda x: x['매도가'] if x['매도']=="매도" else "", axis=1)
df.drop(['매도'], axis=1, inplace=True)
df['투입비중(%)'] = df.apply(lambda x: (f_factor/x['1일레인지(%)'])*100 if x['1일레인지(%)'] > 0 and (f_factor/x['1일레인지(%)'])*100 < 50 else 0 if pd.isna(x['1일레인지(%)']) else 50, axis=1)
df['조정수익률(%)'] = df.apply(lambda x: benefit(x), axis= 1).fillna(0)
df['자산'] = 1
df['최대자산'] = 1
df.head()

asset = None
max_asset = None
for index, value in df[['조정수익률(%)','자산','최대자산']].iterrows():
    if asset is None and max_asset is None:
        asset = value['자산']
        max_asset = value['최대자산']
        
    try:
        # 자산
        if not np.isnan(value['조정수익률(%)']):
            asset = asset*(1+value['조정수익률(%)']/100)
            df.loc[index, '자산'] = asset
        
        # 최대자산
        # print(index, max_asset, asset)
        if max_asset < asset:
            df.loc[index, '최대자산'] = asset 
    except Exception as e:
        print(e)
        pass
# 손실(DropDown)
df['손실(%)'] = df.apply(lambda x: ((x['자산']/x['최대자산'])-1)*100 if x['자산'] < x['최대자산'] else 0, axis=1)
df['투자사례'] = df.apply(lambda x: "승" if x['조정수익률(%)'] > 0 else ("패" if x['조정수익률(%)'] < 0 else '-'), axis=1)
df.head()

 

2. 투자분석

  • 수익 및 MDD
print('수익 :{:.2f}%'.format(float(df['자산'].iloc[-1] - 1)*100)) ## 수익 :-3.28%
print('MDD :{:.2f}%'.format(float(df['손실(%)'].min())))         ## MDD :-10.99%
  • 승률분석
# 투자사례
df_winlose = df['투자사례'].value_counts().reset_index()
cnt_win = int(df_winlose.loc[df_winlose['index']=='승']['투자사례'])
cnt_lose = int(df_winlose.loc[df_winlose['index']=='패']['투자사례'])
print('승: {}'.format(cnt_win))   ## 승: 39
print('패: {}'.format(cnt_lose))  ## 패: 59
print('승률: {:.2f}%'.format(100*(cnt_win/(cnt_win+cnt_lose))))  ##승률: 39.80%
win = float(df.loc[df['조정수익률(%)']>0, '조정수익률(%)'].mean())
lose = float(df.loc[df['조정수익률(%)']<0, '조정수익률(%)'].mean())
print('승 : {:>.2f}%'.format(win))  ## 승 : 0.94%
print('패 : {:>.2f}%'.format(lose)) ## 패 : -0.67%
print('손익비: {:>.2f}%'.format(win/(-lose))) ## 손익비: 1.41%
  • 하루 X% 이상 깨진 사례
for i in range(1, 6):
    print('하루 {}% 이상 깨진 사례 : {}건'.format(i, df.loc[df['조정수익률(%)']<-i, '조정수익률(%)'].count()))
    
## 하루 1% 이상 깨진 사례 : 12건
## 하루 2% 이상 깨진 사례 : 4건
## 하루 3% 이상 깨진 사례 : 0건
## 하루 4% 이상 깨진 사례 : 0건
## 하루 5% 이상 깨진 사례 : 0건