일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 파이썬
- backtest
- TimeSeries
- 프로그래머스
- 백테스트
- docker
- 볼린저밴드
- hackerrank
- 코딩테스트
- lstm
- 빅데이터분석기사
- 파트5
- 데이터분석전문가
- 파이썬 주식
- 비트코인
- 토익스피킹
- GridSearchCV
- sarima
- PolynomialFeatures
- Quant
- Programmers
- SQL
- 데이터분석
- 변동성돌파전략
- Python
- randomforest
- 실기
- ADP
- 주식
- Crawling
- Today
- Total
데이터 공부를 기록하는 공간
볼린저밴드-추세추종-문제점 본문
백테스트에서 슬리피지와 수수료를 고려하지 않았었다.
수수료 0.05%는 그리 크지 않을 것이라 생각했다.
10만원 기준, 1회 매수,매도 세트로 수행시 0.1%로 100원의 수수료가 들어간다.
1000만원 기준, 1회 매수, 매도 세트로 수행 시 0.1%로 1만원의 수수료가 들어간다.
슬리피지는 내가 백테스트를 했을 때 원하는 가격과 실제로 내가 매수/매도하는 가격의 차이인데 이것이 꽤나 크다.
지금부터 보인다.
다음은 백테스트를 위한 함수
# window_b : 볼린저밴드 윈도우
# window_f : 현금흐름지표 윈도우
# buy_pb : 매수조건 퍼센트볼린저 하한
# buy_mfi : 매수조건 현금흐름지표 하한
# sell_pb : 매도조건 퍼센트볼린저 상한
# sell_mfi : 매도조건 현금흐름지표 상한
# 0 < pb < 1
# 0 < mfi < 100
def 볼린저밴드_추세추종_찾기2_반전2(data=data, window_b = 20, window_f=10, buy_pb = 0.8, buy_mfi = 80, sell_pb = 0.2, sell_mfi=20, chart=False):
df = data.copy()
df['MA20'] = df['close'].rolling(window=window_b).mean()
df['stddev'] = df['close'].rolling(window=window_b).std()
df['upper'] = df['MA20'] + df['stddev']*2
df['lower'] = df['MA20'] - df['stddev']*2
### PB : %b (종가 - 하단밴드) / (상단밴드 - 하단밴드) ###
df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
### 밴드폭 (상단밴드 - 하단밴드) / 중간밴드
df['bandwidth'] = (df['upper'] - df['lower']) / df['MA20'] * 100
### 중심가격
df['TP'] = (df['high'] + df['low'] + df['close']) / 3
### 긍정적 현금 흐름 : 중심가격이 전일보다 상승한 날들의 현금 흐름의 합
df['PMF'] = 0
### 부정적 현금 흐름 : 중심가격이 전일보다 하락한 날들의 현금 흐름의 합
df['NMF'] = 0
for i in range(len(df.close)-1):
if df.TP.values[i] < df.TP.values[i+1]: # TP값이 전날보다 증가한다면,
df.PMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
df.NMF.values[i+1] = 0
else:
df.PMF.values[i+1] = 0
df.NMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
### 현금 흐름 비율 : Money Flow Ratio
df['MFR'] = df.PMF.rolling(window=window_f).sum() / df.NMF.rolling(window=window_f).sum()
### 현금 흐름 지표 : Money Flow Index = 100 - ( 100 ÷ (1+MFR) )
df['MFI'] = 100 - 100 / (1+df['MFR'])
""" return 변경 """
df['return'] = np.log(df['close'].shift(-1)/df['close']) # 로그수익률
""" """"""""""""" """
## df.PB.values[i] > 0.8 and df.MFI.values[i] > 80:
cond_buy = (df['PB'] < buy_pb) & (df['MFI'] > buy_mfi)
df.loc[cond_buy, 'position'] = 1
## df.PB.values[i] < 0.2 and df.MFI.values[i] < 20:
cond_sell = (df['PB'] > sell_pb) & (df['MFI'] < sell_mfi)
df.loc[cond_sell, 'position'] = 0
df['position'] = df['position'].fillna(method = "ffill")
df['strategy'] = df['position'] * df['return']
df['cumret'] = df['strategy'].cumsum().apply(np.exp)
df['cummax'] = df['cumret'].cummax()
df = df[max(window_b, window_f):]
drawdown = df['cummax'] - df['cumret']
mdd = drawdown.max()
result = df[['return','strategy']].sum().apply(np.exp)
strategy = result['strategy']
print("window_b = ",window_b, "window_f = " , window_f, "buy_pb = ", buy_pb, "buy_mfi = ", buy_mfi, "sell_pb = ", sell_pb, "sell_mfi = ", sell_mfi)
# window_b : 볼린저밴드 윈도우
# window_f : 현금흐름지표 윈도우
# buy_pb : 매수조건 퍼센트볼린저 하한
# buy_mfi : 매수조건 현금흐름지표 하한
# sell_pb : 매도조건 퍼센트볼린저 상한
# sell_mfi : 매도조건 현금흐름지표 상한
# 0 < pb < 1
# 0 < mfi < 100
def 볼린저밴드_추세추종2_반전2(data=data, window_b = 20, window_f=10, buy_pb = 0.8, buy_mfi = 80, sell_pb = 0.2, sell_mfi=20, chart=False):
df = data.copy()
df['MA20'] = df['close'].rolling(window=window_b).mean()
df['stddev'] = df['close'].rolling(window=window_b).std()
df['upper'] = df['MA20'] + df['stddev']*2
df['lower'] = df['MA20'] - df['stddev']*2
### PB : %b (종가 - 하단밴드) / (상단밴드 - 하단밴드) ###
df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
### 밴드폭 (상단밴드 - 하단밴드) / 중간밴드
df['bandwidth'] = (df['upper'] - df['lower']) / df['MA20'] * 100
### 중심가격
df['TP'] = (df['high'] + df['low'] + df['close']) / 3
### 긍정적 현금 흐름 : 중심가격이 전일보다 상승한 날들의 현금 흐름의 합
df['PMF'] = 0
### 부정적 현금 흐름 : 중심가격이 전일보다 하락한 날들의 현금 흐름의 합
df['NMF'] = 0
for i in range(len(df.close)-1):
if df.TP.values[i] < df.TP.values[i+1]: # TP값이 전날보다 증가한다면,
df.PMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
df.NMF.values[i+1] = 0
else:
df.PMF.values[i+1] = 0
df.NMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
### 현금 흐름 비율 : Money Flow Ratio
df['MFR'] = df.PMF.rolling(window=window_f).sum() / df.NMF.rolling(window=window_f).sum()
### 현금 흐름 지표 : Money Flow Index = 100 - ( 100 ÷ (1+MFR) )
df['MFI'] = 100 - 100 / (1+df['MFR'])
#df['return'] = np.log(df['close']/df['close'].shift(1)) # 로그수익률
df['return'] = np.log(df['close'].shift(-1)/df['close']) # 로그수익률
## df.PB.values[i] > 0.8 and df.MFI.values[i] > 80:
cond_buy = (df['PB'] < buy_pb) & (df['MFI'] > buy_mfi)
df.loc[cond_buy, 'position'] = 1
## df.PB.values[i] < 0.2 and df.MFI.values[i] < 20:
cond_sell = (df['PB'] > sell_pb) & (df['MFI'] < sell_mfi)
df.loc[cond_sell, 'position'] = 0
df['position'] = df['position'].fillna(method = "ffill")
df['strategy'] = df['position'] * df['return']
df['cumret'] = df['strategy'].cumsum().apply(np.exp)
df['cummax'] = df['cumret'].cummax()
df = df[max(window_b, window_f):]
drawdown = df['cummax'] - df['cumret']
mdd = drawdown.max()
result = df[['return','strategy']].sum().apply(np.exp)
print("backtest results : ")
print(data.index[0] , " ~ ", data.index[-1])
print("buy : sell = " + str(df.loc[cond_buy].shape[0]) + " : " + str(df.loc[cond_sell].shape[0]))
print("return : ", np.round(result['return']*100,1),"%")
print("strategy : ", np.round(result['strategy']*100,1),"%")
print("mdd : △", np.round(mdd*100,0),"%")
if chart == True:
fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, figsize=(20,8))
ax1.plot(df.index, df['close'], color='#0000ff', label='close')
ax1.plot(df.index, df['upper'], 'r--', label='upper band')
ax1.plot(df.index, df['lower'], 'c--', label='lower band')
ax1.plot(df.index, df['MA20'], 'k--', label=f'MA{window_b}')
ax1.fill_between(df.index, df['upper'], df['lower'], color='0.9')
for i in range(len(df.close)):
if df.PB.values[i] > buy_pb and df.MFI.values[i] > buy_mfi:
ax1.plot(df.index.values[i], df.close.values[i], 'r^')
elif df.PB.values[i] < sell_pb and df.MFI.values[i] < sell_mfi:
ax1.plot(df.index.values[i], df.close.values[i], 'bv')
ax1.legend(loc='best')
ax2.plot(df.index, df['PB']*100, 'b', label= '%B x 100')
ax2.plot(df.index, df['MFI'], 'g--', label=f'MFI({window_f} day)')
ax2.set_yticks([-20, 0, 20, 40, 60, 80, 100, 120])
for i in range(len(df.close)):
if df.PB.values[i] < buy_pb and df.MFI.values[i] > buy_mfi:
ax2.plot(df.index.values[i], 0, 'r^')
elif df.PB.values[i] > sell_pb and df.MFI.values[i] < sell_mfi:
ax2.plot(df.index.values[i], 0, 'gv')
#ax2.grid(True)
ax2.legend(loc='best')
df[['return','strategy']].cumsum().apply(np.exp).plot(ax=ax3)
최적의 파라미터를 찾아보기 >>
results = pd.DataFrame(columns = ['window_b', 'window_f','buy_pb','buy_mfi','sell_pb','sell_mfi','strategy', 'mdd'])
#window_b = np.arange(13,26,6)
window_b = [13,19,25]
#window_f = np.arange(3,16,3)
window_f = [3,6,9]
#buy_mfi = np.arange(1, 32, 10)
buy_pb = [0.2, 0.4, 0.6]
sell_pb = [0.65, 0.75, 0.85]
buy_mfi = [60,80]
sell_mfi = [30,50]
i=0
for b in window_b:
for f in window_f:
for b_mfi in buy_mfi:
for s_mfi in sell_mfi:
for b_pb in buy_pb:
for s_pb in sell_pb:
try:
strategy, mdd = 볼린저밴드_추세추종_찾기2_반전2(data=data, window_b = b, window_f = f, buy_pb = b_pb, buy_mfi = b_mfi, sell_pb = s_pb, sell_mfi=s_mfi)
results.loc[i, 'window_b'] = b
results.loc[i, 'window_f'] = f
results.loc[i, 'buy_pb'] = b_pb
results.loc[i, 'buy_mfi'] = b_mfi
results.loc[i, 'sell_pb'] = s_pb
results.loc[i, 'sell_mfi'] = s_mfi
results.loc[i, 'strategy'] = strategy
results.loc[i, 'mdd'] = mdd
i+=1
except:
print('error')
pass
window_b = 25, window_f=3, buy_pb=0.6, buy_mfi=60, sell_pb=0.65, sell_mfi=50 일 때,
백테스트를 했을 때는
1달간, 11% 최종수익 수익, MDD 12% 였었음
좋은 시나리오는 아니었으나, 백테스트와 실전과 차이를 알아보기 위해 이 알고리즘으로 테스트
○ 실전 과 백테스트 간 차이
도지코인
2022.01.15 00 ~ 24시 약 24시간
매수 시에는 보통 백테스트를 하는 종가보다 비싼가격으로 매수
매도 시에는 보통 백테스트를 하는 종가보다 저렴한 가격으로 매도
> 200원의 도지코인에서 1원의 오차가 발생하는 것은 0.5% 수수료 발생하는 것과 같다.
매수 매도시 모두 발생한다면 1%의 오차가 발생하는 것이다.
> 백테스트와 비교하여 거래마다 슬리피지와 수수료가 1.1%씩 발생하므로, 이를 극복하기 위해서는
1.1% 이상의 차이가 나는 거래만을 수행해야 한다.
(해결책?)
- 백테스트에서 수수료를 넣고 본다.
- 완벽하게 수익률이 나는 거래만을 찾아본다.
- 시장가 매수/매도가 아닌 지정가 매수/매도를 한다 ?
- 익절, 손절 조건을 정해놓는다?
'STOCK > 비트코인' 카테고리의 다른 글
plotly 캔들스틱, 저항선 그리기 (0) | 2022.01.16 |
---|---|
볼린저밴드-찾기 (0) | 2022.01.16 |
볼린저밴드-추세추종-return 계산하기(백테스트 문제점) (0) | 2022.01.11 |
볼린저밴드-추세추종 (실전 테스트) (0) | 2022.01.11 |
볼린저밴드-추세추종 (0) | 2022.01.10 |