将人工智能(AI)模型集成到已有的MQL5交易策略中
概述
在本文中,我们将AI模型集成到已有的MQL5交易策略中,我们将采用上一篇文章中提到的订单块结合斐波那契(Fibonacci)的方法。许多现有的MQL5交易策略依赖于固定的指标、僵化的阈值或预定义的模式,这些策略在不同市场周期中可能效果欠佳。其缺乏从历史数据中学习的能力,无法识别复杂模式,也无法根据不断变化的条件动态调整决策。
将AI模型融入MQL5交易策略,可通过注入基于机器学习的适应性和决策能力,帮助克服现有挑战。通过使用长短期记忆网络(LSTM)或预测分析等技术,AI能够分析大量历史和实时数据集,从而生成更明智的交易行动。与僵化、预定义的策略不同,增强人工智能的系统能够通过学习不断变化的市场条件,动态调整并优化其方法。这有助于实现更精准的交易时机把握、更有效的风险缓解,并随着时间的推移提高盈利能力。
入门指南
在开始前,第一步是将现有的MQL5代码转换为Python代码。由于我们要将AI融入交易策略,因此拥有一个基于Python原始MQL5的版本至关重要。这种转换确保了我们能够无缝地融入AI驱动的增强功能,而不会破坏策略的核心功能。Python版本的代码应精确复制MQL5脚本的行为,包括交易执行逻辑、指标计算、订单管理以及任何风险管理规则。这样确保了AI模型将与一个与在MetaTrader 5上运行的系统行为完全相同的系统进行交互,从而在全面集成之前实现准确的测试和优化。完成这一步骤后,我们就可以着手嵌入机器学习模型、用市场数据训练AI,并最终创建一个智能、自适应的交易系统,以提升决策能力和表现。
MQL5代码:
#include <Trade/Trade.mqh> #include <Arrays\ArrayObj.mqh> CTrade trade; #define BullOB clrLime #define BearOB clrRed //+------------------------------------------------------------------+ //| Global vars | //+------------------------------------------------------------------+ double Lots = 0.01; int takeProfit = 170; int length = 100; input double stopLoss = 350; input double Mgtn = 0.85; bool isBullishOB = false; bool isBearishOB = false; input int Time1Hstrt = 3; input int Time1Hend = 4; class COrderBlock : public CObject { public: int direction; datetime time; double high; double low; bool traded; string rectName; string tradeRectName; COrderBlock(int dir, datetime t, double h, double l) { direction = dir; time = t; high = h; low = l; traded = false; rectName = ""; tradeRectName = ""; } void draw(datetime tmS, datetime tmE, color clr) { rectName = "OB REC" + TimeToString(time); ObjectCreate(0, rectName, OBJ_RECTANGLE, 0, time, low, tmS, high); ObjectSetInteger(0, rectName, OBJPROP_FILL, true); ObjectSetInteger(0, rectName, OBJPROP_COLOR, clr); tradeRectName = "OB trade" + TimeToString(time); ObjectCreate(0, tradeRectName, OBJ_RECTANGLE, 0, tmS, high, tmE, low); ObjectSetInteger(0, tradeRectName, OBJPROP_FILL, true); ObjectSetInteger(0, tradeRectName, OBJPROP_COLOR, clr); } void removeDrawings() { if (ObjectFind(0, rectName) != -1) { ObjectDelete(0, rectName); // Delete the main rectangle } if (ObjectFind(0, tradeRectName) != -1) { ObjectDelete(0, tradeRectName); // Delete the trade rectangle } } }; // Pointer to CArrayObj // Declare the dynamic array to hold order blocks CArrayObj *orderBlocks; color OBClr; datetime T1; datetime T2; int OnInit() { orderBlocks = new CArrayObj(); // Allocate memory for the array return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { ObjectsDeleteAll(0, "OB"); // Clear and free the order blocks if (orderBlocks != NULL) { orderBlocks.Clear(); // This will delete objects inside delete orderBlocks; // Free the array memory orderBlocks = NULL; } } void OnTick() { if (isNewBar()) { static int prevDay = 0; MqlDateTime structTime; TimeCurrent(structTime); structTime.min = 0; structTime.sec = 0; structTime.hour = Time1Hstrt; datetime timestrt = StructToTime(structTime); structTime.hour = Time1Hend; datetime timend = StructToTime(structTime); getOrderB(); double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); for (int i = orderBlocks.Total() - 1; i >= 0; i--) { COrderBlock *OB = (COrderBlock *)orderBlocks.At(i); if (CheckPointer(OB) != POINTER_INVALID && !OB.traded) { if(OB.direction > 0 && Ask < OB.high){ double entry = Ask; double tp = getHigh(iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, iBarShift(_Symbol, PERIOD_CURRENT, OB.time))); double sl = NormalizeDouble(OB.low - Mgtn, _Digits); T2 = getTime(0); OB.draw(T1, T2, BullOB); trade.Buy(Lots, _Symbol, entry, sl, tp, "OB buy"); OB.traded = true; //OB.removeDrawings(); orderBlocks.Delete(i); // Delete from array delete OB; // Free memory } } if(CheckPointer(OB) != POINTER_INVALID && !OB.traded){ if (OB.direction < 0 && Bid > OB.low) { double entry = Bid; double tp = getLow(iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, iBarShift(_Symbol, PERIOD_CURRENT, OB.time))); double sl = NormalizeDouble(OB.high + Mgtn, _Digits); T2 = getTime(0); OB.draw(T1, T2, BearOB); trade.Sell(Lots, _Symbol, entry, sl, tp, "OB sell"); OB.traded = true; //OB.removeDrawings(); orderBlocks.Delete(i); // Delete from array delete OB; // Free memory } } } } } void getOrderB(){ static int prevDay = 0; MqlDateTime structTime; TimeCurrent(structTime); structTime.min = 0; structTime.sec = 0; structTime.hour = Time1Hstrt; datetime timestrt = StructToTime(structTime); structTime.hour = Time1Hend; datetime timend = StructToTime(structTime); int visibleBars = (int)ChartGetInteger(0,CHART_VISIBLE_BARS); for(int i = 1; i <= visibleBars; i++){ if(getOpen(i) < getClose(i)){ // index is i since the loop starts from i which is = 1 "for(int i = 1)..." if(getOpen(i + 2) < getClose(i + 2)){ if(getOpen(i + 3) > getClose(i + 3) && getOpen(i + 3) < getClose(i + 2)){ Print("Bullish Order Block confirmed at: ", TimeToString(getTime(i + 2), TIME_DATE||TIME_MINUTES)); //isBullishOB = true; //OB = new COrderBlock(); int direction = 1; datetime time = getTime(i + 3); double high = getHigh(i + 3); double low = getLow(i + 3); isBullishOB = true; OBClr = isBullishOB ? BullOB : BearOB; // specify strt time T1 = time; // reset BULLOB flag isBullishOB = false; // crucial COrderBlock *newOB = new COrderBlock(direction, time, high, low); orderBlocks.Add(newOB); break; //delete newOB; } } } if(getOpen(i) > getClose(i)){ if(getOpen(i + 2) > getClose(i + 2)){ if(getOpen(i + 3) < getClose(i + 3) && getOpen(i + 3) < getClose(i + 2)){ Print("Bearish Order Block confirmed at: ", TimeToString(getTime(i + 2), TIME_DATE||TIME_MINUTES)); //isBearishOB = true; //OB = new COrderBlock(); int direction = -1; datetime time = getTime(i + 3); double high = getHigh(i + 3); double low = getLow(i + 3); isBearishOB = true; OBClr = isBearishOB ? BearOB : BullOB; T1 = time; // reset the BEAROB flag isBearishOB = false; // crusssial COrderBlock *newOB = new COrderBlock(direction, time, high, low); orderBlocks.Add(newOB); break; //delete newOB; } } } } } double getHigh(int index) { return iHigh(_Symbol, _Period, index); } double getLow(int index) { return iLow(_Symbol, _Period, index); } double getOpen(int index){ return iOpen(_Symbol, _Period, index); } double getClose(int index){ return iClose(_Symbol, _Period, index); } datetime getTime(int index) { return iTime(_Symbol, _Period, index); } bool isNewBar() { // Memorize the time of opening of the last bar in the static variable static datetime last_time = 0; // Get current time datetime lastbar_time = (datetime)SeriesInfoInteger(Symbol(), Period(), SERIES_LASTBAR_DATE); // First call if (last_time == 0) { last_time = lastbar_time; return false; } // If the time differs (new bar) if (last_time != lastbar_time) { last_time = lastbar_time; return true; } // If no new bar, return false return false; } void deler(){ static int prevDay = 0; MqlDateTime structTime; TimeCurrent(structTime); structTime.min = 0; structTime.sec = 0; structTime.hour = Time1Hstrt; datetime timestrt = StructToTime(structTime); structTime.hour = Time1Hend; datetime timend = StructToTime(structTime); }
在之前的文章中,我们讨论了这段代码的核心功能与设计,它实现了一种算法交易策略,该策略围绕识别并交易“订单块”展开——这是一种特定的K线形态,被认为能预示潜在的市场反转。作为一款EA,它将价格行为分析与自动化交易执行相结合。代码通过COrderBlock类,利用面向对象编程来存储关键形态数据(时间戳、价格边界、方向),并管理图表上的可视化标注。一个动态数组CArrayObj用于跟踪活跃的订单块,确保在多个图表实例中高效管理内存并实时监控形态。
getOrderB()函数通过扫描历史K线来识别特定的看涨/看跌序列,从而驱动形态识别。当一根看跌K线后紧跟三根连续的看涨K线时,识别为看涨订单块;而看跌形态则要求一根看涨K线后有三根看跌K线。检测到的形态会被实例化为带有方向标识(1表示看涨,-1表示看跌)的COrderBlock对象,并存储在数组中。代码还融入了用户可配置的时间过滤器(Time1Hstrt、Time1Hend),以聚焦于特定的交易时段,增强形态的相关性。
在每根新K线出现时(通过isNewBar()检测),EA会在OnTick()函数中处理活跃的订单块。对于看涨形态,当价格行为突破订单块的高点时,EA会建立多头头寸,并将止损设置在形态低点减去一定幅度(Mtgn)的位置。看跌交易则在价格跌破形态低点时触发,止损设置在形态高点上方。EA使用CTrade进行订单管理,根据最近的摆动高点/低点计算止盈水平。已执行的交易会自动移除其关联的订单块和图表标注,以避免重复信号。
该系统采用了多重保障措施:止损距离根据经纪商的点数要求进行标准化处理,仓位大小固定为Lots,并使用可视化指标(彩色矩形)提供策略验证的透明度。用户可以通过输入变量自定义关键参数,如止损距离(StopLoss)、利润幅度(Mtgn)和交易时段。代码在自动化与主观判断元素之间取得了平衡——虽然形态检测是算法化的,但交易执行会尊重经纪商特定的限制条件,如最小止损距离和点差考虑。
Python版本:
在正式开始之前,和往常一样,我们需要获取历史市场数据。这些数据将作为训练我们的AI模型以及验证策略准确性的基础。我假设到目前为止,你已经熟悉如何获取历史数据了。然而,如果您不确定或需要复习一下,请参阅我关于将MQL5与数据处理软件包集成的指南的第一部分。接下来,我们只需加载历史数据即可。
import pandas as pd # Load historical data file_path = '/home/int_junkie/Documents/DataVisuals/AI inside MQL5/XAUUSD_H1.csv' data = pd.read_csv(file_path) # Display the first few rows and column names print(data.head()) print(data.columns)
在构建该代码的Python版本时,我们确保其能够有效处理历史市场数据,这些数据是训练AI模型的基础。第一步是建立一个数据管道,使脚本能够摄入、预处理并整理历史价格数据,包括开盘价、最高价、最低价、收盘价、成交量以及任何相关的技术指标等关键特征。这种结构化的格式使AI模型能够从过去的市场行为中学习有意义的模式。
此外,我们还集成了训练LSTM模型的功能。LSTM是一种特殊的循环神经网络(RNN),以其分析序列数据和捕捉长期依赖关系的能力而闻名。训练过程与原始MQL5策略中现有的买入和卖出逻辑保持一致,确保模型能够根据历史价格行为做出交易决策。通过将过去的数据映射到相应的买入和卖出信号,LSTM模型逐渐优化其预测能力,使其能够更有效地预测潜在的市场走势。这种结构化的方法使我们能够弥合传统基于规则的交易与AI驱动的决策之间的差距,最终提高交易系统的适应性和准确性。
import pandas as pd import numpy as np from datetime import datetime from keras.models import Sequential from keras.layers import LSTM, Dense import tensorflow as tf # Constants LOTS = 0.01 TAKE_PROFIT = 170 STOP_LOSS = 350 MGTN = 0.85 TIME1_HSTRT = 3 TIME1_HEND = 4 # Helper functions def get_high(data, index): return data.iloc[index]['<HIGH>'] def get_low(data, index): return data.iloc[index]['<LOW>'] def get_open(data, index): return data.iloc[index]['<OPEN>'] def get_close(data, index): return data.iloc[index]['<CLOSE>'] def get_time(data, index): return data.iloc[index]['DATETIME'] # Combined datetime column def is_new_bar(current_time, last_time): return current_time != last_time class OrderBlock: def __init__(self, direction, time, high, low): self.direction = direction self.time = time self.high = high self.low = low self.traded = False def get_order_blocks(data): order_blocks = [] visible_bars = len(data) for i in range(1, visible_bars - 3): # Adjusted to avoid index errors if get_open(data, i) < get_close(data, i): # Bullish condition if get_open(data, i + 2) < get_close(data, i + 2): if get_open(data, i + 3) > get_close(data, i + 3) and get_open(data, i + 3) < get_close(data, i + 2): print(f"Bullish Order Block confirmed at: {get_time(data, i + 2)}") direction = 1 time = get_time(data, i + 3) high = get_high(data, i + 3) low = get_low(data, i + 3) order_blocks.append(OrderBlock(direction, time, high, low)) break if get_open(data, i) > get_close(data, i): # Bearish condition if get_open(data, i + 2) > get_close(data, i + 2): if get_open(data, i + 3) < get_close(data, i + 3) and get_open(data, i + 3) < get_close(data, i + 2): print(f"Bearish Order Block confirmed at: {get_time(data, i + 2)}") direction = -1 time = get_time(data, i + 3) high = get_high(data, i + 3) low = get_low(data, i + 3) order_blocks.append(OrderBlock(direction, time, high, low)) break return order_blocks def simulate_trading(data, order_blocks): trades = [] last_time = None for i, row in data.iterrows(): current_time = row['DATETIME'] if is_new_bar(current_time, last_time): last_time = current_time bid = row['<CLOSE>'] # Assuming bid price is close price ask = row['<CLOSE>'] # Assuming ask price is close price for ob in order_blocks: if not ob.traded: if ob.direction > 0 and ask < ob.high: # Buy condition entry = ask tp = data.iloc[:i]['<HIGH>'].max() # Take profit as highest high sl = ob.low - MGTN # Stop loss trades.append({ 'time': current_time, 'direction': 'buy', 'entry': entry, 'tp': tp, 'sl': sl }) ob.traded = True if ob.direction < 0 and bid > ob.low: # Sell condition entry = bid tp = data.iloc[:i]['<LOW>'].min() # Take profit as lowest low sl = ob.high + MGTN # Stop loss trades.append({ 'time': current_time, 'direction': 'sell', 'entry': entry, 'tp': tp, 'sl': sl }) ob.traded = True return trades
以下代码实现了一种基于订单块检测和历史市场数据交易模拟的交易策略。代码首先导入必要的库,包括用于数据处理的Pandas、用于数值运算的NumPy,以及用于潜在AI集成的Keras/TensorFlow。脚本定义了关键的交易参数,如手数(LOTS)、止盈(TAKE_PROFIT)、止损(STOP_LOSS)和风险管理因子(MGTN)。辅助函数从数据集中提取关键价格点(开盘价、最高价、最低价、收盘价)和时间戳值,确保对历史数据进行结构化访问。引入了OrderBlock类,用于存储检测到的看涨或看跌订单块的信息,这些订单块是价格行为交易中的关键区域,机构可能在此区域放置大量订单。
get_order_blocks函数扫描历史价格数据,根据一系列看涨或看跌的价格走势检测潜在的订单块。该函数识别价格行为发生转变的模式,并存储检测到的订单块的时间、最高价和最低价。一旦识别出订单块,simulate_trading函数便执行模拟交易。该函数遍历历史数据,检查是否出现新的K线图(蜡烛),以及价格条件是否符合基于检测到的订单块的买入或卖出标准。如果满足买入或卖出条件,该函数会记录一笔交易,并根据过去的最高价/最低价动态设置入场价格、止盈和止损。这种设置允许通过分析历史交易表现来对策略进行回测,为进一步集成AI以优化交易执行奠定了基础。
# Columns: ['<DATE>', '<TIME>', '<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>'] data = pd.read_csv('/home/int_junkie/Documents/DataVisuals/AI inside MQL5/XAUUSD_H1.csv', delimiter='\t') # Combine DATE and TIME into a single DATETIME column data['DATETIME'] = pd.to_datetime(data['<DATE>'] + ' ' + data['<TIME>']) # Drop the original DATE and TIME columns data.drop(columns=['<DATE>', '<TIME>'], inplace=True) # Step 1: Detect order blocks order_blocks = get_order_blocks(data) # Step 2: Simulate trading based on order blocks trades = simulate_trading(data, order_blocks)
合并数据后,为保持数据集的整洁性,原始的日期和时间列会被移除。接下来,脚本调用get_order_blocks(data)函数,该函数会扫描价格数据,以识别潜在的看涨和看跌订单块,这些订单块是价格可能发生重大反转或机构下大单的关键区域。一旦检测到订单块,simulate_trading(data, order_blocks)函数便会运行回测,检查市场条件是否触发了这些订单块,并根据预先设定的入场、止盈和止损规则执行模拟的买入或卖出交易。基本上,我们是利用过去的价格行为数据来评估该交易策略的有效性。
# Features: Historical OHLC data # Labels: Buy (1), Sell (-1), Hold (0) labels = [] for i, row in data.iterrows(): label = 0 # Hold by default for trade in trades: if trade['time'] == row['DATETIME']: label = 1 if trade['direction'] == 'buy' else -1 labels.append(label) data['label'] = labels # Step 4: Train LSTM model (example using Keras) from keras.models import Sequential from keras.layers import LSTM, Dense # Prepare data for LSTM def create_sequences(data, seq_length): X, y = [], [] for i in range(len(data) - seq_length): X.append(data.iloc[i:i + seq_length][['<OPEN>', '<HIGH>', '<LOW>', '<CLOSE>']].values) y.append(data.iloc[i + seq_length]['label']) return np.array(X), np.array(y) seq_length = 50 # Sequence length for LSTM X, y = create_sequences(data, seq_length) # Build LSTM model model = Sequential() model.add(LSTM(50, input_shape=(seq_length, 4))) # 4 features: open, high, low, close model.add(Dense(1, activation='tanh')) # Output: -1 (sell), 0 (hold), 1 (buy) model.compile(optimizer='adam', loss='mse') # Train the model model.fit(X, y, epochs=20, batch_size=32) # Save the model model.save('lstm_trading_model.h5')
输出:


此代码段利用历史价格数据训练一个LSTM模型,以预测交易决策。数据集被标记为三种可能的信号:1(买入)、-1(卖出)或0(持有)。标签的分配方式是遍历历史时间戳——如果在特定时间发生了交易,标签则反映其方向;否则,默认为0(无操作)。这样就创建了一个监督学习框架,模型通过该框架学习将过去的市场数据序列与后续的交易行为相关联。
对于LSTM训练,数据被重新构建为50步序列的OHLC(开盘价、最高价、最低价、收盘价)值。每个序列作为输入,用于预测下一个时间步的标签。模型架构包括:
- 一个LSTM层 (50个单元),用于分析4个特征输入(OHLC)中的时间模式。
- 一个带有tanh激活函数的全连接输出层 ,用于生成-1到1之间的预测值。
模型使用Adam优化器和均方误差(MSE)损失函数训练20个周期 。训练完成后,模型被保存为lstm_trading_model.h5,随时可用于交易策略中生成实时信号。
部署模型
随着LSTM模型现已序列化为.h5文件,接下来的阶段是在MQL5生态系统中将其投入实际使用。这需要在Python的机器学习框架和MQL5的本地交易基础设施之间创建一个互操作层,因为MQL5不支持直接集成Python。一个标准的解决方案是部署一个基于Python的微服务(使用轻量级框架,如Flask或FastAPI)来托管训练好的模型。该服务器充当预测端点:从MQL5接收格式化的市场数据,执行基于LSTM的推理,并实时返回交易信号。在MQL5方面,您的EA利用WebRequest() API将顺序价格数据传输到该端点,并解析其JSON响应,从而将原始预测转化为可执行的操作。
成功实现这一桥梁后,重点将转向实时策略集成。EA将自主地将精选的数据批次(例如,50根K线的OHLC序列)以可配置的间隔传输到推理服务器。Python服务对该输入进行预处理,通过LSTM架构执行张量运算,并返回带有置信度指标的概率性交易指令(买入/卖出/持有)。这些信号随后被输入到EA的决策流程中,在那里它们与预定义的风险参数(动态止损阈值、盈利目标比率和头寸规模算法)相结合,以执行受管理的交易。为了降低运营风险,建议实施分阶段推出策略:在沙盒化的模拟环境中验证系统,持续监控推理延迟和模型漂移,并嵌入应急协议(例如,在服务器停机或预测异常时恢复到基于技术指标的策略),以在异常情况下维持系统功能。
结论
在这个项目中,我们选取了一款现有的MQL5交易策略,并通过集成基于LSTM神经网络的AI决策流程对其进行了增强。我们首先将MQL5策略的核心逻辑翻译成Python代码,利用历史OHLC数据复现其交易行为。接着,我们识别出关键的交易信号,如订单块,并模拟交易以生成用于训练AI的有标签数据。随后,我们利用历史价格数据序列对LSTM模型进行训练,以预测下一个市场走势应为买入、卖出还是持有。最后,我们将训练好的模型保存为.h5文件,以便部署用于实时或半自动化交易。
将AI集成到传统的基于规则的策略中,为交易者带来了强大的优势。与遵循固定条件的静态逻辑不同,LSTM模型能够学习复杂的价格行为,并随时间推移适应不断变化的市场动态。这样使得该策略更加灵活,可能会更加准确,且不易受到刚性系统可能产生的错误信号的影响。交易者受益于一个集两者之长的系统:既具备技术规则的结构性和可靠性,又拥有机器学习的适应性和学习能力。
这种混合方法不仅改善了入场和出场决策,还为实时交易中更智能、数据驱动的风险管理打开了大门。与遵循固定条件的静态逻辑不同,LSTM模型能够学习复杂的价格行为,并随时间推移适应不断变化的市场动态。这样使得该策略更加灵活,可能会更加准确,且不易受到刚性系统可能产生的错误信号的影响。交易者受益于一个集两者之长的系统:既具备技术规则的结构性和可靠性,又拥有机器学习的适应性和学习能力。这种混合方法不仅提升了入场和出场决策的质量,还为实时交易中更智能、基于数据的风险管理提供了可能。
| 文件名 | 描述 |
|---|---|
| FIB_OB.mq5 | 包含原始MQL5策略文件 |
| FIB_OB to AI.ipynb | 包含用于转换策略逻辑、训练模型并保存模型的笔记本文件 |
| XAUUSD_H1.csv | 包含黄金兑美元历史价格数据的文件 |
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16973
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
价格行为分析工具包开发(第二十部分):外部资金流(4)——相关性路径探索器
新手在交易中的10个基本错误
市场模拟(第七部分):套接字(一)
是否可以让人工智能持续学习?