첫 번째 커밋

This commit is contained in:
javamon
2025-12-06 22:28:22 +09:00
commit 4c263e7866
21 changed files with 9733 additions and 0 deletions

335
power_backtest.py Normal file
View File

@@ -0,0 +1,335 @@
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 매도로 구성) => 보류 => 보조지표를 시그널로 활용
# 봉별 단일 시그널로 활용할 지, 복합 시그널(두개 이상)로 활용 할지..
# 매매 패턴의 경우 데이터에 존재하는지 체크 후 패턴 리스트에 추가
'''