From 4c263e7866edc346d7b6c4339d819ee1090bc9f5 Mon Sep 17 00:00:00 2001 From: javamon Date: Sat, 6 Dec 2025 22:28:22 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B2=AB=20=EB=B2=88=EC=A7=B8=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 141 ++++ __strategy.py | 1041 ++++++++++++++++++++++++ backtest.py | 482 ++++++++++++ backtest.pyx | 304 ++++++++ becktest_with_cuda.py | 1 + db.py | 194 +++++ desktop.ini | 5 + final_backtest.py | 1649 +++++++++++++++++++++++++++++++++++++++ indicator_backtest.py | 1575 +++++++++++++++++++++++++++++++++++++ indicator_util.py | 913 ++++++++++++++++++++++ new_backtest.py | 364 +++++++++ power_backtest.py | 335 ++++++++ profile_backtest.py | 344 ++++++++ requirements.txt | 40 + signal_helper.py | 666 ++++++++++++++++ strategies/desktop.ini | 5 + strategies/indicator.py | 86 ++ strategy.py | 1137 +++++++++++++++++++++++++++ test_slicing.py | 356 +++++++++ trade/candle.py | 90 +++ trade/desktop.ini | 5 + 21 files changed, 9733 insertions(+) create mode 100644 .gitignore create mode 100644 __strategy.py create mode 100644 backtest.py create mode 100644 backtest.pyx create mode 100644 becktest_with_cuda.py create mode 100644 db.py create mode 100644 desktop.ini create mode 100644 final_backtest.py create mode 100644 indicator_backtest.py create mode 100644 indicator_util.py create mode 100644 new_backtest.py create mode 100644 power_backtest.py create mode 100644 profile_backtest.py create mode 100644 requirements.txt create mode 100644 signal_helper.py create mode 100644 strategies/desktop.ini create mode 100644 strategies/indicator.py create mode 100644 strategy.py create mode 100644 test_slicing.py create mode 100644 trade/candle.py create mode 100644 trade/desktop.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17f2abc --- /dev/null +++ b/.gitignore @@ -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/ \ No newline at end of file diff --git a/__strategy.py b/__strategy.py new file mode 100644 index 0000000..96f42bc --- /dev/null +++ b/__strategy.py @@ -0,0 +1,1041 @@ +from backtesting import Backtest, Strategy +from trade.candle import CandlePatterns +import talib, numpy, math +from signal_helper import * + +from pyti import stochrsi +from pyti import chande_momentum_oscillator + +import numpy as np +from scipy.signal import argrelextrema + +import sys + + +class StrategyCandlePattern(Strategy): + use_indicators = None + indicators_data = None + _use_patterns = None + pattern_data = None + _sl_percent = 0.03 # 4% + _r_sl_percent = 0.05 # 4% + + # Stop Profit/Loss + up_target = None + down_target = None + + # 캔들 패턴 + cp = CandlePatterns() + + # 매수 패턴 + _positive_patterns = cp.get_long_patterns() + _negative_patterns = cp.get_short_patterns() + _fence_patterns = cp.get_fence_patterns() + + data_len = None + + # position_closed_time + position_closed_time = None + + def init(self): + date = self.data.Date + close = self.data.Close + low = self.data.Low + high = self.data.High + open = self.data.Open + volume = self.data.Volume + + self.data_len = len(close) - 1 + + data = [list()] * len(self.data.Close) + + for p in self.use_indicators: + # for test + print('입력된 보조지표 :', p) + + indicator = None + values = None + f = None + + indicator, values = list(p.items())[0] + + if indicator != 'DI' and hasattr(talib, indicator): + f = getattr(talib, indicator) + + if indicator is 'CDLPTN': + + for i in range(0, len(close)): + t_d = [] + + if self.data.Candle_pattern[i] is None: + continue + + pattern, value = list(self.data.Candle_pattern[i].items())[0] + + if pattern in self._fence_patterns: # 중립 패턴 + t_d.append(is_trade_fence_pattern(pattern, value)) + elif pattern in self._positive_patterns: # 매수 패턴 + t_d.append(True) + elif pattern in self._negative_patterns: # 매도 패턴 + t_d.append(False) + + data[i] = data[i] + t_d + + # 보조지표 추가 시작 + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + # print(date[i], res_arr[i]) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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] = data[i] + t_d + + # 보조지표 추가 끝 + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + ''' 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + 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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + self.indicators_data = data + + def next(self): + idx = (self._broker._i) - 1 + + sl = self.data.Close[-1] - (self.data.Close[-1] * self._sl_percent) + r_sl = self.data.Close[-1] + (self.data.Close[-1] * self._r_sl_percent) + date = self.data.Date[-1] + + # 배열 동시 시그널 매매 + if len(self.indicators_data[idx]) == len(self.use_indicators): + # None value pass + if not (None in self.indicators_data[idx]): + if all(self.indicators_data[idx]): + # only short test + # if not self.orders.is_short: + # self.position.close() + + if len(self.use_indicators) is 1: # 단일 지표일 경우 + if not self.orders.is_long and not any(self.indicators_data[idx - 1]): + # self.buy() + # self.buy(tp=r_sl) + # self.buy(sl=sl, tp=r_sl) + self.buy(sl=sl) + elif len(self.use_indicators) > 1: # 여러 지표일 경우 + if not self.orders.is_long: + # self.buy() + # self.buy(tp=r_sl) + # self.buy(sl=sl, tp=r_sl) + self.buy(sl=sl) + + elif not any(self.indicators_data[idx]): + # 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() diff --git a/backtest.py b/backtest.py new file mode 100644 index 0000000..e9a95d0 --- /dev/null +++ b/backtest.py @@ -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() diff --git a/backtest.pyx b/backtest.pyx new file mode 100644 index 0000000..6271f74 --- /dev/null +++ b/backtest.pyx @@ -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() + diff --git a/becktest_with_cuda.py b/becktest_with_cuda.py new file mode 100644 index 0000000..36efa88 --- /dev/null +++ b/becktest_with_cuda.py @@ -0,0 +1 @@ +print('cuda') \ No newline at end of file diff --git a/db.py b/db.py new file mode 100644 index 0000000..32f571b --- /dev/null +++ b/db.py @@ -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) diff --git a/desktop.ini b/desktop.ini new file mode 100644 index 0000000..8934518 --- /dev/null +++ b/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip= ¶ ˴ϴ. +IconFile=C:\Program Files\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/final_backtest.py b/final_backtest.py new file mode 100644 index 0000000..2722f16 --- /dev/null +++ b/final_backtest.py @@ -0,0 +1,1649 @@ +from backtesting import Backtest, Strategy +# from backtesting.lib import crossover + +from backtesting.test import SMA, GOOG +import sys +import talib, numpy, pybithumb, time, random +import pandas as pd +import numpy as np +from itertools import product, combinations +from pprint import pprint + +# for divergence +import numpy as np +import pandas as pd +import math +import matplotlib.pyplot as plt +from scipy import stats +from scipy.stats import linregress +import statsmodels +from pandas import Series +import talib + + +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(p_x_d, x_d, p_y_d, y_d) -> bool: + try: + return p_x_d < p_y_d and x_d > y_d + except IndexError: + return False + + +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 + + +''' +Long/Short Divergence +params : + i - index + cdl_cnt - candle count + low - low values + high - high values + res_arr - indicator values +''' + + +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 + + +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 StrategyCandlePatternOdds(Strategy): + mode = 0 # win : 0 / lose : 1 + _sl_percent = 0.03 # 2% + + def init(self): + pass + + def next(self): + idx = (self._broker._i) - 1 + price = self.data.Close[idx] + is_pattern = self.pattern_data[idx] + + # Long 포지션 패턴 검증 + if self.mode is 0: + if is_pattern: + pattern, value = list(self.pattern_data[idx].items())[0] + + if not self.orders.is_long and pattern in self._use_patterns: + self.buy(sl=price - (price * self._sl_percent), tp=price + (price * self._sl_percent)) + # self.buy() + + # elif self._open_price != None: + # if self.data.High[idx] > self.data.High[idx-20:idx-1]: + # self.position.close() + # self._open_price = None + + # Short 포지션 패턴 검증 + # elif self.mode is 1: + # 진입 + # 탈출 + # + # 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 CandlePatterns._positive_patterns: # 매수 패턴 + # if not self.orders.is_long: + # self.buy(sl=sl) + # elif pattern in CandlePatterns._negative_patterns: # 매도 패턴 + # if self.orders.is_long: + # self.position.close() + # elif pattern in CandlePatterns._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 CandlePatterns._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) + + +class StrategyIndicators(Strategy): + use_indicators = None + indicators_data = None + # value = dict() + _sl_percent = 0.03 # 2% + + def init(self): + close = self.data.Close + low = self.data.Low + high = self.data.High + open = self.data.Open + volume = self.data.Volume + + data = [list()] * len(self.data.Close) + + for p in self.use_indicators: + indicator = None + values = None + f = None + + indicator, values = list(p.items())[0] + + if indicator != 'DI' and hasattr(talib, indicator): + f = getattr(talib, indicator) + + if indicator is 'CDLPTN': + + for i in range(0, len(close)): + t_d = [] + + if self.data.Candle_pattern[i] is None: + continue + + pattern, value = list(self.data.Candle_pattern[i].items())[0] + + if pattern in CandlePatterns._fence_patterns: # 중립 패턴 + if int(value) > 0: + t_d.append(True) + elif int(value) < 0: + t_d.append(False) + elif pattern in CandlePatterns._positive_patterns: # 매수 패턴 + t_d.append(True) + elif pattern in CandlePatterns._negative_patterns: # 매도 패턴 + t_d.append(False) + + + data[i] = data[i] + t_d + + if indicator is 'RSI': # 베이스 지표 + res_arr = f(close, values) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 수치 매매 + if res_arr[i] < 25: + t_d.append(True) + elif res_arr[i] > 75: + t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(slowk)): + t_d = [] + if crossover(slowk[i - 1], slowk[i], slowd[i - 1], slowd[i]): + t_d.append(False) + elif crossover(slowd[i - 1], slowd[i], slowk[i - 1], slowk[i]): + t_d.append(True) + + data[i] = data[i] + t_d + + 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] < 20: # 25초과 - 추세추종 / 20 미만 역추세 매매 + t_d.append(True) + else: + 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() + + for i in range(start, len(res_arr_plus)): + t_d = [] + + if res_arr_plus[i] > res_arr_minus[i]: + t_d.append(True) + else: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'APO': # 주도 지표 + res_arr = f(close, fastperiod=values[0], slowperiod=values[1], matype=0) + 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) + else: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'AROON': # 주도 지표 + aroondown, aroonup = f(high, low, timeperiod=values) + start = 1 + numpy.isnan(aroondown).sum() + + for i in range(start, len(aroondown)): + t_d = [] + + if aroonup[i] > 50 and crossover(aroondown[i - 1], aroondown[i], aroonup[i - 1], aroonup[i]): + t_d.append(False) + elif aroondown[i] < 50 or crossover(aroonup[i - 1], aroonup[i], aroondown[i - 1], aroondown[i]): + t_d.append(True) + + data[i] = data[i] + t_d + + elif indicator is 'AROONOSC': # 베이스 지표 + 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 res_arr[i] > 0: + t_d.append(True) + elif res_arr[i] > 90: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'BOP': # 베이스 지표 + res_arr = f(open, high, low, close) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # if res_arr[i] < -0.8: # 과매도 매수 + # # t_d.append(True) + # # elif res_arr[i] > 0.7: # 과매수 매도 + # # t_d.append(False) + + if res_arr[i] > 0 and res_arr[i - 1] < 0: + t_d.append(True) + elif res_arr[i] < 0 and res_arr[i - 1] > 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'CCI': # 베이스 지표 / 추세 + 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 = [] + # 120/120 + + # 시그널 + # if res_arr[i] > 0 and res_arr[i-1] < 0: + # t_d.append(True) + # elif res_arr[i] < 0 and res_arr[i-1] > 0: + # t_d.append(False) + + # 베이스 + if res_arr[i] > 0: # 과매도 -100 / 과매수 +100 + t_d.append(True) + elif res_arr[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'CMO': # 베이스 지표 / 추세 + 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] > -50 and res_arr[i-1] < -50: + # t_d.append(True) + # elif res_arr[i] < 50 and res_arr[i-1] > 50: + # t_d.append(False) + + # 베이스 + 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 'DX': # 베이스 지표 / 추세 + # 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] < 10: + # t_d.append(True) + # elif res_arr[i] > 40: + # t_d.append(False) + # + # data[i] = data[i] + t_d + + elif indicator is 'MACD': # 주도 지표 + macd, macdsignal, macdhist = f(close, + fastperiod=values[0], + slowperiod=values[1], + signalperiod=values[2]) + + start = 1 + numpy.isnan(macd).sum() + + for i in range(start, len(macd)): + t_d = [] + if macd[i] > macdsignal[i] and macd[i - 1] < macdsignal[i - 1] and macd[i] > 0: + t_d.append(True) + elif macd[i] < macdsignal[i] and macd[i - 1] > macdsignal[i - 1] or macd[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MACDFIX': # 주도 지표 + macd, macdsignal, macdhist = f(close, signalperiod=values) + start = 1 + numpy.isnan(macd).sum() + + for i in range(start, len(macd)): + t_d = [] + + if macd[i] > macdsignal[i] and macd[i - 1] < macdsignal[i - 1] and macd[i] > 0: + t_d.append(True) + elif macd[i] < macdsignal[i] and macd[i - 1] > macdsignal[i - 1] or macd[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MFI': # 베이스 지표 / 역추세 + res_arr = f(high, low, close, volume, timeperiod=values) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 베이스 + if res_arr[i] < 20: + t_d.append(True) + elif res_arr[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MOM': # 베이스 지표 / 추세 + 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 'PPO': # 주도 지표 + res_arr = f(close, fastperiod=values[0], slowperiod=values[1], matype=0) + 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 'ROC': # 베이스 지표 / 추세 + 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 '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() + + for i in range(start, len(fastk)): + t_d = [] + + if fastd[i] < 20: + t_d.append(True) + elif fastd[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'STOCHRSI': # 주도 지표 + fastk, fastd = f(close, timeperiod=values[0], fastk_period=values[1], fastd_period=values[2], + fastd_matype=0) + start = 1 + numpy.isnan(fastk).sum() + + for i in range(start, len(fastk)): + t_d = [] + + # print(fastk[i], fastd[i]) + + if fastd[i] < 20: + t_d.append(True) + elif fastd[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'TRIX': # 베이스 지표 / 역추세 => 매수/매도 시점 괜찮다 + trix = f(close, timeperiod=values[0]) + + t_data = pd.DataFrame(trix) + trix_signal = t_data.rolling(values[1]).mean().values # 이동평균 시그널 생성 + + del t_data + + start = 1 + numpy.isnan(trix_signal).sum() + + for i in range(start, len(trix_signal)): + t_d = [] + + # 14.77% + if trix[i] > trix_signal[i][0]: + t_d.append(True) + elif trix[i] < trix_signal[i][0]: + t_d.append(False) + + # base signal + # if trix[i] > 0: + # t_d.append(True) + # elif trix[i] < 0: + # t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(res_arr)): + t_d = [] + + if res_arr[i] < 30: + t_d.append(True) + elif res_arr[i] > 70: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'WILLR': # 베이스 지표 => 강세장에서 효과를 발휘 + 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 = [] + + # print(res_arr[i]) + if res_arr[i] > -50: + t_d.append(True) + elif res_arr[i] < -50: + t_d.append(False) + + # 베이스 + # if res_arr[i] < -80: + # t_d.append(True) + # elif res_arr[i] > -20: + # t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(upperband)): + t_d = [] + + # if close[i] > lowerband[i] and close[i-1] < lowerband[i-1]: + # t_d.append(True) + # elif close[i] < upperband[i] and close[i-1] > upperband[i-1] or high[i-1] > upperband[i-1]: + # t_d.append(False) + + # best (7.72%) 캔들이 상/하 밴드에 닿고 밴드 확장, 이평선 => 빈도가 매우 낮음 + # if low[i] < lowerband[i] and (upperband[i]-lowerband[i]) > (upperband[i-1]-lowerband[i-1]) and close[i] > middleband[i]: + # t_d.append(True) + # elif high[i] > upperband[i] and (upperband[i]-lowerband[i]) > (upperband[i-1]-lowerband[i-1]) and close[i] < middleband[i]: + # t_d.append(False) + + if close[i] < lowerband[i]: + t_d.append(True) + elif close[i] > upperband[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'EMA' or \ + indicator is 'DEMA' or \ + indicator is 'MA' or \ + indicator is 'SMA': # 주도 지표 / 추세 + long = f(close, timeperiod=values[0]) + short = f(close, timeperiod=values[1]) + + start = 1 + numpy.isnan(long).sum() + + for i in range(start, len(long)): + t_d = [] + + if long[i] < short[i]: + t_d.append(True) + elif long[i] > short[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + # elif indicator is 'KAMA': # 주도 지표 / 사용법 모름 + # 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] > res_arr[i-1] and res_arr[i] < res_arr[i-1]: + # t_d.append(True) + # elif res_arr[i] < res_arr[i-1] and res_arr[i] > res_arr[i-1]: + # t_d.append(False) + # + # data[i] = data[i] + t_d + + elif indicator is 'MAMA': # 주도 지표 / 추세 + # print(values[0]/10, values[1]/100) + mama, fama = f(close, fastlimit=values[0], slowlimit=values[1]) + + start = 1 + numpy.isnan(mama).sum() + for i in range(start, len(mama)): + t_d = [] + + if mama[i] > fama[i]: + t_d.append(True) + elif mama[i] < fama[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + # elif indicator is 'MAVP': # 주도 지표 => 강세장에서 효과를 발휘 => 모르게따 periods? + # 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 = [] + # + # print(res_arr[i]) + # + # data[i] = data[i] + t_d + + 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 / 100, maximum=values / 10) + 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 'T3' or indicator is 'TEMA' or indicator is 'TRIMA' or indicator is 'WMA': # 주도 지표 => 추세 + long = f(close, timeperiod=values[0]) + short = f(close, timeperiod=values[1]) + + start = 1 + numpy.isnan(long).sum() + + for i in range(start, len(long)): + t_d = [] + + if short[i] > long[i]: + t_d.append(True) + elif short[i] < long[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'AD': # 베이스 지표 - 거래량 + res_arr = f(high, low, close, volume) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 가격-거래량 반비례 => 추세 반전 + if close[i] > close[i - 1] and res_arr[i] < res_arr[i - 1]: + t_d.append(False) + elif close[i] < close[i - 1] and res_arr[i] > res_arr[i - 1]: + t_d.append(True) + + data[i] = data[i] + t_d + + + elif indicator is 'ADOSC': # 베이스 지표 - 거래량 + res_arr = f(high, low, close, volume, fastperiod=values[0], slowperiod=values[1]) + + 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(False) + elif res_arr[i] > 0: + t_d.append(True) + + # 가격-거래량 반비례 => 추세 반전 + # if close[i] > close[i - 1] and res_arr[i] < res_arr[i - 1]: + # t_d.append(False) + # elif close[i] < close[i - 1] and res_arr[i] > res_arr[i - 1]: + # t_d.append(True) + + data[i] = data[i] + t_d + + 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() + + 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]: + t_d.append(True) + elif minus_di[i] > minus_di[i - 1]: + t_d.append(False) + + data[i] = data[i] + t_d + + 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] = data[i] + t_d + + elif indicator is 'OBV_DIV': # 주도 지표 / 역추세 - 다이버전스 + res_arr = f(close, volume) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'RSI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'RSI') + res_arr = f(close, values) + cdl_cnt = 14 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ADX_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ADX') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ADXR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ADXR') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'BOP_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'BOP') + res_arr = f(open, high, low, close) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'CCI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'CCI') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'CMO_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'CMO') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'MFI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'MFI') + res_arr = f(high, low, close, volume, timeperiod=values) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'MOM_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'MOM') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROC_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROC') + res_arr = f(close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROCP_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROCP') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROCR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROCR') + res_arr = f(close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'STOCH_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'STOCH') + slowk, res_arr = f(high, low, close, fastk_period=values[0], slowk_period=values[1], + slowd_period=values[2], + slowk_matype=0, slowd_matype=0) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'STOCHF_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'STOCHF') + fastk, res_arr = f(high, low, close, fastk_period=values[0], fastd_period=values[1], fastd_matype=0) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'TRIX_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'TRIX') + res_arr = f(close, timeperiod=values) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + is_trade = None + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + is_trade = True + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + is_trade = False + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator is 'WILLR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'WILLR') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + is_trade = None + + if start < cdl_cnt * 2: + start = cdl_cnt * 2 + + for i in range(start, len(res_arr)): + t_d = [] + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + is_trade = True + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + is_trade = False + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + # sys.exit(1) + self.indicators_data = data + + def next(self): + idx = (self._broker._i) - 1 + sl = self.data.Close[-1] - (self.data.Close[-1] * self._sl_percent) + + # 배열 동시 시그널 매매 + if len(self.indicators_data[idx]) == len(self.use_indicators): + # None value pass + if not (None in self.indicators_data[idx]): + if all(self.indicators_data[idx]): + if not self.orders.is_long: + self.buy(sl=sl) + elif not any(self.indicators_data[idx]): + if self.orders.is_long: + self.position.close() + + +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]}) + + return res_data + + +''' +"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] +# df = df[-1440:] # 최근 두달 데이터 +data_columns_init(df) + +cash = 1000 +commission = .005 +top_profit = -30 +top_cash = 0 +data = df +start_time = time.time() +data['Candle_pattern'] = get_candle_pattern_arr(data) + +StrategyCandlePatternOdds.pattern_data = get_candle_pattern_arr(data) + +# for pattern in ['CDL3OUTSIDE']: +# # for pattern in CandlePatterns._fence_patterns: +# StrategyCandlePatternOdds._use_patterns = [pattern] +# +# bt = Backtest(data, StrategyCandlePatternOdds, cash=cash, commission=commission) +# bt.run() +# +# print(pattern, bt._results['Win Rate [%]'], '%') +# print('거래 횟수 :', bt._results['# Trades']) +# print('-' * 60) +# bt.plot() +# +# # 승률 55퍼 이상 +# # if bt._results['# Trades'] > 0 and bt._results['Win Rate [%]'] > 55: +# # print(pattern, bt._results['Win Rate [%]'], '%') +# # print('-' * 60) +# # bt.plot() +# +# sys.exit(1) + +''' +지표 혼합 방식(모든 지표 동시 시그널 매매 - 현재 방식) or 지표 조합 방식(각 지표별 시그널 매매) +- 추세, 역추세 봇에 따라 지표의 값이 다르고 시그널로 활용할 지 베이스로 활용 할 지도 달라진다. +- 주도 지표는 실질적인 매매 시그널이 있는 지표 +- 베이스 지표(추세) => 주도 지표(역추세)로 구분하여 활용 +- 베이스 지표는 보조적인 역할을 담당 +- DM, DI 지표 사용법을 숙지해서 다시 구현 + +ADX(Average Directional Movement Index)는 추세를 측정하는 지표로, 다른 지표들과 혼합되어 사용된다. +APO(Absolute Price Oscillator) => 굉장히 매수시점이 좋다. 매도 시점만 받쳐줄 지표와 혼합되면 굉장할 듯. +AROON 지표는 매수 전략만으로 활용 된다.(long 포지션 매매) +BOP(Balance Of Power) : 상승세와 하락세가 서로 경합하면서 주가를 한쪽으로 이동시키려는 강도를 보여준다. +=> 추세 지표 + +SAR(Parabolic SAR) : 매매 시점이 좋으나, 거래 빈도가 높아 수익률은 낮다. => 조합 사용 시 매우 효과적일듯 +OBV(On Balance Volume) : 가격과 다이버전시로 활용 못할 시 무의미한 지표 - 매매 파워 척도 => 상승장/하락장 판단에 +매우 유용한 지표 +''' +using_indicators = { + 'trade_indicators': ['STOCH', 'DMI', 'DI', 'APO', 'AROON', 'MACD', 'PPO', + 'STOCHF', 'STOCHRSI', 'ULTOSC', 'EMA', 'DEMA', 'MA', 'SMA', 'MAMA', 'MAVP', + 'MIDPOINT', 'MIDPRICE', 'SAR', 'T3', '', 'TEMA', 'TRIMA', 'WMA'], + 'base_indicators': ['RSI', 'ADX', 'ADXR', 'AROONOSC', 'BOP', 'CCI', 'CMO', 'MFI', 'MOM', 'ROC', 'ROCP', + 'ROCR', 'ROCR100', 'TRIX', 'WILLR', 'BBANDS', 'AD', 'ADOSC', 'OBV'], + 'div_indicators': ['RSI_DIV', 'OBV_DIV', 'ADXR_DIV', 'ADX_DIV', 'BOP_DIV', 'CCI_DIV', 'CMO_DIV', + 'MFI_DIV', 'MOM_DIV', 'ROC_DIV', 'ROCP_DIV', 'ROCR_DIV', 'STOCH_DIV', 'STOCHF_DIV', + 'TRIX_DIV', 'WILLR_DIV'] +} + +# for test +# using_indicators = { +# 'trade_indicators':['SMA'], +# 'base_indicators': ['RSI'], +# 'div_indicators': ['RSI_DIV'] +# } +# - 캔들패턴 승률 측정 시뮬레이터 구현 => 매수, 매도, 중립 패턴 승률 +# 패턴 승률 => 매수 패턴은 고가 갱신 시, 매도 패턴은 저가 갱신시 +# best_patterns = ['CDLCONCEALBABYSWALL', 'CDLMORNINGSTAR', 'CDLSHOOTINGSTAR', 'CDLKICKINGBYLENGTH', 'CDLINNECK', 'CDLHARAMI', 'CDLGRAVESTONEDOJI', 'CDLUPSIDEGAP2CROWS', 'CDLXSIDEGAP3METHODS', 'CDLCOUNTERATTACK', 'CDLEVENINGDOJISTAR', 'CDL3LINESTRIKE', 'CDLKICKING', 'CDLRISEFALL3METHODS', 'CDLUNIQUE3RIVER'] +data['Candle_pattern'] = get_candle_pattern_arr(data) +indicators_values = get_indicators_args(['TRIX_DIV']) +# indicators_values = get_indicators_args(using_indicators['div_indicators']) +# indicators_values = [{'CDLPTN' : None}] + +# 캔들 패턴 검증 + +# case 1 : 베이스 지표 - 캔들 패턴과 검증 +for indicator in indicators_values: + StrategyIndicators.use_indicators = [indicator] + [{'CDLPTN': None}] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + print("최종 금액 : %0.2f" % bt._results['Equity Final [$]']) + print("총 수익률 : %0.2f%%" % bt._results['Return [%]']) + print(StrategyIndicators.use_indicators) + # bt.plot() + # exit(1) + + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + + # exit(1) + # temp_array.append(indicator) + +sys.exit(1) + +none_stat_filtered = {} + +# case 0 : 주도 지표 거래 발생 여부 검증 20분 +for type in using_indicators: + temp_array = [] + base_values = get_indicators_args(using_indicators[type]) + + for indicator in base_values: + StrategyIndicators.use_indicators = [indicator] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + + if bt._results['# Trades'] > 0: + temp_array.append(indicator) + + none_stat_filtered.update({type: temp_array}) + +e = int(time.time() - start_time) +print('1차 필터링 종료 :', '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) + +# case 2 : 주도 지표 - 베이스 지표와 검증 +main_filterd = [] +base_filterd = [] + +for trade_indicator in none_stat_filtered['trade_indicators'] + none_stat_filtered['div_indicators']: + for base_indicator in none_stat_filtered['base_indicators']: + StrategyIndicators.use_indicators = [trade_indicator, base_indicator] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + # 수익률, 승률, 승패 등으로 필터링 기준 조정 => 승률이 좋던가, 수익이 좋던가 + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + + if int(bt._results['Return [%]']) > 0: + main_filterd.append(trade_indicator) + base_filterd.append(base_indicator) + +e = int(time.time() - start_time) +print('2차 필터링 종료 :', '{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) + +# 중복 제거 +main_filterd = [dict(t) for t in set([tuple(d.items()) for d in main_filterd])] +base_filterd = [dict(t) for t in set([tuple(d.items()) for d in base_filterd])] + +# 랜덤워크로 최대수익 보조지표 조합 도출 +while True: + main_indicators = random.choices(main_filterd, k=random.randrange(1, 5)) + base_indicators = random.choices(base_filterd, k=random.randrange(0, len(base_filterd))) + + StrategyIndicators.use_indicators = main_indicators + base_indicators + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + +# 17가지 4시간 +e = int(time.time() - start_time) +print('{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) diff --git a/indicator_backtest.py b/indicator_backtest.py new file mode 100644 index 0000000..9797036 --- /dev/null +++ b/indicator_backtest.py @@ -0,0 +1,1575 @@ +from backtesting import Backtest, Strategy +# from backtesting.lib import crossover + +from backtesting.test import SMA, GOOG +import sys +import talib, numpy, pybithumb, time, random +import pandas as pd +import numpy as np +from itertools import product, combinations +from pprint import pprint + +# for divergence +import numpy as np +import pandas as pd +import math +import matplotlib.pyplot as plt +from scipy import stats +from scipy.stats import linregress +import statsmodels +from pandas import Series +import talib + + +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(p_x_d, x_d, p_y_d, y_d) -> bool: + try: + return p_x_d < p_y_d and x_d > y_d + except IndexError: + return False + + +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 + + +''' +Long/Short Divergence +params : + i - index + cdl_cnt - candle count + low - low values + high - high values + res_arr - indicator values +''' +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 + +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 StrategyIndicators(Strategy): + use_indicators = None + indicators_data = None + # value = dict() + _sl_percent = 0.03 # 2% + + def init(self): + close = self.data.Close + low = self.data.Low + high = self.data.High + open = self.data.Open + volume = self.data.Volume + + data = [list()] * len(self.data.Close) + + for p in self.use_indicators: + indicator = None + values = None + f = None + + indicator, values = list(p.items())[0] + + if indicator != 'DI' and hasattr(talib, indicator): + f = getattr(talib, indicator) + + if indicator is 'CDLPTN': + + for i in range(0, len(close)): + t_d = [] + + if self.data.Candle_pattern[i] is None: + continue + + pattern, value = list(self.data.Candle_pattern[i].items())[0] + + if pattern in CandlePatterns._positive_patterns: # 매수 패턴 + t_d.append(True) + elif pattern in CandlePatterns._negative_patterns: # 매도 패턴 + t_d.append(False) + # elif pattern in CandlePatterns._fence_patterns: # 중립 패턴 + # if int(value) > 0: + # t_d.append(True) + # elif int(value) < 0: + # t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'RSI': # 베이스 지표 + res_arr = f(close, values) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 수치 매매 + if res_arr[i] < 25: + t_d.append(True) + elif res_arr[i] > 75: + t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(slowk)): + t_d = [] + if crossover(slowk[i-1], slowk[i], slowd[i-1], slowd[i]): + t_d.append(False) + elif crossover(slowd[i-1], slowd[i], slowk[i-1], slowk[i]): + t_d.append(True) + + data[i] = data[i] + t_d + + 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] < 20: # 25초과 - 추세추종 / 20 미만 역추세 매매 + t_d.append(True) + else: + 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() + + for i in range(start, len(res_arr_plus)): + t_d = [] + + if res_arr_plus[i] > res_arr_minus[i]: + t_d.append(True) + else: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'APO': # 주도 지표 + res_arr = f(close, fastperiod=values[0], slowperiod=values[1], matype=0) + 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) + else: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'AROON': # 주도 지표 + aroondown, aroonup = f(high, low, timeperiod=values) + start = 1 + numpy.isnan(aroondown).sum() + + for i in range(start, len(aroondown)): + t_d = [] + + if aroonup[i] > 50 and crossover(aroondown[i-1], aroondown[i], aroonup[i-1], aroonup[i]): + t_d.append(False) + elif aroondown[i] < 50 or crossover(aroonup[i-1], aroonup[i], aroondown[i-1], aroondown[i]): + t_d.append(True) + + data[i] = data[i] + t_d + + elif indicator is 'AROONOSC': # 베이스 지표 + 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 res_arr[i] > 0: + t_d.append(True) + elif res_arr[i] > 90: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'BOP': # 베이스 지표 + res_arr = f(open, high, low, close) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # if res_arr[i] < -0.8: # 과매도 매수 + # # t_d.append(True) + # # elif res_arr[i] > 0.7: # 과매수 매도 + # # t_d.append(False) + + if res_arr[i] > 0 and res_arr[i-1] < 0: + t_d.append(True) + elif res_arr[i] < 0 and res_arr[i-1] > 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'CCI': # 베이스 지표 / 추세 + 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 = [] + # 120/120 + + # 시그널 + # if res_arr[i] > 0 and res_arr[i-1] < 0: + # t_d.append(True) + # elif res_arr[i] < 0 and res_arr[i-1] > 0: + # t_d.append(False) + + # 베이스 + if res_arr[i] > 0: # 과매도 -100 / 과매수 +100 + t_d.append(True) + elif res_arr[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'CMO': # 베이스 지표 / 추세 + 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] > -50 and res_arr[i-1] < -50: + # t_d.append(True) + # elif res_arr[i] < 50 and res_arr[i-1] > 50: + # t_d.append(False) + + # 베이스 + 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 'DX': # 베이스 지표 / 추세 + # 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] < 10: + # t_d.append(True) + # elif res_arr[i] > 40: + # t_d.append(False) + # + # data[i] = data[i] + t_d + + elif indicator is 'MACD': # 주도 지표 + macd, macdsignal, macdhist = f(close, + fastperiod=values[0], + slowperiod=values[1], + signalperiod=values[2]) + + start = 1 + numpy.isnan(macd).sum() + + for i in range(start, len(macd)): + t_d = [] + if macd[i] > macdsignal[i] and macd[i-1] < macdsignal[i-1] and macd[i] > 0: + t_d.append(True) + elif macd[i] < macdsignal[i] and macd[i-1] > macdsignal[i-1] or macd[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MACDFIX': # 주도 지표 + macd, macdsignal, macdhist = f(close, signalperiod=values) + start = 1 + numpy.isnan(macd).sum() + + for i in range(start, len(macd)): + t_d = [] + + if macd[i] > macdsignal[i] and macd[i-1] < macdsignal[i-1] and macd[i] > 0: + t_d.append(True) + elif macd[i] < macdsignal[i] and macd[i-1] > macdsignal[i-1] or macd[i] < 0: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MFI': # 베이스 지표 / 역추세 + res_arr = f(high, low, close, volume, timeperiod=values) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 베이스 + if res_arr[i] < 20: + t_d.append(True) + elif res_arr[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'MOM': # 베이스 지표 / 추세 + 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 'PPO': # 주도 지표 + res_arr = f(close, fastperiod=values[0], slowperiod=values[1], matype=0) + 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 'ROC': # 베이스 지표 / 추세 + 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 '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() + + for i in range(start, len(fastk)): + t_d = [] + + if fastd[i] < 20: + t_d.append(True) + elif fastd[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'STOCHRSI': # 주도 지표 + fastk, fastd = f(close, timeperiod=values[0], fastk_period=values[1], fastd_period=values[2], fastd_matype=0) + start = 1 + numpy.isnan(fastk).sum() + + for i in range(start, len(fastk)): + t_d = [] + + # print(fastk[i], fastd[i]) + + if fastd[i] < 20: + t_d.append(True) + elif fastd[i] > 80: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'TRIX': # 베이스 지표 / 역추세 => 매수/매도 시점 괜찮다 + trix = f(close, timeperiod=values[0]) + + t_data = pd.DataFrame(trix) + trix_signal = t_data.rolling(values[1]).mean().values # 이동평균 시그널 생성 + + del t_data + + start = 1 + numpy.isnan(trix_signal).sum() + + for i in range(start, len(trix_signal)): + t_d = [] + + # 14.77% + if trix[i] > trix_signal[i][0]: + t_d.append(True) + elif trix[i] < trix_signal[i][0]: + t_d.append(False) + + # base signal + # if trix[i] > 0: + # t_d.append(True) + # elif trix[i] < 0: + # t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(res_arr)): + t_d = [] + + if res_arr[i] < 30: + t_d.append(True) + elif res_arr[i] > 70: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'WILLR': # 베이스 지표 => 강세장에서 효과를 발휘 + 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 = [] + + # print(res_arr[i]) + if res_arr[i] > -50: + t_d.append(True) + elif res_arr[i] < -50: + t_d.append(False) + + # 베이스 + # if res_arr[i] < -80: + # t_d.append(True) + # elif res_arr[i] > -20: + # t_d.append(False) + + data[i] = data[i] + t_d + + 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() + + for i in range(start, len(upperband)): + t_d = [] + + # if close[i] > lowerband[i] and close[i-1] < lowerband[i-1]: + # t_d.append(True) + # elif close[i] < upperband[i] and close[i-1] > upperband[i-1] or high[i-1] > upperband[i-1]: + # t_d.append(False) + + # best (7.72%) 캔들이 상/하 밴드에 닿고 밴드 확장, 이평선 => 빈도가 매우 낮음 + # if low[i] < lowerband[i] and (upperband[i]-lowerband[i]) > (upperband[i-1]-lowerband[i-1]) and close[i] > middleband[i]: + # t_d.append(True) + # elif high[i] > upperband[i] and (upperband[i]-lowerband[i]) > (upperband[i-1]-lowerband[i-1]) and close[i] < middleband[i]: + # t_d.append(False) + + if close[i] < lowerband[i]: + t_d.append(True) + elif close[i] > upperband[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'EMA' or \ + indicator is 'DEMA' or \ + indicator is 'MA' or \ + indicator is 'SMA': # 주도 지표 / 추세 + long = f(close, timeperiod=values[0]) + short = f(close, timeperiod=values[1]) + + start = 1 + numpy.isnan(long).sum() + + for i in range(start, len(long)): + t_d = [] + + if long[i] < short[i]: + t_d.append(True) + elif long[i] > short[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + # elif indicator is 'KAMA': # 주도 지표 / 사용법 모름 + # 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] > res_arr[i-1] and res_arr[i] < res_arr[i-1]: + # t_d.append(True) + # elif res_arr[i] < res_arr[i-1] and res_arr[i] > res_arr[i-1]: + # t_d.append(False) + # + # data[i] = data[i] + t_d + + elif indicator is 'MAMA': # 주도 지표 / 추세 + # print(values[0]/10, values[1]/100) + mama, fama = f(close, fastlimit=values[0], slowlimit=values[1]) + + start = 1 + numpy.isnan(mama).sum() + for i in range(start, len(mama)): + t_d = [] + + if mama[i] > fama[i]: + t_d.append(True) + elif mama[i] < fama[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + # elif indicator is 'MAVP': # 주도 지표 => 강세장에서 효과를 발휘 => 모르게따 periods? + # 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 = [] + # + # print(res_arr[i]) + # + # data[i] = data[i] + t_d + + 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/100, maximum=values/10) + 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 'T3' or indicator is 'TEMA' or indicator is 'TRIMA' or indicator is 'WMA': # 주도 지표 => 추세 + long = f(close, timeperiod=values[0]) + short = f(close, timeperiod=values[1]) + + start = 1 + numpy.isnan(long).sum() + + for i in range(start, len(long)): + t_d = [] + + if short[i] > long[i]: + t_d.append(True) + elif short[i] < long[i]: + t_d.append(False) + + data[i] = data[i] + t_d + + elif indicator is 'AD': # 베이스 지표 - 거래량 + res_arr = f(high, low, close, volume) + start = 1 + numpy.isnan(res_arr).sum() + + for i in range(start, len(res_arr)): + t_d = [] + + # 가격-거래량 반비례 => 추세 반전 + if close[i] > close[i-1] and res_arr[i] < res_arr[i-1]: + t_d.append(False) + elif close[i] < close[i-1] and res_arr[i] > res_arr[i-1]: + t_d.append(True) + + data[i] = data[i] + t_d + + + elif indicator is 'ADOSC': # 베이스 지표 - 거래량 + res_arr = f(high, low, close, volume, fastperiod=values[0], slowperiod=values[1]) + + 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(False) + elif res_arr[i] > 0: + t_d.append(True) + + # 가격-거래량 반비례 => 추세 반전 + # if close[i] > close[i - 1] and res_arr[i] < res_arr[i - 1]: + # t_d.append(False) + # elif close[i] < close[i - 1] and res_arr[i] > res_arr[i - 1]: + # t_d.append(True) + + data[i] = data[i] + t_d + + 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() + + 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]: + t_d.append(True) + elif minus_di[i] > minus_di[i-1]: + t_d.append(False) + + data[i] = data[i] + t_d + + 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] = data[i] + t_d + + elif indicator is 'OBV_DIV': # 주도 지표 / 역추세 - 다이버전스 + res_arr = f(close, volume) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'RSI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'RSI') + res_arr = f(close, values) + cdl_cnt = 14 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ADX_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ADX') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ADXR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ADXR') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'BOP_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'BOP') + res_arr = f(open, high, low, close) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'CCI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'CCI') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'CMO_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'CMO') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'MFI_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'MFI') + res_arr = f(high, low, close, volume, timeperiod=values) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'MOM_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'MOM') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROC_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROC') + res_arr = f(close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROCP_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROCP') + res_arr = f(close, timeperiod=values) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'ROCR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'ROCR') + res_arr = f(close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'STOCH_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'STOCH') + slowk, res_arr = f(high, low, close, fastk_period=values[0], slowk_period=values[1], slowd_period=values[2], + slowk_matype=0, slowd_matype=0) + cdl_cnt = 15 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + + if indicator is 'STOCHF_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'STOCHF') + fastk, res_arr = f(high, low, close, fastk_period=values[0], fastd_period=values[1], fastd_matype=0) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'TRIX_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'TRIX') + res_arr = f(close, timeperiod=values) + cdl_cnt = 20 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + if indicator is 'WILLR_DIV': # 주도 지표 / 역추세 - 다이버전스 + f = getattr(talib, 'WILLR') + res_arr = f(high, low, close, timeperiod=values) + cdl_cnt = 10 + start = 1 + numpy.isnan(res_arr).sum() + + if start < cdl_cnt*2: + start = cdl_cnt*2 + + for i in range(start, len(res_arr)): + t_d = [] + + if is_divergence(i, cdl_cnt, low, high, res_arr) is True: + t_d.append(True) + elif is_divergence(i, cdl_cnt, low, high, res_arr) is False: + t_d.append(False) + + data[i] = data[i] + t_d + + # sys.exit(1) + self.indicators_data = data + + def next(self): + idx = (self._broker._i)-1 + sl = self.data.Close[-1] - (self.data.Close[-1] * self._sl_percent) + + # 배열 동시 시그널 매매 + if len(self.indicators_data[idx]) == len(self.use_indicators): + if all(self.indicators_data[idx]): + if not self.orders.is_long: + self.buy(sl=sl) + elif not any(self.indicators_data[idx]): + if self.orders.is_long: + self.position.close() + + +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]}) + + + return res_data + +''' +"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] +df = df[-1440:] # 최근 두달 데이터 +data_columns_init(df) + +cash = 1000 +commission = .005 +top_profit = 0 +top_cash = 0 +data = df +start_time = time.time() + +''' +지표 혼합 방식(모든 지표 동시 시그널 매매 - 현재 방식) or 지표 조합 방식(각 지표별 시그널 매매) +- 추세, 역추세 봇에 따라 지표의 값이 다르고 시그널로 활용할 지 베이스로 활용 할 지도 달라진다. +- 주도 지표는 실질적인 매매 시그널이 있는 지표 +- 베이스 지표(추세) => 주도 지표(역추세)로 구분하여 활용 +- 베이스 지표는 보조적인 역할을 담당 +- DM, DI 지표 사용법을 숙지해서 다시 구현 + +ADX(Average Directional Movement Index)는 추세를 측정하는 지표로, 다른 지표들과 혼합되어 사용된다. +APO(Absolute Price Oscillator) => 굉장히 매수시점이 좋다. 매도 시점만 받쳐줄 지표와 혼합되면 굉장할 듯. +AROON 지표는 매수 전략만으로 활용 된다.(long 포지션 매매) +BOP(Balance Of Power) : 상승세와 하락세가 서로 경합하면서 주가를 한쪽으로 이동시키려는 강도를 보여준다. +=> 추세 지표 + +SAR(Parabolic SAR) : 매매 시점이 좋으나, 거래 빈도가 높아 수익률은 낮다. => 조합 사용 시 매우 효과적일듯 +OBV(On Balance Volume) : 가격과 다이버전시로 활용 못할 시 무의미한 지표 - 매매 파워 척도 => 상승장/하락장 판단에 +매우 유용한 지표 +''' +using_indicators = { + 'trade_indicators':['STOCH', 'DMI', 'DI', 'APO', 'AROON', 'MACD', 'PPO', + 'STOCHF', 'STOCHRSI', 'ULTOSC', 'EMA', 'DEMA', 'MA', 'SMA', 'MAMA', 'MAVP', + 'MIDPOINT', 'MIDPRICE', 'SAR', 'T3', '', 'TEMA', 'TRIMA', 'WMA'], + 'base_indicators': ['RSI', 'ADX', 'ADXR', 'AROONOSC', 'BOP', 'CCI', 'CMO', 'MFI', 'MOM', 'ROC', 'ROCP', + 'ROCR', 'ROCR100', 'TRIX', 'WILLR', 'BBANDS', 'AD', 'ADOSC', 'OBV'], + 'div_indicators': ['RSI_DIV', 'OBV_DIV', 'ADXR_DIV', 'ADX_DIV', 'BOP_DIV', 'CCI_DIV', 'CMO_DIV', + 'MFI_DIV', 'MOM_DIV', 'ROC_DIV', 'ROCP_DIV', 'ROCR_DIV', 'STOCH_DIV', 'STOCHF_DIV', + 'TRIX_DIV', 'WILLR_DIV'] +} + +# for test +# using_indicators = { +# 'trade_indicators':['SMA'], +# 'base_indicators': ['RSI'], +# 'div_indicators': ['RSI_DIV'] +# } + +none_stat_filtered = {} + +# case 0 : 주도 지표 거래 발생 여부 검증 20분 +for type in using_indicators: + temp_array = [] + base_values = get_indicators_args(using_indicators[type]) + + for indicator in base_values: + StrategyIndicators.use_indicators = [indicator] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + + if bt._results['# Trades'] > 0: + temp_array.append(indicator) + + none_stat_filtered.update({type: temp_array}) + +e = int(time.time() - start_time) +print('1차 필터링 종료 :','{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) + +# case 2 : 주도 지표 - 베이스 지표와 검증 +main_filterd = [] +base_filterd = [] + +for trade_indicator in none_stat_filtered['trade_indicators'] + none_stat_filtered['div_indicators']: + for base_indicator in none_stat_filtered['base_indicators']: + StrategyIndicators.use_indicators = [trade_indicator, base_indicator] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + # 수익률, 승률, 승패 등으로 필터링 기준 조정 => 승률이 좋던가, 수익이 좋던가 + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + + if int(bt._results['Return [%]']) > 0: + main_filterd.append(trade_indicator) + base_filterd.append(base_indicator) + +e = int(time.time() - start_time) +print('2차 필터링 종료 :','{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) + +# 중복 제거 +main_filterd = [dict(t) for t in set([tuple(d.items()) for d in main_filterd])] +base_filterd = [dict(t) for t in set([tuple(d.items()) for d in base_filterd])] + +# 랜덤워크로 최대수익 보조지표 조합 도출 +while True: + main_indicators = random.choices(main_filterd, k=random.randrange(1, 5)) + base_indicators = random.choices(base_filterd, k=random.randrange(0, len(base_filterd))) + + StrategyIndicators.use_indicators = main_indicators + base_indicators + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + +pass + +# 주도 매매 보조지표 검증 +for indicator in res: + StrategyIndicators.use_indicators = [indicator] + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + # print(StrategyIndicators.use_indicators, "수익률 : %0.2f%%" % bt._results['Return [%]']) + + # 수익률, 승률, 승패 등으로 필터링 기준 조정 => 승률이 좋던가, 수익이 좋던가 + if bt._results['Return [%]'] > top_profit and bt._results['# Trades'] > 0: + + 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(StrategyIndicators.use_indicators) + print('-' * 60) + # bt.plot() + + if int(bt._results['Return [%]']) > 0 and bt._results['# Trades'] > 0: + indicators.append(indicator) + +# print('경우의 수 :', len(indicators)) +sys.exit(1) + +''' +작업 목록 +- OBV 지표 다이버전스 활용 구현 +- DI, DX 지표 사용법 숙지 후 구현 +- 다이버전스를 활용하는 지표들 다이버전스 활용 구현 +- 주도 지표, 베이스 지표 분류 +- 캔들 패턴, 주도, 베이스 지표 조합 시뮬레이팅 구현 => 무작위 +''' + +# 다이버전스가 가능한 지표는 다이버전스 주도 지표로 분류(매매 지표) +# RSI => RSI : 베이스 지표 + RSIDIV : 다이버전스 주도 지표 + +# 두개 이상의 복합 보조지표 검증 +for idx in list(range(2, len(indicators)+1)): + for indic in list(combinations(indicators, idx)): + StrategyIndicators.use_indicators = list(indic) + + bt = Backtest(data, StrategyIndicators, cash=cash, commission=commission) + bt.run() + + 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(StrategyIndicators.use_indicators) + print('-' * 60) + bt.plot() + + # init variables + del bt + StrategyIndicators.value = dict() + +# 17가지 4시간 +e = int(time.time() - start_time) +print('{:02d}:{:02d}:{:02d}'.format(e // 3600, (e % 3600 // 60), e % 60)) diff --git a/indicator_util.py b/indicator_util.py new file mode 100644 index 0000000..29521f8 --- /dev/null +++ b/indicator_util.py @@ -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 diff --git a/new_backtest.py b/new_backtest.py new file mode 100644 index 0000000..0e58439 --- /dev/null +++ b/new_backtest.py @@ -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() diff --git a/power_backtest.py b/power_backtest.py new file mode 100644 index 0000000..85828b2 --- /dev/null +++ b/power_backtest.py @@ -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 매도로 구성) => 보류 => 보조지표를 시그널로 활용 +# 봉별 단일 시그널로 활용할 지, 복합 시그널(두개 이상)로 활용 할지.. +# 매매 패턴의 경우 데이터에 존재하는지 체크 후 패턴 리스트에 추가 +''' \ No newline at end of file diff --git a/profile_backtest.py b/profile_backtest.py new file mode 100644 index 0000000..eaa51ac --- /dev/null +++ b/profile_backtest.py @@ -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() +''' diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3ea2351 --- /dev/null +++ b/requirements.txt @@ -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 diff --git a/signal_helper.py b/signal_helper.py new file mode 100644 index 0000000..5d6e8ae --- /dev/null +++ b/signal_helper.py @@ -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 + + diff --git a/strategies/desktop.ini b/strategies/desktop.ini new file mode 100644 index 0000000..8934518 --- /dev/null +++ b/strategies/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip= ¶ ˴ϴ. +IconFile=C:\Program Files\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file diff --git a/strategies/indicator.py b/strategies/indicator.py new file mode 100644 index 0000000..234978f --- /dev/null +++ b/strategies/indicator.py @@ -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() diff --git a/strategy.py b/strategy.py new file mode 100644 index 0000000..73f8966 --- /dev/null +++ b/strategy.py @@ -0,0 +1,1137 @@ +from backtesting import Backtest, Strategy +from trade.candle import CandlePatterns +import talib, numpy, math +from signal_helper import * + +from pyti import stochrsi +from pyti import chande_momentum_oscillator + +import sys + + +class StrategyCandlePattern(Strategy): + use_indicators = None + indicators_data = None + _use_patterns = None + pattern_data = None + _sl_percent = 0.03 # 4% + _r_sl_percent = 0.05 # 4% + + # Stop Profit/Loss + up_target = None + down_target = None + + # 캔들 패턴 + cp = CandlePatterns() + + # 매수 패턴 + _positive_patterns = cp.get_long_patterns() + _negative_patterns = cp.get_short_patterns() + _fence_patterns = cp.get_fence_patterns() + + data_len = None + + # position_closed_time + position_closed_time = None + + # trade type + trade_type = 'single' + + # set position + is_short = None + + def init(self): + close = self.data.Close + low = self.data.Low + high = self.data.High + open = self.data.Open + volume = self.data.Volume + # date = self.data.Volume + date = self.data.index + + self.indicators_data = None + + self.data_len = len(close) - 1 + + data = [list()] * len(self.data.Close) + # data = np.array([list()] * len(self.data.Close)) + + for p in self.use_indicators: + indicator = None + values = None + f = None + + indicator, values = list(p.items())[0] + + if indicator != 'DI' and hasattr(talib, indicator): + f = getattr(talib, indicator) + + if indicator == 'CDLPTN': + + for i in range(0, len(close)): + t_d = [] + + if self.data.Candle_pattern[i] is None: + continue + + pattern, value = list(self.data.Candle_pattern[i].items())[0] + + if pattern in self._fence_patterns: # 중립 패턴 + t_d.append(is_trade_fence_pattern(pattern, value)) + elif pattern in self._positive_patterns: # 매수 패턴 + t_d.append(True) + elif pattern in self._negative_patterns: # 매도 패턴 + t_d.append(False) + + data[i] = data[i] + t_d + + # 보조지표 추가 시작 + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == 'ADX' or indicator == '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 == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + # print(date[i], res_arr[i]) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == 'ROCP' or indicator == 'ROCR' or indicator == '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 == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == 'EMA' or \ + indicator == 'DEMA' or \ + indicator == 'MA' or \ + indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 == '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 == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == 'TRIMA' or indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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] = data[i] + t_d + + # 보조지표 추가 끝 + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + ''' CMO 지표 수치 불일치 ''' + if indicator == 'CMO_DIV': + # res_arr = chande_momentum_oscillator.chande_momentum_oscillator(close, 9) + + f = getattr(talib, 'CMO') + res_arr = f(close, timeperiod=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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + t_d.append(is_trade) + + data[i] = data[i] + t_d + + if indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == '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 + + t_d.append(is_trade) + + data[i] = data[i] + t_d + + elif indicator == 'HEI': # heikenashi + # df = pd.DataFrame( + # {'Open': open, 'High': high, 'Low': low, 'Close': close, 'Volume': volume}, + # ).copy() + # res_arr = heikenashi(df) + + 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 + + self.indicators_data = data + + def next(self): + idx = (self._broker._i) - 1 + price = self.data.Close[-1] + sl = 0 + tp = 0 + + if float(self.position.size) >= 0: + self.is_short = None + + if self.down_target > 0: + sl = price - (price * self.down_target) + if self.up_target > 0: + tp = price + (price * self.up_target) + + # print('is_long', self.orders.is_long) + # print('is_short', self.is_short) + + # 배열 동시 시그널 매매 + if len(self.indicators_data[idx]) == len(self.use_indicators): + # Set Long Position + if not (None in self.indicators_data[idx]): + if all(self.indicators_data[idx]): + if self.is_short: + self.position.close() + self.is_short = None + + if len(self.use_indicators) is 1: # 단일 지표일 경우 + if not self.orders.is_long and not any(self.indicators_data[idx - 1]): + if sl != 0 and tp != 0: + self.buy(sl=sl, tp=tp) + self.is_short = None + elif sl != 0: + self.buy(sl=sl) + self.is_short = None + elif tp != 0: + self.buy(tp=tp) + self.is_short = None + else: + self.buy() + self.is_short = None + + elif len(self.use_indicators) > 1: # 여러 지표일 경우 + # print(self.data.index[idx]) + if not self.orders.is_long: + if sl != 0 and tp != 0: + self.buy(sl=sl, tp=tp) + self.is_short = None + elif sl != 0: + self.buy(sl=sl) + self.is_short = None + elif tp != 0: + self.buy(tp=tp) + self.is_short = None + else: + self.buy() + self.is_short = None + + # Set Short Position + elif not any(self.indicators_data[idx]): + if self.orders.is_long: + self.position.close() + + if self.trade_type == 'double': + if len(self.use_indicators) is 1: # 단일 지표일 경우 print('short', idx) + + if all(self.indicators_data[idx - 1]) and self.is_short is None: + if sl > 0 and tp > 0: + self.sell(sl=tp, tp=sl) + self.is_short = True + elif tp > 0: + self.sell(sl=tp) + self.is_short = True + elif sl > 0: + self.sell(tp=sl) # lib not working + self.is_short = True + else: + self.sell() + self.is_short = True + + elif len(self.use_indicators) > 1: # 여러 지표일 경우 + if self.is_short is None: + if sl > 0 and tp > 0: + self.sell(sl=tp, tp=sl) + self.is_short = True + elif tp > 0: + self.sell(sl=tp) + self.is_short = True + elif sl > 0: + self.sell(tp=sl) # lib not working + self.is_short = True + else: + self.sell() + self.is_short = True + + # 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() diff --git a/test_slicing.py b/test_slicing.py new file mode 100644 index 0000000..395be9d --- /dev/null +++ b/test_slicing.py @@ -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() # 동기 + + + + + diff --git a/trade/candle.py b/trade/candle.py new file mode 100644 index 0000000..c57f6b8 --- /dev/null +++ b/trade/candle.py @@ -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 diff --git a/trade/desktop.ini b/trade/desktop.ini new file mode 100644 index 0000000..8934518 --- /dev/null +++ b/trade/desktop.ini @@ -0,0 +1,5 @@ +[.ShellClassInfo] +InfoTip= ¶ ˴ϴ. +IconFile=C:\Program Files\Google\Drive\googledrivesync.exe +IconIndex=16 + \ No newline at end of file