English Русский Español Deutsch 日本語 Português
preview
使用 Python 和 MetaTrader5 python 软件包及 ONNX 模型文件进行深度学习预测和排序

使用 Python 和 MetaTrader5 python 软件包及 ONNX 模型文件进行深度学习预测和排序

MetaTrader 5交易系统 | 19 八月 2024, 12:20
334 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

概述

深度学习是机器学习的一个子领域,专注于人工神经网络,灵感来自人脑的结构和功能。它涉及训练模型来执行任务,而无需显式编程,而是通过从数据中学习模式和表征。深度学习能够自动学习分层特征和表征,在图像和语音识别、自然语言处理等多个领域都非常有效,因此备受关注。

深度学习中的关键概念:

  • 神经网络:深度学习的基本单位,由相互连接的节点或神经元组成,组织分层。
  • 深度神经网络(DNN):具有多层结构的神经网络,可以学习复杂的模式。
  • 训练:使用标记数据调整模型参数以最小化预测结果和实际结果之间差异的过程。
  • 激活函数:应用于神经元输出的函数,引入非线性,使模型能够学习更复杂的关系。
  • 反向传播:用于根据预测误差更新模型权重的优化算法。

Python:Python 是一种高级、通用的编程语言,以可读性和易用性著称。它拥有庞大的库和框架生态系统,使其适用于各种应用程序,包括 web 开发、数据科学、人工智能等。

Python 的主要特性:

  • 可读性:Python 的语法具有可读性和简洁性,使开发人员可以用较少行数的代码轻松表达概念。
  • 丰富的库:Python 拥有丰富的库和框架,可用于各种应用,如用于数据处理的 NumPy 和 pandas、用于深度学习的 TensorFlow 和 PyTorch、用于 web 开发的 Django 和 Flask 等。
  • 社区支持:Python 拥有一个庞大而活跃的开发者社区,为其不断改进和提供大量学习资源做出了贡献。

使用 Python 进行深度学习:Python 因其丰富的库和框架而成为深度学习的热门语言。Python 深度学习的两个主要的库是 TensorFlow 和 PyTorch。这些库为构建和训练神经网络提供了高水平的抽象,使初学者和有经验的从业人员都能使用。

总之,深度学习是更广泛的机器学习领域中一种强大的方法,而 Python 则是实现深度学习模型和解决方案的绝佳编程语言。Python 与深度学习的结合推动了不同领域的进步,使复杂的任务变得更容易完成、更自动化。


使用 Python 和 MQL5 软件包订购:您似乎对将 Python 与 MQL5 软件包结合起来处理交易订单感兴趣。MQL5 是为 MetaTrader 5(MT5)平台算法交易而设计的专门语言。以下是您如何将 Python 与 MQL5 集成进行交易的总体概述:

  • 深度学习 Python 脚本:使用深度学习库(如 TensorFlow、PyTorch)开发 Python 脚本,为您的特定交易策略创建和训练模型。此脚本将根据您的深度学习模型处理分析和决策。

  • Python 和 MQL5 之间的通信:在 Python 脚本和 MetaTrader 5 平台之间建立通信桥梁。这可以通过各种方法来实现,例如套接字、REST API 或者其他通信协议。目的是使您的 Python 脚本能够向 MetaTrader 终端发送交易信号或订单。

  • 在 MQL5 中执行订单:在您的 MQL5 脚本或 EA 交易中,实现从 Python 脚本接收信号的逻辑,并在 MetaTrader 5 平台上执行相应的买入/卖出订单。这涉及到利用 MQL5 语言提供的交易函数。


ONNX

即 Open Neural Network Exchange(开放神经网络交换),是一种开源格式,旨在促进人工智能(AI)模型在各种框架间的互操作性。ONNX 由微软和 Facebook 共同开发和支持,允许用户使用一种框架训练模型,而使用另一种框架部署模型。

ONNX 的关键优势在于它能够作为 TensorFlow、PyTorch 等不同深度学习框架之间的中间格式。这种互换性简化了将机器学习模型集成到不同应用、环境和平台的过程。

在 MQL5(MetaQuotes Language 5)中,ONNX 模型的集成包括将经过训练的机器学习模型转换为 ONNX 格式。一旦采用 ONNX 格式,模型就可以加载到 MQL5 脚本或 EA 交易系统中,从而在 MetaTrader 5 平台中利用先进的机器学习功能来制定算法交易策略。


简要介绍将要展示的内容。

在接下来的文章中,我们将深入探讨将深度学习应用于自动交易的迷人世界。重点将放在一个利用先进的深度学习技术设计自动交易的程序上。我们将探索使用平均绝对误差(MAE)、均方误差(MSE)和R平方(R2)等关键指标测试模型性能的复杂性。本次探索旨在深入了解深度学习模型在算法交易环境中的有效性,揭示其预测准确性和整体稳健性。加入我们,共同探索深度学习、自动交易和全面性能测试的交集。


此外,我们还将探索将 Python 脚本 (.py) 转换为可执行文件的过程,以确保提供一个全面、用户友好的软件包。这份循序渐进的指南将提供详细的见解,不遗余力地确保对整个过程有透彻的了解。文章结束时,你不仅会对在自动交易中实施深度学习有深入的理解,还会掌握将解决方案打包成方便的可执行格式的实用知识。请静待我们的深入讲解,从编码到功能齐全的可执行程序的无缝导航,让您的体验兼具教育性和实用性。


作为本文的最后一步,我将创建一个脚本来生成 ONNX 格式的输出。该脚本将有助于使用智能交易系统 (EA) 将 ONNX 模型整合到 MetaTrader 5 (MT5) 中。这一过程增强了机器学习功能在MT5平台中的适应性和集成性,促进了更复杂、更高效的算法交易策略的开发。


在开始编码之前

我们的目标是开发一个 Python 机器人,它将经历两步过程:首先,对可用数据进行深度学习分析,然后执行交易指令。鉴于 MT5 可以下载大量的分时报价数据,我们的策略就是获取这些数据并将其转换为价格信息。换算方法是取两个分时报价值之间的平均值,这样我们就可以创建一个更易于管理的市场价格表述,供机器人进一步分析和决策。


但在我们继续之前,我们需要使用 Python,为了方便 Python 设置,建议的方法是下载并安装 Anaconda。此外,安装 Visual Studio Code 将为脚本编写提供一个用户友好的环境。


Anaconda

VSC

安装好 Anaconda 和 Visual Studio Code 后,下一步就是在 Visual Studio Code 中使用 pip 安装一些软件包。

您可以在 Visual Studio Code 中打开集成的终端,并使用以下命令(如果使用 Conda/Anaconda,则在 Anaconda Prompt 中运行这些命令):

pip install [package1] [package2] ...

将 [package1] 、[package2] 等替换为项目所需的特定 Python 软件包的名称。这将确保您的环境配备了必要的工具和库,以继续开发 Python 机器人。


软件包

您需要为 Python 环境安装特定的软件包,包括 MetaTrader 5、TensorFlow 和 Anaconda 附带的其他库。以下是如何安装这些库的总体指南:

  1. MetaTrader 5:MetaTrader 5 (MT5) 是一个交易平台。遗憾的是,截至我上次在 2022 年 1 月更新知识时,MetaTrader 5 的官方发行版中还没有直接可用的 Python API。不过,可能存在一些第三方库或封装。请查看 MetaTrader 5 官方文档或相关社区论坛,了解最新信息。   

    pip install MetaTrader5
  2. TensorFlow:TensorFlow 是一个流行的机器学习库。您可以在终端或命令提示符下使用以下 pip 命令进行安装:

    pip install tensorflow


让我们开始编码

成功安装了所需的库之后,下一步就是开始编写 Python 代码。为此,您需要导入已安装的库。在 Python 中,您可以使用 import 语句将一个库的功能引入到您的代码中。
import MetaTrader5 as mt5
from MetaTrader5 import *
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.regularizers import l2
from sklearn.model_selection import KFold


from MetaTrader5 import *

这一代码行导入 MetaTrader5 库中的所有函数和类。使用 *(通配符)表示导入所有内容,但由于潜在的命名空间冲突,一般不鼓励这种做法。

import numpy as np

这一行导入 NumPy 库,并赋予其别名 np 。NumPy 广泛用于 Python 中的数值运算。

from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score

这些代码行将从 scikit-learn 库中导入特定的函数/类。它包括用于数据预处理、模型评估和模型选择的实用程序。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.regularizers import l2

这些代码行从 TensorFlow 库中导入组件,该库常用于深度学习。它引入了序列模型、密集层和 L2 正则化。

from sklearn.model_selection import KFold

这一行代码从 scikit-learn 中导入 KFold 类,该类通常用于机器学习中的交叉验证。

总之,您的脚本准备使用 MetaTrader5 库获取金融数据,使用 NumPy 进行数值运算,使用 scikit-learn 作为机器学习工具,使用 TensorFlow 进行深度学习。你似乎正在建立一个金融数据分析环境,并可能建立一个用于金融预测的机器学习模型。确保使用适当的软件包管理器(如 pip 或 conda)在 Python 环境中安装了这些库


下载数据

为下载分时报价数据,首先,我们需要连接到数据提供商(mt5),然后从那里下载我们需要的内容。为此,我们将采用以下方式:
# You will need to update the values for path, login, pass, and server according to your specific case.
creds = {
    "path": "C:/Program Files/XXX MT5/terminal64.exe",
    "login": account,
    "pass": clave_secreta,
    "server": server_account,
    "timeout": 60000,
    "portable": False
}
# We launch the MT5 platform and connect to the server with our username and password.
if mt5.initialize(path=creds['path'],
                  login=creds['login'],
                  password=creds['pass'],
                  server=creds['server'],
                  timeout=creds['timeout'],
                  portable=creds['portable']):
    
    print("Plataform MT5 launched correctly")
else:
    print(f"There has been a problem with initialization: {mt5.last_error()}")

完成上述操作后,我们就可以继续下载我们感兴趣的交易品种的相关数据,数据形式为分时报价。

rates = rates.drop(index=rates.index)
rates = mt5.copy_ticks_from(symbol, utc_from, 1000000000, mt5.COPY_TICKS_ALL)

这段代码首先清除 "rates" 数据帧中的任何现有数据,然后使用 MetaTrader 5 库中的 copy_ticks_from 函数获取特定交易品种和时间范围的分时报价数据。

由于我们的目标是将数据保存在 Pandas DataFrame 中(Pandas DataFrame 通常与 NumPy 一起使用),以便更好地在 Python 中进行操作,因此我们将把数据转到 Pandas DataFrame 中。
rates_frame=pd.DataFrame()
# Empty DataFrame
rates_frame = rates_frame.drop(index=rates_frame.index)
rates_frame = pd.DataFrame(rates)

这段代码初始化一个名为 rates_frame 的空 Pandas DataFrame,清除其中的任何现有数据,然后用 rates 变量中的数据填充。

为了简化操作,我们将数据中的买入价和卖出价相加后再除以二。这样,我们就得到了平均值,这将是深度学习的输入。
rates_frame['time']=pd.to_datetime(rates_frame['time'], unit='s')
rates_frame['close']=(rates_frame['ask']+rates_frame['bid'])/2

该代码将 "time" 列转换为以秒为单位的日期时间格式,并计算 "ask" 和 "bid" 值的平均值,将结果赋值给 rates_frame DataFrame 中名为 "close" 的新列。

在预测价格走势等金融应用中,深度学习输入数据的选择取决于各种因素,没有放之四海而皆准的解决方案。不过,有一些注意事项和通用准则需要牢记:


原始数据对比衍生功能(振荡器和指标):

1.原始数据:

  • 优点:
    • 保留所有可用信息,没有额外假设。
    • 允许神经网络直接从原始输入数据中学习模式。
  • 考虑因素:
    • 可能包含噪音或无关信息。
    • 可能需要进行更多的数据预处理。

2.衍生功能(振荡器和指标):

  • 优点:
    • 特征工程可以捕捉特定的市场动态。
    • 可提供结构性更强的数据表现形式。
  • 考虑因素:
    • 引入与哪些特征相关的假设。
    • 其有效性取决于所选择的指标及其参数。


最佳做法和考虑因素:

1.数据规范化:

  • 将数据缩放至一致的范围(如 0 和 1 之间),以提高训练时的收敛性。

2.序列长度:

  • 对于时间序列数据,序列长度很重要。试验不同的长度,在捕捉相关模式和计算效率之间找到最佳平衡点。

3.时间方面:

  • 考虑纳入时间因素,如延迟值或滑动窗口,以捕捉时间依赖性。

4.特征选择:

  • 尝试使用不同的特征子集,以确定哪些特征对特定任务最有参考价值。

5.模型架构:

  • 根据输入数据的性质调整神经网络架构。递归神经网络(RNN)和长短期记忆网络(LSTM)对连续数据非常有效。

6.正则化:

  • 使用正则化技术(如剔除)防止过度拟合,尤其是在处理高维数据时。

7.超参数调整:

  • 尝试不同的超参数设置,为模型找到最佳配置。

在实践中,根据经验比较不同的方法通常是有益的。一些任务可能受益于原始数据的简单性,而另一些任务可能会从精心设计的衍生特征中获得性能。在为模型学习提供足够信息与避免噪音或无关细节之间取得平衡至关重要。此外,随着时间的推移,市场动态可能会发生变化,因此要定期重新评估所选择的方法。


数据转换

现在,我们已经在 Pandas DataFrame 中获得了数据,可以将其作为输入发送给深度学习进行处理。在 MQL5 中已经有很多关于深度学习的有趣文章,所以我就不多说了。我将直接介绍 Python 的实用性方面。但在为 TensorFlow 发送数据以发挥其魔力之前,我们需要有一个时间偏移的输入,我们将应用于数据进行预测(偏移的时间将是我们想要预测的时间)。所以,我们就有了以下代码。


在深度学习的背景下,特别是在时间序列预测中,移动 DataFrame 通常是为了创建输入和目标变量的序列。以下是在时间序列预测的深度学习背景下使用移位的原因:

  1. 时间依赖性:深度学习模型,如递归神经网络(RNN)或长短期记忆网络(LSTM),可以捕捉连续数据中的时间依赖性。移位 DataFrame 可以创建序列,其中每个输入序列对应一段过去的数据,而相应的目标序列则代表未来的数据。

  2. 顺序学习:深度学习模型能有效学习连续数据中的模式和依赖关系。通过移位 DataFrame,可以确保输入序列和目标序列在时间上保持一致,使模型能够从历史背景中学习,并根据该背景做出预测。

  3. 训练输入输出对:转换有助于为深度学习模型创建训练示例。转换过的 DataFrame 中的每一行都可视为一对输入输出,其中输入是过去的观测值序列,输出是未来要预测的目标变量。


number_of_rows= seconds
empty_rows = pd.DataFrame(np.nan, index=range(number_of_rows), columns=df.columns)
df = df._append(empty_rows, ignore_index=True)
df['target'] = df['close'].shift(-seconds)
print("df modified",df)
  • number_of_rows 是一个变量,代表秒数。
  • empty_rows 会创建一个带有 NaN 值的新 DataFrame,其列数与原始 DataFrame ( df ) 相同。
  • df.append(empty_rows, ignore_index=True) 将空行添加到原始 DataFrame ( df ) 中,同时忽略索引以确保索引的连续性。
  • df['target'] = df['close'].shift(-seconds) 新建一列 "target",包含按指定秒数的负值移动的 "close" 值。这通常是在为预测建模准备时间序列数据时进行的。

现在,剩下的就是清理数据,以便我们可以将其用作 TensorFlow 的输入。

# Drop NaN values
df=df.dropna()

结果是修改后的 DataFrame ( df ),增加了用 NaN 值填充的行,以及一个新的 "target" 列,用于放置时间偏移的 "close" 值。


深度学习

使用 TensorFlow 和 Keras 构建和训练用于时间序列预测的神经网络。

# Split the data into features (X) and target variable (y)
X=[]
y=[]
X = df2[['close']]
y = df2['target']

# Split the data into training and testing sets
X_train=[]
X_test=[]
y_train=[]
y_test=[]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False)

# Standardize the features
X_train_scaled=[]
X_test_scaled=[]
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# Build a neural network model
model=None
model = Sequential()
model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg)))
model.add(Dense(1, activation='linear'))

# Compile the model[]
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2, verbose=1)

# Use the model to predict the next 4 instances
X_predict=[]
X_predict_scaled=[]

predictions = pd.DataFrame()
predictions=[]
# Empty DataFrame
#predictions = predictions.drop(index=predictions.index)
X_predict = df2.tail(segundos)[['close']]
X_predict_scaled = scaler.transform(X_predict)
predictions = model.predict(X_predict_scaled)

# Print actual and predicted values for the next 
    n  instances
print("Actual Value for the Last Instances:")
print(df.tail(1)['close'].values)

print("\nPredicted Value for the Next Instances:")
print(predictions[:, 0])
predictions=pd.DataFrame(predictions)

利用历史金融数据准备和训练一个神经网络,以预测未来的数值。它使用 TensorFlow 和 Keras 库构建和训练模型。然后,根据训练好的模型打印下一个实例的预测结果。

解释:

  • 该模型被初始化为顺序模型,这意味着它是一个线性层堆栈。

  • 密集层(Dense layers)代表神经网络中的全连接层。参数包括神经元(单元)数、激活函数、输入形状(仅适用于第一层)和内核正则化(本例中为 L2 正则化,其强度由 k_reg 指定)。

  • activation='relu' 表示整流线性单元(ReLU)激活函数,常用于隐藏层。

  • 最后一层有一个具有线性激活函数的神经元,表示回归输出。如果这是一项分类任务,则会使用不同的激活函数,如 "sigmoid" 或 "softmax"。

这种结构是一种前馈神经网络,具有多个用于回归的隐藏层。可以根据具体任务和数据集特征调整参数和分层。


预测

这个模型的近似程度如何?

# Calculate and print mean squared error
mse = mean_squared_error(y_test, model.predict(X_test_scaled))
print(f"\nMean Squared Error: {mse}")

# Calculate and print mean absolute error
mae = mean_absolute_error(y_test, model.predict(X_test_scaled))
print(f"\nMean Absolute Error: {mae}")

# Calculate and print R2 Score
r2 = r2_score(y_test, model.predict(X_test_scaled))
print(f"\nR2 Score: {r2}")

这里提供的代码会计算并打印训练好的回归模型的三个评估指标。

解释:

  • 平均平方误差 (MSE):它测量的是预测值与实际值之间的平均平方差。MSE 越低,模型越好。

  • 平均绝对误差 (MAE):它测量预测值和实际值之间的平均绝对差值。与 MSE 一样,数值越低,模型性能越好。

  • R2 分数:它也称为判定系数,用于衡量因变量中可从自变量预测的变异比例。R2 分数为 1 表示完全拟合,而分数为 0 则表示模型不比预测目标变量的平均值更好。负值表示模型性能不佳。

通过这些指标,我们可以了解训练好的模型在测试集上的表现如何。

在这种情况下,您需要对数据量(输入延迟)和世代数(epoch)进行试验。例如,我使用了 900 天的数据和 EURUSD 2 小时时间段的 1 个世代(我稍后会更好地解释)。然而,我也用更少的数据和更多的世代取得了良好的结果。


订单

现在,我们只需要测量脚本返回数据所需的时间。这样,我们就可以在图表上标出我们的预测起点(因为预测开始的时间要早一些,例如,在我的情况下,大约需要 15 到 20 分钟)。当这段时间过去后,我们才会开始下订单。此外,在广泛使用该机器人后,我注意到在整个移位的前四分之一预测最为准确。为了提高准确性,我会在图表上进行限制和标记,并在那个特定时段内执行订单。使用 `time.now()` 可以轻松实现这一点。我们可以捕捉到当时的时间,并将其转换为秒,因为我们的预测和 x 轴刻度都是以秒为单位的。有鉴于此,我们将标记预测的起始点,并根据该起始点触发订单。

这是一个典型的使用 MetaTrader 5 (MT5) 的 Python 订单。函数 `open_trade_sell2` 表示在 MT5 平台上下达卖出订单的通用结构。它利用 `mt5.order_send` 方法发送交易请求以执行订单。参数包括交易操作("买入" 或 "卖出")、交易品种、手数、随机整数(可能用于幻数识别)、止盈(tp)、止损(sl)和偏差。

def open_trade_sell2(action, symbol, lot,random_integer, tp, sl, deviation):
        '''https://www.mql5.com/en/docs/integration/python_metatrader5/mt5ordersend_py
        '''
        # prepare the buy request structure
        symbol_info = get_info(symbol)
        
        if action == 'buy':
            trade_type = mt5.ORDER_TYPE_BUY
            price = mt5.symbol_info_tick(symbol).ask
        elif action =='sell':
            trade_type = mt5.ORDER_TYPE_SELL
            price = mt5.symbol_info_tick(symbol).bid
        point = mt5.symbol_info(symbol).point
        print("el precio mt5 es:", price)

        buy_request = {
            "action": mt5.TRADE_ACTION_DEAL,
            "symbol": symbol,
            "volume": lot,
            "type": trade_type,
            "price": price,
            "sl":sl,
            "tp":tp,
            "deviation": deviation,
            "magic": random_integer,
            "comment": "python open",
            "type_time": mt5.ORDER_TIME_GTC, # good till cancelled
            "type_filling": mt5.ORDER_FILLING_IOC,
        }
        # send a trading request
        result = mt5.order_send(buy_request)        
        return result, buy_request

该函数是与 MT5 平台交互以自动执行交易策略的脚本的一部分。关键步骤包括确定交易类型、检索当前买入价或卖出价、准备交易请求结构,最后发送交易请求。


关闭订单

def close_position(position,symbol):
    """This function closes the position it receives as an argument."""
   
    request = {
        'action': mt5.TRADE_ACTION_DEAL,
        'position': position.ticket,
        'magic': position.magic,
        'symbol': symbol,
        'volume': position.volume,
        'deviation': 50,
        'type': mt5.ORDER_TYPE_BUY if position.type == 1 else mt5.ORDER_TYPE_SELL,
        'type_filling': mt5.ORDER_FILLING_FOK,
        'type_time': mt5.ORDER_TIME_GTC,
        'comment': "mi primera orden desde Python"
    }
    return mt5.order_send(request)

# Now, we define a new function that serves to close ALL open positions.
def close_all_positions(symbol):
    """This function closes ALL open positions and handles potential errors."""
   
    positions = mt5.positions_get()
    for position in positions:
        if close_position(position,symbol).retcode == mt5.TRADE_RETCODE_DONE:
            print(f"Position {position.ticket} closed correctly.")
        else:
            print(f"An error occurred while closing the position.{position.ticket}: {mt5.last_error()}")

我们还可以使用我为此修改的 EA 关闭订单。在这种情况下,.py 文件会创建一个文档,并以分钟为单位写入关闭订单的时间(如果使用该文件,请记住将该文件放在 mt5 的文件文件夹中)。

#Specify the path for the file and open it in write mode
file_path = "C:/XXX/MetaQuotes/Terminal/XXX/MQL5/Files/python_"+symbol+"_file.txt"
try:
    with open(file_path, "w") as file:
        # Write file parameters
        file.write(str(the_value))
    print(f"File '{file_path}' created.")
except Exception as e:
    print(f"Error creating file: {e}")

这是我从这个那里获得的经过修改的 EA(您必须在文件文件夹中添加正确的路径):

//+------------------------------------------------------------------+
//|                                               Timer_modified.mq5 |
//|                                                                  |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2021, Vladimir Karputov"
#property link      "https://www.mql5.com/ru/market/product/43516"
#property version   "1.000"

//--- display the window of input parameters when launching the script
#property script_show_inputs

//--- parameters for data reading
input string InpFileName="python_file.txt"; // file name
input string InpDirectoryName="Files"; // directory name
//---
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
//#include <File.mqh>
//---
CPositionInfo  m_position;                   // object of CPositionInfo class
CTrade         m_trade;                      // object of CTrade class
//--- input parameters
input uchar    InpAfterHour   = 0; // After: Hour ... (max 255)
input uchar    InpAfterMinutes= 42; // After: Minutes ... (max 255)
input uchar    InpAfterSeconds= 59; // After: Seconds ... (max 255)
//---
int file_handles;
uchar value_uchar;
long     m_after  = 0;
bool         started=false;     // flag of counter relevance
string str1,str2,str3,str4;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

int OnInit()
  {
  str1="C:/XXX/MetaQuotes/Terminal/XXXX/MQL5/Files/python_";
  str3="_file.txt";
  str2=Symbol();
  str4=str1+str2+str3;

   // Open file to read
   string file_path = str4;
   file_handles = FileOpen(file_path, FILE_READ|FILE_TXT);
   
   if(file_handles != INVALID_HANDLE)
     {
      PrintFormat("File %s opened correctly", file_path);
      
      // Read uchar value of the file
      if (FileReadInteger(file_handles, INT_VALUE))
        {
         PrintFormat("Uchar value read from file: %u", value_uchar);
         m_after  = 0;
         m_after=value_uchar;
        }
      else
        {
         Print("Error when reading the uchar value from file");
        }

      // Close file after read
      FileClose(file_handles);
     }
   else
     {
      m_after  = 0;
      m_after=InpAfterHour*60*60+InpAfterMinutes*60+InpAfterSeconds;
      PrintFormat("Error when opening file %s, error code = %d", file_path, GetLastError());
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   for(int i=PositionsTotal()-1; i>=0; i--) // returns the number of current positions
      if(m_position.SelectByIndex(i)) // selects the position by index for further access to its properties
        {
         if(TimeCurrent()-m_position.Time()>=m_after)
            m_trade.PositionClose(m_position.Ticket()); // close a position
        }
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| called when a Trade event arrives                                |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(started) SimpleTradeProcessor();   
  }  
 void SimpleTradeProcessor()
  {
  str1="C:/Users/XXX/MetaQuotes/Terminal/XXX/MQL5/Files/python_";
  str3="_file.txt";
  str2=Symbol();
  str4=str1+str2+str3;
     // Open file to read it
   string file_path = str4;
   file_handles = FileOpen(file_path, FILE_READ|FILE_TXT);
   
   if(file_handles != INVALID_HANDLE)
     {
      PrintFormat("File %s is opened correctly", file_path);
      
      // Reads the uchar value from the file
      if (FileReadInteger(file_handles, INT_VALUE))
        {
         PrintFormat("value_uchar read form file: %u", value_uchar);
         m_after  = 0;
         m_after=value_uchar;
        }
      else
        {
         Print("Error while reading the value uchar from file");
        }

      // Closes the file after reading
      FileClose(file_handles);
     }
   else
     {
      m_after  = 0;
      m_after=InpAfterHour*60*60+InpAfterMinutes*60+InpAfterSeconds;
      PrintFormat("Error when opening the fileo %s, Code error = %d", file_path, GetLastError());
     }    
  }

我介绍了两种不同的订单关闭方式。在 Python 中,我唯一要做的就是使用 `time.sleep(t)`,其中 `t` 是从下单到我想关闭订单之间的秒数。然而,这种方法并不实用,因为需要等待,消耗了本可以用于其他脚本运行的时间。我让你们自己决定使用哪种方法来关闭订单。

例如,我们可以通过减少使用 "time.sleep()",在下达买入或卖出指令与关闭指令之间生成更多订单。如何?我们可以利用修改后的智能交易系统 (EA),将幻数纳入其中,使其只关闭具有指定幻数和交易品种的订单。这样,脚本就不会因为睡眠而暂停,而是可以保持激活状态并继续工作。


可视化

为了有效地将其可视化,我们将绘制图表来观察趋势。我们将标出最可靠预测的范围,并指出这些范围内的最大值和最小值,最后保存图表。

plt.axvline(x=pt_division2, color='gray', linestyle='--', label='75 %')
plt.axvline(x=center, color='grey', linestyle='--', label='50 %')
plt.axvline(x=pt_division3, color='blue', linestyle='--', label='33 %')
plt.axvline(x=pt_division1, color='gray', linestyle='--', label='25 %')
plt.axvline(x=pt_division6, color='blue', linestyle='--', label='16 %')
plt.axvline(x=pt_division10, color='yellow', linestyle='--', label='10 %')
plt.axvline(x=pt_division14, color='blue', linestyle='--', label='7 %')
plt.axvline(x=pt_division20, color='yellow', linestyle='--', label='5 %')
plt.axvline(x=entry_point, color='orange', linestyle='--', label='entrada')
plt.axvline(x=value_25, color='orange', linestyle='--', label='salida 20%')
plt.axvline(x=maximal_index, color='red', linestyle='--', label='maximal') ##### ni idea de porqué no pinta correctamente la linea
plt.axvline(x=manimal_index, color='red', linestyle='--', label='minimal')# lo mismo aquí

plt.plot(dff5.iloc[:, 0], linestyle='-', label='Predicted')

plt.xlabel('Instances')
plt.ylabel('Prediction Price')
plt.legend()
plt.title(f'Predicted {symbol} y quedan en minutos: ' + str(MyParameter))
plt.savefig('Predicted_for_'+str(symbol)+'_quedan_'+str(MyParameter)+'_minutos_desde_'+str(formatted_now2)+'_.png')


脚本如何工作?

现在,这个脚本是如何工作的?首先,它加载库,然后记录跟踪当前预测阶段的确切时刻。之后,它将初始化 MT5、下载数据、处理数据并提供结果。我们使用这些结果生成一个图,并在精确的时刻执行订单,包括订单关闭的特定时间。此外,您还可以灵活选择关闭订单的方法。

如果你想对同一货币对重复这个过程,一个简单的循环就足够了,这样可以很容易地连续运行程序。只有一个问题 - 如何确定最佳输入参数?其实很简单,如果模型的 R2 接近 1,且 MSE 和 MAE 接近零,则表示近似度良好。不过,要小心过度拟合。一旦确定了最佳输入参数值,就可以不考虑 MSE、MAE 和 R2,因为它们的计算需要大量时间,而在这种情况下,最重要的是获得结果的速度。


制作可执行文件

当我们开始使用货币对时,在下单之前首先需要知道的是我们的预测是否准确。鉴于 Python 在不断发展,你可能会发现需要更新库,把一切都打包起来也没什么坏处。这样,您就有了一个整合的包,可以进行测试和实验。

为此,我们将使用 Tkinter,并使用 auto-py-to-exe 将其转换为可执行文件。这样,我们在确定输入数据时就不会遇到更多问题,而且可以更加谨慎地处理负责执行订单的 .py 文件。

我将演示如何创建用于测试目的的可执行文件,而将用于执行订单的可执行文件留给您自己定制。

对于可执行文件,我将使用 Tkinter,它提供了一个可以输入数值的图形界面。请记住,文件路径应写为"/"或"\"。界面将获取模型的精度值并显示图表。

以下是图形用户界面(gui_console)的代码

import tkinter as tk
from program_object import execute_program_with
from program_object_without import execute_program_without
import sys

class Aplication:
    def __init__(self, root):
        self.root = root
        self.root.title("DeepLearning Forecast")
        
        # labels and entries
        label1 = tk.Label(root, text="Input account mt5:")
        label1.pack(pady=5)

        self.value1_entry = tk.Entry(root)
        self.value1_entry.pack(pady=5)

        label2 = tk.Label(root, text="Input password:")
        label2.pack(pady=5)

        self.value2_entry = tk.Entry(root)
        self.value2_entry.pack(pady=5)

        label3 = tk.Label(root, text="Input server of mt5:")
        label3.pack(pady=5)

        self.value3_entry = tk.Entry(root)
        self.value3_entry.pack(pady=5)

        label4 = tk.Label(root, text="Input delay in days (for ticks):")
        label4.pack(pady=5)

        self.value4_entry = tk.Entry(root)
        self.value4_entry.pack(pady=5)

        label5 = tk.Label(root, text="Input timeframe 1 (1h),2,4,1d,1w:")
        label5.pack(pady=5)

        self.value5_entry = tk.Entry(root)
        self.value5_entry.pack(pady=5)

        label6 = tk.Label(root, text="Input epochs:")
        label6.pack(pady=5)

        self.value6_entry = tk.Entry(root)
        self.value6_entry.pack(pady=5)

        label7 = tk.Label(root, text="Input symbol:")
        label7.pack(pady=5)

        self.value7_entry = tk.Entry(root)
        self.value7_entry.pack(pady=5)


        label8 = tk.Label(root, text="Input path for mt5:")
        label8.pack(pady=5)

        self.value8_entry = tk.Entry(root)
        self.value8_entry.pack(pady=5)

        # Radio button to select program to execute
        self.opcion_var = tk.StringVar(value="program_object")
        radio_btn_object = tk.Radiobutton(root, text="With r2 Score, MAE & MSE", variable=self.opcion_var, value="program_object")
        radio_btn_object.pack(pady=5)
        radio_btn_object_without = tk.Radiobutton(root, text="Without", variable=self.opcion_var, value="program_object_without")
        radio_btn_object_without.pack(pady=5)

        # Botón start
        boton_execute = tk.Button(root, text="Run Program", command=self.execute_programa)
        boton_execute.pack(pady=10)

        # Botón close
        boton_quit = tk.Button(root, text="Exit", command=root.destroy)
        boton_quit.pack(pady=10)

    def write(self, text):
        # this method y called when sys.stdout.write is used
        self.salida_text.insert(tk.END, text)
    def flush(self):
        pass
    def execute_program(self):
        # Obteined value of the selected option
        selected_program = self.opcion_var.get()

        # Obteined value of inputs
        value1 = self.value1_entry.get()
        value2 = self.value2_entry.get()
        value3 = self.value3_entry.get()
        value4 = self.value4_entry.get()
        value5 = self.value5_entry.get()
        value6 = self.value6_entry.get()
        value7 = self.value7_entry.get()
        value8 = self.value8_entry.get()

                # Redirects stdout & stderr to the console
        sys.stdout = sys.__stdout__
        sys.stderr = sys.__stderr__

        # Calls the function to execute a selected program and pass values as arguments
        if selected_program == "program_object":
            execute_program_with(value1, value2, value3, value4, value5, value6, value7, value8)
        elif selected_program == "program_object_without":
            execute_program_without(value1, value2, value3, value4, value5, value6, value7, value8)
        # Restores stdout to a predeterminate value to no have conflicts
        sys.stdout = self
        sys.stderr = self

if __name__ == "__main__":
    root = tk.Tk()
    app = Aplication(root)
    root.mainloop()

而这就是调用的代码(在这里您必须粘贴您的代码):

import sys
import MetaTrader5 as mt5
from MetaTrader5 import *
import matplotlib.pyplot as plt
import seaborn as sns  # Import seaborn for residual plot



def main(value1, value2, value3, value4, value5, value6, value7,value8):
    # Now you can use variables value1, value2, value3, value4, value5, value6, value7 en tu programa
    print("Numero de cuenta mt5 en mt5: ", value1)
    print("Password en mt5: ", value2)
    print("Servidor en mt5: ", value3)
    print("Delay dias ticks: ", value4)
    print("Time frame: ", value5)
    print("Epochs: ", value6)
    print("Symbol: ", value7)
    print("Path a mt5: ", value8)
    

def execute_program_with(value1, value2, value3, value4, value5, value6, value7, value8):
    main(value1, value2, value3, value4, value5, value6, value7, value8)

并以此结尾

if __name__ == "__main__":
    value1, value2, value3, value4, value5, value6, value7 = sys.argv[1:]
    main(value1, value2, value3, value4, value5, value6, value7)

有两个,一个包含模型预测分数和图表,另一个只显示图表。

要将其转换为可执行文件,我们将使用 auto-py-to-exe。首先,安装程序库。

pip install auto-py-to-exe

我们只需在终端中键入 auto-py-to-exe 即可使其运行

只需记住在脚本位置添加 gui_consola,再添加 programa_objetivo.py 和 programa_objetivo_sin.py 文件,然后点击将 py 转换为 exe 即可。

auto-py-to-exe

您可能需要使用 conda 环境,为此只需写入

conda activate XXX

是 XXX 的环境名称。

您可能会遇到 Conda 问题。要解决这个问题,可以考虑从控制面板卸载 Anaconda(导航至 "卸载程序"),然后直接从其官方网站安装 Python。您可以在这里下载 - 确保选择您的操作系统。

安装 Python 时,请记住将路径添加到系统变量中。此外,禁用长度限制。

此外,禁用长度限制。要完成设置,请在终端或命令提示符下运行以下命令安装 matplotlib、seaborn 和 sklearn:

pip install matplotlib
pip install seaborn
pip install scikit-learn


GUI

术语 "Tk GUI "是指使用 Python 中的 Tkinter 工具包创建的图形用户界面(GUI)。Tkinter 是 Python 标准库中的一个内置模块,可帮助开发图形用户界面。

看起来是这样的:

GUI


记住添加 mt5 的路径,并将此处的"/"改为"\":

C:/Program Files/XXX MT5/terminal64.exe


GUI

当你将其打包成可执行文件并运行时,在图形用户界面上看到的大致就是这个样子。

001


测试器的结果如下:

测试器001


这就是测试器的样子,红色矩形内的值是重要值,显然这些值并不好,我应该让 r2 分数接近 1,MSE 和 MAE 接近 0,我应该尝试使用更多的数据和/或更多的历时,并在脚本中更改 L2 的值(我向您保证,您可以得到一个很好的拟合结果,并且有时间下订单......这就像一个游戏。

使用的衡量标准:

R2(R 平方)、MSE(平均平方误差)和 MAE(平均绝对误差)是统计学和机器学习中常用的指标,用于评估模型的性能:

  1. R2(R 平方):

    • R2是一个统计度量,表示因变量中可从自变量预测的方差比例。
    • 这是一个从 0 到 1 的尺度,其中 0 表示模型不能解释因变量的可变性,1 表示完美解释。
  2. MSE(均方误差):

    • MSE 是计算实际值与预测值之间平均平方差的指标。
    • 它对较大的错误比较小的错误惩罚更重,使其对异常值敏感。
    • 在数学上,它是平方差的总和除以观测数据的数量。
  3. MAE(平均绝对误差):

    • MAE 是计算实际值与预测值之间平均绝对差值的指标。
    • 与 MSE 不同,它不对差值进行平方运算,因此对异常值的敏感度较低。
    • 它是绝对差异的总和除以观测数据的数量。

这些指标通常用于回归分析,以评估模型预测结果变量的能力。一般来说,R2 值越高,MSE 或 MAE 值越低,说明模型性能越好。


过度拟合

交易深度学习中的过度拟合涉及训练一个模型,该模型在历史数据上表现良好,但无法有效地推广到新的、未见过的数据。以下是在交易深度学习中与过度拟合相关的一些危险:

  1. 泛化受限:过拟合模型可能会捕捉到历史数据中的噪声或特定模式,而这些噪声或模式并不代表真实的市场动态。因此,该模型可能难以推广到新的市场条件或未见过的数据。
  2. 实时数据性能差:由于过拟合模型本质上是在记忆历史数据,因此当暴露在实时市场数据中时,它可能会表现不佳。该模型可能会做出不准确的预测,或者无法适应不断变化的市场趋势。
  3. 市场机制变化:金融市场是动态的,不同的市场机制(牛市、熊市、横盘)可能会表现出独特的特征。在特定市场机制下训练出来的过度拟合模型可能会在市场机制发生变化时失效,因为它缺乏适应性。
  4. 错误信号:过度拟合会导致模型捕捉到历史数据中的噪声或异常值,从而产生错误信号。这些错误信号会误导交易策略,导致实际交易中的财务损失。
  5. 数据窥探偏差:数据窥探或数据泄露会加剧过度拟合。如果模型在训练过程中无意中接触到测试集的信息,那么它所学习的模式可能无法很好地泛化到真正未见过的数据中。
  6. 超参数灵敏度:过度拟合的模型可能对超参数过于敏感,使其难以在不同市场条件下进行微调以获得稳健的性能。

为了减轻这些危险,交易深度学习的从业者经常采用交叉验证、正则化技术等策略,并结合代表对市场动态更广泛理解的特征。此外,在解释历史数据上的模型性能并验证其在样本外数据集上的有效性时,有必要采取谨慎的方法。对模型进行定期监测和再训练对于适应不断变化的市场条件也至关重要。

深度学习中的交叉验证

交叉验证是一种用于评估机器学习模型性能的统计技术。在深度学习中,交叉验证涉及将数据集划分为多个子集或折叠。该模型在其中一些折叠上进行训练,然后在剩余的折叠上进行测试。这一过程会重复多次,每次迭代都会使用不同的折叠进行训练和测试。然后,在所有迭代中对性能指标进行平均,以提供对模型性能的更稳健的评估。

常见的交叉验证类型包括 k 折交叉验证(将数据集分为 k 折,每折都作为测试集使用一次)和留空交叉验证(将每个数据点作为单折处理)。

交叉验证有助于评估模型对未见数据的泛化程度,并降低过拟合或欠拟合的风险。

核正则化器(kernel_regularizer=l2):

在深度学习和神经网络中,"核正则化器"一词指的是一种应用于神经网络层权重(或核)的正则化技术。"kernel_regularizer=l2" 参数具体表示对权重使用 L2 正则化。

L2 正则化在损失函数中增加了一个惩罚项,该惩罚项与权重的平方大小成正比。这样做的目的是防止任何一个权重变得过大,从而主导学习过程。这有助于阻止模型过于接近训练数据,从而防止过度拟合。

总之,"kernel_regularizer=l2" 是一种正则化技术,用于通过惩罚大权重来控制神经网络的复杂性,是解决深度学习模型中过拟合问题的工具之一。


图形

该图形将保存在运行脚本的位置。

标题处显示的数字是从第一条橙色线到图表结束的时间,以分钟为单位。

您可以更改图表中需要的内容(例如,只显示橙色线条之间的范围,使用此行代码):

plt.plot(dff5.iloc[punto_entrada, valor_25], linestyle='-', label='Predicted')

只需阅读代码,并试着理解它。

图表

这是它所生成的图表。红线表示应在何处下达买入/卖出和平仓指令。橙色线条代表模型被认为更可靠、更准确的范围,特别是在前 20% 的区间。在这种情况下,我使用的是 4 小时的时间框架,因此最佳时间间隔是第一小时,显示了进场和退出的最大和最小点。一旦脚本结束,它就会返回循环,如果我们添加了订单,它就会持续运行。其余的标记线有助于在手动下单时在图表上定位自己。

预测中给出的价格只是起引导作用,重要的是知道区间的最小值和最大值在哪里


如何使用这一切

我所做的是:

首先,我使用图形用户界面对一个交易品种进行实验,用 R2、MAE 和 MSE 对其进行测试。我遍历了不同的 epoch 值和延迟天数。其次,一旦我确定了有希望的预测值,确保谨慎避免高估,我就会过渡到脚本。我将脚本自动化,自主执行订单,并租赁了一个 VPS,以便在一周内持续执行订单。值得注意的是,如果遇到高误差,还有一个参数可以调整。例如,就加密货币而言,人们可能会选择 0.01 的 L2,而不是外汇使用的 0.001。通过反复试验,可以确定数据量(延迟天数)和世代的最佳值。


值得注意的是,计算 MSE、R2 和 MAE 大约需要一个世代。因此,建议先进行实验。一旦实现了良好的模型近似,就可以在不使用这些参数的情况下使用模型。在 Python 中,这可以通过注释掉相关代码或使用 # 符号来实现。


我在这个 Python 程序中使用的是一个循环。如果在关闭订单时出现故障(我已实施了取消交易品种订单的机制),我也会使用 timer.mql5,因为我已为订单设置了时间间隔,具体为一小时。这样可以确保任何订单的开启时间都不会超过一个小时。


循环

要循环运行 DeepLearningForecast,我们可以这样做(Loop.py),而不是使用 while = True:

import time
import subprocess

def execute_program():
    
    subprocess.run(['python', 'DeepLearningForecast.py'])

# Stablish and interval in seconds (for example, 60 seconds)
seconds_interval = 5


while True:
    execute_program()
    print(f"Program executed. Waiting {seconds_interval} seconds")

    # waits the interval before executing the program
    time.sleep(seconds_interval)


Telegram 机器人

在 MT5 中,我们已经有了多种查看信息的方法,但对于这个机器人,我有时很想知道它是否能准确预测并正确执行订单。为了跟踪这些操作,并将预测结果与实际图表进行比较,我将图表和相关信息发送给 Telegram 机器人。我现在将解释如何创建 Telegram 机器人以及如何通过 Telegram 发送数据。

逐步指导你在 Telegram 上创建一个机器人,并获取其聊天 ID 和令牌,以便通过请求发送信息:

在 Telegram 上创建一个机器人:

  1. 打开 Telegram 并搜索 BotFather:

    • 打开 Telegram,在搜索栏中搜索 "BotFather"。
    • 与 BotFather 开始聊天。
  2. 创建新机器人:

    • 使用 /newbot 命令创建新机器人。
    • 按照 BotFather 的说明为机器人分配名称和用户名。
    • 完成后,BotFather 将为您提供一个令牌。验证机器人时需要此令牌。

获取聊天 ID:

  1. 与机器人开始聊天:

    • 创建机器人后,在 Telegram 上发起聊天。
  2. 访问 Bot API URL:

    • 打开网络浏览器,访问以下 URL,将 BOT_TOKEN 替换为您机器人的令牌:
    https://api.telegram.org/botBOT_TOKEN/getUpdates
  1. 给机器人发送信息:

    • 返回 Telegram,向发起聊天的机器人发送信息。
  2. 刷新浏览器页面:

    • 返回到输入机器人 API URL 的浏览器页面。
    • 刷新页面。
  3. 查找聊天 ID:

    • 在 JSON 响应中,查找包含消息历史记录的部分。
    • 查找代表您最近发送的信息的对象。
    • 在该对象中,找到 chat 字段,其中的 id 字段将是聊天 ID。


这样做的结果是,您会收到这样一条信息(以跟踪机器人的运行情况)

Telegram 消息



其他修复

由于我不喜欢关闭订单的方式,所以我修改了脚本,以通过使用趋势完成。我添加了简单移动平均线 (SMA),并指示它在价格越过 SMA 时关闭订单。

所以要解决这个问题:

SMA


我加了这个:

def obtain_sma_pandas(symbol, periodo):
    prices = mt5.copy_rates_from_pos(symbol, mt5.TIMEFRAME_M1, 0, periodo)
    df_sma = pd.DataFrame(prices)
    sma = df_sma['close'].rolling(window=periodo).mean().iloc[-1]
    return sma

还有这个

    action="buy"
    price = mt5.symbol_info_tick(symbol).ask
    period_sma=14
    sma = obtain_sma_pandas(symbol, period_sma)
    while True:
        # Obtener el precio actual y la SMA
        actual_price = price
        sma = obtain_sma_pandas(symbol, period_sma)

        # Si el precio está por debajo de la SMA, cerrar la orden
        if actual_price > sma:
            close_all_position(symbol)
            continue

        # Esperar antes de verificar nuevamente
        time.sleep(10)

您还可以在脚本中添加 adx,以便在趋势中寻找交易机会:

def calculate_adx(high, low, close, period):

    tr_pos = []
    tr_neg = []

    for i in range(1, len(close)):
        trh = max(high[i] - high[i - 1], 0)
        trl = max(low[i - 1] - low[i], 0)

        if trh > trl:
            tr_pos.append(trh)
            tr_neg.append(0)
        elif trl > trh:
            tr_pos.append(0)
            tr_neg.append(trl)
        else:
            tr_pos.append(0)
            tr_neg.append(0)

    atr = pd.Series(tr_pos).ewm(span=period, adjust=False).mean() + pd.Series(tr_neg).ewm(span=period, adjust=False).mean()
    dx = (pd.Series(tr_pos).ewm(span=period, adjust=False).mean() / atr) * 100
    adx = dx.ewm(span=period, adjust=False).mean()

    return adx.iloc[-1]


ONNX

现在,利用 MetaQuotes 的文章《如何在 MQL5 中使用 ONNX 模型》中的思路,我正在将模型转换为 ONNX 格式。按照同一篇文章中提供的指导原则,我将把生成的 ONNX 模型集成到基础智能交易系统(EA)中,以启动交易操作。这种方法可将机器学习模型无缝集成到 MQL5 环境中,从而增强交易算法的能力。

在把格式转化为 ONNX 之前,有必要下载数据。为此,我们将使用我上传的脚本 (ticks_to_csv)。只需将其保存在 MQL5 EA 文件夹中,在集成开发环境中打开并编译即可。完成后,将脚本添加到图表中,让它运行一段时间(因为它会下载一个交易品种的所有分时报价,所以可能需要一段时间)。在日志中,当这个过程结束时,您会看到一条完成信息。作为参考,我曾将其用于EUR/USD,占用了几个G的字节。

#property script_show_inputs
#property strict
//--- Requesting 100 million ticks to be sure we receive the entire tick history
input long      getticks=100000000000; // The number of required ticks
string fileName = "ticks_data.csv";
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   int     attempts=0;     // Count of attempts
   bool    success=false;  // The flag of a successful copying of ticks
   MqlTick tick_array[];   // Tick receiving array
   MqlTick lasttick;       // To receive last tick data

   SymbolInfoTick(_Symbol,lasttick);
//--- Make 3 attempts to receive ticks
   while(attempts<3)
     {

      //--- Measuring start time before receiving the ticks
      uint start=GetTickCount();
      //--- Requesting the tick history since 1970.01.01 00:00.001 (parameter from=1 ms)
      long received=CopyTicks(_Symbol,tick_array,COPY_TICKS_ALL,1,getticks);

      // Check if ticks were successfully copied
      if(received > 0)
        {
         // Open the CSV file for writing
         int fileHandle = FileOpen(fileName, FILE_WRITE | FILE_CSV);

         // Check if the file was opened successfully
         if(fileHandle != INVALID_HANDLE)
           {
            // Write the CSV header
            FileWrite(fileHandle, "Time,Bid,Ask");

            // Write tick data to the CSV file
            for(long i = 0; i < received; i++)
              {
               string csvLine = StringFormat("%s,%.5f,%.5f", TimeToString(tick_array[i].time), tick_array[i].bid, tick_array[i].ask);
               FileWrite(fileHandle, csvLine);
              }


            // Close the CSV file
            FileClose(fileHandle);

            // Print success message
            Print("Downloaded ", received, " ticks for symbol ", _Symbol, " and period ", Period());
            Print("Ticks data saved to ", fileName);
           }
         else
           {
            // Print an error message if the file could not be opened
            Print("Failed to open the file for writing. Error code: ", GetLastError());
           }
        }
      else
        {
         // Print an error message if no ticks were downloaded
         Print("Failed to download ticks. Error code: ", GetLastError());
        }

      if(received!=-1)
        {
         //--- Showing information about the number of ticks and spent time
         PrintFormat("%s: received %d ticks in %d ms",_Symbol,received,GetTickCount()-start);
         //--- If the tick history is synchronized, the error code is equal to zero
         if(GetLastError()==0)
           {
            success=true;
            break;
           }
         else
            PrintFormat("%s: Ticks are not synchronized yet, %d ticks received for %d ms. Error=%d",
                        _Symbol,received,GetTickCount()-start,_LastError);
        }
      //--- Counting attempts
      attempts++;
      //--- A one-second pause to wait for the end of synchronization of the tick database
      Sleep(1000);
     }
  }

我们将从这些数据(CSV)中读取并使用我们需要的片段,将其转换为 DataFrame(DeepLearning_ONNX.py 已定义了截至今天的过去 1000 天的数据)。您可以使用整个数据集,但需要足够的内存。

下载数据后,我们就可以使用 DeepLearningForecast_ONNX_training.py 开始训练 ONNX 模型。(请将该文件保存在 MQL5 文件夹下的文件夹中)。

它以这种方法导入数据:

rates = pd.read_csv(file_path, encoding='utf-16le')

以此方法创建一个模型:

onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)


结论

总之,本文概述了一种使用深度学习开发用于自动交易的 Python 脚本的综合方法。我们探索了 MetaTrader 5 的集成、数据预处理、模型训练和买卖订单的执行。使用 Tkinter 和 auto-py-to-exe 创建用户界面友好的可执行文件,增加了脚本的实用性。

此外,还就选择适当的输入数据、评估模型性能和实施有效的平仓策略提供了考虑因素。该脚本的可视化输出显示了预测、可靠范围和下单位置,为自动和手动交易提供了清晰、可操作的概览。

最后,我们将学习如何导入货币对的所有分时报价。我们将使用 Python 读取这些数据,创建 ONNX 模型,并且(如果下一篇文章令人感兴趣)在 MT5 中使用智能交易系统 (EA) 执行该模型。

不过,必须强调的是,金融市场具有动态性,需要对模型进行持续监控、调整和可能的改进。概述的框架是一个坚实的基础,鼓励用户根据不断变化的市场条件和他们的具体交易目标进一步定制和改进。


免责声明

免责声明: 过去的业绩并不预示未来的结果。交易加密货币和其他金融工具存在风险;没有任何策略能保证在任何情况下都能盈利。在做出投资决定之前,一定要进行全面的研究,并征求金融专业人士的意见。


本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/13975

附加的文件 |
Timer.mq5 (4.57 KB)
Timer_modified.mq5 (9.73 KB)
Loop.py (0.4 KB)
gui_console.py (3.85 KB)
program_object.py (23.96 KB)
ticks_to_csv.mq5 (3.27 KB)
开发具有 RestAPI 集成的 MQL5 强化学习代理(第 4 部分):在 MQL5 中组织类中的函数 开发具有 RestAPI 集成的 MQL5 强化学习代理(第 4 部分):在 MQL5 中组织类中的函数
本文讨论 MQL5 中从面向过程编码向面向对象编程 (OOP) 的过渡,重点是与 REST API 的集成。今天,我们将讨论如何将 HTTP 请求函数(GET 和 POST)组织到类中。我们将仔细研究代码重构,并展示如何用类方法替换孤立的函数。本文包含实用的示例和测试。
频域中的滤波和特征提取 频域中的滤波和特征提取
在本文中,我们探索了在时间序列由数字滤波器在频域上进行表达的应用,如此即可提取也许对预测模型有用的独特特征。
种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试 种群优化算法:改变概率分布的形状和位移,并基于智能头足类生物(SC)进行测试
本文研究了改变概率分布形状对优化算法性能的影响。我们将进行的实验,会用到智能头足类生物(SC)测试算法,从而评估优化问题背景下各种概率分布的效能。
用于时间序列挖掘的数据标签(第 6 部分):使用 ONNX 在 EA 中应用和测试 用于时间序列挖掘的数据标签(第 6 部分):使用 ONNX 在 EA 中应用和测试
本系列文章介绍了几种时间序列标注方法,可以创建符合大多数人工智能模型的数据,根据需要进行有针对性的数据标注可以使训练好的人工智能模型更符合预期的设计,提高我们模型的准确性,甚至帮助模型实现质的飞跃!