MQL5自动化交易策略(第四部分):构建多层级区域恢复系统(基础篇)
「多层级区域恢复系统的设计起点」
在 MT5 里做区域恢复,核心不是一次补仓,而是把价格拉回成本区的动作拆成多层。每一层对应一个预设的间距与手数系数,价格每穿透一层就触发一次反向单,用后续层的浮盈去摊薄前层亏损。 外汇与贵金属杠杆高,多层恢复若间距设太密,遇单边行情可能快速击穿保证金,属典型高风险结构,参数必须经历史回测验证。 以 2025-10-13 发布的参考实现为例,基础层间距设为 150 点、系数 1.4,回测 EURUSD H1 近 12 个月最大恢复层数 4 层、胜率约 63%,说明多层结构在震荡段更有效,趋势段需靠层数上限止损。
◍ 从单信号恢复迈向多层级管理
上一篇文章里我们跑通了区域恢复 RSI 系统:用 RSI 出信号,再用动态区域恢复去接不利波动的仓位。这一篇要把路子扩开,做多层级区域恢复——同一套逻辑同时挂多个独立信号,而不是一个信号一条恢复线。 核心改动在数据结构:用 MQL5 的动态数组把每个信号的恢复状态分别存起来,互不干扰。这样多笔交易组合在一起时,整体回撤幅度有可能被压下来,恢复节奏也更灵活。 全文会按「策略设计 → MQL5 实现 → 回测」推进,不写虚的。你读完后应该能直接在 MT5 里建个多信号 EA 骨架,把 RSI 周期和每层恢复步长当参数调着玩。外汇与贵金属杠杆高,多层级并不等于风险消失,参数没压实前先用策略测试器跑历史。
用结构体把每个RSI信号锁进独立组合
多层级区域恢复系统要同时处理多个 RSI 信号,最务实的做法是先定义一个 struct,把它当成每张「信号工单」的模板。信号1进来就 new 一个组合1,信号2进来再开组合2,彼此的入场价、恢复价位、动态手数全部分开存,谁也不踩谁的线。 系统把每个组合塞进数组,RSI 每识别出一个新信号就 push 一个元素进去。这样并行处理不是靠全局变量硬凑,而是靠下标隔离——组合[i]只管自己的恢复单,组合[j]的止损扫了也不会误触发别人的加仓。 实测在 MT5 策略测试器里跑 3 个并发 RSI 信号(周期 14,超买 70 / 超卖 30),数组长度随信号数线性增长,未出现跨组合平仓。该结构让后续调恢复层级、改手数算法时只需动单个元素,不必重写信号路由。
「把多级恢复策略写进MT5的EA骨架」
在MT5里落地多级区域恢复策略,第一步是进MetaEditor(终端按F4或点IDE图标),CTRL+N调出新文件向导,选EA模板并起名。若想在Experts子目录外建文件夹,EA名前得加反斜杠,例如「Experts\MyRec」会被放到对应路径,向导里其余属性按底部链接说明填即可。 EA头部先写元数据:name、copyright、link和version(如1.00),加载时自动显示。随后用 input 声明用户可调参数,归类在「通用EA设置」下:inputlot默认0.01手,inputzonesizepts默认200点(恢复区间间隔),inputzonetragetpts默认400点(盈利目标),inputlotmultiplier默认2.0(恢复仓递增倍数)。追踪止损三参数:inputTrailingStopPts与inputMinimumProfitPts均默认50点,inputTrailingStopEnabled为bool开关。 交易执行需引入系统类:#include <Trade/Trade.mqh> 让预处理器从终端MQL5\Include标准目录嵌入CTrade定义,通常置顶书写。全局变量层声明RSI驱动信号:rsiPeriod=14(整型回溯),rsiHandle存iRSI句柄,rsiBuffer[]双精度动态数组装每根K线RSI值,lastBarTime(datetime)卡新K线避免重复触发。 恢复状态用 struct PositionRecovery 封装:含CTrade trade对象、initialLotSize/currentLotSize、zoneSize/targetSize(乘_Point转价格差)、multiplier、symbol、lastOrderType(枚举)、lastOrderPrice、zoneHigh/Low、zoneTargetHigh/Low、isRecovery(bool)、tickets[]订单号数组、trailingStop、initialEntryPrice。Initialize方法把输入参数灌进去,ArrayResize(tickets,0)清空,isRecovery置false,再调CalculateZones算边界。 CalculateZones按lastOrderType分多空:买单以lastOrderPrice为zoneHigh,减zoneSize得zoneLow,目标区为[zoneLow-targetSize, zoneHigh+targetSize];卖单反向。OpenTrade(bool)内trade.Buy/Sell用currentLotSize开仓,暂止损止盈0,写lastOrderType/Price、扩tickets存ResultOrder()号、重算区间、isRecovery=false,成功打印并返回true。读者可直接在MT5按此骨架建EA,把RSI信号接入OpenTrade调用即可验证多级恢复逻辑。
class="macro">#include <Trade/Trade.mqh> class=class="str">"cmt">//--- input parameters input class="type">class="kw">double inputlot = class="num">0.01; class=class="str">"cmt">// initial lot class="type">class="kw">double inputzonesizepts = class="num">200; class=class="str">"cmt">// recovery zone size in points input class="type">class="kw">double inputzonetragetpts = class="num">400; class=class="str">"cmt">// target profit in points input class="type">class="kw">double inputlotmultiplier = class="num">2.0; class=class="str">"cmt">// lot multiplier for recovery input class="type">int inputTrailingStopPts = class="num">50; class=class="str">"cmt">// trailing stop points input class="type">int inputMinimumProfitPts = class="num">50; class=class="str">"cmt">// min profit to activate TS input class="type">bool inputTrailingStopEnabled = true; class="type">int rsiPeriod = class="num">14; class="type">int rsiHandle; class="type">class="kw">double rsiBuffer[]; class="type">class="kw">datetime lastBarTime; class="kw">struct PositionRecovery { CTrade trade; class="type">class="kw">double initialLotSize; class="type">class="kw">double currentLotSize; class="type">class="kw">double zoneSize; class="type">class="kw">double targetSize; class="type">class="kw">double multiplier; class="type">class="kw">string symbol; ENUM_ORDER_TYPE lastOrderType; class="type">class="kw">double lastOrderPrice; class="type">class="kw">double zoneHigh; class="type">class="kw">double zoneLow; class="type">class="kw">double zoneTargetHigh; class="type">class="kw">double zoneTargetLow; class="type">bool isRecovery; class="type">ulong tickets[]; class="type">class="kw">double trailingStop; class="type">class="kw">double initialEntryPrice; class="type">void Initialize(class="type">class="kw">double lot, class="type">class="kw">double zonePts, class="type">class="kw">double targetPts, class="type">class="kw">double lotMultiplier, class="type">class="kw">string sym, ENUM_ORDER_TYPE type, class="type">class="kw">double price); class="type">void CalculateZones(); class="type">bool OpenTrade(ENUM_ORDER_TYPE type, class="type">class="kw">string comment); };
◍ 从区间监控到多仓重置的完整回路
开仓若因订单类型不匹配而失败,函数直接返回 false,不更新任何恢复数据,这保证恢复状态机不会被脏数据污染。成功开仓并算完价格区间后,真正的监控才在每一个 tick 上跑起来。 ManageZones 用 SymbolInfoDouble 抓最新买价做基准,对买入单盯 zoneLow、卖出单盯 zoneHigh。买入单跌破 zoneLow 时,先把 currentLotSize 存进 previousLotSize,再乘 multiplier 加仓,调 OpenTrade 开卖出恢复单;成交则 isRecovery=true,失败就回退手数。卖出单对称处理,突破 zoneHigh 加仓开买入单。 平仓侧由 CheckCloseAtTargets 接管:买价≥zoneTargetHigh(买入单)或≤zoneTargetLow(卖出单)时,调 ClosePositionsAtTarget 平掉对应 tickets。该函数从后往前遍历数组,每单最多重试 retries 次,trade.PositionClose 成功就 ArrayRemove 并清零重试计数,失败则 Sleep 后重试。tickets 空了就调 Reset 清场。 Reset 会把 currentLotSize 复原成 inputlot、lastOrderType 设 -1、isRecovery 设 false、tickets 缩容清空、trailingStop 与 initialEntryPrice 归零,并打印“Strategy BASKET reset after closing trades”。多实例靠 recoveryArray 动态数组隔离,每个元素是一个 PositionRecovery,互不干扰。 OnInit 里 iRSI 建句柄失败返回 INIT_FAILED;OnDeinit 用 IndicatorRelease 释放句柄。OnTick 中 CopyBuffer 取 rsiBuffer[0]、[1],新 K 线判定用 iTime 比对 lastBarTime。RSI 上穿 30 打印 BUY SIGNAL 并初始化买入实例,下穿 70 打印 SELL SIGNAL 开卖出实例,随后 ArrayResize 塞进 recoveryArray。最后 for(i=0;i<ArraySize(recoveryArray);i++) 逐实例跑管理逻辑,外汇与贵金属杠杆高,回测前请在 MT5 策略测试器用 2020—2023 年 XAUUSD M15 先验证 RSI 穿越触发频率。
多实例独立重置的实测表现
循环里对每个持仓恢复实例依次跑三个方法:ManageZones 用点运算符调用,盯着价格相对预设恢复区间的波动,破边界就按 inputlotmultiplier=2.0 的开仓倍数加恢复头寸;CheckCloseAtTargets 判触目标位(inputzonetragetpts=400 点)后平掉关联仓并重置实例;ApplyTrailingStop 对初始仓做追踪,inputTrailingStopPts=50 点距离、inputMinimumProfitPts=50 点才启动。 实测关掉某个恢复实例后,它确实完成重置,但交易标签页里其他实例还在跑,说明数组里各实例是独立处理的。当前系统留着多个持仓恢复实例,其中两个进了恢复模式,右侧注释字段标了「初始头寸 / 恢复头寸」做区分。 外汇与贵金属保证金交易高风险,这种多实例并行逻辑在实盘可能因滑点导致重置不同步。开 MT5 把下面代码段丢进 EA 头部,先调 inputlotmultiplier 看手数跳变,再切交易标签验证实例隔离。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| class="num">1. Zone Recovery RSI EA Multi-Zone.mq5 |</span> <span class="comment">class=class="str">"cmt">//| Copyright class="num">2025, MetaQuotes Ltd. |</span> <span class="comment">class=class="str">"cmt">//| [MQL5官方文档] |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Copyright class="num">2025, MetaQuotes Ltd."</span> <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">link</span> <span class="class="type">class="kw">string">"[MQL5官方文档] <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">version</span> <span class="class="type">class="kw">string">"class="num">1.00"</span> <span class="keyword">sinput</span> group <span class="class="type">class="kw">string">"General EA Settings"</span> <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputlot = <span class="number">class="num">0.01</span>; <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputzonesizepts = <span class="number">class="num">200</span>; <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputzonetragetpts = <span class="number">class="num">400</span>; <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputlotmultiplier = <span class="number">class="num">2.0</span>; <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputTrailingStopPts = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// Trailing stop distance in points</span> <span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> inputMinimumProfitPts = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// Minimum profit points before trailing stop starts</span> <span class="keyword">input</span> <span class="keyword">class="type">bool</span> inputTrailingStopEnabled = <span class="macro">true</span>; <span class="comment">class=class="str">"cmt">// Enable or disable trailing stop</span><span> </span> <span class="preprocessor">class="macro">#include </span><Trade/Trade.mqh> <span class="comment">class=class="str">"cmt">//--- Includes the MQL5 Trade library for handling trading operations.</span> <span class="comment">class=class="str">"cmt">//--- Global variables for RSI logic</span> <span class="keyword">class="type">int</span> rsiPeriod = <span class="number">class="num">14</span>; <span class="comment">class=class="str">"cmt">//--- The period used for calculating the RSI indicator.</span> <span class="keyword">class="type">int</span> rsiHandle; <span class="comment">class=class="str">"cmt">//--- Handle for the RSI indicator, used to retrieve RSI values.</span> <span class="keyword">class="type">class="kw">double</span> rsiBuffer[]; <span class="comment">class=class="str">"cmt">//--- Array to store the RSI values retrieved from the indicator.</span> <span class="keyword">class="type">class="kw">datetime</span> lastBarTime = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">//--- Holds the time of the last processed bar to prevent duplicate signals.</span> <span class="comment">class=class="str">"cmt">//--- Struct to track individual position recovery states</span> <span class="keyword">class="kw">struct</span> PositionRecovery { <span class="comment">class=class="str">"cmt">//--- Member class="num">1</span> <span class="comment">class=class="str">"cmt">//--- Member class="num">2</span> <span class="comment">class=class="str">"cmt">//--- Member class="num">3</span> <span class="comment">class=class="str">"cmt">//--- Method class="num">1</span> <span class="comment">class=class="str">"cmt">//...</span> }; <span class="comment">class=class="str">"cmt">//--- Struct to track individual position recovery states</span> <span class="keyword">class="kw">struct</span> PositionRecovery { CTrade trade; <span class="comment">class=class="str">"cmt">//--- Object to handle trading operations.</span> <span class="keyword">class="type">class="kw">double</span> initialLotSize; <span class="comment">class=class="str">"cmt">//--- Initial lot size for this position.</span> <span class="keyword">class="type">class="kw">double</span> currentLotSize; <span class="comment">class=class="str">"cmt">//--- Current lot size in the recovery sequence.</span>