Files
oh_my_bot_simulation/power_backtest.py
2025-12-06 22:28:22 +09:00

335 lines
13 KiB
Python

from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from backtesting.test import SMA, GOOG
import sys
import talib, numpy, time, random
from itertools import product, combinations
import pybithumb
class CandlePatterns:
# 매수 패턴
_positive_patterns = [
'CDL3STARSINSOUTH', # Three Stars In The South
'CDL3WHITESOLDIERS', # 적삼병
'CDLCONCEALBABYSWALL',
'CDLDRAGONFLYDOJI',
'CDLLADDERBOTTOM',
'CDLMORNINGDOJISTAR',
'CDLMORNINGSTAR',
'CDLTAKURI',
'CDLHAMMER',
]
# 매도 패턴
_negative_patterns = ['CDLEVENINGDOJISTAR', # 석별형
'CDL2CROWS', # 2봉 까마귀형
'CDL3BLACKCROWS', # 흑삼병
'CDLADVANCEBLOCK', # 블록형 : 매수 탄력 약화, 고점에서 경고 패턴
'CDLDARKCLOUDCOVER',
'CDLEVENINGDOJISTAR',
'CDLEVENINGSTAR',
'CDLGRAVESTONEDOJI',
'CDLHANGINGMAN',
'CDLIDENTICAL3CROWS',
'CDLINNECK',
'CDLHOMINGPIGEON',
'CDLMATCHINGLOW',
'CDLONNECK',
'CDLSHOOTINGSTAR',
'CDLUPSIDEGAP2CROWS',
'CDLINVERTEDHAMMER',
]
# 중립 패턴
_fence_patterns = ['CDL3INSIDE',
'CDL3LINESTRIKE',
'CDL3OUTSIDE',
'CDLABANDONEDBABY',
'CDLBELTHOLD', # 상승/하락 샅바형
'CDLBREAKAWAY',
'CDLCLOSINGMARUBOZU',
'CDLCOUNTERATTACK',
'CDLCONCEALBABYSWALL',
'CDLENGULFING',
'CDLGAPSIDESIDEWHITE',
'CDLHARAMI',
'CDLHARAMICROSS',
# 'CDLHIGHWAVE', # 꼬리나 머리털이 길때
'CDLHIKKAKE',
'CDLHIKKAKEMOD',
'CDLKICKING',
'CDLKICKINGBYLENGTH',
# 'CDLLONGLEGGEDDOJI', # Long Legged Doji
# 'CDLLONGLINE', # Long Line Candle
# 'CDLMARUBOZU', # Marubozu
'CDLMATHOLD',
'CDLPIERCING',
# 'CDLRICKSHAWMAN ', # 그냥 도지임
# 'CDLSHORTLINE', # Short Line Candle 5:5
'CDLRISEFALL3METHODS',
'CDLSEPARATINGLINES',
# 'CDLSPINNINGTOP', # 그냥 도지임
'CDLSTALLEDPATTERN',
'CDLTASUKIGAP',
# 'CDLTHRUSTING', # 지속형
# 'CDLTRISTAR', # 이 패턴은 거의 안나옴 추세 반전 패턴
'CDLUNIQUE3RIVER',
'CDLXSIDEGAP3METHODS',
]
# 역 중립 패턴(음봉때 진입, 양봉때 탈출)
_r_fence_patterns = ['CDLSTICKSANDWICH',
]
def get_fence_patterns(self):
return self._fence_patterns
def get_r_fence_patterns(self):
return self._r_fence_patterns
# 중복 제거
def get_trade_patterns(self):
return self._negative_patterns + self._positive_patterns
def get_long_patterns(self):
return self._positive_patterns
def get_short_patterns(self):
return self._negative_patterns
class StrategyCandlePattern(Strategy):
_use_patterns = None
pattern_data = None
_sl_percent = 0.03 # 2%
# 캔들 패턴
cp = CandlePatterns()
# 매수 패턴
_positive_patterns = cp.get_long_patterns()
_negative_patterns = cp.get_short_patterns()
_fence_patterns = cp.get_fence_patterns()
_r_fence_patterns = cp.get_r_fence_patterns()
def init(self):
close = self.data.Close
low = self.data.Low
high = self.data.High
open = self.data.Open
data = [None] * len(self.data.Close)
for p in self._use_patterns:
f = getattr(talib, p)
res_arr = f(open, high, low, close)
# 100 is plus candle / -100 is minus candle
for i in range(0, len(res_arr)):
# print(p, res_arr[i])
if int(res_arr[i]) is not 0:
data[i] = {p : int(res_arr[i])}
self.pattern_data = data
def next(self):
idx = (self._broker._i)-1
# 스탑 로스 = 현재가격 - (현재가격*0.03) => 2퍼센트 스탑로스
if self.pattern_data[idx] is not None:
pattern, value = list(self.pattern_data[idx].items())[0]
sl = self.data.Close[-1] - (self.data.Close[-1] * self._sl_percent)
if pattern in self._positive_patterns: # 매수 패턴
if not self.orders.is_long:
self.buy(sl=sl)
elif pattern in self._negative_patterns: # 매도 패턴
if self.orders.is_long:
self.position.close()
elif pattern in self._fence_patterns: # 중립 패턴
if int(value) > 0:
if not self.orders.is_long:
self.buy(sl=sl)
elif int(value) < 0:
if self.orders.is_long:
self.position.close()
# self.sell()
elif pattern in self._r_fence_patterns: # 역중립 패턴(역 추세)
if int(value) > 0:
if self.orders.is_long:
self.position.close()
# self.sell()
elif int(value) < 0:
if not self.orders.is_long:
self.buy(sl=sl)
def data_columns_init(data):
# data.reset_index(level=0, inplace=True)
t_col = []
for c in data.columns:
t_col.append(c.lower().capitalize())
data.columns = t_col
start_time = time.time()
'''
"day": "24H",
"hour12": "12H",
"hour6": "06H",
"hour": "01H",
"minute30": "30M",
"minute10": "10M",
"minute5": "05M",
"minute3": "03M",
'''
df = pybithumb.get_ohlcv('BTC', 'hour') # params : 종목, 시간
df = df[:-1]
data_columns_init(df)
df = df[-1440:] # 최근 두달 데이터
cash = 1000
commission = .005
top_profit = 0
top_cash = 0
data = df # GOOG
# for test
count = 0
cp = CandlePatterns()
fence_patterns = cp.get_fence_patterns() + cp.get_r_fence_patterns()
trade_patterns = cp.get_trade_patterns()
filtered_patterns = []
long_patterns = cp.get_long_patterns()
short_patterns = cp.get_short_patterns()
# 베스트 패턴
best_patterns = ['CDLUNIQUE3RIVER', 'CDLSHOOTINGSTAR', 'CDL3BLACKCROWS', 'CDL3STARSINSOUTH', 'CDLXSIDEGAP3METHODS', 'CDLHARAMI', 'CDLGRAVESTONEDOJI', 'CDLONNECK', 'CDLDARKCLOUDCOVER', 'CDLEVENINGDOJISTAR']
StrategyCandlePattern._use_patterns = best_patterns
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
if bt._results['Return [%]'] > top_profit:
top_profit = bt._results['Return [%]']
top_cash = bt._results['Equity Final [$]']
print("최종 금액 : %0.2f" % bt._results['Equity Final [$]'])
print("총 수익률 : %0.2f%%" % bt._results['Return [%]'])
print('-' * 60)
bt.plot()
# 랜덤 픽
while True:
r_long_patterns = random.choices(long_patterns, k=random.randrange(1, len(long_patterns)))
r_short_patterns = random.choices(short_patterns, k=random.randrange(1, len(short_patterns)))
r_fence_patterns = random.choices(fence_patterns, k=random.randrange(0, len(fence_patterns)))
filtered_patterns = list(set(r_long_patterns + r_short_patterns + r_fence_patterns))
StrategyCandlePattern._use_patterns = filtered_patterns
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
if bt._results['Return [%]'] > top_profit:
top_profit = bt._results['Return [%]']
top_cash = bt._results['Equity Final [$]']
print("최종 금액 : %0.2f" % bt._results['Equity Final [$]'])
print("총 수익률 : %0.2f%%" % bt._results['Return [%]'])
print(StrategyCandlePattern._use_patterns)
print('-' * 60)
bt.plot()
pass
# Filtering Trade Patterns
for pattern in list(combinations(trade_patterns, 2)):
StrategyCandlePattern._use_patterns = pattern
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
# 해당 패턴이 데이터에 존재 할 경우 추가
if bt._results['Return [%]'] != 0:
filtered_patterns += pattern
filtered_patterns = list(set(filtered_patterns))
# Filtering Fence Patterns
for pattern in fence_patterns:
StrategyCandlePattern._use_patterns = [pattern]
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
# 수익률, 승률, 승패 등으로 필터링 기준 조정 => 승률이 좋던가, 수익이 좋던가
if bt._results['Return [%]'] > top_profit:
top_profit = bt._results['Return [%]']
top_cash = bt._results['Equity Final [$]']
print("최종 금액 : %0.2f" % bt._results['Equity Final [$]'])
print("총 수익률 : %0.2f%%" % bt._results['Return [%]'])
print(StrategyCandlePattern._use_patterns)
print('-' * 60)
# 해당 패턴이 데이터에 존재 할 경우 추가
if bt._results['Return [%]'] != 0:
filtered_patterns.append(pattern)
# 모든 경우의 수
for index in list(range(2, len(filtered_patterns) + 1)):
# for index in list(range(len(all_patterns), len(all_patterns) + 1)):
for patterns in list(combinations(filtered_patterns, index)):
StrategyCandlePattern._use_patterns = patterns
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
if count < len(StrategyCandlePattern._use_patterns):
count = len(StrategyCandlePattern._use_patterns)
print(len(StrategyCandlePattern._use_patterns))
e = int(time.time() - start_time)
print('{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
if bt._results['Return [%]'] > top_profit:
top_profit = bt._results['Return [%]']
top_cash = bt._results['Equity Final [$]']
# print(bt._results)
print("최종 금액 : %0.2f" % bt._results['Equity Final [$]'])
print("총 수익률 : %0.2f%%" % bt._results['Return [%]'])
print(StrategyCandlePattern._use_patterns)
print('-'*60)
bt.plot()
del bt
e = int(time.time() - start_time)
print('{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
'''
사이클 주기 : 3달?
매수/매도 패턴 쌍으로 수익 실현 횟수를 수치화 하여 확률적 접근으로? => 패턴별 승패수 = 승률제
시간봉 두달 37.62%
['CDLHOMINGPIGEON', 'CDLSHOOTINGSTAR', 'CDLRISEFALL3METHODS', 'CDLINNECK', 'CDLXSIDEGAP3METHODS', 'CDLLADDERBOTTOM', 'CDLABANDONEDBABY', 'CDL3LINESTRIKE', 'CDLTASUKIGAP', 'CDL3STARSINSOUTH']
시간봉 두달 35퍼 :
['CDL3WHITESOLDIERS', 'CDL3WHITESOLDIERS', 'CDLEVENINGDOJISTAR', 'CDL2CROWS', 'CDLSHOOTINGSTAR', 'CDLINNECK', 'CDLONNECK', 'CDLADVANCEBLOCK', 'CDLIDENTICAL3CROWS', 'CDLDARKCLOUDCOVER', 'CDLINNECK', 'CDL3LINESTRIKE', 'CDLCONCEALBABYSWALL', 'CDLXSIDEGAP3METHODS']
5분봉 일주일 6퍼 :
('CDLMORNINGDOJISTAR', 'CDLHANGINGMAN', 'CDLHIKKAKEMOD', 'CDLSEPARATINGLINES', 'CDLUNIQUE3RIVER')
- 캔들 신호에 따라 매매, 보조지표는 시그널로
- 패턴별 신뢰도 측정 => 이브닝스타 - 5봉 뒤 가격 다운(종가)
- 해당 데이터에 패턴이 있는지 체크 후 필터 리스트에 추가
- 매매 패턴은 그래도 사용하고, 보조지표 기반 시그널로 작동 => 패턴별 가중치가 다르게 => 슈팅스타 백점 등
- 보조지표를 베이스 시그널로 활용
- 시간대별로 반복문 추가
- 최대 패턴 갯수 구하기
# 매수/매도 패턴끼리 먼저 조합하여 경우의 수 도출
# 수익이 있는 캔들 패턴만 보조지표와 조합 => 중립 캔들 패턴만, 매수/매도 시그널 패턴은 그냥 조합 사용
# 패턴을 시그널로 활용(사용중인 모든 패턴이 True 일때만 매수/ False 매도로 구성) => 보류 => 보조지표를 시그널로 활용
# 봉별 단일 시그널로 활용할 지, 복합 시그널(두개 이상)로 활용 할지..
# 매매 패턴의 경우 데이터에 존재하는지 체크 후 패턴 리스트에 추가
'''