1650 lines
61 KiB
Python
1650 lines
61 KiB
Python
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))
|