将 MQL5 与数据处理包集成(第 1 部分):高级数据分析和统计处理
概述
金融市场产生大量数据,仅出于技术分析的目的进行分析可能具有挑战性,仅靠手动进行技术分析并不能使交易者分析和解释数据中的模式、趋势和异常。Jupyter Lab 等高级数据分析包允许交易者执行复杂的统计分析、机器学习和数据可视化。这将有助于识别可以获利的交易机会,了解市场行为、季节性趋势,并预测未来的价格走势。
收集历史数据
首先,我们需要以 .csv 格式保存的 MetaTrader 5 历史数据,因此只需启动您的 MetaTrader 平台,然后在 MetaTrader 5 窗格/面板顶部导航至 >工具,然后 >选项,您将进入图表选项。然后,您需要选择要下载的图表中的柱形数量。最好选择无限柱数的选项,因为我们将根据日期进行操作,并且我们不知道在给定的时间段内有多少柱形。

此后,您现在需要下载实际数据。为此,您必须导航至 >查看,然后转到 >交易品种,您将进入规范选项卡,只需导航至 >柱或报价取决于要下载什么类型的数据。继续并输入您要下载的历史数据的开始和结束日期,然后单击请求按钮下载数据并将其保存为 .csv 格式。

完成所有这些步骤后,您就可以成功从 MetaTrader 交易平台下载历史数据。现在您需要下载并设置 Jupyter Lab 环境以进行分析。要下载并设置 Jupyter Lab,你可以前往其官方网站网站并遵循简单的步骤下载。根据您使用的操作系统类型,您可以选择是否使用 pip 、 conda 或 brew 。
在 Jupyter Lab 上加载 MetaTrader 5 历史数据
要在 Jupyter Lab 上成功加载您的 MetaTrader 5 历史数据,您必须知道您选择下载数据的文件夹,然后在 Jupyter Lab 上导航到该文件夹。要开始,您必须加载数据并检查列的名称。我们必须检查列的名称,以便正确处理列并避免因使用错误的列名而可能出现的错误。
预处理
在深入分析数据之前,我们需要对数据进行预处理。
1.日期时间解析:将日期列转换为日期时间格式。
2.缺失值处理:处理任何缺失的值。
3.特征工程:如果有必要,创建新的特征。
Python 代码:
import pandas as pd # Load historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path) # Display the first few rows and column names print(data.head()) print(data.columns)
输出:

从输出中我们可以看到,列中存在一些特殊字符“ <> ”和“ \t ”,这表明该文件是制表符分隔的,现在我们可以继续使用正确的列名加载历史数据。
在下面的代码中我们将进行以下操作:
1.导入库:
- Pandas 是 Python 中一个强大的数据处理库。
- 技术分析库(TA lib)用于金融市场数据的技术分析。
2.载入历史数据:
- File-path 用于指定包含历史数据的 CSV 文件的位置。
- Pd.read 将 CSV 文件读入 pandas 的 Data-Frame 中。分隔符“\t”表示文件以制表符分隔。
3.显示数据:
- Data.head 打印数据帧的前几行来验证内容。
- Data.columns 打印列名以验证数据帧的结构。
4.按日期给数据排序:
- Date.sort,根据 '<DATE>' 列对数据帧进行排序。这确保数据按时间顺序排列。
5.计算 RSI:
- TA.RSI 使用 '<CLOSE>' 价格列计算相对强弱指数 (RSI)。
- Time-period, 指定了 14 周期的 RSI。
- 计算出的值存储在数据帧中名为“RSI”的新列中。
6.显示更新后的数据:
- Data.head,打印更新后的数据帧的前几行以验证 RSI 的计算。
import pandas as pd
import talib as ta
# Load historical data
file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv'
data = pd.read_csv(file_path, delimiter='\t')
# Display the first few rows and column names to verify
print(data.head())
print(data.columns)
# Ensure data is sorted by the correct date column
data.sort_values('<DATE>', inplace=True)
# Calculate RSI using the '<CLOSE>' price column
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14)
# Display the first few rows to verify
print(data.head()) 输出:

从输出中我们可以看到 RSI 列下有 NAN 值,我们必须正确处理这些 NAN 值。RSI 函数需要最少数量的数据点来计算指标,在初始阶段,它可能会返回 NAN 值。为了正确处理 NAN 值,我们需要知道“<CLOSE>”列的数据类型。确保数据帧和列访问正确。以下 Python 代码展示了如何处理 NAN 值。
import pandas as pd import talib as ta # Load the historical data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t')data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Verify the column names print(data.columns) # Convert the column to the correct data type if necessary data['<CLOSE>'] = data['<CLOSE>'].astype(float) # Calculate the RSI data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Display the RSI values print(data[['<CLOSE>', 'RSI']].tail(20))
输出:

我们可以看到我们已经正确处理了 NAN 值。如果您看到初始期间的 NAN 值(这是由于锁定期而预期得到的),您可以按如下方式处理它们:
data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) data['RSI'] = data['RSI'].fillna(0) # or use any other method to handle NaN values
探索性数据分析
探索性数据分析(EDA,Exploratory Data Analysis) 的主要目标是通过总结数据的主要特征来理解数据的底层结构。在执行 EDA 时,我们要发现模式并识别数据中的趋势和关系。在 EDA 中,我们还检测异常和异常值,或任何可能需要进一步调查的异常观察结果。我们还检查了可能影响后续分析的数据假设。然后,我们通过查找需要解决的数据中的任何缺失值、错误和不一致来执行数据清理。
我们使用以下 python 脚本来执行 EDA:
import seaborn as sns import matplotlib.pyplot as plt import warnings warnings.filterwarnings("ignore") for i in data.select_dtypes(include="number").columns: sns.histplot(data=data, x=i) plt.show()
在导入所有必要的库后,从上面的代码开始,我们忽略警告以获得干净的输出。然后我们遍历数字列,' data.select_dtypes(include="number")' 返回仅包含数字列的数据帧,'columns' 返回这些列的名称。然后继续根据数据绘制直方图,以下是根据上述代码绘制的直方图。






在统计处理操作之后,我们可以继续在收集的数据上训练模型。我们的目标是能够根据我们收集的历史数据做出预测。我们将仅使用 RSI 指标进行预测。在执行之前,我们需要知道数据本身之间存在的关系,我们通过执行相关矩阵来实现。我们应该能够解释存在的关系是正相关、负相关还是不相关。
- 正相关:接近 1 的值表示两个变量之间存在很强的正相关性。例如,如果“开盘价”和“收盘价”之间的相关性接近 1,则意味着它们朝同一方向移动。
- 负相关:接近 -1 的值表示强烈的负相关性。例如,如果“交易量”和“价格”之间的相关性接近 -1,则意味着随着交易量的增加,价格趋于下降。
- 无相关:接近于 0 的值表示变量之间没有相关性。

import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import r2_score, mean_squared_error import talib as ta # Technical Analysis library # Load the data file_path = '/home/int_junkie/Documents/ML/XAUUSD.m_H1_historical.csv' data = pd.read_csv(file_path, delimiter='\t') # Exploratory Data Analysis (EDA) print(data.info()) print(data.describe()) # Visualize the closing price plt.figure(figsize=(12, 6)) plt.plot(data['<CLOSE>']) plt.title('XAUUSD Closing Price') plt.xlabel('<DATE>') plt.ylabel('Price') plt.show() # Feature Engineering data['RSI'] = ta.RSI(data['<CLOSE>'], timeperiod=14) # Drop rows with missing values data.dropna(inplace=True) # Define target variable data['Target'] = data['<CLOSE>'].shift(-1) data.dropna(inplace=True) # Split the data X = data[['RSI']] # Only use RSI as the feature y = data['Target'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, shuffle=False) # Model Development model = RandomForestRegressor(n_estimators=100, random_state=42) model.fit(X_train, y_train) # Predictions y_pred = model.predict(X_test) # Evaluate the model mse = mean_squared_error(y_test, y_pred) print(f'Mean Squared Error: {mse}') # Visualize the predictions plt.figure(figsize=(12, 6)) plt.plot(y_test.index, y_test, label='Actual Values') plt.plot(y_test.index, y_pred, label='Predicted Values') plt.xlabel('Samples') plt.ylabel('TARGET_CLOSE') plt.title('Actual vs Predicted Values') plt.legend() plt.show()
这是我们在训练模型时得到的输出:


虽然从数据分析中我们可以看到预测值与实际值不匹配。这表明该模型没有有效地捕获底层模式。有几个因素可能会导致这种情况。
1.训练数据不足:如果数据集太小,模型可能没有足够的信息来学习完整的信息。
2.过度拟合:该模型可能已经记住了学习数据,而不是从中学习概括。这通常是在模型过于复杂或没有正确概括的情况下发生的。
3.欠拟合:该模型可能过于简单,无法捕捉数据中的潜在模式。如果模型没有足够的复杂性,或者缺少重要特征,就会发生这种情况。
4.特征工程:糟糕的特征选择或工程设计不足可能会导致模型没有足够的相关信息来进行预测。
诊断和解决问题的步骤:
1.检查数据:确保数据干净、一致且经过适当的预处理。
2.评估模型性能:使用均方误差(MSE)和平均绝对误差(MAB)等矩阵。
3.改进特征工程:尝试不同的特征,包括技术指标、滞后值和其他相关财务指标。
4.调整超参数:使用网格搜索或随机搜索等技术为您的模型找到最佳超参数。
将所有内容放在 MQL5 中
保存模型后,我们需要创建一个用于预测的 python 脚本。此脚本将加载模型并进行预测。
import joblib import sys import os
- Joblib:用于加载序列化的机器学习模型。
- Sys:这是“system”(系统)的缩写,提供对命令行参数的访问。
- OS:这是“Operating System”(操作系统)的缩写,用于检查文件的存在并执行文件操作。
model_path = sys.argv[/home/int_junkie/Documents/ML/random_forest_model.pkl] features_path = sys.argv[/home/int_junkie/Documents/ML/features.txt]
- Sys.argv[1]:第一个命令行参数是模型文件的路径。
- Sys.argv[2]:第二个命令行参数是特征文件的路径。
print(f"Model path: {model_path}") print(f"Features path: {features_path}")
打印调试信息,打印模型和特征文件的路径以用于调试目的。
if not os.path.exists(model_path): print(f"Error: Model file not found at {model_path}") sys.exit(1) if not os.path.exists(features_path): print(f"Error: Features file not found at {features_path}") sys.exit(1)
- 检查模型文件和特征文件是否存在。
- 如果其中任意一个不存在,则打印错误消息并以状态代码 1 退出脚本。
model = joblib.load(model_path)
- 从指定的 “model-path” 加载训练过的机器学习模型。
with open(features_path, 'r') as f: features = [float(line.strip()) for line in f]
- 打开特征文件进行读取。
- 读取每一行,删除所有前导/尾随空格,将文件内容转换为浮点数,并将其存储在 “features” 列表中。
prediction = model.predict([features])[0]
- 使用加载的模型根据加载的特征进行预测。
- Model.predict:返回预测列表(在本例中为单个预测),并且以 “[0]” 提取第一个预测。
print(prediction)
- 将预测打印到标准输出。该输出可被另一个脚本或程序(例如 MQL5)捕获,以做出交易决策。
MQL5
在 OnInit() 中加载模型。
int OnInit(){ // Load the model and feature names string modelPath = "/home/int_junkie/Documents/ML/random_forest_model.pkl"; string featurePath = "/home/int_junkie/Documents/ML/features.txt"; // Your code to load the model (use appropriate library for pkl files) // Initialize the features double features[]; int fileHandle = FileOpen(featurePath, FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string line; while(!FileIsEnding(fileHandle)) { line = FileReadString(fileHandle); ArrayResize(features, ArraySize(features) + 1); features[ArraySize(features) - 1] = StringToDouble(line); } FileClose(fileHandle); } return(INIT_SUCCEEDED); }
在 OnTick() 中从 python 文件中读取预测。
void OnTick(){ // Declare static variables to retain values across function calls static bool isNewBar = false; static int prevBars = 0; // Get the current number of bars int newbar = iBars(_Symbol, _Period); // Check if the number of bars has changed if (prevBars == newbar) { // No new bar isNewBar = false; } else { // New bar detected isNewBar = true; // Update previous bars count to current prevBars = newbar; } // Update the features based on current data double features[]; ArrayResize(features, 1); features[0] = iClose(Symbol(), 0, 0); // Write the features to a file int fileHandle = FileOpen("/home/int_junkie/Documents/ML/features.txt", FILE_WRITE | FILE_TXT); if (fileHandle != INVALID_HANDLE) { for (int i = 0; i < ArraySize(features); i++) { FileWrite(fileHandle, DoubleToString(features[i])); } FileClose(fileHandle); } else { Print("Error: Cannot open features file for writing"); return; } // Call the Python script to get the prediction string command = "python /home/int_junkie/Documents/ML/predict.py /home/int_junkie/Documents/ML/random_forest_model.pkl /home/int_junkie/Documents/ML/features.txt"; int result = ShellExecuteA(command); if(result != 0) { Print("Error: ShellExecuteA failed with code ", result); return; } // Read the prediction from a file Sleep(1000); // Wait for the Python script to complete fileHandle = FileOpen("/home/int_junkie/Documents/ML/prediction.txt", FILE_READ | FILE_TXT); if (fileHandle != INVALID_HANDLE) { string prediction = FileReadString(fileHandle); FileClose(fileHandle); double pred_value = StringToDouble(prediction); // Generate trading signals based on predictions double some_threshold = 0.0; // Define your threshold if (pred_value > some_threshold) { // Buy signal double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),Digits()); double sl = Ask - stopLoss * _Point; double tp = Ask + takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_BUY, lotsize, Ask, sl, tp, "ML"); } else if (pred_value < some_threshold) { // Sell signal double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),Digits()); double sl = Bid + stopLoss * _Point; double tp = Bid - takeProfit * _Point; trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, lotsize, Bid, sl, tp, "ML"); } } else { Print("Error: Cannot open prediction file for reading"); } }
在 OnTick 中,变量 “command” 准备一个字符串命令,以使用特征文件和模型文件作为参数来运行 python 脚本。ShellExecuteA(command)使用 ShellExecuteA 执行 python 脚本。'Sleep (1000)',等待 1 秒钟 python 脚本完成执行。之后我们打开预测文件进行读取。然后我们检查预测文件是否成功打开,如果成功,则读取预测。如果没有成功,则打印错误。阈值变量用于做出交易决策。如果它大于预测值,则产生买入信号。如果它小于预测值,则产生卖出信号。
结论
总而言之,我们从MetaTrader 5 交易平台收集了数据。我们收集的同一日期随后会在 Jupyter Lab 中执行数据分析和统计处理。在 Jupyter Lab 中进行分析后,我们使用该模型并将其集成到 MQL5 上,以根据识别的模式做出交易决策。将 MQL5 与 Jupyter Lab 集成解决了 MQL5 中分析、统计和可视化功能有限的问题。该过程增强了策略制定,提高了数据处理效率,并为交易中的高级数据分析和统计处理提供了协作、灵活和强大的环境。
总之,通过将 MQL5 与 Jupyter Lab 集成,我们可以执行高级数据分析和统计处理。有了这些功能,您可以开发任何类型的交易策略,并使用先进的技术优化交易策略。因此,它在动态和数据密集型的金融交易领域提供了显著的竞争优势。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/15155
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
无政府社会优化(ASO)算法
您应当知道的 MQL5 向导技术(第 26 部分):移动平均和赫斯特(Hurst)指数