在 MQL5 中构建自优化智能交易系统(第八部分):多策略分析·进阶篇
(2/3)· 把均线、RSI、威廉指标封进三个类,让程序定期只跑最盈利的一种而非全开
◍ 用收盘价与开盘价均线交叉做多空判定
在 MT5 里把策略拆成类,最实用的落点是两个虚函数:BuySignal 与 SellSignal。它们不负责下单,只回答「现在该不该多 / 该不该空」这一个布尔问题。 开仓逻辑很直白:当收盘价均线读数高于开盘价均线,多头信号成立;反过来开盘价均线在上,则空头信号成立。代码里用 ma_array[0] 存收盘价均线、ma_array[1] 存开盘价均线,下标顺序写错会导致信号完全反向。 Update 方法负责把指标缓冲区刷进数组,并且用乘法判 0:若两条均线当前值相乘不等于 0 才返回 true。任一为 0 说明数据没拉到,直接返回 false 避免脏信号。外汇与贵金属杠杆高,这类交叉信号在震荡市可能频繁假突破,实盘前务必在策略测试器跑历史数据验证。 构造器里 new 出两个 MA 对象,分别绑 PRICE_CLOSE 与 PRICE_OPEN,周期、偏移、平滑方式由外部传入。改一个参数就能切换快慢线风格,建议先拿 EURUSD 的 M15 跑一遍看信号密度。
class="kw">virtual class="type">bool BuySignal(class="type">void); class="kw">virtual class="type">bool SellSignal(class="type">void); class="type">bool OpenCloseMACrossover::BuySignal(class="type">void) { class=class="str">"cmt">//--- Our buy signal is generated if the close moving average is above the open. class="kw">return(ma_array[class="num">0].GetCurrentReading()>ma_array[class="num">1].GetCurrentReading()); } class="type">bool OpenCloseMACrossover::SellSignal(class="type">void) { class=class="str">"cmt">//--- Our sell signal is generated if the open moving average is above the close. class="kw">return(ma_array[class="num">0].GetCurrentReading()<ma_array[class="num">1].GetCurrentReading()); } class="type">bool OpenCloseMACrossover::Update(class="type">void) { class=class="str">"cmt">//--- Copy indicator readings class=class="str">"cmt">//--- We will always get the buffer size from the parent class ma_array[class="num">0].SetIndicatorValues(Strategy::GetIndicatorBufferSize(),true); ma_array[class="num">1].SetIndicatorValues(Strategy::GetIndicatorBufferSize(),true); class=class="str">"cmt">//--- Make sure neither of the indicator values equal class="num">0 if((ma_array[class="num">0].GetCurrentReading() * ma_array[class="num">1].GetCurrentReading()) != class="num">0) class="kw">return(true); class=class="str">"cmt">//--- If one/both indicator values equal class="num">0, something went wrong. class="kw">return(false); } OpenCloseMACrossover::OpenCloseMACrossover(class="type">class="kw">string symbol,ENUM_TIMEFRAMES time_frame,class="type">int period,class="type">int shift,ENUM_MA_METHOD ma_mode) { class=class="str">"cmt">//--- Create two instances of our moving average indiator objects ma_array[class="num">0] = new MA(symbol,time_frame,period,shift,ma_mode,PRICE_CLOSE); ma_array[class="num">1] = new MA(symbol,time_frame,period,shift,ma_mode,PRICE_OPEN); class=class="str">"cmt">//--- Give feedback
「析构与全局常量的落地写法」
在 MT5 用类封装策略时,析构函数里必须手动释放 new 出来的指标对象,否则 EA 反复加载卸载会漏内存。下面这段把两条均线句柄数组里的元素逐个 delete,再打一行日志确认卸载干净:
OpenCloseMACrossover::~OpenCloseMACrossover()
{
delete ma_array[0];
delete ma_array[1];
Print("Strategy deinitialized correctly. Goodbye");
}
实盘外汇与贵金属波动大、杠杆高,对象没清干净可能导致后续品种切换时指标句柄错乱,建议你在策略测试器里反复加载/移除 EA 观察日志。
常量定义直接决定策略骨架。示例把均线类型锁成 MODE_EMA、周期 10、日线 PERIOD_D1、偏移 0,并额外给了持仓周期 HOLDING_PERIOD 为 5 根 K 线。改这几个宏,回测曲线可能明显不同,动手前先记一下原值方便比对。
全局区引入了 CTrade 和系统外的 TradeInfo、Time 两个自定义指针,以及 ma_open[]/ma_close[] 双缓冲区与对应句柄。外汇黄金高风险,句柄取数为 -1 时若不强判就下单,会触发无效交易报错。
OpenCloseMACrossover::~OpenCloseMACrossover() { class=class="str">"cmt">//--- Delete the custom objects we made class="kw">delete ma_array[class="num">0]; class="kw">delete ma_array[class="num">1]; class=class="str">"cmt">//--- Give feedback Print("Strategy deinitialized correctly. Goodbye"); } class="macro">#define MA_TYPE MODE_EMA class="macro">#define MA_PERIOD class="num">10 class="macro">#define MA_TIME_FRAME PERIOD_D1 class="macro">#define MA_SHIFT class="num">0 class="macro">#define HOLDING_PERIOD class="num">5 class="macro">#include <Trade\Trade.mqh> class="macro">#include <VolatilityDoctor\Trade\TradeInfo.mqh> class="macro">#include <VolatilityDoctor\Time\Time.mqh> CTrade Trade; TradeInfo *TradeInformation; Time *TradeTime; class="type">class="kw">double ma_open[],ma_close[]; class="type">int ma_open_handler,ma_close_handler; intn position_timer;
EA 生命周期与每日新蜡烛的调度骨架
MT5 智能交易系统的骨架不在算信号,而在把指标句柄、自定义对象和新旧蜡烛节奏管顺。OnInit 里同时挂了收盘 MA 与开盘 MA 两个 iMA 句柄,并 new 了 Time 与 TradeInfo 两个动态实例,任何句柄拿到 INVALID_HANDLE 就直接回 false,避免后续空指针崩在实时 tick 上。 OnDeinit 容易被忽略:IndicatorRelease 释放两个 MA 句柄,delete 清掉 TradeTime 和 TradeInformation。不收干净的话,切换周期或重加载 EA 可能残留内存对象,MT5 终端日志里偶尔会报句柄泄漏。 真正的节奏控制器在 OnTick:用 TradeTime.NewCandle() 拦每日新蜡烛,只在此时 Update() 并判断 PositionsTotal()==0 才去 CheckSignal()。若已有持仓,则靠 position_timer 累加,到 HOLDING_PERIOD 才 Trade.PositionClose(Symbol())——这意味着持仓是以「新蜡烛根数」计龄,而不是秒数。 把 HOLDING_PERIOD 从默认改到 3 或 5,在 XAUUSD 的 M15 上回看,会发现平仓触发点从日线切换漂移到了第 3~5 根 M15 收盘,外汇与贵金属杠杆高,这种计时方式对滑点和点差敏感,建议开 MT5 用策略测试器跑一根品种验证计时逻辑。
class="type">int OnInit() { class=class="str">"cmt">//--- Our technical indicators ma_close_handler = iMA(Symbol(),MA_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE,PRICE_CLOSE); ma_open_handler = iMA(Symbol(),MA_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE,PRICE_OPEN); class=class="str">"cmt">//--- Create dynamic instances of our custom types TradeTime = new Time(Symbol(),MA_TIME_FRAME); TradeInformation = new TradeInfo(Symbol(),MA_TIME_FRAME); class=class="str">"cmt">//--- Safety checks if(ma_close_handler == INVALID_HANDLE) class="kw">return(false); if(ma_open_handler == INVALID_HANDLE) class="kw">return(false); class=class="str">"cmt">//--- Everything was fine class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//--- End of OnInit Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">//--- Delete the indicators and dynamic objects IndicatorRelease(ma_close_handler); IndicatorRelease(ma_open_handler); class="kw">delete TradeTime; class="kw">delete TradeInformation; } class=class="str">"cmt">//--- End of Deinit Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- Check if a new daily candle has formed if(TradeTime.NewCandle()) { class=class="str">"cmt">//--- Update our technical indicators Update(); class=class="str">"cmt">//--- If we have no open positions if(PositionsTotal() == class="num">0) { class=class="str">"cmt">//--- Reset the position timer position_timer = class="num">0; class=class="str">"cmt">//--- Check for a trading signal CheckSignal(); } class=class="str">"cmt">//--- Otherwise else { class=class="str">"cmt">//--- The position has reached maturity if(position_timer == HOLDING_PERIOD) Trade.PositionClose(Symbol()); class=class="str">"cmt">//--- Otherwise keep holding else position_timer++; } } } class=class="str">"cmt">//--- End of OnTick Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Update our technical indicators | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void Update(class="type">void) { class=class="str">"cmt">//--- Call the CopyBuffer method to get updated indicator values
◍ 信号判定与常量回收的收口写法
把两条均线的最新值比大小,就能直接驱动下单逻辑。下面这段 CheckSignal 里,ma_close[0] 大于 ma_open[0] 就市价买最小手数,反之就卖最小手数,return 提前退出避免重复触发。 CopyBuffer(ma_close_handler,0,0,1,ma_close); CopyBuffer(ma_open_handler,0,0,1,ma_open); 这两行负责把指标句柄里第 0 根柱的缓冲拉进数组,每次刷新只取 1 个值,开销极低。 策略用的 EMA 周期固定为 10、时间框架 PERIOD_D1、偏移 0,持仓参考周期 HOLDING_PERIOD 设为 5 根日线。外汇与贵金属杠杆高,这类裸叉策略在震荡日线里可能连续假突破,实盘前务必在 MT5 用最小手数跑历史数据验证。 #undef 把 MA_PERIOD、MA_SHIFT、MA_TIME_FRAME、MA_TYPE、HOLDING_PERIOD 全部撤掉,防止常量名污染后续编译单元;这种收口习惯在把多个 EA 拼进同一项目时值得沿用。
CopyBuffer(ma_close_handler,class="num">0,class="num">0,class="num">1,ma_close); CopyBuffer(ma_open_handler,class="num">0,class="num">0,class="num">1,ma_open); } class=class="str">"cmt">//--- End of Update Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for a trading signal using our cross-over strategy | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckSignal(class="type">void) { class=class="str">"cmt">//--- Long positions when the close moving average is above the open if(ma_close[class="num">0] > ma_open[class="num">0]) { Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),class="num">0,class="num">0,""); class="kw">return; } class=class="str">"cmt">//--- Otherwise class="type">short else if(ma_close[class="num">0] < ma_open[class="num">0]) { Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),class="num">0,class="num">0,""); class="kw">return; } } class=class="str">"cmt">//--- End of CheckSignal Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Undefine system constants | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#undef MA_PERIOD class="macro">#undef MA_SHIFT class="macro">#undef MA_TIME_FRAME class="macro">#undef MA_TYPE class="macro">#undef HOLDING_PERIOD class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| MSA Test class="num">1.mq5 | class=class="str">"cmt">//| Gamuchirai Ndawana | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Gamuchirai Ndawana" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Define system constants | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define MA_TYPE MODE_EMA class="macro">#define MA_PERIOD class="num">10 class="macro">#define MA_TIME_FRAME PERIOD_D1 class="macro">#define MA_SHIFT class="num">0 class="macro">#define HOLDING_PERIOD class="num">5 class=class="str">"cmt">//+------------------------------------------------------------------+