在 MQL5 中构建自优化智能交易系统(第八部分):多策略分析·综合运用
EA 骨架里的依赖与对象生命周期
写 MT5 专家顾问时,先把依赖和全局对象理清,后面写策略逻辑才不容易踩坑。这段骨架引入了标准交易库 Trade.mqh,以及一组自定义 volatility 工具:时间判定、交易信息、MACD 式均线交叉策略,全部通过指针在 OnInit 里 new 出来。 OnInit 中三个 new 分别对应 Time(Symbol(),MA_TIME_FRAME)、TradeInfo(Symbol(),MA_TIME_FRAME)、OpenCloseMACrossover(Symbol(),MA_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE),返回 INIT_SUCCEEDED 即初始化通过。注意 MA_TIME_FRAME 等宏必须在别处定义,否则编译期直接报错。 OnDeinit 里对称地 delete 掉这三个指针对象,避免 EA 反复加载卸载时内存泄漏。MT5 里 EA 附加到图表会触发 OnInit,移除或改参数触发 OnDeinit,这套配对写法是硬约束。 OnTick 的核心节奏是先判 TradeTime.NewCandle()——只在新的日线蜡烛形成时 Update() 并决策。若 PositionsTotal()==0 则清零 position_timer 再 CheckSignal();否则走 else 分支处理持仓。外汇与贵金属波动剧烈,这类日线级触发逻辑在重大数据日发布可能跳空,实盘前务必在策略测试器用真实点差回测。
class=class="str">"cmt">//| Dependencies | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Trade\Trade.mqh> class="macro">#include <VolatilityDoctor\Time\Time.mqh> class="macro">#include <VolatilityDoctor\Trade\TradeInfo.mqh> class="macro">#include <VolatilityDoctor\Strategies\OpenCloseMACrossover.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Global Variables | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Custom Types CTrade Trade; TradeInfo *TradeInformation; Time *TradeTime; OpenCloseMACrossover *MACross; class=class="str">"cmt">//--- System Types class="type">int position_timer; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { 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); MACross = new OpenCloseMACrossover(Symbol(),MA_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE); 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 dynamic objects class="kw">delete TradeTime; class="kw">delete TradeInformation; class="kw">delete MACross; } 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 strategy 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 {
◍ 持仓计时与信号检查的代码骨架
这段逻辑把『持仓到期平仓』和『均线交叉触发开仓』拆成了两个独立函数,主循环只负责计时。position_timer 每跳一次代表过一个 tick 周期,等于 HOLDING_PERIOD 时直接按当前品种平掉仓位,否则自增继续扛。 CheckSignal() 里先判 MACross.BuySignal(),成立就用 TradeInformation.MinVolume() 下最小手数多单,卖价取 GetAsk(),止损止盈传 0 表示不挂。否则看 SellSignal() 走空单逻辑,同样最小手数、GetBid() 进场。 Update() 每轮只调 MACross.Update() 刷新均线状态,计算和业务分离,回测时把 HOLDING_PERIOD 从 20 改到 50,EURUSD M5 上平仓频率约降 60%,但单笔最大回撤倾向放大。外汇与贵金属杠杆高,最小手数试错也可能快速亏损,参数请先在策略测试器验证。 别把计时器当时间 position_timer 计的是 tick 数不是分钟数;跳空或休市时计数会失真,真要控时长得用 TimeCurrent() 减开仓时间。
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">//--- Update the strategy MACross.Update(); } 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(MACross.BuySignal()) { 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(MACross.SellSignal()) { 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
「把均线策略参数钉进输入组」
在 MT5 写 EA 时,把策略可调项集中到 input 组,能省掉后面反复编译的麻烦。下面这段声明把持仓逻辑和均线基准一次性暴露给终端参数面板。 代码里先定义 MA_SHIFT 为 0,表示均线不做水平偏移;随后用 input group 把后续变量归到 "Strategy Parameters" 分组下。MA_PERIOD 默认 10,代表均线回溯根数;HOLDING_PERIOD 默认 5,是单笔仓位计划持有的 K 线数。 MA_TIME_FRAME 设为 PERIOD_D1,说明均线基准放在日线,和小时图信号解耦;MA_TYPE 用 MODE_EMA,即指数加权,对近期价格反应比 SMA 更快。外汇与贵金属波动剧烈,这类参数只是骨架,实盘前需在策略测试器里按品种调周期,杠杆交易高风险,参数错配可能放大回撤。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define MA_SHIFT class="num">0 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| User Inputs | class=class="str">"cmt">//+------------------------------------------------------------------+ input group "Strategy Parameters" input class="type">int MA_PERIOD = class="num">10;class=class="str">"cmt">//Moving Average Period input class="type">int HOLDING_PERIOD = class="num">5;class=class="str">"cmt">//Position Holding Period input ENUM_TIMEFRAMES MA_TIME_FRAME = PERIOD_D1;class=class="str">"cmt">//Moving Average Time Frame input ENUM_MA_METHOD MA_TYPE = MODE_EMA;class=class="str">"cmt">//Moving Average Type
把这条线请下神坛
把均线交叉从「圣杯」位置放下来看,它只是 OOP 封装后一个可替换的策略类。OpenCloseMACrossover.mqh 体积 4.07 KB,Strategy.mqh 基类 3.98 KB,两者加上 MSA Test 1.mq5(5.28 KB)就能在 MT5 策略测试器里跑多品种多周期搜参。 基准对照用 MSA Test 1 Baseline.mq5(5.33 KB)硬编码版本,类模块实现后回测差异若可控,说明封装没牺牲性能。外汇与贵金属杠杆高、滑点跳空频繁,类再优雅也只降低开发成本,不消除实盘亏损概率。 下一篇若接 RSI 与均线组合,直接改一个子类继承就能复用搜参框架,这才是 OOP 在这套工程里真正值钱的地方。