첫 번째 커밋

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

482
backtest.py Normal file
View File

@@ -0,0 +1,482 @@
import sys, time, random, os
import json
# load strategy
# from strategies.indicator import StrategyIndicator
from backtesting import Backtest # short-tp error
# load functions
# from indicator_util import get_indicators_values
from signal_helper import *
# load db
from db import DB
from itertools import combinations
from datetime import datetime
from dateutil.relativedelta import relativedelta
# from trade.candle import CandlePatterns
import warnings
from time import sleep
from strategy import StrategyCandlePattern
from multiprocessing import Process, freeze_support
import gc
warnings.filterwarnings(action='ignore')
_db = DB()
# set config
start_time = time.time()
top_cash = 0
top_profit = 20
top_win_rate = 90
# best_pattern_arr = []
# [0, 0.23, 0.38, 0.5, 0.61, 0.78, 0.88, 1]
# pivonachi
profit_arr = [0] + pivo(25)
loss_arr = [0] + pivo(25)
# 시그널 보조 지표
base_indicators = [
{'HEI': None},
{'RSI_DIV': 14},
{'MFI_DIV': 14},
{'CCI_DIV': 20},
{'WILLR_DIV': 28},
{'RSI': 14},
{'BBANDS': [20, 2]},
{'BBANDS': [34, 2]},
{'CCI': 14},
{'AROON': 14},
{'SAR': [0.00252, 0.22]},
{'AROONOSC': 14},
{'BOP': 14},
{'CCI': 20},
{'MFI': 14},
{'MOM': 10},
{'MOM': 14},
{'ROC': 9},
{'ROC': 14},
{'WILLR': 14},
]
# 시그널 주도 지표(필터링될 지표)
signal_indicators = [
{'STOCH_DIV': [14, 1, 1]},
{'STOCH_DIV': [14, 3, 3]},
# {'STOCH_DIV': [20, 12, 12]},
{'STOCHRSI_DIV': [14, 14, 3]},
{'CMO_DIV': 14},
{'CCI_DIV': 14},
{'ADX_DIV': 14},
{'BOP_DIV': 0},
{'OBV_DIV': 0},
{'MOM_DIV': 10},
{'ROC_DIV': 14},
{'ROC_DIV': 9},
{'STOCH_DIV': [14, 3, 14]},
{'STOCH_DIV': [14, 3, 5]},
{'ADOSC_DIV': [3, 10]},
{'ULTOSC_DIV': [7, 14, 28]},
{'TRIX': [14, 9]},
{'STOCH': [20, 12, 12]},
{'STOCH': [14, 3, 14]},
{'STOCH': [14, 3, 5]},
{'DMI': 14},
{'DI': 21},
{'APO': [10, 20]},
{'MACD': [12, 26, 9]},
{'MACDFIX': 26},
{'MACDFIX': 9},
{'MACDFIX': 14},
{'MACDFIX': 31},
{'PPO': [12, 26, 9]},
{'STOCHF': [14, 3]},
{'STOCHRSI': [14, 14, 3]},
{'ULTOSC': [7, 14, 28]},
{'EMA': 30},
{'EMA': 55},
{'DEMA': 55},
{'DEMA': 100},
{'DEMA': 200},
{'MA': 21},
{'MA': 55},
{'MA': 100},
{'MAMA': [0.5, 0.05]},
{'T3': [100, 10]},
{'TRIMA': 30},
{'TRIMA': 50},
{'WMA': 30},
{'WMA': 20},
]
'''
# for test
# base_indicators = [{'RSI': 14}, {'BBANDS': [34, 2]}, {'CCI': 20}, {'MFI': 14}, {'MOM': 14}]
# signal_indicators = [{'STOCH_DIV': [14, 3, 3]}, {'ROC_DIV': 14}, {'STOCH_DIV': [14, 3, 5]}]
data = _db.get_price_data_from_item_table('crypto/bithumb/KRW-BTC/day', '6_M')
df = pd.DataFrame(data)
df.set_index(df['date'], inplace=True)
data_columns_init(df)
df.index.name = str('crypto/bithumb/KRW-BTC/day').replace('/', '_')
StrategyCandlePattern.use_indicators = [{'HEI': None}]
StrategyCandlePattern.up_target = float(0)
StrategyCandlePattern.down_target = float(0)
bt = Backtest(df, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
print(bt._results)
bt.plot()
sys.exit(1)
'''
# multi Treading - item
def simulrating_by_item(item, data, all_indicators, idx, t_time):
# from guppy.heapy import RM # for monitoring
global start_time
# global _db
_db = DB()
cash = 1000
commission = .005
print('작업 시작 : %s / 지표 수 : %s / 시간프레임 : %s' % (item['job'], idx, t_time))
# get top_profit and win rate from item-t_time
top_info = _db.select_top_date(item['job'])
min_profit = 22
if str(item['trade_type']) == 'double':
min_profit = 30
if top_info['profit_rate'] is None:
global top_profit
global top_win_rate
else:
top_profit = float(top_info['profit_rate'])
top_win_rate = float(top_info['win_rate'])
# bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
for indicators in list(combinations(all_indicators, idx)):
for profit in profit_arr:
for loss in loss_arr:
# if str(item['trade_type']) == 'double' and loss > 0 and profit == 0: # 숏 포지션 목표가만 있을 시 에러
# continue
# if str(item['trade_type']) == 'double' and (loss != 0 or profit != 0):
# continue
StrategyCandlePattern.use_indicators = list(indicators)
StrategyCandlePattern.up_target = float(profit)
StrategyCandlePattern.down_target = float(loss)
StrategyCandlePattern.trade_type = str(item['trade_type'])
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
# 수익 및 거래 수 제한
if bt._results['# Trades'] >= 5 and bt._results['Return [%]'] > min_profit and \
(bt._results['Return [%]'] > top_profit or float(bt._results['Win Rate [%]']) >= 100):
filename = 'chart/' + str(item['job']).replace('/', '_') + '_' + str(t_time) + '_' + str(
time.time())
_db.insert_simul_result(
{
'item': str(item['job']).replace('/', '_'),
'time_type': t_time,
'results': bt._results,
'stop_profit': profit,
'stop_loss': loss,
'use_patterns': json.dumps(StrategyCandlePattern.use_indicators),
'use_pattern_cnt': len(StrategyCandlePattern.use_indicators),
'filename': filename,
'period_cycle': item['period_cycle'],
'trade_type': item['trade_type'],
})
if bt._results['Return [%]'] > top_profit:
top_profit = bt._results['Return [%]']
# print('-' * 60)
# print('트레이딩 종류 :', item['trade_type'])
# print('시간봉 :', t_time)
# print('지표 조합 개수 :', idx)
# print('지표 :', StrategyCandlePattern.use_indicators)
# print("적용된 스탑프로핏 : %0.2f%%" % profit)
# print("적용된 스탑로스 : %0.2f%%" % loss)
# print("총 수익률 : %0.2f%%" % bt._results['Return [%]'])
# print("최종 금액 : %0.2f" % bt._results['Equity Final [$]'])
# print("거래 수 :", bt._results['# Trades'])
# print("파일명 :", filename)
# print('-' * 60)
# bt.plot()
if bt._results['Return [%]'] > 50 and float(
bt._results['Win Rate [%]']) >= 100: # 최상의 수익인 경우 차트 저장
bt.plot(filename=filename, open_browser=False)
del [[filename]]
if bt._results['Win Rate [%]'] >= top_win_rate:
top_win_rate = bt._results['Win Rate [%]']
# best_pattern_arr.append({
# 't_time': t_time,
# 'indicators': indicators,
# 'profit': profit,
# 'loss': loss,
# 'return': bt._results['Return [%]'],
# 'trades': bt._results['# Trades'],
# })
bt = None
del [[bt]]
gc.collect()
e = int(time.time() - start_time)
print('(완료) 조합 지표 개수 :', idx, t_time, '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
print(datetime.now())
del [[cash, commission, top_info, _db]]
# del [[item, data, all_indicators, idx, t_time]]
gc.collect()
# multi Treading - fackage
def simulrating_by_item_fackage(item, data, t_time):
global start_time
global base_indicators
global signal_indicators
global _db
# _db = DB()
cash = 1000
commission = .005
top_info = _db.select_top_date(item['job'])
min_profit = 17
if str(item['trade_type']) == 'double':
min_profit = 17
if top_info['profit_rate'] is None:
global top_profit
global top_win_rate
else:
top_profit = float(top_info['profit_rate'])
top_win_rate = float(top_info['win_rate'])
# bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
filtered_signal_indicators = []
for indicator in signal_indicators:
for profit in profit_arr:
for loss in loss_arr:
# if str(item['trade_type']) == 'double' and loss > 0 and profit == 0: # 숏 포지션 목표가만 있을 시 에러
# continue
# if str(item['trade_type']) == 'double' and (loss != 0 or profit != 0):
# continue
# for test
# StrategyCandlePattern.use_indicators = [{"WMA": 30}, {"EMA": 30}, {"STOCHF": [14, 3]}]
StrategyCandlePattern.use_indicators = [indicator]
StrategyCandlePattern.up_target = float(profit)
StrategyCandlePattern.down_target = float(loss)
StrategyCandlePattern.trade_type = str(item['trade_type'])
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
bt.run()
# 수익 및 거래 수 제한
if float(bt._results['Return [%]']) > min_profit and float(bt._results['# Trades']) >= 5:
if indicator not in filtered_signal_indicators:
filtered_signal_indicators.append(indicator)
# if float(bt._results['Return [%]']) > top_profit or float(bt._results['Win Rate [%]']) >= 60:
# filename = 'chart/' + str(item['job']).replace('/', '_') + '_' + str(t_time) + '_' + str(
# time.time())
# 단일 지표 미사용
# _db.insert_simul_result(
# {
# 'item': str(item['job']).replace('/', '_'),
# 'time_type': t_time,
# 'results': bt._results,
# 'stop_profit': profit,
# 'stop_loss': loss,
# 'use_patterns': json.dumps(StrategyCandlePattern.use_indicators),
# 'use_pattern_cnt': len(StrategyCandlePattern.use_indicators),
# 'filename': filename,
# 'period_cycle': item['period_cycle'],
# 'trade_type': item['trade_type'],
# })
# if bt._results['Return [%]'] > top_profit:
# top_profit = bt._results['Return [%]']
# if bt._results['Win Rate [%]'] > top_win_rate:
# top_win_rate = bt._results['Win Rate [%]']
# bt.plot(filename=filename, open_browser=False)
# best_pattern_arr.append({
# 't_time': t_time,
# 'indicators': [indicator],
# 'profit': profit,
# 'loss': loss,
# 'return': bt._results['Return [%]'],
# 'trades': bt._results['# Trades'],
# })
bt = None
del [[bt]]
# 총 32개 이하로 유지
filtered_signal_indicators = filtered_signal_indicators[:12]
e = int(time.time() - start_time)
print('시그널 지표 필터링 완료 :', '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
print('지표 총합 :', len(filtered_signal_indicators) + len(base_indicators))
print('필터 지표 리스트', filtered_signal_indicators)
if len(filtered_signal_indicators) < 2:
print(item, t_time, '- 수익 모델 조건을 만족하는 전략이 없습니다.')
return
all_indicators = filtered_signal_indicators[::-1] + base_indicators
joined = []
for idx in range(2, 5):
_p = Process(target=simulrating_by_item, args=(item, data, all_indicators, idx, t_time,))
_p.start()
joined.append(_p)
del [[item, data, all_indicators, idx, t_time, cash, commission, top_info]]
gc.collect()
# 프로세스 조인
for _p in joined:
_p.join()
# multi Treading - time
def simulrating_by_time(item, t_time):
global _db
'''
"day": "24H",
"hour12": "12H",
"hour6": "06H",
"hour": "01H",
"minute30": "30M",
"minute10": "10M",
"minute5": "05M",
"minute3": "03M",
'''
data = _db.get_price_data_from_item_table(item['job'], item['period_cycle'])
df = pd.DataFrame(data)
# date = df['date'].copy()
# 최소 데이터 기준 일자 조건문(2달)
std_date = datetime.now() - relativedelta(months=2)
if df['date'][0] > std_date:
del [[df]]
return
df.set_index(df['date'], inplace=True)
data_columns_init(df)
df.index.name = str(item['job']).replace('/', '_')
if t_time != 'hour' and t_time != 'day':
time_type = {
'hour': '60min',
'hour2': '120min',
'hour4': '240min',
'hour6': '360min',
'hour12': '720min',
# 'week': 'W',
# 'month': 'M',
}
ohlc_dict = {
'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last',
'Volume': 'sum'
}
# df = df.resample(time_type[t_time], how=ohlc_dict, label='left', base=180) # UTC (트레이딩뷰)
df = df.resample(time_type[t_time], how=ohlc_dict, label='left', base=540) # UTC (트레이딩뷰)
df = df[:-1]
simulrating_by_item_fackage(item, df, t_time)
df = None
del [[df]]
def is_bot_by_exchange_name(item):
target = item['job'].split('/')
return _db.is_bot_by_exchange_name(target[1])
# reboot for windows
def reboot():
print('재부팅 예약 되었습니다.', datetime.now())
return os.system("shutdown -t 60 -r -f")
def record_item_per_time():
# 테이블 생성해서 쓰고 지우기
print('record_item_per_time')
print(os.path.expanduser())
def start_backtest():
global _db
print('Started Simulating.', datetime.now())
zzz = True
for item in _db.get_cron_list():
# 이미 작동중인 봇이 있을 경우 제외 - 거래소 기준
if is_bot_by_exchange_name(item):
continue
if item['time_unit'] == 'hour':
for t_time in ['hour6']: # 6시간봉만 시뮬레이팅
# for t_time in ['hour6', 'hour4']: # 4시간, 6시간봉 시뮬레이팅
if _db.is_simulated_item_per_hour(item['job'], t_time):
continue
simulrating_by_time(item, t_time)
# 타임프레임 시뮬레이터 상태 임시 저장
_db.insert_item_from_simul_item_per_hour(item['job'], t_time)
return reboot()
# 타임프레임 시뮬레이터 상태 삭제
_db.remove_item_from_simul_item_per_hour(item['job'])
_db.update_simul_init_value(item['job'])
return reboot()
simulrating_by_time(item, item['time_unit'])
_db.update_simul_init_value(item['job'])
return reboot()
if zzz:
print('There is no list of items to run.', datetime.now())
time.sleep(43400)
return reboot()
if __name__ == '__main__':
freeze_support()
start_backtest()