볼린저밴드-추세추종 (실전 테스트)
테스트시간 : 약 15시간
비트코인 5분봉 데이터 활용
볼린저 추세추종 실적
시작금액 : 100,000 원
최종금액 : 98,968 원
손실 : △1,032원
매수-매도 횟수 : 14번
수수료 : 약 100원 * 14 = 1400원
슬리피지 : 계산 따로 안함
▶ 역시나 수수료를 극복하기 어렵다.
### 볼린저밴드 추세추종
### 5분
import time
import pyupbit
import datetime
import numpy as np
import pandas as pd
access = XXXXXXXX
secret = YYYYYYYY
def get_balance(ticker):
"""잔고 조회"""
balances = upbit.get_balances()
for b in balances:
if b['currency'] == ticker:
if b['balance'] is not None:
return float(b['balance'])
else:
return 0
def get_current_price(ticker):
"""현재가 조회"""
return pyupbit.get_orderbook(ticker=ticker)["orderbook_units"][0]["ask_price"]
PB = 0
MFI = 0
position = np.NaN
def 추세추종(window_b = 6, window_f = 3, buy_pb = 0.8, buy_mfi = 80, sell_pb = 0.2, sell_mfi=20, chart=False):
df = pyupbit.get_ohlcv("KRW-BTC", interval = "minute5", count=12*max(window_b, window_f))
df[f'MA{window_b}'] = df['close'].rolling(window=window_b).mean()
df['stddev'] = df['close'].rolling(window=window_b).std()
df['upper'] = df[f'MA{window_b}'] + df['stddev']*2
df['lower'] = df[f'MA{window_b}'] - df['stddev']*2
df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
df['bandwidth'] = (df['upper'] - df['lower']) / df[f'MA{window_b}'] * 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]
df['MFR'] = df.PMF.rolling(window=window_f).sum() / df.NMF.rolling(window=window_f).sum()
df['MFI'] = 100 - 100 / (1+df['MFR'])
cond_buy = (df['PB'] > buy_pb) & (df['MFI'] > buy_mfi)
df.loc[cond_buy, 'position'] = 1
cond_sell = (df['PB'] < sell_pb) & (df['MFI'] < sell_mfi)
df.loc[cond_sell, 'position'] = 0
df['position'] = df['position'].fillna(method = "ffill")
PB = df.PB.values[-1]
MFI = df.MFI.values[-1]
position = df.position.values[-1]
return PB, MFI, position
# 로그인, 객채 생성
upbit = pyupbit.Upbit(access, secret)
print("autotrade start")
# 변수 설정
op_mode = False # operation mode 시작은 False로
hold = False # 현재 코인 보유 여부
# 자동매매 시작
while True:
try:
now = datetime.datetime.now()
current_price = get_current_price("KRW-BTC")
# 5분단위부터 operation 가능하도록 설정
if now.minute%5==0 and now.second<=59:
op_mode=True
# 매 1분 0 ~ 10초 마다
if op_mode is True and now.minute % 1 == 0 and now.second <=10 :
PB, MFI, position = 추세추종()
# 매수
if hold is False and position == 1:
krw = get_balance("KRW")
if krw > 5000:
upbit.buy_market_order("KRW-BTC", krw*0.9995)
hold = True
# 매도
elif hold is True and position == 0:
btc = get_balance("BTC")
if btc > 0.00008:
upbit.sell_market_order("KRW-BTC", btc*0.9995)
hold = False
# 출력
print(f"현재시간: {now}, PB: {PB}, MFI: {MFI}, position: {position}, 현재가: {current_price}, 보유상태: {hold}, 동작여부: {op_mode}")
time.sleep(10)
except Exception as e:
print(f"현재시간: {now}, {e}")
time.sleep(10)
○ 작동화면
02시 52분 6초, PB 0.8 MFI 100 으로 50610000원에 매수
4시 55분 6초 PB 0.18 MFI 0으로 50801000원에 매도 (+0.38%)
4시 57분 8초 PB 0.26, MFI 0으로 50911000원에 매수 [오류 : 매수할 조건이 아닌데 매수가 됨]
4시 49분 9초 PB 0.19, MFI 0으로 50858000원에 매도 (△0.1%)
오류 원인 ① : 가장 최근 값이 확정된 데이터가 아니다.
PB = df.PB.values[-1]
MFI = df.MFI.values[-1]
position = df.position.values[-1]
로 데이터를 받게 되는데
예를들어 조회시간이 4시 11분 2초면, 4시 10분 데이터가 가장 마지막 데이터가 된다. (5분봉기준)
그치만 4시 10분 데이터의 OHLC는 4시 10분 00초 ~4시 14분 59초 까지 데이터 에 대해서기 때문에,
4시 11분 2초 데이터가 Close 값이 되고, 4시 10분 0초가 Open 데이터가 된다.
4시 13분 5초에 데이터를 다시 조회하면 4시 10분 데이터는 4시 13분 5초 데이터가 Close 데이터가 된다.
즉, 4시 15분 데이터로 뭔가 의사결정을 하면 복잡하다.
오류원인 ② : method = "ffill"을 활용하여 앞 데이터의 값을 불러오게 해두었다.
cond_buy = (df['PB'] > buy_pb) & (df['MFI'] > buy_mfi)
df.loc[cond_buy, 'position'] = 1
cond_sell = (df['PB'] < sell_pb) & (df['MFI'] < sell_mfi)
df.loc[cond_sell, 'position'] = 0
df['position'] = df['position'].fillna(method = "ffill")
오류원인 ③ : 5분봉 데이터 조회를 1분마다 하도록 하였다.
# 매 1분 0 ~ 10초 마다
if op_mode is True and now.minute % 1 == 0 and now.second <=10 :
6분에 매수 7분에 매도
8분에 매수 9분에 매도
뭔가 이상하지 않은가?
분명 position = 1이었는데 바로 0으로 바뀐다.
이것은 계속 변화하는 최종값(오류원인①)이 계속 바뀌기 때문에 순간 PB =0.83으로 position=1이었다가 PB = 0.78로 감소하였고, 5분봉 데이터를 1분마다 조회하고 있어서 발생하는 문제일 수 있다.
○ 해결책
① 데이터 최신값을 -1 기준이 아닌 -2 기준으로 확인 해야 할 듯 (변경되지 않는 확정값으로 )
(백테스트는 그대로 진행하지만 실전에서는 최신 분봉데이터가 계속 변경되기 때문에 신경써야할 듯)
② 확정값을 반영하기 위해서는 5분봉이기에 5분봉 확정이 되자마자 바로 실행을 해야 슬리피지가 발생하지 않는다.
(슬리피지 : 원하는 매수/매도가에 행사를 하지 못해 발생하는 비용)
③
○ 추가 보완책
(현재) 매수 PB 및 MFI는 80% 기준이고, 매도 PB 및 MFI는 20% 기준이다.
> 매도를 가격이 너무 떨어진 다음에 하는 것으로 생각이 든다.
> 매도 PB 및 MFI를 30%로 올리는 것이 어떨까? 조금 더 빨리 팔게 될 것이다.