첫 번째 커밋
This commit is contained in:
141
.gitignore
vendored
Normal file
141
.gitignore
vendored
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
__pycache__
|
||||||
|
/__pycache__
|
||||||
|
__pycache__/
|
||||||
|
chart/
|
||||||
|
/chart
|
||||||
|
build/
|
||||||
|
/build
|
||||||
|
.idea
|
||||||
|
/.idea
|
||||||
|
.idea/
|
||||||
|
venv
|
||||||
|
/venv
|
||||||
|
venv/
|
||||||
|
.Python
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
StrategyCandlePattern.html
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
1041
__strategy.py
Normal file
1041
__strategy.py
Normal file
File diff suppressed because it is too large
Load Diff
482
backtest.py
Normal file
482
backtest.py
Normal 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()
|
||||||
304
backtest.pyx
Normal file
304
backtest.pyx
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import sys, time, random, os
|
||||||
|
|
||||||
|
# load strategy
|
||||||
|
# from strategies.indicator import StrategyIndicator
|
||||||
|
from backtesting import Backtest
|
||||||
|
|
||||||
|
# load functions
|
||||||
|
from indicator_util import get_indicators_values
|
||||||
|
from signal_helper import *
|
||||||
|
|
||||||
|
# Exchange API
|
||||||
|
import pybithumb
|
||||||
|
|
||||||
|
from itertools import combinations
|
||||||
|
from datetime import datetime
|
||||||
|
from trade.candle import CandlePatterns
|
||||||
|
import warnings
|
||||||
|
# from threading import Thread
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from strategy import StrategyCandlePattern
|
||||||
|
from multiprocessing import Process, freeze_support
|
||||||
|
from multiprocessing import Pool
|
||||||
|
import multiprocessing
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
warnings.filterwarnings(action='ignore')
|
||||||
|
|
||||||
|
# set config
|
||||||
|
start_time = time.time()
|
||||||
|
cash = 1000
|
||||||
|
commission = .005
|
||||||
|
top_cash = 0
|
||||||
|
top_profit = 25
|
||||||
|
top_win_rate = 60
|
||||||
|
best_pattern_arr = []
|
||||||
|
|
||||||
|
# pivonachi
|
||||||
|
profit_arr = [0] + pivo(100)
|
||||||
|
loss_arr = [0] + pivo(10)
|
||||||
|
|
||||||
|
# 시그널 보조 지표
|
||||||
|
base_indicators = [
|
||||||
|
{'RSI_DIV': 14},
|
||||||
|
{'MFI_DIV': 14},
|
||||||
|
{'CMO_DIV': 9},
|
||||||
|
{'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, 3, 3]},
|
||||||
|
{'STOCH_DIV': [14, 1, 1]},
|
||||||
|
# {'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]}]
|
||||||
|
|
||||||
|
# multi Treading - item
|
||||||
|
def simulrating_by_item(item, data, all_indicators, idx, t_time):
|
||||||
|
# def simulrating_by_item(data):
|
||||||
|
global top_profit
|
||||||
|
global top_win_rate
|
||||||
|
global start_time
|
||||||
|
global cash
|
||||||
|
global commission
|
||||||
|
|
||||||
|
for indicators in list(combinations(all_indicators, idx)):
|
||||||
|
for profit in profit_arr:
|
||||||
|
for loss in loss_arr:
|
||||||
|
StrategyCandlePattern.use_indicators = list(indicators)
|
||||||
|
StrategyCandlePattern.up_target = float(profit)
|
||||||
|
StrategyCandlePattern.down_target = float(loss)
|
||||||
|
|
||||||
|
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
|
||||||
|
bt.run()
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 5:
|
||||||
|
print('-' * 60)
|
||||||
|
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("파일명 :", str(item)+'_'+str(t_time)+'_'+str(idx)+'_'+str(time.time()))
|
||||||
|
print('-' * 60)
|
||||||
|
# bt.plot(filename=str(item)+'_'+str(t_time)+'_'+str(idx)+'_'+str(time.time()))
|
||||||
|
|
||||||
|
top_profit = bt._results['Return [%]']
|
||||||
|
|
||||||
|
best_pattern_arr.append({
|
||||||
|
't_time': t_time,
|
||||||
|
'indicators': indicators,
|
||||||
|
'profit': profit,
|
||||||
|
'loss': loss,
|
||||||
|
'return': bt._results['Return [%]'],
|
||||||
|
'trades': bt._results['# Trades'],
|
||||||
|
})
|
||||||
|
|
||||||
|
del bt
|
||||||
|
|
||||||
|
e = int(time.time() - start_time)
|
||||||
|
print('(완료) 조합 지표 개수 :', idx, t_time, '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
|
||||||
|
print(datetime.now())
|
||||||
|
|
||||||
|
|
||||||
|
# multi Treading - fackage
|
||||||
|
def simulrating_by_item_fackage(item, data, t_time):
|
||||||
|
global top_profit
|
||||||
|
global top_win_rate
|
||||||
|
global start_time
|
||||||
|
global base_indicators
|
||||||
|
global signal_indicators
|
||||||
|
global cash
|
||||||
|
global commission
|
||||||
|
|
||||||
|
filtered_signal_indicators = []
|
||||||
|
|
||||||
|
for indicator in signal_indicators:
|
||||||
|
for profit in profit_arr:
|
||||||
|
for loss in loss_arr:
|
||||||
|
StrategyCandlePattern.use_indicators = [indicator]
|
||||||
|
StrategyCandlePattern.up_target = float(profit)
|
||||||
|
StrategyCandlePattern.down_target = float(loss)
|
||||||
|
|
||||||
|
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
|
||||||
|
bt.run()
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if bt._results['Return [%]'] > 15 and bt._results['# Trades'] > 5:
|
||||||
|
if indicator not in filtered_signal_indicators:
|
||||||
|
filtered_signal_indicators.append(indicator)
|
||||||
|
|
||||||
|
if bt._results['Return [%]'] > top_profit:
|
||||||
|
print('-' * 60)
|
||||||
|
print('시간봉 :', t_time)
|
||||||
|
print('지표 조합 개수 :', 1)
|
||||||
|
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('-' * 60)
|
||||||
|
# bt.plot()
|
||||||
|
|
||||||
|
top_profit = bt._results['Return [%]']
|
||||||
|
|
||||||
|
best_pattern_arr.append({
|
||||||
|
't_time': t_time,
|
||||||
|
'indicators': [indicator],
|
||||||
|
'profit': profit,
|
||||||
|
'loss': loss,
|
||||||
|
'return': bt._results['Return [%]'],
|
||||||
|
'trades': bt._results['# Trades'],
|
||||||
|
})
|
||||||
|
|
||||||
|
del bt
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
all_indicators = filtered_signal_indicators[::-1] + base_indicators
|
||||||
|
|
||||||
|
joined = []
|
||||||
|
for idx in range(2, 5):
|
||||||
|
# simulrating_by_item(item, data, all_indicators, idx, t_time)
|
||||||
|
|
||||||
|
# item_thread = Thread(target=simulrating_by_item, args=(item, data, all_indicators, idx, t_time,))
|
||||||
|
# item_thread.start()
|
||||||
|
|
||||||
|
# with multiprocessing.Pool(processes=psutil.cpu_count(logical=False)) as pool:
|
||||||
|
# pool = Pool(processes=4)
|
||||||
|
# func = simulrating_by_item(item, data, all_indicators, idx, t_time)
|
||||||
|
# pool.map(func)
|
||||||
|
|
||||||
|
_p = Process(target=simulrating_by_item, args=(item, data, all_indicators, idx, t_time,))
|
||||||
|
# _p.daemon = True
|
||||||
|
_p.start()
|
||||||
|
joined.append(_p)
|
||||||
|
|
||||||
|
for _p in joined:
|
||||||
|
_p.join()
|
||||||
|
|
||||||
|
del item, data, all_indicators, idx, t_time
|
||||||
|
|
||||||
|
|
||||||
|
# multi Treading - time
|
||||||
|
def simulrating_by_time(item, t_time):
|
||||||
|
'''
|
||||||
|
"day": "24H",
|
||||||
|
"hour12": "12H",
|
||||||
|
"hour6": "06H",
|
||||||
|
"hour": "01H",
|
||||||
|
"minute30": "30M",
|
||||||
|
"minute10": "10M",
|
||||||
|
"minute5": "05M",
|
||||||
|
"minute3": "03M",
|
||||||
|
'''
|
||||||
|
df = pybithumb.get_ohlcv(item, t_time) # params : 종목, 시간
|
||||||
|
|
||||||
|
# 최근 두달 데이터 = 실질
|
||||||
|
if t_time == 'hour':
|
||||||
|
df = df[-1600:]
|
||||||
|
elif t_time == 'hour6':
|
||||||
|
df = df[-266:] # 최근 두달 데이터 = 실질
|
||||||
|
elif t_time == 'hour12':
|
||||||
|
df = df[-133:] # 최근 두달 데이터 = 실질
|
||||||
|
elif t_time == 'day':
|
||||||
|
df = df[-85:] # 최근 두달 데이터 = 실질
|
||||||
|
|
||||||
|
data_columns_init(df)
|
||||||
|
data = df
|
||||||
|
|
||||||
|
simulrating_by_item_fackage(item, data, t_time)
|
||||||
|
|
||||||
|
|
||||||
|
def start_backtest():
|
||||||
|
print('Started Simulating.')
|
||||||
|
|
||||||
|
for t_time in ['hour', 'hour6', 'hour12']:
|
||||||
|
simulrating_by_time('BTC', t_time)
|
||||||
|
break
|
||||||
|
# p = Process(target=simulrating_by_time, args=('BTC', t_time,))
|
||||||
|
# p.start()
|
||||||
|
# p.join() # sync
|
||||||
|
|
||||||
|
# thread = Thread(target=simulrating_by_time, args=('BTC', t_time,))
|
||||||
|
# thread.start()
|
||||||
|
# thread.join() # 동기
|
||||||
|
|
||||||
|
print('Ended Simulating.')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
freeze_support()
|
||||||
|
start_backtest()
|
||||||
|
|
||||||
1
becktest_with_cuda.py
Normal file
1
becktest_with_cuda.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print('cuda')
|
||||||
194
db.py
Normal file
194
db.py
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
import sys
|
||||||
|
import pymysql
|
||||||
|
import json
|
||||||
|
from datetime import date, timedelta, datetime
|
||||||
|
import time
|
||||||
|
|
||||||
|
db_info = {'host': '192.168.0.2', 'port': 3306, 'user': 'javamon', 'passwd': '@Wtkdwns117424', 'db': 'oh_my_bot_admin',
|
||||||
|
'charset': 'utf8',
|
||||||
|
'max_allowed_packet': '67108864'}
|
||||||
|
|
||||||
|
|
||||||
|
class DB:
|
||||||
|
def execute(self, sql=None, data=None):
|
||||||
|
try:
|
||||||
|
res = None
|
||||||
|
|
||||||
|
if not sql is None:
|
||||||
|
connection = pymysql.connect(host=db_info['host'], port=db_info['port'], user=db_info['user'],
|
||||||
|
passwd=db_info['passwd'], db=db_info['db'],
|
||||||
|
charset=db_info['charset'],
|
||||||
|
max_allowed_packet=db_info['max_allowed_packet'],
|
||||||
|
init_command="SET SESSION time_zone='+09:00'", connect_timeout=60)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with connection.cursor(pymysql.cursors.DictCursor) as cursor:
|
||||||
|
cursor.execute(sql, data)
|
||||||
|
res = cursor.fetchall()
|
||||||
|
|
||||||
|
connection.commit()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
return res
|
||||||
|
except Exception as e:
|
||||||
|
time.sleep(2)
|
||||||
|
return self.execute(sql, data)
|
||||||
|
|
||||||
|
def select_only_one_row(self, sql):
|
||||||
|
return self.execute(sql)[0]
|
||||||
|
|
||||||
|
def select_exchage_id_by_name(self, name):
|
||||||
|
sql = "SELECT `f_id`, `id` FROM oh_my_bot_admin.exchange WHERE `name`= '%s';" % name
|
||||||
|
|
||||||
|
return self.select_only_one_row(sql)
|
||||||
|
|
||||||
|
def select_item_id_by_ids_name(self, f_i, e_i, name):
|
||||||
|
sql = "SELECT `id` FROM oh_my_bot_admin.item " \
|
||||||
|
"WHERE `f_id`= '%s' AND `e_id`= '%s' AND `name`='%s';" % (f_i, e_i, name)
|
||||||
|
|
||||||
|
return self.select_only_one_row(sql)['id']
|
||||||
|
|
||||||
|
def select_top_date(self, item):
|
||||||
|
t_name = str(item).replace('/', '_')
|
||||||
|
|
||||||
|
profit_sql = "SELECT MAX(profit_rate) FROM oh_my_bot_admin.simulation " \
|
||||||
|
"WHERE `t_table_name` = '%s';" % t_name
|
||||||
|
|
||||||
|
win_rate_sql = "SELECT MAX(win_rate) FROM oh_my_bot_admin.simulation " \
|
||||||
|
"WHERE `t_table_name` = '%s';" % t_name
|
||||||
|
|
||||||
|
profit = self.execute(profit_sql)
|
||||||
|
win_rate = self.execute(win_rate_sql)
|
||||||
|
|
||||||
|
return {
|
||||||
|
'profit_rate': profit[0]['MAX(profit_rate)'],
|
||||||
|
'win_rate': win_rate[0]['MAX(win_rate)'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def isNaN(self, num):
|
||||||
|
return num != num
|
||||||
|
|
||||||
|
def insert_simul_result(self, result):
|
||||||
|
'''
|
||||||
|
`f_i` INT NOT NULL COMMENT 'finance_id',
|
||||||
|
`e_i` INT NOT NULL COMMENT 'exchage_id',
|
||||||
|
`item_id` INT NOT NULL,
|
||||||
|
`time_type` VARCHAR(100) NOT NULL COMMENT 'item data time type',
|
||||||
|
`t_table_name` VARCHAR(100) NOT NULL COMMENT 'Target item table name',
|
||||||
|
`profit_rate` FLOAT NOT NULL COMMENT 'Return(Profit) rate',
|
||||||
|
`win_rate` FLOAT NOT NULL COMMENT 'Wins rate',
|
||||||
|
`trades` INT NOT NULL,
|
||||||
|
`stop_loss` FLOAT NOT NULL,
|
||||||
|
`stop_profit` FLOAT NOT NULL,
|
||||||
|
`used_patterns` VARCHAR(255) NOT NULL COMMENT 'used indicator and patterns',
|
||||||
|
`file_name` VARCHAR(255) NULL COMMENT 'simulation result file name',
|
||||||
|
`start_date` DATETIME NOT NULL,
|
||||||
|
`end_date` DATETIME NOT NULL,
|
||||||
|
`duration_days` INT NOT NULL COMMENT 'simulation period days',
|
||||||
|
`trade_type` NULL DEFAULT 'single' COMMENT 'trade type(doubly trade)',
|
||||||
|
'''
|
||||||
|
|
||||||
|
i_info = result['item'].split('_')
|
||||||
|
ids = self.select_exchage_id_by_name(i_info[1])
|
||||||
|
item_id = self.select_item_id_by_ids_name(ids['f_id'], ids['id'], i_info[2])
|
||||||
|
s_d, days = self.get_start_date_on_hour(result['period_cycle'])
|
||||||
|
|
||||||
|
if self.isNaN(result['results']['Return [%]']) or self.isNaN(result['results']['Win Rate [%]']):
|
||||||
|
return
|
||||||
|
|
||||||
|
sql = '''
|
||||||
|
INSERT INTO `oh_my_bot_admin`.`simulation` (`f_i`, `e_i`, `item_id`,
|
||||||
|
`time_type`, `t_table_name`, `profit_rate`, `win_rate`, `trades`, `stop_loss`,
|
||||||
|
`stop_profit`, `used_patterns`, `file_name`, `start_date`, `end_date`, `duration_days`, `trade_type`)
|
||||||
|
VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');
|
||||||
|
''' % (ids['f_id'], ids['id'], item_id, result['time_type'], result['item'],
|
||||||
|
result['results']['Return [%]'], result['results']['Win Rate [%]'],
|
||||||
|
result['results']['# Trades'], result['stop_loss'], result['stop_profit'],
|
||||||
|
str(result['use_patterns']), result['filename'], result['results']['Start'],
|
||||||
|
result['results']['End'], days, str(result['trade_type']),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.execute(sql)
|
||||||
|
|
||||||
|
def update_simul_init_value(self, job):
|
||||||
|
sql = "UPDATE `oh_my_bot_admin`.`cron` SET `init_simul` = 'Y' WHERE (`job` = '%s');" % job
|
||||||
|
|
||||||
|
self.execute(sql)
|
||||||
|
|
||||||
|
def get_cron_list(self):
|
||||||
|
# sql = "SELECT `job`, `period_cycle`, `time_unit` FROM oh_my_bot_admin.cron WHERE `type` = 'price' AND `init_simul` = 'N';"
|
||||||
|
sql = "SELECT * FROM oh_my_bot_admin.cron WHERE `type` = 'price' AND `init_simul` = 'N' ORDER BY id ASC;"
|
||||||
|
# sql = "SELECT * FROM oh_my_bot_admin.cron WHERE `type` = 'price';"
|
||||||
|
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def get_price_data_from_item_table(self, job_name, period):
|
||||||
|
tb_name = self.job_name_convert_to_tb_name(job_name)
|
||||||
|
start_date, days = self.get_start_date_on_hour(period)
|
||||||
|
|
||||||
|
date_arr = str(start_date).split('-')
|
||||||
|
start_date = datetime(int(date_arr[0]), int(date_arr[1]), int(date_arr[2]), 1, 0, 0)
|
||||||
|
|
||||||
|
sql = "SELECT `open`, `high`, `low`, `close`, `volume`, `date` FROM " \
|
||||||
|
"oh_my_bot_admin.%s WHERE `date` >= '%s'" % (tb_name, start_date)
|
||||||
|
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def get_start_date_on_hour(self, period='2_M'):
|
||||||
|
# for period in ['6_M', '3_M', '1_Y', '2_Y', '2_W', '3_W']:
|
||||||
|
d = str(period).split('_')
|
||||||
|
|
||||||
|
int2type = {
|
||||||
|
"W": 8,
|
||||||
|
"M": 32,
|
||||||
|
"Y": 366,
|
||||||
|
}
|
||||||
|
|
||||||
|
days = int(d[0]) * int(int2type[d[1]])
|
||||||
|
|
||||||
|
return date.today() - timedelta(days=days), days
|
||||||
|
|
||||||
|
def job_name_convert_to_tb_name(self, job_name):
|
||||||
|
b = '_'
|
||||||
|
_ = str(job_name).split('/')
|
||||||
|
|
||||||
|
_f = b.join([_[0], _[1]]).lower()
|
||||||
|
_e = b.join([_[2], _[3]]).upper()
|
||||||
|
|
||||||
|
return b.join([_f, _e]).replace('-', '_')
|
||||||
|
|
||||||
|
def is_bot_by_exchange_name(self, e_name):
|
||||||
|
sql = "SELECT id FROM oh_my_bot_admin.exchange WHERE `name` = '%s';" % e_name
|
||||||
|
res = self.execute(sql)
|
||||||
|
|
||||||
|
sql = "SELECT id FROM oh_my_bot_admin.bot WHERE `e_i`='%s' AND `status` = 'Y';" % res[0]['id']
|
||||||
|
res = self.execute(sql)
|
||||||
|
|
||||||
|
if res is ():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def is_simulated_item_per_hour(self, job, t_time):
|
||||||
|
sql = "SELECT * FROM oh_my_bot_admin.sumul_item_for_time " \
|
||||||
|
"WHERE `job`='%s' and `time_unit` = '%s';" % (job, t_time)
|
||||||
|
|
||||||
|
res = self.execute(sql)
|
||||||
|
|
||||||
|
if res is ():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def insert_item_from_simul_item_per_hour(self, job, t_time):
|
||||||
|
sql = "INSERT INTO `oh_my_bot_admin`.`sumul_item_for_time` (`job`, `time_unit`) VALUES ('%s', '%s');" % (
|
||||||
|
job, t_time)
|
||||||
|
|
||||||
|
return self.execute(sql)
|
||||||
|
|
||||||
|
def remove_item_from_simul_item_per_hour(self, job):
|
||||||
|
sql = "DELETE FROM `oh_my_bot_admin`.`sumul_item_for_time` WHERE (`job` = '%s');" % job
|
||||||
|
|
||||||
|
return self.execute(sql)
|
||||||
5
desktop.ini
Normal file
5
desktop.ini
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[.ShellClassInfo]
|
||||||
|
InfoTip=<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>¶<EFBFBD><C2B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>˴ϴ<CBB4>.
|
||||||
|
IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
|
||||||
|
IconIndex=16
|
||||||
|
|
||||||
1649
final_backtest.py
Normal file
1649
final_backtest.py
Normal file
File diff suppressed because it is too large
Load Diff
1575
indicator_backtest.py
Normal file
1575
indicator_backtest.py
Normal file
File diff suppressed because it is too large
Load Diff
913
indicator_util.py
Normal file
913
indicator_util.py
Normal file
@@ -0,0 +1,913 @@
|
|||||||
|
from pyti import chande_momentum_oscillator
|
||||||
|
import talib, numpy, math
|
||||||
|
from signal_helper import *
|
||||||
|
|
||||||
|
from trade.candle import CandlePatterns
|
||||||
|
|
||||||
|
|
||||||
|
def get_indicators_values(t_data, indicators):
|
||||||
|
r_data = pd.DataFrame()
|
||||||
|
|
||||||
|
date = t_data.Volume # for test
|
||||||
|
close = t_data.Close
|
||||||
|
low = t_data.Low
|
||||||
|
high = t_data.High
|
||||||
|
open = t_data.Open
|
||||||
|
volume = t_data.Volume
|
||||||
|
|
||||||
|
cp = CandlePatterns()
|
||||||
|
_positive_patterns = cp.get_long_patterns()
|
||||||
|
_negative_patterns = cp.get_short_patterns()
|
||||||
|
_fence_patterns = cp.get_fence_patterns()
|
||||||
|
|
||||||
|
|
||||||
|
for p in indicators:
|
||||||
|
indicator_info = None
|
||||||
|
indicator = None
|
||||||
|
values = None
|
||||||
|
f = None
|
||||||
|
|
||||||
|
# data = []
|
||||||
|
data = [None] * len(close)
|
||||||
|
indicator, values = list(p.items())[0]
|
||||||
|
|
||||||
|
if type(values) == int:
|
||||||
|
indicator_info = indicator +"_"+ str(values)
|
||||||
|
elif type(values) == list:
|
||||||
|
indicator_info = indicator+"_"+"_".join(map(str, values))
|
||||||
|
|
||||||
|
if indicator != 'DI' and hasattr(talib, indicator):
|
||||||
|
f = getattr(talib, indicator)
|
||||||
|
|
||||||
|
if indicator is 'CDLPTN':
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(0, len(close)):
|
||||||
|
if t_data.Candle_pattern[i] is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pattern, value = list(t_data.Candle_pattern[i].items())[0]
|
||||||
|
|
||||||
|
if pattern in _fence_patterns: # 중립 패턴
|
||||||
|
is_trade = is_trade_fence_pattern(pattern, value)
|
||||||
|
elif pattern in _positive_patterns: # 매수 패턴
|
||||||
|
is_trade = True
|
||||||
|
elif pattern in _negative_patterns: # 매도 패턴
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
# 보조지표 추가 시작
|
||||||
|
|
||||||
|
elif indicator is 'STOCH': # 주도 지표
|
||||||
|
slowk, slowd = f(high, low, close, fastk_period=values[0],
|
||||||
|
slowk_period=values[1], slowd_period=values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(slowk).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(slowk)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(slowk[i], slowk[i - 1], slowd[i], slowd[i - 1]) and slowd[i] < 50:
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(slowd[i], slowd[i - 1], slowk[i], slowk[i - 1]) and slowd[i] > 50:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'ADX' or indicator is 'ADXR': # 베이스 지표
|
||||||
|
res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] < 13:
|
||||||
|
t_d.append(True)
|
||||||
|
elif res_arr[i] > 45:
|
||||||
|
t_d.append(False)
|
||||||
|
|
||||||
|
data[i] = data[i] + t_d
|
||||||
|
|
||||||
|
elif indicator is 'DI': # 주도 지표
|
||||||
|
res_arr_plus = talib.PLUS_DI(high, low, close, timeperiod=values)
|
||||||
|
res_arr_minus = talib.MINUS_DI(high, low, close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr_plus).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr_plus)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(res_arr_plus[i], res_arr_plus[i - 1], res_arr_minus[i], res_arr_minus[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(res_arr_minus[i], res_arr_minus[i - 1], res_arr_plus[i], res_arr_plus[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'APO': # 주도 지표
|
||||||
|
res_arr = f(close, fastperiod=values[0], slowperiod=values[1], matype=0)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] > 0:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'AROON': # 주도 지표 => 추격
|
||||||
|
aroondown, aroonup = f(high, low, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(aroondown).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(aroondown)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(aroonup[i], aroonup[i - 1], aroondown[i], aroondown[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(aroondown[i], aroondown[i - 1], aroonup[i], aroonup[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'AROONOSC': # 베이스 지표 - 추세
|
||||||
|
res_arr = f(high, low, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 0 초과 상승추세, 0 미만 하락 추세
|
||||||
|
if res_arr[i] > 0 and res_arr[i - 1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0 and res_arr[i - 1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'BOP': # 베이스 지표
|
||||||
|
res_arr = f(open, high, low, close)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] > 0 and res_arr[i - 1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0 and res_arr[i - 1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'CCI': # 베이스 지표 / 추세
|
||||||
|
res_arr = f(close, close, close, timeperiod=values)
|
||||||
|
# res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 베이스
|
||||||
|
if res_arr[i] < -100: # 과매도 -100 / 과매수 +100
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] > 100:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'CMO': # 베이스 지표 / 추세 => 수치 불일치
|
||||||
|
# res_arr = f(close, timeperiod=values)
|
||||||
|
res_arr = chande_momentum_oscillator.chande_momentum_oscillator(close, values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] > 0: # 과매도 -100 / 과매수 +100
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MACD': # 주도 지표
|
||||||
|
macd, macdsignal, macdhist = f(close,
|
||||||
|
fastperiod=values[0],
|
||||||
|
slowperiod=values[1],
|
||||||
|
signalperiod=values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(macd).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(macd)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(macd[i], macd[i - 1], macdsignal[i], macdsignal[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(macdsignal[i], macdsignal[i - 1], macd[i], macd[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MACDFIX': # 주도 지표
|
||||||
|
macd, macdsignal, macdhist = f(close, signalperiod=values)
|
||||||
|
start = 1 + numpy.isnan(macd).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(macd)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(macd[i], macd[i - 1], macdsignal[i], macdsignal[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(macdsignal[i], macdsignal[i - 1], macd[i], macd[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MFI': # 베이스 지표 / 역추세
|
||||||
|
res_arr = f(close, close, close, volume, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 베이스
|
||||||
|
if res_arr[i] > 20 and res_arr[i - 1] < 20:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] > 80 and res_arr[i - 1] < 80:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MOM': # 베이스 지표 / 추세
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 베이스
|
||||||
|
if res_arr[i] > 0 and res_arr[i - 1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0 and res_arr[i - 1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'PPO': # 주도 지표
|
||||||
|
ppo = f(close, fastperiod=values[0], slowperiod=values[1], matype=1)
|
||||||
|
ppo_slow = moving_average(ppo, values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(ppo_slow).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
for i in range(start, len(ppo)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(ppo[i], ppo[i - 1], ppo_slow[i], ppo_slow[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(ppo_slow[i], ppo_slow[i - 1], ppo[i], ppo[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'ROC': # 베이스 지표 / 추세
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] > 0 and res_arr[i - 1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < 0 and res_arr[i - 1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'ROCP' or indicator is 'ROCR' or indicator is 'ROCR100': # 베이스 지표 / 추세
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 베이스
|
||||||
|
if res_arr[i] > 0:
|
||||||
|
t_d.append(True)
|
||||||
|
elif res_arr[i] < 0:
|
||||||
|
t_d.append(False)
|
||||||
|
|
||||||
|
data[i] = data[i] + t_d
|
||||||
|
|
||||||
|
elif indicator is 'STOCHF': # 주도 지표
|
||||||
|
fastk, fastd = f(high, low, close, fastk_period=values[0], fastd_period=values[1], fastd_matype=0)
|
||||||
|
start = 1 + numpy.isnan(fastk).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(fastk)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 높은 거래 빈도로 인해 매매조건 추가
|
||||||
|
if fastd[i] < 40 and crossover(fastk[i], fastk[i - 1], fastd[i], fastd[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif fastd[i] > 60 and crossover(fastd[i], fastd[i - 1], fastk[i], fastk[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'STOCHRSI': # 주도 지표
|
||||||
|
rsi_k, rsi_d = stoch_rsi(close, values[0], values[1], values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(rsi_d).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(rsi_d)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if rsi_k[i] < 25 and crossover(rsi_k[i], rsi_k[i - 1], rsi_d[i], rsi_d[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif rsi_k[i] > 75 and crossover(rsi_d[i], rsi_d[i - 1], rsi_k[i], rsi_k[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'TRIX': # 베이스 지표 / 역추세 => 매수/매도 시점 괜찮다
|
||||||
|
trix = f(close, timeperiod=values[0])
|
||||||
|
trix_signal = moving_average(trix, values[1])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(trix_signal).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(trix_signal)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if crossover(trix[i], trix[i - 1], trix_signal[i], trix_signal[i - 1]):
|
||||||
|
is_trade = True
|
||||||
|
elif crossover(trix_signal[i], trix_signal[i - 1], trix[i], trix[i - 1]):
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'ULTOSC': # 주도 지표
|
||||||
|
res_arr = f(high, low, close, timeperiod1=values[0], timeperiod2=values[1], timeperiod3=values[2])
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] < 30:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] > 70:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'WILLR': # 베이스 지표 => 강세장에서 효과를 발휘
|
||||||
|
res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# print(date[i], res_arr[i])
|
||||||
|
if res_arr[i] > -20:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] < -80:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'BBANDS': # 베이스 지표 / 역추세
|
||||||
|
upperband, middleband, lowerband = f(close, timeperiod=values[0], nbdevup=values[1], nbdevdn=values[1],
|
||||||
|
matype=0)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(upperband).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(upperband)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if high[i] > upperband[i] and upperband[i] > upperband[i - 1] and low[i] > middleband[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif low[i] < lowerband[i] and lowerband[i] < lowerband[i - 1] and high[i] < middleband[i]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'EMA' or \
|
||||||
|
indicator is 'DEMA' or \
|
||||||
|
indicator is 'MA' or \
|
||||||
|
indicator is 'SMA': # 주도 지표 / 추세
|
||||||
|
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if close[i] > res_arr[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif close[i] < res_arr[i]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MAMA': # 주도 지표 / 추세
|
||||||
|
mama, fama = f(close, fastlimit=values[0], slowlimit=values[1])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(mama).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(mama)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if mama[i] > fama[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif mama[i] < fama[i]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'MIDPOINT': # 주도 지표 => 추세
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if close[i] > res_arr[i]:
|
||||||
|
t_d.append(True)
|
||||||
|
elif close[i] < res_arr[i]:
|
||||||
|
t_d.append(False)
|
||||||
|
|
||||||
|
data[i] = data[i] + t_d
|
||||||
|
|
||||||
|
elif indicator is 'MIDPRICE': # 주도 지표 => 추세
|
||||||
|
res_arr = f(high, low, timeperiod=values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if close[i] > res_arr[i]:
|
||||||
|
t_d.append(True)
|
||||||
|
elif close[i] < res_arr[i]:
|
||||||
|
t_d.append(False)
|
||||||
|
|
||||||
|
data[i] = data[i] + t_d
|
||||||
|
|
||||||
|
elif indicator is 'SAR': # 주도 지표 => 역추세
|
||||||
|
res_arr = f(high, low, acceleration=values[0], maximum=values[1])
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if close[i] > res_arr[i] and close[i - 1] < res_arr[i - 1]:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] > close[i] and res_arr[i - 1] < close[i - 1]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'T3': # indicator is 'WMA': # 주도 지표 => 추세
|
||||||
|
long = f(close, timeperiod=values[0])
|
||||||
|
short = f(close, timeperiod=values[1])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(long).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(long)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if short[i] > long[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif short[i] < long[i]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'TRIMA' or indicator is 'WMA': # 주도 지표 => 추세
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if close[i] > res_arr[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif close[i] < res_arr[i]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'AD': # 베이스 지표 - 거래량
|
||||||
|
res_arr = f(high, low, close, volume)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 가격-거래량 반비례 => 추세 반전
|
||||||
|
if close[i] > close[i - 1] and res_arr[i] < res_arr[i - 1]:
|
||||||
|
is_trade = False
|
||||||
|
elif close[i] < close[i - 1] and res_arr[i] > res_arr[i - 1]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'ADOSC_DIV': # 베이스 지표 - 거래량
|
||||||
|
res_arr = talib.ADOSC(high, low, close, volume, fastperiod=values[0], slowperiod=values[1])
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'DMI': # 주도 지표 - 추세(추매)
|
||||||
|
adx = talib.ADX(high, low, close, timeperiod=values)
|
||||||
|
plus_di = talib.PLUS_DI(high, low, close, timeperiod=values)
|
||||||
|
minus_di = talib.MINUS_DI(high, low, close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(adx).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(adx)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if adx[i] > adx[i - 1]:
|
||||||
|
if plus_di[i] > minus_di[i] and adx[i] > minus_di[i]:
|
||||||
|
is_trade = True
|
||||||
|
elif minus_di[i] > minus_di[i - 1]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'OBV': # 베이스 지표 / 역추세
|
||||||
|
res_arr = f(close, volume)
|
||||||
|
cdl_cnt = 20
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
if start < cdl_cnt:
|
||||||
|
start = cdl_cnt
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
# 고가 갱신 시 매도 / 저가 갱신 시 매수
|
||||||
|
if res_arr[i] > max(res_arr[i - cdl_cnt:i]):
|
||||||
|
t_d.append(False)
|
||||||
|
elif res_arr[i] < min(res_arr[i - cdl_cnt:i]):
|
||||||
|
t_d.append(True)
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'RSI': # 베이스 지표
|
||||||
|
res_arr = f(close, values)
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr[i] < 30:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr[i] > 70:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator is 'RSI_DIV': # 주도 지표 / 역추세 - 다이버전스
|
||||||
|
f = getattr(talib, 'RSI')
|
||||||
|
res_arr = f(close, values)
|
||||||
|
|
||||||
|
cdl_cnt = 100
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
if start < cdl_cnt:
|
||||||
|
start = cdl_cnt
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
if is_divergence is True and res_arr[i - 1] < 50:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False and res_arr[i - 1] > 50:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'OBV_DIV':
|
||||||
|
f = getattr(talib, 'OBV')
|
||||||
|
res_arr = f(close, volume)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'WILLR_DIV':
|
||||||
|
f = getattr(talib, 'WILLR')
|
||||||
|
res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True and res_arr[i - 1] < -50:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False and res_arr[i - 1] > -50:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'ADX_DIV':
|
||||||
|
f = getattr(talib, 'ADX')
|
||||||
|
res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'BOP_DIV':
|
||||||
|
f = getattr(talib, 'BOP')
|
||||||
|
res_arr = f(open, high, low, close)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'CCI_DIV':
|
||||||
|
f = getattr(talib, 'CCI')
|
||||||
|
res_arr = f(high, low, close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True and res_arr[i - 1] < -100:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False and res_arr[i - 1] > 100:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'MFI_DIV':
|
||||||
|
f = getattr(talib, 'MFI')
|
||||||
|
res_arr = f(close, close, close, volume, timeperiod=values)
|
||||||
|
# res_arr = f(high, low, close, volume, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
# is_divergence = is_divergence_v3(i, close, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True and res_arr[i - 1]:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False and res_arr[i - 1]:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
''' CMO 지표 수치 불일치 '''
|
||||||
|
if indicator is 'CMO_DIV':
|
||||||
|
res_arr = chande_momentum_oscillator.chande_momentum_oscillator(close, 9)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True and res_arr[i - 1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False and res_arr[i - 1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'MOM_DIV':
|
||||||
|
f = getattr(talib, 'MOM')
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True: # and res_arr[i-1] < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False: # and res_arr[i-1] > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'ROC_DIV':
|
||||||
|
f = getattr(talib, 'ROC')
|
||||||
|
res_arr = f(close, timeperiod=values)
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True: # and round(res_arr[i-1]) < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False: # and round(res_arr[i-1]) > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'STOCH_DIV':
|
||||||
|
f = getattr(talib, 'STOCH')
|
||||||
|
|
||||||
|
# res_arr, slowd = f(high, low, close,
|
||||||
|
slowk, res_arr = f(high, low, close,
|
||||||
|
fastk_period=values[0],
|
||||||
|
slowk_period=values[1],
|
||||||
|
slowd_period=values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
|
||||||
|
if is_divergence is True: # and round(res_arr[i-1]) < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False: # and round(res_arr[i-1]) > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
if indicator is 'STOCHRSI_DIV':
|
||||||
|
rsi_k, res_arr = stoch_rsi(close, values[0], values[1], values[2])
|
||||||
|
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
i = i - 1
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v3(i, close, res_arr, date)
|
||||||
|
if is_divergence is True:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
|
||||||
|
elif indicator is 'ULTOSC_DIV': # 주도 지표
|
||||||
|
res_arr = talib.ULTOSC(high, low, close, timeperiod1=values[0], timeperiod2=values[1],
|
||||||
|
timeperiod3=values[2])
|
||||||
|
start = 1 + numpy.isnan(res_arr).sum()
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(start, len(res_arr)):
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
is_divergence = is_divergence_v2(i, high, low, res_arr, date)
|
||||||
|
if is_divergence is True: # and round(res_arr[i-1]) < 0:
|
||||||
|
is_trade = True
|
||||||
|
elif is_divergence is False: # and round(res_arr[i-1]) > 0:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
data[i] = is_trade
|
||||||
|
|
||||||
|
elif indicator == 'HEI': # heikenashi
|
||||||
|
df = pd.DataFrame(
|
||||||
|
{'open': open, 'high': high, 'low': low, 'close': close, 'volume': volume},
|
||||||
|
).copy()
|
||||||
|
res_arr = heikin_ashi(df)
|
||||||
|
|
||||||
|
is_trade = None
|
||||||
|
|
||||||
|
for i in range(1, len(res_arr)):
|
||||||
|
|
||||||
|
t_d = []
|
||||||
|
|
||||||
|
if res_arr.loc[i]['close'] > res_arr.loc[i]['open'] and \
|
||||||
|
res_arr.loc[i - 1]['open'] > res_arr.loc[i - 1]['close']:
|
||||||
|
is_trade = True
|
||||||
|
elif res_arr.loc[i]['open'] > res_arr.loc[i]['close'] and \
|
||||||
|
res_arr.loc[i - 1]['close'] > res_arr.loc[i - 1]['open']:
|
||||||
|
is_trade = False
|
||||||
|
|
||||||
|
t_d.append(is_trade)
|
||||||
|
|
||||||
|
data[i] = data[i] + t_d
|
||||||
|
|
||||||
|
r_data[indicator_info] = data
|
||||||
|
|
||||||
|
return r_data
|
||||||
364
new_backtest.py
Normal file
364
new_backtest.py
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
import sys, time, random, os
|
||||||
|
import json
|
||||||
|
|
||||||
|
# load strategy
|
||||||
|
from backtesting import Backtest # short-tp error
|
||||||
|
|
||||||
|
# load functions
|
||||||
|
from signal_helper import *
|
||||||
|
|
||||||
|
# load db
|
||||||
|
from db import DB
|
||||||
|
|
||||||
|
from itertools import combinations
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
|
from strategy import StrategyCandlePattern
|
||||||
|
from multiprocessing import Process, freeze_support
|
||||||
|
|
||||||
|
import gc
|
||||||
|
|
||||||
|
|
||||||
|
class Simulator:
|
||||||
|
_db = DB()
|
||||||
|
data = None
|
||||||
|
|
||||||
|
# set config
|
||||||
|
top_cash = 0
|
||||||
|
top_profit = 20
|
||||||
|
top_win_rate = 60
|
||||||
|
# best_pattern_arr = []
|
||||||
|
|
||||||
|
cash = float(1000)
|
||||||
|
commission = float(.005)
|
||||||
|
|
||||||
|
# set strategy
|
||||||
|
strategy = StrategyCandlePattern
|
||||||
|
|
||||||
|
# [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)
|
||||||
|
|
||||||
|
filtered_signal_indicators = []
|
||||||
|
|
||||||
|
# 시그널 보조 지표
|
||||||
|
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},
|
||||||
|
]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
print('Started Simulating.', datetime.now())
|
||||||
|
|
||||||
|
zzz = True
|
||||||
|
|
||||||
|
for item in self._db.get_cron_list():
|
||||||
|
# 이미 작동중인 봇이 있을 경우 제외 - 거래소 기준
|
||||||
|
if self.is_bot_by_exchange_name(item):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if item['time_unit'] == 'hour':
|
||||||
|
for t_time in ['hour6', 'hour4']: # 4시간, 6시간봉 시뮬레이팅
|
||||||
|
self.simulrating_by_time(item, t_time)
|
||||||
|
|
||||||
|
self._db.update_simul_init_value(item['job'])
|
||||||
|
return self.reboot()
|
||||||
|
|
||||||
|
self.simulrating_by_time(item, item['time_unit'])
|
||||||
|
self._db.update_simul_init_value(item['job'])
|
||||||
|
|
||||||
|
return self.reboot()
|
||||||
|
|
||||||
|
if zzz:
|
||||||
|
print('There is no list of items to run.', datetime.now())
|
||||||
|
time.sleep(43200)
|
||||||
|
|
||||||
|
return self.reboot()
|
||||||
|
|
||||||
|
# multi Treading - item
|
||||||
|
def simulrating_by_item(self, item, all_indicators, idx, t_time):
|
||||||
|
start_time = self.get_this_time()
|
||||||
|
|
||||||
|
|
||||||
|
print('작업 시작 : %s / 지표 수 : %s / 시간프레임 : %s' % (item['job'], idx, t_time))
|
||||||
|
|
||||||
|
# get top_profit and win rate from item-t_time
|
||||||
|
top_info = self._db.select_top_date(item['job'])
|
||||||
|
min_profit = 17
|
||||||
|
if str(item['trade_type']) == 'double':
|
||||||
|
min_profit = 35
|
||||||
|
|
||||||
|
# bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
|
||||||
|
|
||||||
|
for indicators in list(combinations(all_indicators, idx)):
|
||||||
|
for profit in self.profit_arr:
|
||||||
|
for loss in self.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
|
||||||
|
|
||||||
|
self.strategy.use_indicators = list(indicators)
|
||||||
|
self.strategy.up_target = float(profit)
|
||||||
|
self.strategy.down_target = float(loss)
|
||||||
|
self.strategy.trade_type = str(item['trade_type'])
|
||||||
|
|
||||||
|
bt = Backtest(self.data, self.strategy, cash=self.cash, commission=self.commission)
|
||||||
|
bt.run()
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if bt._results['# Trades'] >= 5 and bt._results['Return [%]'] > min_profit and \
|
||||||
|
(bt._results['Return [%]'] > self.top_profit or float(bt._results['Win Rate [%]']) >= 100):
|
||||||
|
filename = 'chart/' + str(item['job']).replace('/', '_') + '_' + str(t_time) + '_' + str(
|
||||||
|
time.time())
|
||||||
|
|
||||||
|
self._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(self.strategy.use_indicators),
|
||||||
|
'use_pattern_cnt': len(self.strategy.use_indicators),
|
||||||
|
'filename': filename,
|
||||||
|
'period_cycle': item['period_cycle'],
|
||||||
|
'trade_type': item['trade_type'],
|
||||||
|
})
|
||||||
|
|
||||||
|
if bt._results['Return [%]'] > self.top_profit:
|
||||||
|
self.top_profit = bt._results['Return [%]']
|
||||||
|
# print('-' * 60)
|
||||||
|
# print('트레이딩 종류 :', item['trade_type'])
|
||||||
|
# print('시간봉 :', t_time)
|
||||||
|
# print('지표 조합 개수 :', idx)
|
||||||
|
# print('지표 :', self.strategy.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 [%]'] > 40 and float(
|
||||||
|
bt._results['Win Rate [%]']) >= 80: # 최상의 수익인 경우 차트 저장
|
||||||
|
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())
|
||||||
|
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
# multi Treading - fackage
|
||||||
|
def simulrating_by_item_fackage(self, item, t_time):
|
||||||
|
start_time = self.get_this_time()
|
||||||
|
top_info = self._db.select_top_date(item['job'])
|
||||||
|
|
||||||
|
min_profit = 7
|
||||||
|
if str(item['trade_type']) == 'double':
|
||||||
|
min_profit = 16
|
||||||
|
|
||||||
|
for indicator in self.signal_indicators:
|
||||||
|
for profit in self.profit_arr:
|
||||||
|
for loss in self.loss_arr:
|
||||||
|
self.strategy.use_indicators = [indicator]
|
||||||
|
self.strategy.up_target = float(profit)
|
||||||
|
self.strategy.down_target = float(loss)
|
||||||
|
self.strategy.trade_type = str(item['trade_type'])
|
||||||
|
|
||||||
|
bt = Backtest(self.data, self.strategy, cash=self.cash, commission=self.commission)
|
||||||
|
bt.run()
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if float(bt._results['Return [%]']) > min_profit and float(bt._results['# Trades']) >= 5:
|
||||||
|
if indicator not in self.filtered_signal_indicators:
|
||||||
|
self.filtered_signal_indicators.append(indicator)
|
||||||
|
|
||||||
|
bt = None
|
||||||
|
del [[bt]]
|
||||||
|
|
||||||
|
e = int(time.time() - start_time)
|
||||||
|
print('시그널 지표 필터링 완료 :', '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
|
||||||
|
print('지표 총합 :', len(self.filtered_signal_indicators) + len(self.base_indicators))
|
||||||
|
print('필터 지표 리스트', self.filtered_signal_indicators)
|
||||||
|
|
||||||
|
if len(self.filtered_signal_indicators) < 2:
|
||||||
|
print(item, t_time, '- 수익 모델 조건을 만족하는 전략이 없습니다.')
|
||||||
|
return
|
||||||
|
|
||||||
|
all_indicators = self.filtered_signal_indicators[::-1] + self.base_indicators
|
||||||
|
|
||||||
|
joined = []
|
||||||
|
for idx in range(2, 5):
|
||||||
|
_p = Process(target=self.simulrating_by_item, args=(item, all_indicators, idx, t_time,))
|
||||||
|
_p.start()
|
||||||
|
joined.append(_p)
|
||||||
|
|
||||||
|
# 프로세스 조인
|
||||||
|
for _p in joined:
|
||||||
|
_p.join()
|
||||||
|
|
||||||
|
|
||||||
|
def simulrating_by_time(self, item, t_time):
|
||||||
|
self.top_profit = 20
|
||||||
|
self.top_win_rate = 60
|
||||||
|
|
||||||
|
'''
|
||||||
|
"day": "24H",
|
||||||
|
"hour12": "12H",
|
||||||
|
"hour6": "06H",
|
||||||
|
"hour": "01H",
|
||||||
|
"minute30": "30M",
|
||||||
|
"minute10": "10M",
|
||||||
|
"minute5": "05M",
|
||||||
|
"minute3": "03M",
|
||||||
|
'''
|
||||||
|
data = self._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=540)
|
||||||
|
df = df.resample(time_type[t_time], label='left', base=540).agg(ohlc_dict)
|
||||||
|
|
||||||
|
df = df[:-1]
|
||||||
|
|
||||||
|
self.data = df
|
||||||
|
self.simulrating_by_item_fackage(item, t_time)
|
||||||
|
self.data = None
|
||||||
|
|
||||||
|
def get_this_time(self):
|
||||||
|
return time.time()
|
||||||
|
|
||||||
|
def is_bot_by_exchange_name(self, item):
|
||||||
|
target = item['job'].split('/')
|
||||||
|
|
||||||
|
return self._db.is_bot_by_exchange_name(target[1])
|
||||||
|
|
||||||
|
# reboot for windows
|
||||||
|
def reboot(self):
|
||||||
|
print('재부팅 예약 되었습니다.')
|
||||||
|
return os.system("shutdown -t 60 -r -f")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
b = Simulator()
|
||||||
|
b.run()
|
||||||
335
power_backtest.py
Normal file
335
power_backtest.py
Normal 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 매도로 구성) => 보류 => 보조지표를 시그널로 활용
|
||||||
|
# 봉별 단일 시그널로 활용할 지, 복합 시그널(두개 이상)로 활용 할지..
|
||||||
|
# 매매 패턴의 경우 데이터에 존재하는지 체크 후 패턴 리스트에 추가
|
||||||
|
'''
|
||||||
344
profile_backtest.py
Normal file
344
profile_backtest.py
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
import sys, time, random, os
|
||||||
|
import json
|
||||||
|
|
||||||
|
# load strategy
|
||||||
|
# from strategies.indicator import StrategyIndicator
|
||||||
|
from backtesting import Backtest # short-tp error
|
||||||
|
import tracemalloc
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from cProfile import Profile
|
||||||
|
from pstats import Stats
|
||||||
|
|
||||||
|
import gc
|
||||||
|
|
||||||
|
warnings.filterwarnings(action='ignore')
|
||||||
|
|
||||||
|
|
||||||
|
_db = DB()
|
||||||
|
|
||||||
|
# set config
|
||||||
|
start_time = time.time()
|
||||||
|
top_cash = 0
|
||||||
|
top_profit = 20
|
||||||
|
top_win_rate = 60
|
||||||
|
# best_pattern_arr = []
|
||||||
|
|
||||||
|
# [0, 0.23, 0.38, 0.5, 0.61, 0.78, 0.88, 1]
|
||||||
|
# pivonachi
|
||||||
|
profit_arr = [0] + pivo(60)
|
||||||
|
loss_arr = [0] + pivo(60)
|
||||||
|
|
||||||
|
# 시그널 보조 지표
|
||||||
|
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},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# 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 = 7
|
||||||
|
if str(item['trade_type']) == 'double':
|
||||||
|
min_profit = 14
|
||||||
|
|
||||||
|
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'])
|
||||||
|
|
||||||
|
|
||||||
|
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]] # 112.3MB
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
df = None
|
||||||
|
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]
|
||||||
|
|
||||||
|
return 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():
|
||||||
|
return 'reboot!'
|
||||||
|
# return os.system("shutdown -t 60 -r -f")
|
||||||
|
|
||||||
|
def start_backtest():
|
||||||
|
global _db
|
||||||
|
|
||||||
|
print('Started Simulating.', datetime.now())
|
||||||
|
|
||||||
|
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', 'hour4']: # 4시간, 6시간봉 시뮬레이팅
|
||||||
|
return simulrating_by_time(item, t_time)
|
||||||
|
|
||||||
|
return reboot()
|
||||||
|
|
||||||
|
simulrating_by_time(item, item['time_unit'])
|
||||||
|
|
||||||
|
return reboot()
|
||||||
|
|
||||||
|
return reboot()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
tracemalloc.start()
|
||||||
|
freeze_support()
|
||||||
|
start_backtest()
|
||||||
|
|
||||||
|
# profiler = Profile()
|
||||||
|
# profiler.runcall(start_backtest)
|
||||||
|
#
|
||||||
|
# stats = Stats(profiler)
|
||||||
|
# stats.strip_dirs()
|
||||||
|
# stats.sort_stats()
|
||||||
|
# stats.print_stats()
|
||||||
|
|
||||||
|
'''
|
||||||
|
메모리 절약해주는 라이브러리 사용하기
|
||||||
|
from profile import profile
|
||||||
|
from time import sleep
|
||||||
|
from sklearn import datasets # Just an example of 3rd party function call
|
||||||
|
|
||||||
|
|
||||||
|
# Method 1
|
||||||
|
run_profiling = profile(datasets.load_digits)
|
||||||
|
data = run_profiling()
|
||||||
|
|
||||||
|
# Method 2
|
||||||
|
@profile
|
||||||
|
def my_function():
|
||||||
|
# do some stuff
|
||||||
|
a_list = []
|
||||||
|
for i in range(1,100000):
|
||||||
|
a_list.append(i)
|
||||||
|
return a_list
|
||||||
|
|
||||||
|
|
||||||
|
res = my_function()
|
||||||
|
'''
|
||||||
40
requirements.txt
Normal file
40
requirements.txt
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
APScheduler==3.10.4
|
||||||
|
backports.zoneinfo==0.2.1
|
||||||
|
Backtesting==0.1.2
|
||||||
|
backtrader==1.9.74.123
|
||||||
|
beautifulsoup4==4.12.3
|
||||||
|
bokeh==2.4.3
|
||||||
|
bs4==0.0.2
|
||||||
|
certifi==2024.2.2
|
||||||
|
cffi==1.15.1
|
||||||
|
charset-normalizer==3.3.2
|
||||||
|
gevent==22.10.2
|
||||||
|
greenlet==3.0.3
|
||||||
|
idna==3.6
|
||||||
|
importlib-metadata==6.7.0
|
||||||
|
Jinja2==3.1.3
|
||||||
|
MarkupSafe==2.1.5
|
||||||
|
numpy==1.21.6
|
||||||
|
packaging==23.2
|
||||||
|
pandas==0.25.1
|
||||||
|
Pillow==9.5.0
|
||||||
|
pycparser==2.21
|
||||||
|
PyMySQL==1.1.0
|
||||||
|
python-dateutil==2.8.2
|
||||||
|
pyti==0.2.2
|
||||||
|
pytz==2024.1
|
||||||
|
PyYAML==6.0.1
|
||||||
|
requests==2.31.0
|
||||||
|
scipy==1.3.1
|
||||||
|
six==1.16.0
|
||||||
|
soupsieve==2.4.1
|
||||||
|
SQLAlchemy==2.0.25
|
||||||
|
tornado==6.2
|
||||||
|
typing_extensions==4.7.1
|
||||||
|
tzdata==2023.4
|
||||||
|
tzlocal==5.1
|
||||||
|
urllib3==1.25.4
|
||||||
|
websocket==0.2.1
|
||||||
|
zipp==3.15.0
|
||||||
|
zope.event==5.0
|
||||||
|
zope.interface==6.1
|
||||||
666
signal_helper.py
Normal file
666
signal_helper.py
Normal file
@@ -0,0 +1,666 @@
|
|||||||
|
import talib
|
||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
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.reset_index(level=0, inplace=True)
|
||||||
|
# t_col.insert(0, 'Date')
|
||||||
|
|
||||||
|
data.columns = t_col
|
||||||
|
|
||||||
|
def heikin_ashi(df):
|
||||||
|
heikin_ashi_df = pd.DataFrame(index=df.index.values, columns=['open', 'high', 'low', 'close'])
|
||||||
|
|
||||||
|
heikin_ashi_df['close'] = (df['open'] + df['high'] + df['low'] + df['close']) / 4
|
||||||
|
|
||||||
|
for i in range(len(df)):
|
||||||
|
if i == 0:
|
||||||
|
heikin_ashi_df.iat[0, 0] = df['open'].iloc[0]
|
||||||
|
else:
|
||||||
|
heikin_ashi_df.iat[i, 0] = (heikin_ashi_df.iat[i - 1, 0] + heikin_ashi_df.iat[i - 1, 3]) / 2
|
||||||
|
|
||||||
|
heikin_ashi_df['high'] = heikin_ashi_df.loc[:, ['open', 'close']].join(df['high']).max(axis=1)
|
||||||
|
|
||||||
|
heikin_ashi_df['low'] = heikin_ashi_df.loc[:, ['open', 'close']].join(df['low']).min(axis=1)
|
||||||
|
|
||||||
|
return heikin_ashi_df
|
||||||
|
|
||||||
|
def get_candle_pattern_arr(data=None, use_patterns=talib.get_function_groups()['Pattern Recognition']):
|
||||||
|
if data is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
r_data = [None] * len(data.Close)
|
||||||
|
|
||||||
|
for p in use_patterns:
|
||||||
|
f = getattr(talib, p)
|
||||||
|
res_arr = f(data.Open, data.High, data.Low, data.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:
|
||||||
|
r_data[i] = {p: int(res_arr[i])}
|
||||||
|
|
||||||
|
return r_data
|
||||||
|
|
||||||
|
|
||||||
|
def crossover(k, pre_k, d, pre_d) -> bool:
|
||||||
|
try:
|
||||||
|
return k > d and pre_k < pre_d
|
||||||
|
except IndexError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_divergence(i, cdl_cnt, low, high, res_arr):
|
||||||
|
# 저가 갱신 / 지표 저점 상승 : 매수
|
||||||
|
if min(low[i - cdl_cnt:i]) < min(low[i - (cdl_cnt * 2):i - cdl_cnt + 1]) and \
|
||||||
|
min(res_arr[i - cdl_cnt:i]) > min(res_arr[i - (cdl_cnt * 2):i - cdl_cnt + 1]):
|
||||||
|
return True
|
||||||
|
# 고가 갱신 / 지표 고점 하락 : 매도
|
||||||
|
elif max(high[i - cdl_cnt:i]) > max(high[i - (cdl_cnt * 2):i - cdl_cnt + 1]) and \
|
||||||
|
max(res_arr[i - cdl_cnt:i]) < max(res_arr[i - (cdl_cnt * 2):i - cdl_cnt + 1]):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def moving_average(data, n=3) :
|
||||||
|
return talib.EMA(data, timeperiod=n) # 이동평균
|
||||||
|
|
||||||
|
|
||||||
|
def stoch_rsi(data, rsi_period=14, stoch_period=14, slowk_period=3):
|
||||||
|
# rsi = talib.RSI(close, 14)
|
||||||
|
# rsi_fastd, rsi_slowd = talib.STOCH(rsi, rsi, rsi, fastk_period=14, slowk_period=3, slowd_period=3,
|
||||||
|
# slowk_matype=0, slowd_matype=0)
|
||||||
|
|
||||||
|
rsi = talib.RSI(data, rsi_period)
|
||||||
|
return talib.STOCH(rsi, rsi, rsi, fastk_period=stoch_period, slowk_period=slowk_period, slowd_period=slowk_period,
|
||||||
|
slowk_matype=0, slowd_matype=0)
|
||||||
|
|
||||||
|
|
||||||
|
def is_trade_fence_pattern(pattern, value):
|
||||||
|
p = pattern.upper()
|
||||||
|
|
||||||
|
if p == "CDL3INSIDE":
|
||||||
|
if int(value) < 0:
|
||||||
|
return True
|
||||||
|
elif int(value) > 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDL3LINESTRIKE":
|
||||||
|
if int(value) < 0:
|
||||||
|
return True
|
||||||
|
elif int(value) > 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDL3OUTSIDE":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLABANDONEDBABY": # no data
|
||||||
|
return None
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLBELTHOLD":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLBREAKAWAY":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLCLOSINGMARUBOZU": # 거래 빈도 높고 60퍼 이상의 확률
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLCOUNTERATTACK":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLCONCEALBABYSWALL" or p == "CDLMATHOLD":
|
||||||
|
return None
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLENGULFING":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLGAPSIDESIDEWHITE":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLHARAMI":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLHARAMICROSS":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLHIKKAKEMOD" or p == "CDLHIKKAKE":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLKICKING" or p == "CDLKICKINGBYLENGTH":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLPIERCING":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLRISEFALL3METHODS":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLSEPARATINGLINES": # 역추세로 활용 보류
|
||||||
|
return None
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
elif p == "CDLSTALLEDPATTERN": # 역추세 - 시그널
|
||||||
|
return None
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLTASUKIGAP":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLUNIQUE3RIVER": # 역추세 - 시그널
|
||||||
|
return None
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLXSIDEGAP3METHODS":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLSTICKSANDWICH":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif p == "CDLTRISTAR":
|
||||||
|
if int(value) > 0:
|
||||||
|
return True
|
||||||
|
elif int(value) < 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_indicators_args(indicators):
|
||||||
|
res_data = []
|
||||||
|
|
||||||
|
for i in indicators:
|
||||||
|
if i is 'RSI' or i is 'RSI_DIV': # 베이스 지표
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
# e_range = [14]
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
# res_data = list(range(9, 31))
|
||||||
|
elif i is 'STOCH': # 주도 지표
|
||||||
|
# res_data.append({i: [5, 3, 3]})
|
||||||
|
# continue
|
||||||
|
f_range = list(range(5, 21))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(1, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(1, s_r + 1))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [f_r, s_r, t_r]})
|
||||||
|
|
||||||
|
elif i is 'ADX' or i is 'ADXR' or i is 'DMI': # 베이스 지표
|
||||||
|
e_range = list(range(9, 21))
|
||||||
|
# e_range = [14]DMI
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'DI': # Directional Indicator Plus/Minus # 주도 지표
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
# e_range = [14]
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'APO': # 주도 지표
|
||||||
|
f_range = list(range(17, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(10, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [s_r, f_r]})
|
||||||
|
|
||||||
|
elif i is 'AROON': # 주도 지표
|
||||||
|
e_range = list(range(11, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'AROONOSC': # 베이스 지표
|
||||||
|
e_range = list(range(3, 21))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'BOP': # 베이스
|
||||||
|
res_data.append({i: None})
|
||||||
|
|
||||||
|
elif i is 'CCI': # 베이스
|
||||||
|
e_range = list(range(11, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'CMO': # 베이스
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
# elif i is 'DX': # 베이스 / 추세 추종
|
||||||
|
# e_range = list(range(9, 31))
|
||||||
|
# for v in e_range:
|
||||||
|
# res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'MACD': # 주도 지표
|
||||||
|
f_range = list(range(11, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(9, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(7, s_r + 1))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [s_r, f_r, t_r]})
|
||||||
|
|
||||||
|
elif i is 'MACDFIX': # 주도 지표
|
||||||
|
e_range = list(range(5, 21))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'MFI': # 베이스 / 추세
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'MOM': # 베이스 / 역추세
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'PPO': # 주도 지표
|
||||||
|
f_range = list(range(9, 26))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(10, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [s_r, f_r]})
|
||||||
|
|
||||||
|
elif i is 'ROC' or i is 'ROCP' or i is 'WILLR' or i is 'MIDPOINT' or i is 'MIDPRICE': # 베이스 지표 / 추세
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
# elif i is 'ROCR':# 베이스 지표 / 추세 => 보류
|
||||||
|
# e_range = list(range(9, 31))
|
||||||
|
# for v in e_range:
|
||||||
|
# res_data.append({i: v}
|
||||||
|
|
||||||
|
# elif i is 'ROCR100':# 베이스 지표 / 추세 => 보류
|
||||||
|
# e_range = list(range(9, 31))
|
||||||
|
# for v in e_range:
|
||||||
|
# res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'STOCHF': # 주도 지표
|
||||||
|
f_range = list(range(5, 21))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(3, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'STOCHRSI': # 주도 지표
|
||||||
|
f_range = list(range(5, 21))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(3, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(3, s_r + 1))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [f_r, s_r, t_r]})
|
||||||
|
|
||||||
|
elif i is 'TRIX': # 베이스 지표 / 역추세
|
||||||
|
f_range = list(range(3, 36))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(2, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'ULTOSC': # 주도 지표
|
||||||
|
f_range = list(range(7, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(5, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(5, s_r + 1))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [t_r, s_r, f_r]})
|
||||||
|
|
||||||
|
elif i is 'BBANDS': # 베이스 지표
|
||||||
|
f_range = list(range(9, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(1, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'EMA' or i is 'DEMA' or i is 'MA' or i is 'SMA': # 주도 지표
|
||||||
|
f_range = list(range(9, 36))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(5, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
# elif i is 'KAMA': # 베이스 지표 / 추세 => 사용법 모름
|
||||||
|
# e_range = list(range(9, 36))
|
||||||
|
# for v in e_range:
|
||||||
|
# res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'MAMA': # 주도 지표
|
||||||
|
f_range = list(range(1, 10))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(1, 10))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r / 10, s_r / 100]})
|
||||||
|
|
||||||
|
# elif i is 'MAVP': # 주도 지표
|
||||||
|
# f_range = list(range(9, 36))
|
||||||
|
# for f_r in f_range:
|
||||||
|
# s_range = list(range(10, f_r+1))
|
||||||
|
# for s_r in s_range:
|
||||||
|
# res_data.append({i : [s_r, f_r]})
|
||||||
|
|
||||||
|
elif i is 'SAR': # 베이스 지표 / 역추세 => 거래 빈도만 줄이면 훌륭할 듯
|
||||||
|
e_range = list(range(1, 5))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'T3': # 주도 지표
|
||||||
|
f_range = list(range(5, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(3, f_r))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'TEMA' or i is 'TRIMA' or i is 'WMA': # 주도 지표
|
||||||
|
f_range = list(range(15, 41))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(7, f_r))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'AD': # 베이스 지표 - 거래량
|
||||||
|
res_data.append({i: None})
|
||||||
|
|
||||||
|
elif i is 'ADOSC': # 주도 지표 - 거래량 => 추세
|
||||||
|
f_range = list(range(7, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(3, f_r))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [s_r, f_r]})
|
||||||
|
|
||||||
|
elif i is 'OBV' or i is 'BOP_DIV': # 베이스 지표 - 거래량 기반 상승장, 하락장 지표
|
||||||
|
res_data.append({i: None})
|
||||||
|
|
||||||
|
# 다이버전스 주도 지표 / 역추세
|
||||||
|
elif i is 'ADX_DIV' or i is 'ADXR_DIV' or i is 'AROONOSC_DIV' or i is 'CCI_DIV' or i is 'CMO_DIV' \
|
||||||
|
or i is 'MFI_DIV' or i is 'MOM_DIV' or i is 'ROC_DIV' or i is 'ROCP_DIV' or i is 'ROCR_DIV' \
|
||||||
|
or i is 'TRIX_DIV' or i is 'WILLR_DIV':
|
||||||
|
e_range = list(range(9, 31))
|
||||||
|
for v in e_range:
|
||||||
|
res_data.append({i: v})
|
||||||
|
|
||||||
|
elif i is 'STOCH_DIV':
|
||||||
|
f_range = list(range(5, 21))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(1, f_r + 1))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(1, s_r + 1))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [f_r, s_r, t_r]})
|
||||||
|
|
||||||
|
elif i is 'STOCHF_DIV':
|
||||||
|
f_range = list(range(7, 31))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(3, f_r))
|
||||||
|
for s_r in s_range:
|
||||||
|
res_data.append({i: [f_r, s_r]})
|
||||||
|
|
||||||
|
elif i is 'STOCHRSI_DIV':
|
||||||
|
f_range = list(range(9, 14))
|
||||||
|
for f_r in f_range:
|
||||||
|
s_range = list(range(1, 5))
|
||||||
|
for s_r in s_range:
|
||||||
|
t_range = list(range(1, 5))
|
||||||
|
for t_r in t_range:
|
||||||
|
res_data.append({i: [f_r, s_r, t_r]})
|
||||||
|
|
||||||
|
return res_data
|
||||||
|
|
||||||
|
'''
|
||||||
|
def is_divergence_v4(high, low, res_arr, date, cdl_cnt=50):
|
||||||
|
m_idxs = [i for i, x in enumerate(res_arr) if
|
||||||
|
res_arr[i - 1] < res_arr[i] and res_arr[i - 1] < res_arr[i - 2]]
|
||||||
|
is_diver_long = [v for i, v in enumerate(m_idxs) if low[m_idxs[i]] < low[m_idxs[i-1]] and res_arr[m_idxs[i]] > res_arr[m_idxs[i-1]]]
|
||||||
|
m_idxs = [i for i, x in enumerate(res_arr) if
|
||||||
|
res_arr[i - 1] > res_arr[i] and res_arr[i - 1] > res_arr[i - 2]]
|
||||||
|
is_diver_short = [v for i, v in enumerate(m_idxs) if high[m_idxs[i]] > high[m_idxs[i-1]] and res_arr[m_idxs[i]] < res_arr[m_idxs[i-1]]]
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# 변곡점 캐치 다이버전스 함수 / 고점 및 저점 활용
|
||||||
|
def is_divergence_v2(i, high, low, res_arr, date, cdl_cnt=50):
|
||||||
|
is_rsi_min_1 = res_arr[i - 1] < res_arr[i] and res_arr[i - 1] < res_arr[i - 2]
|
||||||
|
is_rsi_max_1 = res_arr[i - 1] > res_arr[i] and res_arr[i - 1] > res_arr[i - 2]
|
||||||
|
|
||||||
|
# 상승 다이버전스
|
||||||
|
if is_rsi_min_1: # 첫번째 저점 형성
|
||||||
|
rsi_min_1 = res_arr[i - 1]
|
||||||
|
low_1 = low[i - 1]
|
||||||
|
|
||||||
|
for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
# 두번째 저점 형성
|
||||||
|
is_rsi_min_2 = res_arr[s_i - 1] < res_arr[s_i] and res_arr[s_i - 1] < res_arr[s_i - 2]
|
||||||
|
|
||||||
|
if is_rsi_min_2:
|
||||||
|
rsi_min_2 = res_arr[s_i - 1]
|
||||||
|
low_2 = low[s_i - 1]
|
||||||
|
|
||||||
|
if low_1 < low_2: # 저점 갱신
|
||||||
|
if rsi_min_1 > rsi_min_2: # 지표 저점 상승
|
||||||
|
# print('매수 포지션', i, '-' * 50)
|
||||||
|
# print(date[i - 1], '=>', date[s_i - 1])
|
||||||
|
# print(low_1, '=>', low_2)
|
||||||
|
# print(rsi_min_1, '=>', rsi_min_2)
|
||||||
|
# print('현재', res_arr[i-2], '>', res_arr[i - 1], '<', res_arr[i])
|
||||||
|
# print('이전', res_arr[s_i-2], '>', res_arr[s_i - 1], '<', res_arr[s_i])
|
||||||
|
return True
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 하락 다이버전스
|
||||||
|
if is_rsi_max_1:
|
||||||
|
rsi_max_1 = res_arr[i - 1]
|
||||||
|
max_1 = high[i - 1]
|
||||||
|
|
||||||
|
for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
is_rsi_max_2 = res_arr[s_i - 1] > res_arr[s_i] and res_arr[s_i - 1] > res_arr[s_i - 2]
|
||||||
|
|
||||||
|
if is_rsi_max_2:
|
||||||
|
rsi_max_2 = res_arr[s_i - 1]
|
||||||
|
max_2 = high[s_i - 1]
|
||||||
|
|
||||||
|
if max_1 > max_2: # 고점갱신 갱신
|
||||||
|
if rsi_max_1 < rsi_max_2 : # 지표 고점 하락
|
||||||
|
# print('매도 포지션', i, '-'*50)
|
||||||
|
# print(date[i-1], '=>', date[s_i-1])
|
||||||
|
# print(max_1, '=>', max_2)
|
||||||
|
# print(rsi_max_1, '=>', rsi_max_2)
|
||||||
|
# print('현재',res_arr[i-2], '<', res_arr[i - 1], '>', res_arr[i])
|
||||||
|
# print('이전', res_arr[s_i-2], '<', res_arr[s_i - 1], '>', res_arr[s_i])
|
||||||
|
return False
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
# def is_divergence_v2(i, high, low, res_arr, date, cdl_cnt=50):
|
||||||
|
# is_rsi_min_1 = res_arr[i - 1] < res_arr[i] and res_arr[i - 1] < res_arr[i - 2]
|
||||||
|
# is_rsi_max_1 = res_arr[i - 1] > res_arr[i] and res_arr[i - 1] > res_arr[i - 2]
|
||||||
|
#
|
||||||
|
# if is_rsi_min_1: # 상승 다이버전스
|
||||||
|
# rsi_min_1 = res_arr[i - 1]
|
||||||
|
# low_1 = low[i - 1]
|
||||||
|
#
|
||||||
|
# for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
# # 두번째 저점 형성
|
||||||
|
# is_rsi_min_2 = res_arr[s_i - 1] < res_arr[s_i] and res_arr[s_i - 1] < res_arr[s_i - 2]
|
||||||
|
#
|
||||||
|
# if is_rsi_min_2:
|
||||||
|
# rsi_min_2 = res_arr[s_i - 1]
|
||||||
|
# low_2 = low[s_i - 1]
|
||||||
|
#
|
||||||
|
# if low_1 < low_2: # 저점 갱신
|
||||||
|
# # if rsi_min_1 > rsi_min_2 and res_arr[i - 2] > res_arr[s_i - 2]: # 지표 저점 상승
|
||||||
|
# if rsi_min_1 > rsi_min_2: # 지표 저점 상승
|
||||||
|
# # print('매수 포지션', i, '-' * 50)
|
||||||
|
# # print(date[i - 1], '=>', date[s_i - 1])
|
||||||
|
# # print(low_1, '=>', low_2)
|
||||||
|
# # print(rsi_min_1, '=>', rsi_min_2)
|
||||||
|
# # print('현재', res_arr[i-2], '>', res_arr[i - 1], '<', res_arr[i])
|
||||||
|
# # print('이전', res_arr[s_i-2], '>', res_arr[s_i - 1], '<', res_arr[s_i])
|
||||||
|
# return True
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# if is_rsi_max_1: # 하락 다이버전스
|
||||||
|
# rsi_max_1 = res_arr[i - 1]
|
||||||
|
# max_1 = high[i-1]
|
||||||
|
#
|
||||||
|
# for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
# is_rsi_max_2 = res_arr[s_i - 1] > res_arr[s_i] and res_arr[s_i - 1] > res_arr[s_i - 2]
|
||||||
|
#
|
||||||
|
# if is_rsi_max_2:
|
||||||
|
# rsi_max_2 = res_arr[s_i - 1]
|
||||||
|
# max_2 = high[s_i - 1]
|
||||||
|
#
|
||||||
|
# if max_1 > max_2: # 고점갱신 갱신
|
||||||
|
# if rsi_max_1 < rsi_max_2: # 지표 고점 하락
|
||||||
|
# # print('매도 포지션', i, '-'*50)
|
||||||
|
# # print(date[i-1], '=>', date[s_i-1])
|
||||||
|
# # print(max_1, '=>', max_2)
|
||||||
|
# # print(rsi_max_1, '=>', rsi_max_2)
|
||||||
|
# # print('현재',res_arr[i-2], '<', res_arr[i - 1], '>', res_arr[i])
|
||||||
|
# # print('이전', res_arr[s_i-2], '<', res_arr[s_i - 1], '>', res_arr[s_i])
|
||||||
|
# return False
|
||||||
|
# return None
|
||||||
|
#
|
||||||
|
# return None
|
||||||
|
|
||||||
|
# 변곡점 캐치 다이버전스 함수 / 종가 활용
|
||||||
|
def is_divergence_v3(i, close, res_arr, date, cdl_cnt=50):
|
||||||
|
high = close
|
||||||
|
low = close
|
||||||
|
|
||||||
|
is_rsi_min_1 = res_arr[i - 1] < res_arr[i] and res_arr[i - 1] < res_arr[i - 2]
|
||||||
|
is_rsi_max_1 = res_arr[i - 1] > res_arr[i] and res_arr[i - 1] > res_arr[i - 2]
|
||||||
|
|
||||||
|
# 상승 다이버전스
|
||||||
|
if is_rsi_min_1:
|
||||||
|
rsi_min_1 = res_arr[i - 1]
|
||||||
|
low_1 = low[i - 1]
|
||||||
|
|
||||||
|
for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
is_rsi_min_2 = res_arr[s_i - 1] < res_arr[s_i] and res_arr[s_i - 1] < res_arr[s_i - 2]
|
||||||
|
|
||||||
|
if is_rsi_min_2:
|
||||||
|
rsi_min_2 = res_arr[s_i - 1]
|
||||||
|
low_2 = low[s_i - 1]
|
||||||
|
|
||||||
|
if low_1 < low_2: # 저점 갱신
|
||||||
|
if rsi_min_1 > rsi_min_2: # 지표 저점 상승
|
||||||
|
return True
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 하락 다이버전스
|
||||||
|
if is_rsi_max_1:
|
||||||
|
rsi_max_1 = res_arr[i - 1]
|
||||||
|
max_1 = high[i - 1]
|
||||||
|
|
||||||
|
for s_i in range(i - 3, i - cdl_cnt, -1):
|
||||||
|
is_rsi_max_2 = res_arr[s_i - 1] > res_arr[s_i] and res_arr[s_i - 1] > res_arr[s_i - 2]
|
||||||
|
|
||||||
|
if is_rsi_max_2:
|
||||||
|
rsi_max_2 = res_arr[s_i - 1]
|
||||||
|
max_2 = high[s_i - 1]
|
||||||
|
|
||||||
|
if max_1 > max_2: # 고점갱신 갱신
|
||||||
|
if rsi_max_2 > rsi_max_1: # 지표 고점 하락
|
||||||
|
return False
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
# get pivo percent
|
||||||
|
def pivo(n = 1000):
|
||||||
|
if n is 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
pivo_arr = [0, 1]
|
||||||
|
res_arr = []
|
||||||
|
while pivo_arr[len(pivo_arr)-2] + pivo_arr[len(pivo_arr)-1] <= n:
|
||||||
|
pivo_arr.append(pivo_arr[len(pivo_arr)-2] + pivo_arr[len(pivo_arr)-1]) # for percent
|
||||||
|
|
||||||
|
# calculate percent
|
||||||
|
for pivo_val in pivo_arr[2:]:
|
||||||
|
res_arr.append(pivo_val/100)
|
||||||
|
|
||||||
|
return res_arr
|
||||||
|
|
||||||
|
|
||||||
5
strategies/desktop.ini
Normal file
5
strategies/desktop.ini
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[.ShellClassInfo]
|
||||||
|
InfoTip=<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>¶<EFBFBD><C2B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>˴ϴ<CBB4>.
|
||||||
|
IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
|
||||||
|
IconIndex=16
|
||||||
|
|
||||||
86
strategies/indicator.py
Normal file
86
strategies/indicator.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
from backtesting import Strategy
|
||||||
|
from signal_helper import *
|
||||||
|
|
||||||
|
|
||||||
|
class StrategyIndicator(Strategy):
|
||||||
|
use_indicators = None
|
||||||
|
indicators_data = None
|
||||||
|
cal_data = None
|
||||||
|
|
||||||
|
# Stop Profit/Loss
|
||||||
|
up_target = None
|
||||||
|
down_target = None
|
||||||
|
|
||||||
|
data_len = None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.data_len = len(self.data.Close)
|
||||||
|
self.indicators_data = pd.DataFrame()
|
||||||
|
|
||||||
|
for p in self.use_indicators:
|
||||||
|
indicator, values = list(p.items())[0]
|
||||||
|
|
||||||
|
if type(values) == int:
|
||||||
|
indicator_info = indicator +"_"+ str(values)
|
||||||
|
elif type(values) == list:
|
||||||
|
indicator_info = indicator+"_"+"_".join(map(str, values))
|
||||||
|
|
||||||
|
self.indicators_data[indicator_info] = self.cal_data[indicator_info]
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
idx = (self._broker._i) - 1
|
||||||
|
row_data = list(self.indicators_data.iloc[idx])
|
||||||
|
prev_row_data = list(self.indicators_data.iloc[idx-1])
|
||||||
|
|
||||||
|
price = self.data.Close[-1]
|
||||||
|
sl = 0
|
||||||
|
tp = 0
|
||||||
|
|
||||||
|
# 배열 동시 시그널 매매
|
||||||
|
if len(row_data) == len(self.use_indicators) and prev_row_data:
|
||||||
|
if not (None in row_data):
|
||||||
|
if all(row_data):
|
||||||
|
if self.down_target > 0:
|
||||||
|
sl = price - (price * self.down_target)
|
||||||
|
if self.up_target > 0:
|
||||||
|
tp = price + (price * self.up_target)
|
||||||
|
|
||||||
|
# only short test
|
||||||
|
# if not self.orders.is_short:
|
||||||
|
# self.position.close()
|
||||||
|
|
||||||
|
# Only Long
|
||||||
|
if len(self.use_indicators) is 1: # 단일 지표일 경우
|
||||||
|
if not self.orders.is_long and not any(prev_row_data):
|
||||||
|
if sl != 0 and tp != 0:
|
||||||
|
self.buy(sl=sl, tp=tp)
|
||||||
|
elif sl != 0:
|
||||||
|
self.buy(sl=sl)
|
||||||
|
elif tp != 0:
|
||||||
|
self.buy(tp=tp)
|
||||||
|
else:
|
||||||
|
self.buy()
|
||||||
|
|
||||||
|
elif len(self.use_indicators) > 1: # 여러 지표일 경우
|
||||||
|
if not self.orders.is_long:
|
||||||
|
if sl != 0 and tp != 0:
|
||||||
|
self.buy(sl=sl, tp=tp)
|
||||||
|
elif sl != 0:
|
||||||
|
self.buy(sl=sl)
|
||||||
|
elif tp != 0:
|
||||||
|
self.buy(tp=tp)
|
||||||
|
else:
|
||||||
|
self.buy()
|
||||||
|
|
||||||
|
elif not any(row_data):
|
||||||
|
# short test
|
||||||
|
# if self.orders.is_long is None or self.orders.is_short is False:
|
||||||
|
# self.sell(sl=r_sl, tp=sl)
|
||||||
|
|
||||||
|
# only long test
|
||||||
|
if self.orders.is_long:
|
||||||
|
self.position.close()
|
||||||
|
|
||||||
|
# 시뮬레이터 종료 시 포지션 종료
|
||||||
|
if idx == self.data_len - 2:
|
||||||
|
self.position.close()
|
||||||
1137
strategy.py
Normal file
1137
strategy.py
Normal file
File diff suppressed because it is too large
Load Diff
356
test_slicing.py
Normal file
356
test_slicing.py
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
import sys, time, random, os
|
||||||
|
|
||||||
|
# load strategy
|
||||||
|
# from strategies.indicator import StrategyIndicator
|
||||||
|
from backtesting import Backtest
|
||||||
|
|
||||||
|
# load functions
|
||||||
|
from indicator_util import get_indicators_values
|
||||||
|
from signal_helper import *
|
||||||
|
|
||||||
|
# Exchange API
|
||||||
|
import pybithumb
|
||||||
|
|
||||||
|
from itertools import combinations
|
||||||
|
from datetime import datetime
|
||||||
|
from trade.candle import CandlePatterns
|
||||||
|
import warnings
|
||||||
|
# from threading import Thread
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
from strategy import StrategyCandlePattern
|
||||||
|
from multiprocessing import Process, freeze_support
|
||||||
|
from multiprocessing import Pool
|
||||||
|
import multiprocessing
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
warnings.filterwarnings(action='ignore')
|
||||||
|
|
||||||
|
# set config
|
||||||
|
start_time = time.time()
|
||||||
|
cash = 1000
|
||||||
|
commission = .005
|
||||||
|
top_cash = 0
|
||||||
|
top_profit = 25
|
||||||
|
top_win_rate = 60
|
||||||
|
best_pattern_arr = []
|
||||||
|
|
||||||
|
# pivonachi
|
||||||
|
profit_arr = [0] + pivo(100)
|
||||||
|
loss_arr = [0] + pivo(10)
|
||||||
|
|
||||||
|
|
||||||
|
def backtest_run(
|
||||||
|
df,
|
||||||
|
StrategyCandlePattern,
|
||||||
|
indicators,
|
||||||
|
up_target=float(0),
|
||||||
|
down_target=float(0),
|
||||||
|
cash=1000,
|
||||||
|
commission=.005
|
||||||
|
):
|
||||||
|
setattr(StrategyCandlePattern, 'use_indicators', [indicators])
|
||||||
|
setattr(StrategyCandlePattern, 'up_target', float(up_target))
|
||||||
|
setattr(StrategyCandlePattern, 'down_target', float(down_target))
|
||||||
|
|
||||||
|
# StrategyCandlePattern.use_indicators = [indicators]
|
||||||
|
# StrategyCandlePattern.up_target = float(up_target)
|
||||||
|
# StrategyCandlePattern.down_target = float(down_target)
|
||||||
|
|
||||||
|
bt = Backtest(df, StrategyCandlePattern, cash=cash, commission=commission)
|
||||||
|
bt.run()
|
||||||
|
r_data = bt._results
|
||||||
|
|
||||||
|
del bt
|
||||||
|
|
||||||
|
return r_data
|
||||||
|
|
||||||
|
|
||||||
|
# 시그널 보조 지표
|
||||||
|
base_indicators = [
|
||||||
|
{'RSI_DIV': 14},
|
||||||
|
{'MFI_DIV': 14},
|
||||||
|
{'CMO_DIV': 9},
|
||||||
|
{'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, 3, 3]},
|
||||||
|
{'STOCH_DIV': [14, 1, 1]},
|
||||||
|
# {'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]}]
|
||||||
|
|
||||||
|
# multi Treading - item
|
||||||
|
def simulrating_by_item(item, data, all_indicators, idx, t_time):
|
||||||
|
# def simulrating_by_item(data):
|
||||||
|
global top_profit
|
||||||
|
global top_win_rate
|
||||||
|
global start_time
|
||||||
|
global cash
|
||||||
|
global commission
|
||||||
|
|
||||||
|
for indicators in list(combinations(all_indicators, idx)):
|
||||||
|
for profit in profit_arr:
|
||||||
|
for loss in loss_arr:
|
||||||
|
StrategyCandlePattern.use_indicators = list(indicators)
|
||||||
|
StrategyCandlePattern.up_target = float(profit)
|
||||||
|
StrategyCandlePattern.down_target = float(loss)
|
||||||
|
|
||||||
|
bt = Backtest(data, StrategyCandlePattern, cash=cash, commission=commission)
|
||||||
|
bt.run()
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 5:
|
||||||
|
print('-' * 60)
|
||||||
|
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("파일명 :", str(item) + '_' + str(t_time) + '_' + str(idx) + '_' + str(time.time()))
|
||||||
|
print('-' * 60)
|
||||||
|
# bt.plot(filename=str(item)+'_'+str(t_time)+'_'+str(idx)+'_'+str(time.time()))
|
||||||
|
|
||||||
|
top_profit = bt._results['Return [%]']
|
||||||
|
|
||||||
|
best_pattern_arr.append({
|
||||||
|
't_time': t_time,
|
||||||
|
'indicators': indicators,
|
||||||
|
'profit': profit,
|
||||||
|
'loss': loss,
|
||||||
|
'return': bt._results['Return [%]'],
|
||||||
|
'trades': bt._results['# Trades'],
|
||||||
|
})
|
||||||
|
|
||||||
|
del bt
|
||||||
|
|
||||||
|
e = int(time.time() - start_time)
|
||||||
|
print('(완료) 조합 지표 개수 :', idx, t_time, '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60))
|
||||||
|
print(datetime.now())
|
||||||
|
|
||||||
|
|
||||||
|
# multi Treading - fackage
|
||||||
|
def simulrating_by_item_fackage(item, data, t_time):
|
||||||
|
global top_profit
|
||||||
|
global top_win_rate
|
||||||
|
global start_time
|
||||||
|
global base_indicators
|
||||||
|
global signal_indicators
|
||||||
|
global cash
|
||||||
|
global commission
|
||||||
|
|
||||||
|
filtered_signal_indicators = []
|
||||||
|
|
||||||
|
# 시그널 지표 필터링 완료 : 00:02:36
|
||||||
|
for indicator in signal_indicators:
|
||||||
|
for profit in profit_arr:
|
||||||
|
for loss in loss_arr:
|
||||||
|
|
||||||
|
res = backtest_run(
|
||||||
|
data,
|
||||||
|
StrategyCandlePattern,
|
||||||
|
indicators=indicator,
|
||||||
|
up_target=float(profit),
|
||||||
|
down_target=float(loss),
|
||||||
|
cash=cash,
|
||||||
|
commission=commission
|
||||||
|
)
|
||||||
|
|
||||||
|
# 수익 및 거래 수 제한
|
||||||
|
if res['Return [%]'] > 15 and res['# Trades'] > 5:
|
||||||
|
if indicator not in filtered_signal_indicators:
|
||||||
|
filtered_signal_indicators.append(indicator)
|
||||||
|
|
||||||
|
if res['Return [%]'] > top_profit:
|
||||||
|
print('-' * 60)
|
||||||
|
print('시간봉 :', t_time)
|
||||||
|
print('지표 조합 개수 :', 1)
|
||||||
|
print('지표 :', StrategyCandlePattern.use_indicators)
|
||||||
|
print("적용된 스탑프로핏 : %0.2f%%" % profit)
|
||||||
|
print("적용된 스탑로스 : %0.2f%%" % loss)
|
||||||
|
print("총 수익률 : %0.2f%%" % res['Return [%]'])
|
||||||
|
print("최종 금액 : %0.2f" % res['Equity Final [$]'])
|
||||||
|
print("거래 수 :", res['# Trades'])
|
||||||
|
print('-' * 60)
|
||||||
|
# bt.plot()
|
||||||
|
|
||||||
|
top_profit = res['Return [%]']
|
||||||
|
|
||||||
|
best_pattern_arr.append({
|
||||||
|
't_time': t_time,
|
||||||
|
'indicators': [indicator],
|
||||||
|
'profit': profit,
|
||||||
|
'loss': loss,
|
||||||
|
'return': res['Return [%]'],
|
||||||
|
'trades': res['# Trades'],
|
||||||
|
})
|
||||||
|
|
||||||
|
del res
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 02:56
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
all_indicators = filtered_signal_indicators[::-1] + base_indicators
|
||||||
|
|
||||||
|
joined = []
|
||||||
|
for idx in range(2, 3):
|
||||||
|
|
||||||
|
# for idx in range(2, 5):
|
||||||
|
# simulrating_by_item(item, data, all_indicators, idx, t_time)
|
||||||
|
|
||||||
|
_p = Process(target=simulrating_by_item, args=(item, data, all_indicators, idx, t_time,))
|
||||||
|
_p.start()
|
||||||
|
joined.append(_p)
|
||||||
|
|
||||||
|
# item_thread = Thread(target=simulrating_by_item, args=(item, data, all_indicators, idx, t_time,))
|
||||||
|
# item_thread.start()
|
||||||
|
|
||||||
|
# with multiprocessing.Pool(processes=psutil.cpu_count(logical=False)) as pool:
|
||||||
|
# pool = Pool(processes=4)
|
||||||
|
# func = simulrating_by_item(item, data, all_indicators, idx, t_time)
|
||||||
|
# pool.map(func)
|
||||||
|
|
||||||
|
# for _p in joined:
|
||||||
|
# _p.join()
|
||||||
|
|
||||||
|
del item, data, all_indicators, idx, t_time
|
||||||
|
|
||||||
|
|
||||||
|
# multi Treading - time
|
||||||
|
def simulrating_by_time(item, t_time):
|
||||||
|
'''
|
||||||
|
"day": "24H",
|
||||||
|
"hour12": "12H",
|
||||||
|
"hour6": "06H",
|
||||||
|
"hour": "01H",
|
||||||
|
"minute30": "30M",
|
||||||
|
"minute10": "10M",
|
||||||
|
"minute5": "05M",
|
||||||
|
"minute3": "03M",
|
||||||
|
'''
|
||||||
|
df = pybithumb.get_ohlcv(item, t_time) # params : 종목, 시간
|
||||||
|
|
||||||
|
# 최근 두달 데이터 = 실질
|
||||||
|
if t_time == 'hour':
|
||||||
|
df = df[-1600:]
|
||||||
|
elif t_time == 'hour6':
|
||||||
|
df = df[-266:] # 최근 두달 데이터 = 실질
|
||||||
|
elif t_time == 'hour12':
|
||||||
|
df = df[-133:] # 최근 두달 데이터 = 실질
|
||||||
|
elif t_time == 'day':
|
||||||
|
df = df[-85:] # 최근 두달 데이터 = 실질
|
||||||
|
|
||||||
|
data_columns_init(df)
|
||||||
|
data = df
|
||||||
|
|
||||||
|
simulrating_by_item_fackage(item, data, t_time)
|
||||||
|
|
||||||
|
|
||||||
|
# - 반복문 만들기
|
||||||
|
# 단일 지표 필터링 반복문(스톱 로스/프로핏) => 완료
|
||||||
|
# 스톱로스, 스톱프로핏 반복문 => 완료
|
||||||
|
# 지표 조합 반복문 => 완료
|
||||||
|
# 시간봉별 반복문 => 멀티스레딩으로 진행중
|
||||||
|
|
||||||
|
# 1시간, 4시간, 6시간, 12시간 봉, 일봉 => 5개
|
||||||
|
# 멀티스레딩으로 구현 => 시간별, 조합 개수별 => 2, 3, 4, 5, 6, 7
|
||||||
|
# 멀티 프로세싱으로 구현 => 멀티 스레드와 속도비교
|
||||||
|
# 멀티 프로세싱 구현 => 완료
|
||||||
|
# 싸이썬 활용
|
||||||
|
# 위 성능 테스트 후 저조할 경우 랜덤워크로 진행.
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# freeze_support()
|
||||||
|
|
||||||
|
print('Started Simulating.')
|
||||||
|
|
||||||
|
for t_time in ['hour', 'hour6', 'hour12', 'day']:
|
||||||
|
simulrating_by_time('BTC', t_time)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
# p = Process(target=simulrating_by_time, args=('BTC', t_time,))
|
||||||
|
# p.start()
|
||||||
|
# p.join() # sync
|
||||||
|
|
||||||
|
# thread = Thread(target=simulrating_by_time, args=('BTC', t_time,))
|
||||||
|
# thread.start()
|
||||||
|
# thread.join() # 동기
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
90
trade/candle.py
Normal file
90
trade/candle.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
class CandlePatterns:
|
||||||
|
# Long Patterns
|
||||||
|
_positive_patterns = [
|
||||||
|
'CDL3STARSINSOUTH', # Three Stars In The South
|
||||||
|
'CDL3WHITESOLDIERS', # long
|
||||||
|
'CDLCONCEALBABYSWALL', # long - no data
|
||||||
|
# 'CDLDRAGONFLYDOJI', # long 빈도 높음
|
||||||
|
'CDLLADDERBOTTOM', # long
|
||||||
|
'CDLMORNINGDOJISTAR', # long
|
||||||
|
'CDLMORNINGSTAR', # long
|
||||||
|
'CDLADVANCEBLOCK', # long
|
||||||
|
'CDLHOMINGPIGEON', # long
|
||||||
|
# 'CDLINVERTEDHAMMER', # long # 빈도 높음
|
||||||
|
# 'CDLHAMMER', # 빈도 높음
|
||||||
|
# 'CDLTAKURI', # 빈도 높음
|
||||||
|
# 'CDLHANGINGMAN', # 빈도 높음
|
||||||
|
]
|
||||||
|
|
||||||
|
# Short Patterns
|
||||||
|
_negative_patterns = [
|
||||||
|
'CDLEVENINGDOJISTAR', # short
|
||||||
|
'CDL2CROWS', # short
|
||||||
|
'CDL3BLACKCROWS', # short
|
||||||
|
'CDLDARKCLOUDCOVER', # short
|
||||||
|
'CDLEVENINGDOJISTAR', # short
|
||||||
|
'CDLEVENINGSTAR', # short
|
||||||
|
'CDLGRAVESTONEDOJI', # short 빈도 높음
|
||||||
|
'CDLIDENTICAL3CROWS', # short
|
||||||
|
'CDLINNECK', # short
|
||||||
|
'CDLONNECK', # short
|
||||||
|
'CDLSHOOTINGSTAR', # short
|
||||||
|
'CDLUPSIDEGAP2CROWS', # short
|
||||||
|
# 'CDLMATCHINGLOW', # 6:4 short
|
||||||
|
]
|
||||||
|
|
||||||
|
# 중립 패턴
|
||||||
|
_fence_patterns = \
|
||||||
|
[
|
||||||
|
'CDL3INSIDE',
|
||||||
|
'CDL3LINESTRIKE',
|
||||||
|
'CDL3OUTSIDE',
|
||||||
|
'CDLABANDONEDBABY',
|
||||||
|
'CDLBELTHOLD', # 샅바형
|
||||||
|
'CDLBREAKAWAY',
|
||||||
|
'CDLCLOSINGMARUBOZU',
|
||||||
|
'CDLCOUNTERATTACK',
|
||||||
|
'CDLCONCEALBABYSWALL',
|
||||||
|
'CDLENGULFING',
|
||||||
|
'CDLGAPSIDESIDEWHITE',
|
||||||
|
'CDLHARAMI',
|
||||||
|
'CDLHARAMICROSS',
|
||||||
|
'CDLHIKKAKEMOD',
|
||||||
|
'CDLKICKING',
|
||||||
|
'CDLKICKINGBYLENGTH',
|
||||||
|
'CDLMATHOLD',
|
||||||
|
'CDLPIERCING',
|
||||||
|
'CDLRISEFALL3METHODS',
|
||||||
|
'CDLSEPARATINGLINES',
|
||||||
|
'CDLSTALLEDPATTERN',
|
||||||
|
'CDLTASUKIGAP',
|
||||||
|
'CDLTRISTAR',
|
||||||
|
'CDLUNIQUE3RIVER',
|
||||||
|
'CDLXSIDEGAP3METHODS',
|
||||||
|
'CDLSTICKSANDWICH',
|
||||||
|
# 'CDLHIKKAKE', # 높은 빈도
|
||||||
|
# 'CDLHIGHWAVE', # 꼬리나 머리털이 길때
|
||||||
|
# 'CDLLONGLEGGEDDOJI', # Long Legged Doji
|
||||||
|
# 'CDLLONGLINE', # Long Line Candle
|
||||||
|
# 'CDLMARUBOZU', # Marubozu
|
||||||
|
# 'CDLRICKSHAWMAN ', # 그냥 도지임
|
||||||
|
# 'CDLSHORTLINE', # Short Line Candle 5:5
|
||||||
|
# 'CDLTHRUSTING', # 지속형
|
||||||
|
# 'CDLSPINNINGTOP', # 그냥 도지임
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_fence_patterns(self):
|
||||||
|
return self._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
|
||||||
|
|
||||||
|
def get_all_patterns(self):
|
||||||
|
return self._positive_patterns + self._fence_patterns + self._negative_patterns
|
||||||
5
trade/desktop.ini
Normal file
5
trade/desktop.ini
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[.ShellClassInfo]
|
||||||
|
InfoTip=<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>¶<EFBFBD><C2B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>˴ϴ<CBB4>.
|
||||||
|
IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
|
||||||
|
IconIndex=16
|
||||||
|
|
||||||
Reference in New Issue
Block a user