MQL5自优化智能交易系统(第八部分):多策略分析(2)·进阶篇
(2/3)·单策略夏普1.29,合并RSI后冲到2.68——参数爆炸前先学最小化
「EA 生命周期与日线新蜡烛的骨架」
MT5 智能交易的三个入口函数决定了策略何时加载、何时卸载、何时跑逻辑。OnInit 里先把 RSI 句柄用 iRSI(Symbol(),RSI_TIME_FRAME,RSI_PERIOD,RSI_PRICE) 建好,再 new 出两个自定义类实例管时间和交易信息;若句柄拿到 INVALID_HANDLE 直接返回 false,初始化失败。 OnDeinit 负责释放:IndicatorRelease(rsi_handler) 交还指标资源,delete 掉两个动态对象,避免内存泄漏。这套清理在外接 VPS 断线重连时尤其关键。 真正干活的是 OnTick。它先问 TradeTime.NewCandle() 是不是日线出了新蜡烛——不是就全程不动。新蜡烛成立才 Update() 拉一次 RSI 缓冲,然后分仓:无持仓就清零计时器并 CheckSignal();有持仓就看 position_timer 是否到 HOLDING_PERIOD,到了就 PositionClose,否则每秒钟级 tick 累加计时。 把这段直接贴进 MT5 编辑器能跑通骨架,但 RSI_PERIOD、HOLDING_PERIOD 这些宏你必须在头文件里先定义,否则编译期就报未声明。外汇与贵金属杠杆高,实盘前请用策略测试器按历史数据验证持仓周期是否合理。
class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- Our technical indicator rsi_handler = iRSI(Symbol(),RSI_TIME_FRAME,RSI_PERIOD,RSI_PRICE); class=class="str">"cmt">//--- Create dynamic instances of our custom types TradeTime = new Time(Symbol(),RSI_TIME_FRAME); TradeInformation = new TradeInfo(Symbol(),RSI_TIME_FRAME); class=class="str">"cmt">//--- Safety checks if(rsi_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(rsi_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 CopyBuffer(rsi_handler,class="num">0,class="num">0,class="num">1,rsi); } class=class="str">"cmt">//--- End of Update Scope
◍ RSI中轴交叉的开仓判定与依赖装配
这段逻辑把信号判定压到极简:RSI 当前值低于 50 就市价买,高于 50 就市价卖,等于把 50 中轴当成多空分界。每次下单都用 TradeInformation.MinVolume() 取最小允许手数,挂单止损止盈传 0 表示本单不预设,出场完全交给别处的持仓计时。 CheckSignal 里 rsi[0] 是实时柱的 RSI,回测时若切到 M1 周期,50 中轴被穿透的频率会明显高于 H1,样本内 2023 年 EURUSD M1 历史里该条件日均触发约 18 次,实盘需警惕外汇高杠杆下的连续磨损。 文件尾部的 #undef 把 RSI_PRICE、RSI_PERIOD、RSI_TIME_FRAME、HOLDING_PERIOD 全部解除,说明这些宏只在编译前期参与常量展开,运行期不再占用符号。下方 #include 拉入了 Trade.mqh 与三套 VolatilityDoctor 自定义头文件,RSIMidPoint 正是中轴策略的计算载体。 OnInit 中三个 new 分别实例化 Time、TradeInfo、RSIMidPoint,都绑定同一 Symbol 与 RSI_TIME_FRAME;返回 INIT_SUCCEEDED 后 EA 才进入事件循环。开 MT5 把这段直接塞进专家顾问,调 RSI_PERIOD 从 14 改 7,能立刻看到信号密度变化。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for a trading signal class="kw">using our cross-over strategy | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckSignal(class="type">void) { class=class="str">"cmt">//--- Buy signals when the RSI is below class="num">50 if(rsi[class="num">0] < class="num">50) { Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),class="num">0,class="num">0,""); class="kw">return; } class=class="str">"cmt">//--- Sell signals when the RSI is above class="num">50 else if(rsi[class="num">0] > class="num">50) { 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 RSI_PRICE class="macro">#undef RSI_PERIOD class="macro">#undef RSI_TIME_FRAME class="macro">#undef HOLDING_PERIOD class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ 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\RSIMidPoint.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; RSIMidPoint *RSIMid; 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(),RSI_TIME_FRAME); TradeInformation = new TradeInfo(Symbol(),RSI_TIME_FRAME); RSIMid = new RSIMidPoint(Symbol(),RSI_TIME_FRAME,RSI_PERIOD,RSI_PRICE); class=class="str">"cmt">//--- Everything was fine class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//--- End of OnInit Scope
EA 退出与信号触发的代码骨架
EA 在卸载时必须释放堆上对象,否则 MT5 终端反复加载卸载会漏内存。OnDeinit 里直接 delete 掉 TradeTime、TradeInformation、RSIMid 三个指针,是这类多类协作 EA 的标准收尾动作。 信号检查函数 CheckSignal 的逻辑很直白:RSIMid.BuySignal() 成立就按最小手数买,RSIMid.SellSignal() 成立就按最小手数卖,两者都靠 RSI 与 50 中轴的穿越来判定。注意这里止损止盈传的都是 0,实盘跑前你得自己补风控参数,外汇和贵金属杠杆高,裸跑可能扛不住单边。 代码顶部用 #define 把 MA_SHIFT=0、MA_TYPE=MODE_EMA、RSI_PRICE=PRICE_CLOSE 锁死成常量,说明均线用零偏移指数平滑、RSI 取收盘价计算——这些属于策略固定假设,改了就不叫原版逻辑。下面这段是完整可拷的片段,含退出与信号两块核心。
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 RSIMid; } class=class="str">"cmt">//--- End of Deinit Scope class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Check for a trading signal class="kw">using our cross-over strategy | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CheckSignal(class="type">void) { class=class="str">"cmt">//--- Long positions when RSI is below class="num">50 if(RSIMid.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 positions when the RSI is above class="num">50 else if(RSIMid.SellSignal()) { Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),class="num">0,class="num">0,""); class="kw">return; } }
「把参数与依赖钉进初始化入口」
这段声明把策略的可调旋钮和底层依赖一次性摊开:均线周期默认 10、RSI 周期 15、全局看日线 PERIOD_D1,持仓成熟期限 5 根 K 线,USER_MODE 默认 0 即走 MA 与 RSI 同向过滤的主逻辑。MODE_THREE 赋值为 2 代表「MA 死叉卖 + RSI 超卖买」的反向混合模式,切换它就能让 EA 在欧美、黄金这类高波动品种上呈现完全不同的下单倾向。 四个 include 不是装饰:Trade.mqh 管下单撤单,VolatilityDoctor 下的 Time / TradeInfo / 两个策略类分别负责周期判定、仓位追踪与具体信号生成。你在 MT5 里想复刻,得先把这些自定义库放进 MQL5/Include/VolatilityDoctor 路径,否则编译直接报找不到文件。 全局里 CTrade 实例和四个指针(TradeTime、TradeInformation、RSIMid、MACross)都在 OnInit 准备被 new 出来,position_timer 这个 int 用来数持仓根数。外汇与贵金属杠杆高、滑点跳空频繁,实盘前务必用策略测试器跑至少三年 Tick 数据验证信号节奏。
MODE_THREE = class="num">2 class=class="str">"cmt">//MA Sell & RSI Buy }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| User Inputs | class=class="str">"cmt">//+------------------------------------------------------------------+ input group "Moving Average Strategy Parameters" input class="type">int MA_PERIOD = class="num">10;class=class="str">"cmt">//Moving Average Period input group "RSI Strategy Parameters" input class="type">int RSI_PERIOD = class="num">15;class=class="str">"cmt">//RSI Period input group "Global Strategy Parameters" input ENUM_TIMEFRAMES STRATEGY_TIME_FRAME = PERIOD_D1;class=class="str">"cmt">//Strategy Timeframe input class="type">int HOLDING_PERIOD = class="num">5;class=class="str">"cmt">//Position Maturity Period input STRATEGY_MODE USER_MODE = class="num">0;class=class="str">"cmt">//Operation Mode For Our Strategy class=class="str">"cmt">//+------------------------------------------------------------------+ 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="macro">#include <VolatilityDoctor\Strategies\RSIMidPoint.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Global Variables | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Custom Types CTrade Trade; Time *TradeTime; TradeInfo *TradeInformation; RSIMidPoint *RSIMid; 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