Получение данных:
Вы можете найти полный код в Jupyter Notebbok на моем GitHub. Теперь давайте проверим эксперимент и выводы:
Я обнаружил, что внедрение показателей ТА радикально улучшает предсказание моей модели, а также нашел оптимальный временной диапазон и процентную цель для предсказания прорыва.
Я буду использовать значения свечей при открытии рынка по максимуму-минимуму (OHLC) с точностью до 30 минут по паре ETHUSD вместе с объемом, используя API Binance https://www.binance.com/es/binance- апи.
Используя обертку python https://python-binance.readthedocs.io/en/latest/ делаем примерно так
df = klines = client.get_historical_klines(“ETHUSDT”, Client.KLINE_INTERVAL_30MINUTE, “1 Jun, 2020”, “30 Dec, 2022”)
Индикаторы ТА
Затем вычислим все показатели ТА с помощью библиотеки Ta-lib https://mrjbq7.github.io/ta-lib/
cycle_Indicators = ['HT_DCPERIOD', 'HT_DCPHASE', 'HT_TRENDMODE'] stadistics_indicator = [ "TSF" , "LINEARREG_INTERCEPT"] #TA indicators indicators = [ 'CDL2CROWS', 'CDL3BLACKCROWS', 'CDL3INSIDE', 'CDL3LINESTRIKE', 'CDL3OUTSIDE', 'CDL3STARSINSOUTH', 'CDL3WHITESOLDIERS', 'CDLABANDONEDBABY', 'CDLADVANCEBLOCK', 'CDLBELTHOLD', 'CDLBREAKAWAY', 'CDLCLOSINGMARUBOZU', 'CDLCONCEALBABYSWALL', 'CDLCOUNTERATTACK', 'CDLDARKCLOUDCOVER', 'CDLDOJI', 'CDLDOJISTAR', 'CDLDRAGONFLYDOJI', 'CDLENGULFING', 'CDLEVENINGDOJISTAR', 'CDLEVENINGSTAR', 'CDLGAPSIDESIDEWHITE', 'CDLGRAVESTONEDOJI', 'CDLHAMMER', 'CDLHANGINGMAN', 'CDLHARAMI', 'CDLHARAMICROSS', 'CDLHIGHWAVE', 'CDLHIKKAKE', 'CDLHIKKAKEMOD', 'CDLHOMINGPIGEON', 'CDLIDENTICAL3CROWS', 'CDLINNECK', 'CDLINVERTEDHAMMER', 'CDLKICKING', 'CDLKICKINGBYLENGTH', 'CDLLADDERBOTTOM', 'CDLLONGLEGGEDDOJI', 'CDLLONGLINE', 'CDLMARUBOZU', 'CDLMATCHINGLOW', 'CDLMATHOLD', 'CDLMORNINGDOJISTAR', 'CDLMORNINGSTAR', 'CDLONNECK', 'CDLPIERCING', 'CDLRICKSHAWMAN', 'CDLRISEFALL3METHODS', 'CDLSEPARATINGLINES', 'CDLSHOOTINGSTAR', 'CDLSHORTLINE', 'CDLSPINNINGTOP', 'CDLSTALLEDPATTERN', 'CDLSTICKSANDWICH', 'CDLTAKURI', 'CDLTASUKIGAP', 'CDLTHRUSTING', 'CDLTRISTAR', 'CDLUNIQUE3RIVER', 'CDLUPSIDEGAP2CROWS', 'CDLXSIDEGAP3METHODS'] ## Used for generating the variables def get_X (data_ , model_params, data=False ): periods = model_params["periods"] time_periods = model_params ["time_periods"] # It loads the data we get from Binance if data : df = pd.DataFrame(data_ , columns = ['open_time','open', 'high', 'low', 'close', 'volume','close_time', 'quote_asset_volume','num_trades','taker_base_vol','taker_quote_vol', 'ignore'] ) else: df= pd.read_csv( f"{data_}" ) df.drop("Unnamed: 0" , axis=1 , inplace=True) df = df.astype(float ) df.sort_values(by="open_time") df["open_time" ] = pd.to_datetime(df["open_time" ] , utc=True, unit='ms') df["Typical"] = df[['open', 'high', 'low', 'close'] ].mean(axis = 1) for indicator in indicators: df[indicator] = getattr(talib , indicator)( df.open , df.high, df.low , df.close) for cyclic in cycle_Indicators: df[cyclic] = getattr(talib , cyclic) (df.close) for period in periods: for stadistics in stadistics_indicator: df[stadistics] = getattr(talib ,stadistics )(df.close , timeperiod=period) df["SMA_{}".format(period)] = talib.SMA(df["Typical"] , timeperiod=period ) df["DEMA_{}".format(period)] = talib.SMA(df["Typical"] , timeperiod=period ) df["WMA_{}".format(period)] = talib.WMA(df["close"], timeperiod=period) [ df["upperband"] , df["middleband"] , df["lowerband"] ] = talib.BBANDS(df["close"], timeperiod=5, nbdevup=2, nbdevdn=2, matype=0) #volumne indicators df["AD"] = talib.AD( df["high"], df["low"], df["close"], df["volume"]) df["OBV"] = getattr(talib , "OBV") (df.close , df.volume) df["ADOSC"] = talib.ADOSC( df["high"], df["low"], df["close"], df["volume"] , fastperiod=3, slowperiod=10) #volatiliy df["ATR"] = talib.ATR( df["high"], df["low"], df["close"], timeperiod=time_periods[1]) df["NATR"] = talib.NATR( df["high"], df["low"], df["close"], timeperiod=time_periods[1]) df["TRANGE"] = talib.NATR( df["high"], df["low"], df["close"]) #momentum indicators df["RSI"] = talib.RSI( df.close, timeperiod=time_periods[1]) df["ADX"] = talib.ADX( df.high , df.low , df.close, timeperiod=time_periods[2]) df["ADXR"] = talib.ADXR( df.high , df.low , df.close, timeperiod=time_periods[2]) df["APO"] = talib.APO( df.close, fastperiod=time_periods[1] , slowperiod= time_periods[2]) df["AROON_up"] , df["AROON_down"] = talib.AROON( df.high , df.low , timeperiod=time_periods[2]) df["AROONOSC"] = talib.AROONOSC( df.high , df.low , timeperiod=time_periods[2]) df["BOP"] = talib.BOP( df.open , df.high , df.low , df.close ) df["CCI"] = talib.CCI( df.high , df.low , df.close, timeperiod=time_periods[1]) df["CMO"] = talib.CMO( df.close, timeperiod=time_periods[1]) df["DX"] = talib.DX( df.high , df.low , df.close, timeperiod=time_periods[1]) df["macd"], df["macdsignal"], df["macdhist"] = talib.MACD(df.close, fastperiod=time_periods[1], slowperiod=time_periods[2], signalperiod=time_periods[0]) df["macd_"], df["macdsignal_"], df["macdhist_"] = talib.MACDFIX(df.close, signalperiod=time_periods[1]) df["MFI"] = talib.MFI(df.high, df.low, df.close, df.volume, timeperiod=time_periods[2]) df["MINUS_DI"]= talib. MINUS_DI(df.high, df.low,df. close, timeperiod=time_periods[2]) df["close_time" ] = pd.to_datetime(df["close_time" ] , utc=True, unit='ms') df.set_index( "open_time" , inplace=True) df = df.dropna(axis=0) return df
Есть много индикаторов, которые легко рассчитываются для вас! Некоторые из них требуют некоторых параметров, таких как MACD и другие индикаторы импульса, поэтому мы определили эти параметры.
#Some parameters for calculating the TA metrics model_params = {"periods":[ 5, 20 ,30 ], "time_periods" : [5,20,30], "periods" :[5,10,20], "percentage" : 0.04, "space" : 1, "test" : 8 }
Дизайн эксперимента
Тест — это временной диапазон, который мы используем для измерения того, происходит ли прорыв фиксированного процента после последнего времени, когда 8 означает 4 часа, поскольку значение eack означает 30 минут.
Используя это процентное изменение цены, мы помечаем каждую точку набора данных как 1, если происходит длинный прорыв после или до 8 периодов времени, 2, если происходит короткий прорыв, и 0, если ничего из вышеперечисленного не происходит. Мы создаем фрейм данных со следующим кодом
# Used for classifing def get_df (data_ , model_params, data=False ) : percentage =model_params["percentage"] space =model_params["space"] test =model_params["test"] df = get_X (data_ , model_params, data=False ) df["label"]= np.nan data = pd.DataFrame([]) total = int(df.shape[0] / space+test) for i in range(total): try: expect = df.iloc[ (space+test)*i : (space+test) *(i+1),: ].iloc[ -space: ,: ] look = df.iloc[ (space+test)*i : (space+test) *(i+1),: ].iloc[ :-test, :] start = float(look[ -1: ]["close"]) low = expect["low"] high = expect["high"] for i in range (expect.shape[0]): try: low_percentage = ( low -start )/ (start) high_percentage = (high - start )/start shorts = low_percentage <= - percentage longs = high_percentage >= percentage if (shorts.iloc[i]): look.loc[ -1: ,"label"] = 2 data = pd.concat([ data , look[ -1: ]]) break if (longs.iloc[i]): look.loc[ -1: ,"label"] = 1 data = pd.concat([ data , look[ -1: ]]) break if (i == expect.shape[0] -1 ): look.loc[ -1: ,"label"] = 0 data = pd.concat([ data , look[ -1: ]]) break except : #traceback.print_exc() pass except: #traceback.print_exc() pass return data
Вот снимок нашего набора данных: длинные позиции — это красные зоны, а красные — короткие.
Мы разработаем этот эксперимент как классификацию с несколькими метками. Поэтому я буду использовать классификатор Random-Forest. Я буду тестировать с последними 20% данных
def X_y_train_test( percentil , df): #df.reset_index(inplace=True,drop =True) X_train = df.iloc[ :int( df.shape[0] *percentil)].drop([ "label" ] ,axis =1) Y_train = df.iloc[ :int( df.shape[0] *percentil)]["label"] X_test = df.iloc[ int( df.shape[0] *percentil):].drop([ "label" ] ,axis =1) Y_test = df.iloc[ int( df.shape[0] *percentil):]["label"] return [ X_train , X_test , Y_train , Y_test]
И запустите классификатор:
def estimate_metrics(params, df): data_ = df[df['label'].notna()] data_.reset_index(inplace=True ) #data_.drop("index" , axis=1 , inplace=True) data_ =data_.drop( [ "open_time" , "close_time" ], axis=1) #data_.drop("index" , axis=1 , inplace=True) #data_ =data_.drop( [ "open_time" , "close_time" ], axis=1) X_train , X_test , Y_train , Y_test = X_y_train_test(0.8 , data_) oversample = SMOTE(random_state=42) initial_columns = ['open', 'high', 'low', 'close', 'volume', 'quote_asset_volume','num_trades','taker_base_vol','taker_quote_vol', 'ignore'] X_train, Y_train = oversample.fit_resample(X_train, Y_train) X_test ,Y_test = oversample.fit_resample(X_test, Y_test ) #X_train = X_train[initial_columns] #X_test = X_test[initial_columns] # get the data from Binance # klines = client.get_historical_klines(symbol, client.KLINE_INTERVAL_30MINUTE, "5 day ago") from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score parameters = {'n_estimators': 100, 'max_features': 'log2', 'max_depth': 30, 'criterion': 'gini', 'bootstrap': False} rf = RandomForestClassifier( random_state=42 , bootstrap=parameters["bootstrap"], criterion=parameters["criterion"], max_depth=parameters['max_depth'], max_features=parameters['max_features'], n_estimators=parameters["n_estimators"]) rf.fit(X_train,Y_train) Y_pred = rf.predict( X_test) metrics = { "Accuracy": accuracy_score(Y_pred, Y_test) , "Precision" : precision_score(Y_pred, Y_test , average='macro') , "Recall" : recall_score(Y_pred, Y_test , average="macro"), "F1" : f1_score(Y_pred, Y_test, average="macro") } return metrics
Теперь давайте найдем оптимальную цель прорыва
metrics_data = [ ] for percentage in [0.02 , 0.03, 0.04,0.05 ]: for test in [4,6,8,10,12]: model_params = {"periods":[ 5, 20 ,30 ], "time_periods" : [5,20,30], "periods" :[5,10,20], "percentage" : percentage, "space" : 1, "test" : test } df = get_df("ETHUSDT",model_params) metrics = estimate_metrics(model_params , df) metrics["test"]= test metrics["percentage"] = percentage metrics_data = metrics_data + [ metrics] print (metrics_data) pd.DataFrame(metrics_data)
Полученные результаты
Находим следующие результаты:
Таким образом, оптимальная цель прорыва для этого эксперимента — 3% за период времени 4 часа. Теперь если сравнить подгонку модели с теми же параметрами, но обученной только со значениями API Binance
Мы находим большое улучшение для нашей модели! :)
Вы можете получить мой код здесь https://github.com/alice1989123/Ta_trading/blob/master/Algo_Trading.ipynb
Удачного кодирования!