
Пример CNA (сетевого анализа причинно-следственных связей), SMOC (оптимального управления стохастической моделью) и теории игр Нэша с Глубоким обучением
Введение
Мы пройдемся по процессу добавления Глубокого обучения к трем передовым торговым советникам. Эти три советника являются передовыми советниками, опубликованными в виде статей.
Эти три статьи опубликованы здесь, на www.mql5.com
- Применение теории игр Нэша с фильтрацией НММ в трейдинге.
- Пример сетевого анализа причинно-следственных связей (CNA) и модели векторной авторегрессии для прогнозирования рыночных событий
- Пример стохастической оптимизации и оптимального управления
Если вы еще не читали об этих статьях, я очень кратко объясню о чем они, однако настоятельно рекомендую ознакомиться со статьями.
Теория игр Нэша
Равновесие Нэша считается одним из основных компонентов теории игр.
Равновесие Нэша - это концепция в теории игр, при которой предполагается, что каждый игрок знает равновесные стратегии других игроков, и ни один игрок ничего не выигрывает, изменяя только собственную стратегию.
В равновесии Нэша стратегия каждого игрока является оптимальной с учетом стратегий всех остальных игроков. В игре может быть множество равновесий Нэша или ни одного.
Сетевой анализ причинно-следственных связей
Сетевой анализ причинно-следственных связей (CNA) - это метод, используемый для понимания и моделирования сложных причинно-следственных связей между переменными в системе. Применительно к финансовым рынкам это может помочь определить, каким образом различные рыночные события и факторы влияют друг на друга, что потенциально может привести к более точным прогнозам.
Бот использует быстрый вывод причинно-следственных связей.
Быстрый причинно-следственный вывод - это концепция, сочетающая в себе быстрый статистический вывод с причинно-следственным анализом. Он направлен на то, чтобы быстро делать причинно-следственные выводы на основе данных, уравновешивая скорость и понимание причинно-следственных связей. Данный подход полезен в сценариях, требующих принятия быстрых решений на основе данных, а также понимания причинно-следственных связей. Приложения могут включать в себя анализ рынка в режиме реального времени, быстрое принятие бизнес-решений и быстрые эпидемиологические исследования. Данный метод позволяет добиться некоторой глубины анализа в обмен на скорость, что делает его подходящим для работы в зависящих от времени условиях, где понимание причинно-следственных связей имеет решающее значение.
Стохастическая оптимизация
Стохастическое моделирование и оптимизация управления - это математические методы, помогающие решать задачи в условиях неопределенности. Они находят применение в финансах, инженерии, искусственном интеллекте и многих других областях.
Стохастическое моделирование используется для описания систем с элементами случайности, таких как движение цен на фондовом рынке или очередь в ресторане. Оно основано на случайных переменных, распределениях вероятностей и стохастических процессах. Такие методы, как Монте-Карло (Monte Carlo) и цепи Маркова (Markov chains), позволяют моделировать эти процессы и прогнозировать их поведение.
Оптимизация управления помогает найти наилучшие решения для управления системами. Она используется для автоматизации и улучшения работы различных процессов, от управления автомобилями до эксплуатации химических заводов. Основные методы включают в себя линейно-квадратичный контроллер, модельное прогнозирующее управление и обучение с подкреплением.
Глубокое обучение
В Глубоком обучении, применяемом в трейдинге, используются искусственные нейронные сети для анализа и прогнозирования тенденций финансового рынка. Ниже приведен краткий обзор:
- Анализ данных: Модели глубокого обучения обрабатывают огромные объемы финансовых данных, включая историю цен, объемы торгов, экономические индикаторы и даже настроение в новостях.
- Распознавание паттернов: Эти модели позволяют выявлять сложные паттерны и взаимосвязи в рыночных данных, которые могут быть неочевидны для обычных трейдеров или традиционных методов анализа.
- Прогноз: Основываясь на выявляемых паттернах, модели глубокого обучения пытаются прогнозировать будущие движения рынка, ценовые тренды или оптимальные торговые стратегии.
- Автоматизированный трейдинг: Некоторые системы используют глубокое обучение для принятия автономных торговых решений, совершая сделки на основе прогнозов модели.
- Управление рисками: Глубокое обучение может быть использовано для более эффективной оценки торговых рисков и управления ими путем одновременного анализа множества факторов риска.
- Адаптивность: Эти модели могут постоянно обучаться и адаптироваться к изменяющимся рыночным условиям, что со временем потенциально повышает их производительность.
- Высокочастотный трейдинг: Глубокое обучение может быть особенно полезно в сценариях высокочастотной торговли, в которых решающее значение имеют решения, принимаемые за доли секунды.
Хотя глубокое обучение в трейдинге открывает широкие возможности, важно отметить, что рынки сложны и непредсказуемы. Эти модели не являются безошибочными и по-прежнему требуют тщательный надзор и управление рисками.
Зачем нам нужен советник с Глубоким обучением?
Мы создадим модель Глубокого обучения на python, создав модель ONNX, и добавим эту модель к каждому из советников, а также сравним результаты с Глубоким обучением и без него.
Будут использоваться модели ONNX, поскольку их можно легко создать на python, и они являются связующим звеном между различными экосистемами, повышая гибкость и эффективность разработки и развертывания моделей. Python - это быстрый способ быстро превратить ONNX-модели или бэк-тестирование в стратегию. Для настоящей статьи будем использовать Python 3.11.9.
Мы будем использовать Глубокое обучение с этим советником, потому что предыдущие результаты из статей могли бы быть лучше, мы добавим условия для совершения сделок советником либо не добавим, если модели Глубокого обучения также скажут об этом.
Python-скрипт
Будем использовать данный скрипт .py. Данный скрипт создает модель DL в ONNX, а также имеет показатели, позволяющие определить, корректно ли выполнена модель. У DL возникают проблемы при неправильном выполнении, потому что она может быть недостаточно обучена или переобучена, что приводит к неверным прогнозам.
#python 3.11.9, tensorflow==2.12.0, keras==2.12.0, tf2onnx==1.16.0 # python libraries import MetaTrader5 as mt5 import tensorflow as tf import numpy as np import pandas as pd import tf2onnx #import tensorflow as tf #import tf2onnx import keras print(f"TensorFlow version: {tf.__version__}") print(f"Keras version: {keras.__version__}") print(f"tf2onnx version: {tf2onnx.__version__}") # input parameters inp_history_size = 120 sample_size = inp_history_size*3*20 symbol = "EURUSD" optional = "D1_2024" inp_model_name = str(symbol)+"_"+str(optional)+".onnx" if not mt5.initialize(): print("initialize() failed, error code =",mt5.last_error()) quit() # we will save generated onnx-file near the our script to use as resource from sys import argv data_path=argv[0] last_index=data_path.rfind("\\")+1 data_path=data_path[0:last_index] print("data path to save onnx model",data_path) # and save to MQL5\Files folder to use as file terminal_info=mt5.terminal_info() file_path=terminal_info.data_path+"\\MQL5\\Files\\" print("file path to save onnx model",file_path) # set start and end dates for history data from datetime import timedelta, datetime #end_date = datetime.now() end_date = datetime(2024, 1, 1, 0) start_date = end_date - timedelta(days=inp_history_size*20) # print start and end dates print("data start date =",start_date) print("data end date =",end_date) # get rates eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size) # create dataframe df = pd.DataFrame(eurusd_rates) # get close prices only data = df.filter(['close']).values # scale data from sklearn.preprocessing import MinMaxScaler scaler=MinMaxScaler(feature_range=(0,1)) scaled_data = scaler.fit_transform(data) # training size is 80% of the data training_size = int(len(scaled_data)*0.80) print("Training_size:",training_size) train_data_initial = scaled_data[0:training_size,:] test_data_initial = scaled_data[training_size:,:1] # split a univariate sequence into samples def split_sequence(sequence, n_steps): X, y = list(), list() for i in range(len(sequence)): # find the end of this pattern end_ix = i + n_steps # check if we are beyond the sequence if end_ix > len(sequence)-1: break # gather input and output parts of the pattern seq_x, seq_y = sequence[i:end_ix], sequence[end_ix] X.append(seq_x) y.append(seq_y) return np.array(X), np.array(y) # split into samples time_step = inp_history_size x_train, y_train = split_sequence(train_data_initial, time_step) x_test, y_test = split_sequence(test_data_initial, time_step) # reshape input to be [samples, time steps, features] which is required for LSTM x_train =x_train.reshape(x_train.shape[0],x_train.shape[1],1) x_test = x_test.reshape(x_test.shape[0],x_test.shape[1],1) # define model from keras.models import Sequential from keras.layers import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM from keras.metrics import RootMeanSquaredError as rmse from tensorflow.keras import callbacks model = Sequential() model.add(Conv1D(filters=256, kernel_size=2, strides=1, padding='same', activation='relu', input_shape=(inp_history_size,1))) model.add(MaxPooling1D(pool_size=2)) model.add(LSTM(100, return_sequences = True)) model.add(Dropout(0.3)) model.add(LSTM(100, return_sequences = False)) model.add(Dropout(0.3)) model.add(Dense(units=1, activation = 'sigmoid')) model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()]) # Set up early stopping early_stopping = callbacks.EarlyStopping( monitor='val_loss', patience=20, restore_best_weights=True, ) # model training for 300 epochs history = model.fit(x_train, y_train, epochs = 300 , validation_data = (x_test,y_test), batch_size=32, callbacks=[early_stopping], verbose=2) # evaluate training data train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = 32) print(f"train_loss={train_loss:.3f}") print(f"train_rmse={train_rmse:.3f}") # evaluate testing data test_loss, test_rmse = model.evaluate(x_test,y_test, batch_size = 32) print(f"test_loss={test_loss:.3f}") print(f"test_rmse={test_rmse:.3f}") # Define a function that represents your model @tf.function(input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)]) def model_function(x): return model(x) output_path = data_path+inp_model_name # Convert the model to ONNX onnx_model, _ = tf2onnx.convert.from_function( model_function, input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)], opset=13, output_path=output_path ) print(f"Saved ONNX model to {output_path}") # save model to ONNX output_path = data_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") output_path = file_path+inp_model_name onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path) print(f"saved model to {output_path}") #prediction using testing data test_predict = model.predict(x_test) print(test_predict) print("longitud total de la prediccion: ", len(test_predict)) print("longitud total del sample: ", sample_size) plot_y_test = np.array(y_test).reshape(-1, 1) # Selecciona solo el último elemento de cada muestra de prueba plot_y_train = y_train.reshape(-1,1) train_predict = model.predict(x_train) #print(plot_y_test) #calculate metrics from sklearn import metrics from sklearn.metrics import r2_score #transform data to real values value1=scaler.inverse_transform(plot_y_test) #print(value1) # Escala las predicciones inversas al transformarlas a la escala original value2 = scaler.inverse_transform(test_predict.reshape(-1, 1)) #print(value2) #calc score score = np.sqrt(metrics.mean_squared_error(value1,value2)) print("RMSE : {}".format(score)) print("MSE :", metrics.mean_squared_error(value1,value2)) print("R2 score :",metrics.r2_score(value1,value2)) #sumarize model model.summary() #Print error value11=pd.DataFrame(value1) value22=pd.DataFrame(value2) #print(value11) #print(value22) value111=value11.iloc[:,:] value222=value22.iloc[:,:] print("longitud salida (tandas de 1 hora): ",len(value111) ) print("en horas son " + str((len(value111))*60*24)+ " minutos") print("en horas son " + str(((len(value111)))*60*24/60)+ " horas") print("en horas son " + str(((len(value111)))*60*24/60/24)+ " dias") # Calculate error error = value111 - value222 import matplotlib.pyplot as plt # Plot error plt.figure(figsize=(7, 6)) plt.scatter(range(len(error)), error, color='blue', label='Error') plt.axhline(y=0, color='red', linestyle='--', linewidth=1) # Línea horizontal en y=0 plt.title('Error de Predicción ' + str(symbol)) plt.xlabel('Índice de la muestra') plt.ylabel('Error') plt.legend() plt.grid(True) plt.savefig(str(symbol)+str(optional)+'.png') rmse_ = format(score) mse_ = metrics.mean_squared_error(value1,value2) r2_ = metrics.r2_score(value1,value2) resultados= [rmse_,mse_,r2_] # Abre un archivo en modo escritura with open(str(symbol)+str(optional)+"results.txt", "w") as archivo: # Escribe cada resultado en una línea separada for resultado in resultados: archivo.write(str(resultado) + "\n") # finish mt5.shutdown() #show iteration-rmse graph for training and validation plt.figure(figsize = (7,10)) plt.plot(history.history['root_mean_squared_error'],label='Training RMSE',color='b') plt.plot(history.history['val_root_mean_squared_error'],label='Validation-RMSE',color='g') plt.xlabel("Iteration") plt.ylabel("RMSE") plt.title("RMSE" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+'1.png') #show iteration-loss graph for training and validation plt.figure(figsize = (7,10)) plt.plot(history.history['loss'],label='Training Loss',color='b') plt.plot(history.history['val_loss'],label='Validation-loss',color='g') plt.xlabel("Iteration") plt.ylabel("Loss") plt.title("LOSS" + str(symbol)) plt.legend() plt.savefig(str(symbol)+str(optional)+'2.png') #show actual vs predicted (training) graph plt.figure(figsize=(7,10)) plt.plot(scaler.inverse_transform(plot_y_train),color = 'b', label = 'Original') plt.plot(scaler.inverse_transform(train_predict),color='red', label = 'Predicted') plt.title("Prediction Graph Using Training Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+'3.png') #show actual vs predicted (testing) graph plt.figure(figsize=(7,10)) plt.plot(scaler.inverse_transform(plot_y_test),color = 'b', label = 'Original') plt.plot(scaler.inverse_transform(test_predict),color='g', label = 'Predicted') plt.title("Prediction Graph Using Testing Data" + str(symbol)) plt.xlabel("Hours") plt.ylabel("Price") plt.legend() plt.savefig(str(symbol)+str(optional)+'4.png')
Результат, который выдает данный скрипт .py, выглядит примерно так:
177/177 - 51s - loss: 2.4565e-04 - root_mean_squared_error: 0.0157 - val_loss: 4.8860e-05 - val_root_mean_squared_error: 0.0070 - 51s/epoch - 291ms/step 177/177 [==============================] - 15s 82ms/step - loss: 1.0720e-04 - root_mean_squared_error: 0.0104 train_loss=0.000 train_rmse=0.010 42/42 [==============================] - 3s 74ms/step - loss: 4.4485e-05 - root_mean_squared_error: 0.0067 test_loss=0.000 test_rmse=0.007 Saved ONNX model to c:\XXXXXXXXXXXXXXX\EURUSD_D1_2024.onnx saved model to c:\XXXXXXXXXXXXXXXX\EURUSD_D1_2024.onnx saved model to C:\XXXXXXXXXX\MQL5\Files\EURUSD_D1_2024.onnx 42/42 [==============================] - 5s 80ms/step [[0.40353602] [0.39568084] [0.39963558] ... [0.35914657] [0.3671721 ] [0.3618655 ]] longitud total de la prediccion: 1320 longitud total del sample: 7200 177/177 [==============================] - 13s 72ms/step RMSE : 0.005155711558172936 MSE : 2.6581361671078003e-05 R2 score : 0.9915315181564703 Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv1d (Conv1D) (None, 120, 256) 768 max_pooling1d (MaxPooling1D (None, 60, 256) 0 ) lstm (LSTM) (None, 60, 100) 142800 dropout (Dropout) (None, 60, 100) 0 lstm_1 (LSTM) (None, 100) 80400 dropout_1 (Dropout) (None, 100) 0 dense (Dense) (None, 1) 101 ================================================================= Total params: 224,069 Trainable params: 224,069 Non-trainable params: 0 ________________________________________________________________
Данная модель может быть чрезмерно обучена из-за этих результатов, это может быть пределом корректного прогнозирования и она может быть переобучена:
RMSE : 0.005155711558172936 MSE : 2.6581361671078003e-05 R2 score : 0.9915315181564703
Основными показателями являются эти три, R2, которые дают процент проверенных результатов с хорошими прогнозами, а RMSE, MSE - это ошибки. Если вы хотите узнать больше об этом, можете поискать в интернете.
Поскольку мы собираемся сделать это для того же символа (EURUSD), нам нужно создать только одну модель DL, и мы протестируем ее в отношении 2024 года.
Мы выбрали таймфрейм в 1 день, чтобы не использовать слишком много ресурсов наших компьютеров.
Скрипт .py также показывает некоторые сохраненные графики (это еще один способ увидеть, являются ли модели переобученными или недостаточно обученными).
Графики должны выглядеть следующим образом:
Скрипт также сохраняет файл .txt, в котором предыдущие показатели сохраняются в том же порядке, в каком они отображаются в терминале.
Чтобы использовать данный скрипт сначала необходимо установить Python 3.11.9 и установить с ним эти библиотеки; установка пипсов:
tensorflow==2.12.0, keras==2.12.0, tf2onnx==1.16.0, MetaTrader5, pandas, numpy, scikit-learn, tf2onnx, keras
Я настоятельно рекомендую использовать Visual Studio Code, который можно скачать из магазина Microsoft.
Вас могут попросить установить:
Microsoft Visual C++ 2015 - 2022 Redistributable
Я прикрепил скрипт .py, просто откройте его в VSC и запустите.
Если вы хотите что-либо изменить (другой символ, точку, откуда берутся данные, или период времени), измените только эти строки:
symbol = "EURUSD" optional = "D1_2024"
Символ очевиден, я рекомендую по желанию изменить используемый период времени и последнюю дату ввода данных.
eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)
В этой строке вы меняете таймфрейм, например, H1 (1-часовой таймфрейм).
Теперь у нас есть модель Глубокого обучения ONNX, мы можем добавить ее к советнику, который уже есть из других статей.
Я прикреплю модель DL ONNX (2024).
Что мы добавим к советникам?
Изменения в CNA
Это основное, я буду проходить этот процесс очень медленно.
во-первых, в начале, где вводные параметры, необходимо добавить эти строки:
#include <Trade\Trade.mqh> #resource "/Files/EURUSD_D1.onnx" as uchar ExtModel[] #define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; CTrade ExtTrade; int dlsignal=-1; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2
Не забудьте изменить эту строку в соответствии с созданной вами моделью ONNX (сохраните py-скрипты в MQL5/Files/ ... модель).
#resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]
В OnInit() необходимо добавить следующее:
//--- create a model from static buffer ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT); if(ExtHandle==INVALID_HANDLE) { Print("OnnxCreateFromBuffer error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the input tensor we must set them explicitly //--- first index - batch size, second index - series size, third index - number of series (only Close) const long input_shape[] = {1,SAMPLE_SIZE,1}; if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape)) { Print("OnnxSetInputShape error ",GetLastError()); return(INIT_FAILED); } //--- since not all sizes defined in the output tensor we must set them explicitly //--- first index - batch size, must match the batch size of the input tensor //--- second index - number of predicted prices (we only predict Close) const long output_shape[] = {1,1}; if(!OnnxSetOutputShape(ExtHandle,0,output_shape)) { Print("OnnxSetOutputShape error ",GetLastError()); return(INIT_FAILED); }
Где? Везде... это не имеет значения (непосредственно перед возвратом функции).
В функцию OnDeinit также необходимо добавить следующее:
if(ExtHandle!=INVALID_HANDLE) { OnnxRelease(ExtHandle); ExtHandle=INVALID_HANDLE; }
Где? Где угодно, это освобождает модель ONNX.
В функцию OnTick необходимо добавить следующее:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice();
Где? Вначале это важно, потому что результат работы функции PredictPrice() должен быть добавлен в исполнительную логику ордера.
В функции OnTick() также необходимо изменить функцию, которая выполняет ордеры, в данном случае (CNA_DL) мы изменим исходную строку:
int signal = GenerateSignal(symbol, prediction);
на следующую:
int signal = GenerateSignal(symbol, prediction, ExtPredictedClass);
ExtPredictedClass - это результат функции PredictedPrice(), которую мы добавили до этого.
Теперь нам также нужно изменить логику GenerateSignal(), чтобы она использовала ExtPredictedClass:
Нам придется изменить заголовок этой функции:
int GenerateSignal(string symbol, double prediction, int dlsgnl)
и добавим новую переменную в логику для создания ордеров, в данном случае, здесь:
с такой:bool buy_condition = prediction > 0.00001 && rsi < 30 && trend_strong && volatility_ok && fastMA > slowMA; bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA;на такую:
bool buy_condition = prediction > 0.00001 && rsi < 30 && trend_strong && volatility_ok && fastMA > slowMA && dlsgnl==PRICE_UP; bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA && dlsgnl==PRICE_DOWN;
Вот последняя часть, которую нам нужно изменить.
Нам необходимо добавить эти функции в скрипт, где? Куда угодно, в конце, например.
//+------------------------------------------------------------------+ void PredictPrice(void) { static vectorf output_data(1); // vector to get result static vectorf x_norm(SAMPLE_SIZE); // vector for prices normalize //--- check for normalization possibility if(ExtMin>=ExtMax) { Print("ExtMin>=ExtMax"); ExtPredictedClass=-1; return; } //--- request last bars if(!x_norm.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,SAMPLE_SIZE)) { Print("CopyRates ",x_norm.Size()); ExtPredictedClass=-1; return; } float last_close=x_norm[SAMPLE_SIZE-1]; //--- normalize prices x_norm-=ExtMin; x_norm/=(ExtMax-ExtMin); //--- run the inference if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,x_norm,output_data)) { Print("OnnxRun"); ExtPredictedClass=-1; return; } //--- denormalize the price from the output value float predicted=output_data[0]*(ExtMax-ExtMin)+ExtMin; //--- classify predicted price movement float delta=last_close-predicted; if(fabs(delta)<=0.00001) ExtPredictedClass=PRICE_SAME; else { if(delta<0) ExtPredictedClass=PRICE_UP; else ExtPredictedClass=PRICE_DOWN; } } void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); }
Эти две функции являются ключевыми для прогнозирования, одна из них делает прогнозы, а другая получает минимальное и максимальное значение при закрытии.
Изменения в SMOC
Все остается по-прежнему, и изменения такие же, как и предыдущие, за исключением следующего:
Во входных параметрах вы не инициализируете dlsignal или не добавляете CTrade:
#resource "/Files/EURUSD_D1.onnx" as uchar ExtModel[] #define SAMPLE_SIZE 120 long ExtHandle=INVALID_HANDLE; int ExtPredictedClass=-1; datetime ExtNextBar=0; datetime ExtNextDay=0; float ExtMin=0.0; float ExtMax=0.0; //--- price movement prediction #define PRICE_UP 0 #define PRICE_SAME 1 #define PRICE_DOWN 2
В функции OnTick() у нас будет следующее:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice(); static datetime lastBarTime = 0; datetime currentBarTime = iTime(Symbol(), PERIOD_CURRENT, 0); // Only process on new bar if(currentBarTime == lastBarTime) return; lastBarTime = currentBarTime; double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_LAST); int decision = OptimalControl(currentPrice); string logMessage = StringFormat("New bar: Time=%s, Price=%f, Decision=%d", TimeToString(currentBarTime), currentPrice, decision); LogMessage(logMessage); // Manage open order if exists if(orderTicket != 0) { ManageOpenOrder(decision, ExtPredictedClass); } if(orderTicket == 0 && decision != 0 && IsTrendFavorable(decision) && IsLongTermTrendFavorable(decision) && (ExtPredictedClass==PRICE_DOWN || ExtPredictedClass==PRICE_UP)) { ExecuteTrade(decision, ExtPredictedClass); } }
В функцию ManageOpenOrder() внесем следующее изменение:
void ManageOpenOrder(int decision, int dlsignal) { int barsOpen = iBars(Symbol(), PERIOD_CURRENT) - iBarShift(Symbol(), PERIOD_CURRENT, orderOpenTime); LogMessage(StringFormat("Bars open: %d for order ticket: %d", barsOpen, orderTicket)); if(barsOpen >= maxBarsOpen || (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && decision == -1 && dlsignal == PRICE_DOWN) || (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && decision == 1 && dlsignal == PRICE_UP)) { CloseOrder(orderTicket); orderTicket = 0; orderOpenTime = 0; } else { UpdateSLTP(orderTicket, decision); } }
В функцию ExecuteTrade()
void ExecuteTrade(int decision, int dlsignal) { MqlTradeRequest request = {}; MqlTradeResult result = {}; double price; int decision1; if(decision == 1 && dlsignal == PRICE_UP) { price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); request.type = ORDER_TYPE_BUY; decision1 = 1; } else if(decision == -1 && dlsignal == PRICE_DOWN) { price = SymbolInfoDouble(Symbol(), SYMBOL_BID); request.type = ORDER_TYPE_SELL; decision1 = -1; } double sl = CalculateDynamicSL(price, decision1); double tp = CalculateDynamicTP(price, decision1); double adjustedLotSize = AdjustLotSizeForDrawdown(); request.volume = adjustedLotSize; request.action = TRADE_ACTION_DEAL; request.symbol = Symbol(); //request.volume = lotSize; //request.type = (decision == 1 && dlsignal == PRICE_UP) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL; request.price = price; request.sl = sl; request.tp = tp; request.deviation = 10; request.magic = 123456; // Magic number to identify orders from this EA request.comment = (decision == 1) ? "Buy Order" : "Sell Order"; if(OrderSend(request, result)) { Print((decision1 == 1 ? "Buy" : "Sell"), " Order Executed Successfully. Ticket: ", result.order); orderOpenTime = iTime(Symbol(), PERIOD_CURRENT, 0); orderTicket = result.order; } else { Print("Error executing Order: ", GetLastError()); } }
А теперь изменения в скрипт Nash equity.
Изменения в Nash Equity
Все изменения одинаковы, за исключением функции OnTick(), которая будет похожа на эту:
void OnTick() { //--- check new day if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); } //--- check new bar if(TimeCurrent()<ExtNextBar) return; //--- set next bar time ExtNextBar=TimeCurrent(); ExtNextBar-=ExtNextBar%PeriodSeconds(); ExtNextBar+=PeriodSeconds(); //--- check min and max float close=(float)iClose(_Symbol,_Period,0); if(ExtMin>close) ExtMin=close; if(ExtMax<close) ExtMax=close; PredictPrice(); if(!IsNewBar()) return; // Solo procesar en nueva barra MarketRegime hmmRegime = NOT_PRESENT; MarketRegime logLikelihoodRegime = NOT_PRESENT; DetectMarketRegime(hmmRegime, logLikelihoodRegime); // Calcular señales para cada estrategia CalculateStrategySignals(_Symbol, TimeCurrent(), hmmRegime, logLikelihoodRegime); // Verificar si la estrategia de Nash Equilibrium ha generado una señal if(strategies[3].enabled && strategies[3].signal != 0) { if(strategies[3].signal > 0 && ExtPredictedClass==PRICE_UP) { OpenBuyOrder(strategies[3].name); } else if(strategies[3].signal < 0 && ExtPredictedClass==PRICE_DOWN) { OpenSellOrder(strategies[3].name); } } // Actualizar stops de seguimiento si es necesario if(UseTrailingStop) { UpdateTrailingStops(); } }
Результаты
CNA_DL
Настройки
По сравнению с оригинальным советником (CNA_Final_v4):
Сравнение CNA с глубоким обучением и без него
- Прибыльность: Советник без глубокого обучения значительно более прибыльный, но он также сопряжен с большим риском с более высокими просадками.
- Риск: Советник с глубоким обучением имеет меньшие просадки и кажется более консервативным в своем подходе.
- Частота торговли: Советник без глубокого обучения торгует гораздо чаще (1292 сделки против 58).
- Коэффициент выигрыша: У советника с глубоким обучением коэффициент выигрыша немного выше, но оба они ниже 50%.
- Сдвиг стратегии: Советник без глубокого обучения отдает предпочтение коротким сделкам, в то время как советник с глубоким обучением сбалансирован.
- Согласованность: Советник с глубоким обучением, по-видимому, более совместим с небольшими сериями последовательных выигрышей / проигрышей.
В заключение, советник без глубокого обучения представляется более прибыльным, но и более рискованным, в то время как советник с глубоким обучением более консервативен с более низкой доходностью, но и более низким риском. Выбор между ними будет зависеть от склонности трейдера к риску и инвестиционных целей. Советник с глубоким обучением может быть предпочтительнее для более стабильной и консервативной торговли, в то время как советник без глубокого обучения может быть выбран для получения потенциально более высокой доходности за счет более высокого риска.
SMOC_DL
А теперь SMOC с Глубоким обучением
Сравнение SMOC_DL и SMOC:
- Прибыльность: Стратегия 2 прибыльна (4,32), в то время как Стратегия 1 убыточна (-7,33).
- Доходность с поправкой на риск: Стратегия 2 имеет положительный коэффициент Шарпа (1,69), что указывает на более высокую доходность с поправкой на риск по сравнению с отрицательным коэффициентом Шарпа Стратегии 1 (-3,77).
- Коэффициент выигрыша: У Стратегии 2 коэффициент выигрыша выше (59.38%) по сравнению со Стратегией 1 (54%).
- Количество сделок: Стратегия 2 заключает больше сделок (64), чем Стратегия 1 (50), что потенциально указывает на большее количество выявленных возможностей.
- Коэффициент прибыльности: Стратегия 2 имеет коэффициент прибыли выше 1 (1,13), в то время как Стратегия 1 ниже 1 (0,74), что говорит о том, что Стратегия 2 более эффективна при получении прибыли по сравнению с убытками.
- Просадки: Обе стратегии имеют одинаково низкие просадки (0,01% от суммы счета), что свидетельствует о хорошем управлении рисками.
- Распределение сделок: Обе стратегии отдают предпочтение краткосрочным сделкам, но Стратегия 2 имеет более сбалансированное распределение между краткосрочными и долгосрочными сделками.
В целом, Стратегия 2, по-видимому, превосходит в большинстве аспектов, включая прибыльность, доходность с поправкой на риск, коэффициент выигрышей и коэффициент прибыли. Она также берет больше сделок, что потенциально указывает на лучшее выявление возможностей. Обе стратегии демонстрируют хорошее управление рисками при низких просадках. Исходя из этих данных, Стратегия 2 была бы предпочтительным выбором из двух вариантов. Подходящим подходом к использованию DL было бы использование других таймфреймов.
NASH_DL
А теперь без Глубокого обучения:
В целом, стратегия, не связанная с DL, по-видимому, показала лучшие результаты с точки зрения общей прибыльности и доходности с поправкой на риск, несмотря на более высокую максимальную просадку. С её применением торговля шла более активно и она казалась более эффективной с длинными позициями. Стратегия DL была более консервативной, с меньшими просадками, но и более низкой общей доходностью.
Однако важно отметить, что это сравнение основано на одном периоде бэктестирования. Чтобы сделать более обоснованные выводы, следует протестировать эти стратегии в различных временных периодах и рыночных условиях.
Заключение
В настоящем исследовании изучалась интеграция моделей Глубокого обучения (DL) в три передовых торговых советника (EAs): Сетевой анализ причинно-следственных связей (CNA), Стохастическая оптимизация и оптимальное управление (SMOC), а также Теория игр Нэша. Процесс включал в себя создание ONNX-моделей с использованием Python и включение их в существующие MQL5-скрипты.
Результаты такой интеграции в рамках различных стратегий были неоднозначными:
- Что касается стратегии CNA, то версия DL показала более консервативную торговлю с более низкой доходностью, но и с меньшим риском по сравнению с версией без DL. Несмотря на то, что он был менее прибыльным, он продемонстрировал лучшую последовательность и более сбалансированный подход к торговле.
- В случае SMOC версия DL значительно превзошла свой аналог без DL. Она показала более высокую прибыльность, более высокие коэффициенты выигрыша и превосходную доходность с поправкой на риск, что говорит о том, что модель DL эффективно улучшила процесс принятия решений в рамках стратегии.
- Согласно подходу Теории игр Нэша, версия без DL в целом показала лучшие результаты с более высокой общей прибыльностью и доходностью с поправкой на риск, несмотря на более высокую максимальную просадку. Версия DL торговалась более консервативно, но в целом принесла меньшую прибыль.
Эти результаты подчеркивают потенциал интеграции Глубокого обучения в торговые стратегии, а также выделяют важность тщательной реализации и тестирования. Эффективность интеграции DL варьируется в зависимости от базовой стратегии и рыночных условий.
Важно отметить, что эти результаты основаны на конкретных периодах бэктестирования и рыночных условиях. Чтобы сделать более обоснованные выводы, потребуется дальнейшее тестирование в различных таймфреймах и рыночных сценариях.
В заключение, хотя Глубокое обучение может улучшить торговые стратегии, к его внедрению следует подходить осторожно и с тщательным тестированием. Неоднозначные результаты применения различных стратегий свидетельствуют о том, что эффективность DL в торговле не является универсальной и в значительной степени зависит от конкретной стратегии и рыночного контекста. В будущих исследованиях можно было бы изучить возможность оптимизации этих моделей DL для различных таймфреймов и рыночных условий, чтобы потенциально повысить их эффективность в различных торговых сценариях.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15819





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования