您应该知道的 MQL5 向导技术(第 01 部分):回归分析·综合运用
(3/3)· 前两步搭好类和求解器,这篇把回归信号塞进向导、跑测试、收束整个系列
◍ 用向导拼出 ATR 挂单与尾随止损
MQL5 向导能直接组装智能系统,但想做基于 ATR 的挂单和尾随,得自己重载信号类的几个函数。本文不碰目标止盈,只盯两件事:按品种 ATR 倍数挂单,以及用同一指标拖着止损走。 向导生成的 EA 自带输入 Signal_PriceLevel,默认 0。赋负值出破位单,正值出限价单,类型是 double。我们把它 reinterpret 成 ATR 点数的乘数——比如设 1.5,就是离市价 1.5 倍当前 ATR 点数挂单。 尾随类继承 CExpertTrailing,同样吃 ATR。里面的 m_atr_weight 和信号类的 m_price_level 都做成可优化参数,控制止损贴着价格的松紧度。 下面这段是 OpenLongParams / OpenShortParams 的重载核心。无通用信号时,取 Ask 或 Bid 作基准,减去或加上 m_price_level 乘 ATR 主值再乘单位,归一化后作为挂单价,sl/tp 清零,过期时间按周期累加。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Detecting the levels for buying | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalDUAL_RA::OpenLongParams(class="type">class="kw">double &price,class="type">class="kw">double &sl,class="type">class="kw">double &tp,class="type">class="kw">datetime &expiration) { CExpertSignal *general=(m_general!=-class="num">1) ? m_filters.At(m_general) : NULL; class=class="str">"cmt">//--- if(general==NULL) { m_ATR.Refresh(-class="num">1); class=class="str">"cmt">//--- if a base price is not specified explicitly, take the current market price class="type">class="kw">double base_price=(m_base_price==class="num">0.0) ? m_symbol.Ask() : m_base_price; class=class="str">"cmt">//--- price overload that sets entry price to be based on ATR price =m_symbol.NormalizePrice(base_price-(m_price_level*(m_ATR.Main(class="num">0)/m_symbol.Point()))*PriceLevelUnit()); sl =class="num">0.0; tp =class="num">0.0; expiration+=m_expiration*PeriodSeconds(m_period); class="kw">return(true); } class=class="str">"cmt">//--- class="kw">return(general.OpenLongParams(price,sl,tp,expiration)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Detecting the levels for selling | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSignalDUAL_RA::OpenShortParams(class="type">class="kw">double &price,class="type">class="kw">double &sl,class="type">class="kw">double &tp,class="type">class="kw">datetime &expiration) { CExpertSignal *general=(m_general!=-class="num">1) ? m_filters.At(m_general) : NULL; class=class="str">"cmt">//--- if(general==NULL) { m_ATR.Refresh(-class="num">1); class=class="str">"cmt">//--- if a base price is not specified explicitly, take the current market price class="type">class="kw">double base_price=(m_base_price==class="num">0.0) ? m_symbol.Bid() : m_base_price; class=class="str">"cmt">//--- price overload that sets entry price to be based on ATR price =m_symbol.NormalizePrice(base_price+(m_price_level*(m_ATR.Main(class="num">0)/m_symbol.Point()))*PriceLevelUnit()); sl =class="num">0.0; tp =class="num">0.0; expiration+=m_expiration*PeriodSeconds(m_period); class="kw">return(true); } class=class="str">"cmt">//--- class="kw">return(general.OpenShortParams(price,sl,tp,expiration)); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checking trailing stop and/or profit for class="type">long position. | class=class="str">"cmt">//+------------------------------------------------------------------+
「多空双向 ATR 拖止损的判定逻辑」
在 MT5 里用 ATR 做动态追踪止损,核心不是算指标本身,而是判定位是否合法。下面这段类方法同时覆盖了多单与空单的校验,直接决定了止损线会不会被推着走。 多单分支里,先拿 Bid 减去经纪商要求的 StopsLevel 点距得到 level,这是止损能放的最近边界。new_sl 以 level 为基准再减去 ATR 主值换算成点数乘权重,意味着波动越大、止损撤得越远。 base 的取法很关键:若持仓原止损为 0,就用开仓价当底线,否则用现有止损。只有 new_sl 严格大于 base 且低于 level 时,才把新止损写回 sl,避免回缩或穿价。 空单镜像处理,用 Ask 加 StopsLevel 得 level,new_sl 向上加 ATR 距离。判定改成 new_sl 小于 base 且大于 level 才生效。外汇与贵金属杠杆高,这类自动拖损在跳空时可能滑到边界外,实盘前务必用策略测试器跑几组极端点差验证。 把 m_atr_weight 从默认 1.0 调到 1.5,回测 EURUSD H1 会发现止损触发频率下降但单笔回撤扩大,参数倾向需按品种波动个性来定。
class="type">bool CTrailingATR::CheckTrailingStopLong(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- m_ATR.Refresh(-class="num">1); class="type">class="kw">double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); class=class="str">"cmt">//--- sl adjustment to be based on ATR class="type">class="kw">double new_sl=NormalizeDouble(level-(m_atr_weight*(m_ATR.Main(class="num">0)/m_symbol.Point())),m_symbol.Digits()); class="type">class="kw">double pos_sl=position.StopLoss(); class="type">class="kw">double base =(pos_sl==class="num">0.0) ? position.PriceOpen() : pos_sl; class=class="str">"cmt">//--- sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl>base && new_sl<level) sl=new_sl; class=class="str">"cmt">//--- class="kw">return(sl!=EMPTY_VALUE); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checking trailing stop and/or profit for class="type">class="kw">short position. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingATR::CheckTrailingStopShort(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- m_ATR.Refresh(-class="num">1); class="type">class="kw">double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); class=class="str">"cmt">//--- sl adjustment to be based on ATR class="type">class="kw">double new_sl=NormalizeDouble(level+(m_atr_weight*(m_ATR.Main(class="num">0)/m_symbol.Point())),m_symbol.Digits()); class="type">class="kw">double pos_sl=position.StopLoss(); class="type">class="kw">double base =(pos_sl==class="num">0.0) ? position.PriceOpen() : pos_sl; class=class="str">"cmt">//--- sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl<base && new_sl>level) sl=new_sl; class=class="str">"cmt">//--- class="kw">return(sl!=EMPTY_VALUE); }
用策略测试器跑通挂单与市价单对比
编译是 MQL5 向导组装后的必要动作,生成 ex5 智能系统文件并确认无语法错误;之后进策略测试器「输入」选项卡,把 Signal_TakeLevel 与 Signal_StopLevel 都置 0,因为本文离场只走尾随停止或 Signal_ThresholdClose,不靠这两个阈值。 优化最好用你实盘经纪商的真实报价。本文取 2018-01-01 至 2021-01-01 的 V 型区间、EURUSD、4 小时周期,跑两组:一组只用市价单,一组开挂单(取「开立」类,Signal_PriceLevel 从负到正参与优化;若只用市价单则该参数锁 0 不优化)。 回测结果指向一个现象:挂单版本在牺牲部分利润的代价下,回撤比纯市价单更小。测试时资金管采用固定保证金百分比、优化标准设「复杂标准」。外汇与贵金属属高风险品种,历史回测不代表未来;部署前应尽量用历史 tick 数据广测并做前向验证,这一块不在本文范围内。
◍ 把回归信号接进自己的武器库
回归分析里的共线性和判定系数不是摆设,把它们当过滤条件,能帮你在 MT5 向导里搭出更稳的信号底座。这一步做完,别急着上实盘,先拿历史报价跑广泛测试,看看信号在贵金属和外汇品种上的衰减速度——这两类市场高杠杆、跳空频繁,回测漂亮不代表 live 环境能复刻。
有读者在论坛反馈,用 SignalDUAL_RA.mqh 编译 EA 时报错,根因是 _a[r].Set(c,Data(r+c,close)) 把行引用当直接编辑用了;改成 _a.Set(r,c,Data(r+c,close)) 后编译通过。这种细节不自己踩一遍,向导生成的系统永远停在纸面。
向导式 EA 默认绑图表周期,想接多周期数据得动不少地方,不止 OnInit 一处。非固定周期输入若和图表周期冲突,OnInit 会静默返回非零码 1,没报错信息才最坑。下一步值得试的,是把这套回归信号和你经验里的形态或函数库内置信号配对,组合后样本外表现倾向更平滑,但是否适合你的交易节奏,只能你开 MT5 调参验证。
class=class="str">"cmt">//--- 专家输入 input class="type">class="kw">string Expert_Title ="Regr2"; class=class="str">"cmt">// 文件名称 class="type">class="kw">ulong Expert_MagicNumber =class="num">26034; class=class="str">"cmt">// class="type">bool Expert_EveryTick =class="kw">false; class=class="str">"cmt">// input ENUM_TIMEFRAMES timeframe =PERIOD_M5; class=class="str">"cmt">//时间框架 class=class="str">"cmt">//--- 主信号输入 . . . . class="type">int OnInit() { class=class="str">"cmt">//--- 专家初始化 if(!ExtExpert.Init(Symbol(),timeframe,Expert_EveryTick,Expert_MagicNumber)) { class=class="str">"cmt">//--- 失败 printf(__FUNCTION__+": error initializing expert"); ExtExpert.Deinit(); class="kw">return(INIT_FAILED); } class=class="str">"cmt">//--- 创建信号 . . . .