MQL5 向导:如何创建未平仓位的追踪模块·进阶篇
(2/3)· 想用向导生成能改保护单的 EA,却卡在 CExpertTrailing 不会扩写?本文拆开追踪模块骨架
「跟踪止损类的骨架怎么搭」
在 MT5 里写自定义跟踪止损逻辑,第一步往往是继承标准库里的 CExpertTrailing。下面这段头文件骨架只做了一件事:声明 CSampleTrailing 类,并让它公开继承自 CExpertTrailing,此时类体为空,纯粹是占位结构。 实际开发中你会往这个类里重写 TrailingStop() 之类的方法,但原骨架把版权和链接属性写在了重复两次的头部注释块里:#property copyright "Copyright 2010, MetaQuotes Software Corp." 与 #property link 指向的域名,只是元信息,不影响编译行为。 #include <Expert\ExpertTrailing.mqh> 这一行才是关键,它把官方追踪止损基类拉进工程;没有它,继承语句会直接报找不到符号。开 MT5 新建一个 .mqh,把下面代码贴进去能立刻通过编译,验证继承链是否通。
class=class="str">"cmt">// class="macro">#class="kw">import "stdlib.ex5" class=class="str">"cmt">// class="type">class="kw">string ErrorDescription(class="type">int error_code); class=class="str">"cmt">// class="macro">#class="kw">import class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#include <Expert\ExpertTrailing.mqh> class CSampleTrailing : class="kw">public CExpertTrailing { };
◍ 无风险平仓线的类骨架
MT5 标准库里做移动止损,通常会从 CExpertTrailing 派生一个子类,把“把止损推到不亏钱的位置”这件事封装成独立逻辑。下面这段就是最薄的一层壳:只声明了两个可调参数,还没写追踪的具体算法。 m_profit 是触发动作的盈利阈值(单位依品种点值而定,常见设 10~50 点),m_stop_level 是推到成本线之上多少点算“无风险”。两个 setter 直接赋值成员变量,方便在 EA 初始化时从外部传参。 外汇与贵金属杠杆高、滑点突发行情多,这类保本 trailing 只能降低回撤概率,无法消除穿损风险,参数需按品种波动率手调。
class CSampleTrailing : class="kw">public CExpertTrailing { class="kw">protected: class="type">int m_profit; class=class="str">"cmt">//threshold level of profit class="type">int m_stop_level; class=class="str">"cmt">// lossless level class="kw">public: class=class="str">"cmt">//--- methods of setting adjustable parameters class="type">void Profit(class="type">int value) { m_profit=value; } class="type">void StopLevel(class="type">int value) { m_stop_level=value; } };
无风险平仓阈值的类封装
把移动止损逻辑做成独立类,最实用的一点是把「盈利多少点后才护本」和「护本止损摆几点」拆成两个可调整型参数。下面这段头文件定义里,m_profit 就是触发阈值,m_stop_level 是无损平移的止损距离,二者都在 protected 区,外部只能通过公共方法改写。 CSampleTrailing 继承自 CExpertTrailing,等于在官方跟踪止损基类上挂了一层「先赚后移、移到成本位」的规则。Profit() 和 StopLevel() 是两个单行 setter,直接赋值成员变量,没有边界检查——这意味着你在 EA 里调参时若填了负数或极端值,不会在构造阶段报错,只能靠 ValidationSettings() 去拦。 验证方法很直接:在 MT5 的 MQL5 编辑器里新建一个继承该类的 EA,把 m_profit 设成 30、m_stop_level 设成 15,跑历史回测看持仓是否在浮盈 30 点后把止损推到开仓价上方 15 点处。外汇与贵金属杠杆高,这类护本逻辑可能降低回撤但也可能被扫损后踏空,属概率性保护而非确定性收益。
class=class="str">"cmt">//| SampleTrailing.mqh | class=class="str">"cmt">//| Copyright class="num">2010, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| include files | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Expert\ExpertTrailing.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Class CSampleTrailing. | class=class="str">"cmt">//| Purpose: Class for trailing of open positions | class=class="str">"cmt">//| by moving Stop order to a lossless level. | class=class="str">"cmt">//| Is derived from the CExpertTrailing class. | class=class="str">"cmt">//+------------------------------------------------------------------+ class CSampleTrailing : class="kw">public CExpertTrailing { class="kw">protected: class="type">int m_profit; class=class="str">"cmt">// threshold level of profit class="type">int m_stop_level; class=class="str">"cmt">// lossless level class="kw">public: CSampleTrailing(); class=class="str">"cmt">//--- methods of setting adjustable parameters class="type">void Profit(class="type">int value) { m_profit=value; } class="type">void StopLevel(class="type">int value) { m_stop_level=value; } class=class="str">"cmt">//--- method of validating the adjustable parameters class="kw">virtual class="type">bool ValidationSettings(); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Validation of adjustable parameters. | class=class="str">"cmt">//| INPUT: no. | class=class="str">"cmt">//| OUTPUT: true if parameter are correct, class="kw">false - if not. | class=class="str">"cmt">//| REMARK: no. |
「盈利阈值与止损位的参数校验逻辑」
在自定义移动止损类 CSampleTrailing 里,ValidationSettings() 先调用基类校验,再补两道自己的关: symbol 指针为空直接否,盈利阈值和止损位的关系不满足也否。
核心约束写在 (m_profit-m_stop_level)*m_adjusted_point <= m_symbol.StopsLevel()*m_symbol.Point() && m_profit!=0.0 这一行——意思是盈利触发阈值减去保本位后的实际点数,必须严格大于经纪商规定的最小止损距离,否则打印报错并返回 false。
类里 m_profit 是触发移动止损的盈利点数门槛,m_stop_level 是推到开仓价的保本位;两者都靠 Profit() 和 StopLevel() 两个 setter 注入。若你在 MT5 里改了这两个参数却没法初始化,八成是阈值减保本位后挤进了 StopsLevel() 的限制区。
多头检查函数 CheckTrailingStopLong 开头就拦三种情况:position 指针空、m_profit 为 0、或者当前止损已经大于等于开仓价(说明早进保盈区了),直接 return false 不再计算。
class="type">bool CSampleTrailing::ValidationSettings() { if(!CExpertTrailing::ValidationSettings()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- check wheter the Init method is called if(m_symbol==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- check parameters if((m_profit-m_stop_level)*m_adjusted_point<=m_symbol.StopsLevel()*m_symbol.Point() && m_profit!=class="num">0.0) { printf(__FUNCTION__+": threshold level of profit must be greater than the level of setting of orders"); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- ok class="kw">return(true); } class CSampleTrailing : class="kw">public CExpertTrailing { class="kw">protected: class="type">int m_profit; class=class="str">"cmt">// threshold level of profit class="type">int m_stop_level; class=class="str">"cmt">// lossless level class="kw">public: CSampleTrailing(); class=class="str">"cmt">//--- methods of setting adjustable parameters class="type">void Profit(class="type">int value) { m_profit=value; } class="type">void StopLevel(class="type">int value) { m_stop_level=value; } class=class="str">"cmt">//--- method of validation of adjustable parameters class="kw">virtual class="type">bool ValidationSettings(); class=class="str">"cmt">//--- methods of generation of position modification signals class="kw">virtual class="type">bool CheckTrailingStopLong(CPositionInfo* position,class="type">class="kw">double& sl,class="type">class="kw">double& tp); class="kw">virtual class="type">bool CheckTrailingStopShort(CPositionInfo* position,class="type">class="kw">double& sl,class="type">class="kw">double& tp); }; class="type">bool CSampleTrailing::CheckTrailingStopLong(CPositionInfo* position,class="type">class="kw">double& sl,class="type">class="kw">double& tp) { class=class="str">"cmt">//--- check of pointer if(position==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- check of parameter if(m_profit==class="num">0.0) class="kw">return(class="kw">false); class=class="str">"cmt">//--- already in a lossless zone? class="type">class="kw">double open=position.PriceOpen(); if(position.StopLoss()>=open) class="kw">return(class="kw">false);
◍ 空单保本移动的触发与止损重算
多头那段讲完,空单的保本逻辑是镜像的。函数 CheckTrailingStopShort 先判指针和 m_profit 是否为 0,任一为空直接返回 false,不浪费计算。 已处于无损区(即原止损价高于开仓价)时函数也直接退出,说明只在浮亏或刚平价阶段才考虑把止损拉到成本附近,避免重复修改订单。 核心判断是 open - Ask() > m_profit * m_adjusted_point:空单盈利空间超过设定点数(默认 Profit 参数 20 点)后,把 sl 重算为 open - m_stop_level * m_adjusted_point,并经 NormalizePrice 对齐报价精度。 m_stop_level 默认 0,意味着盈利达标后止损直接钉在开仓价;若设为 5,则留 5 点缓冲防毛刺。外汇与贵金属杠杆高,保本触发后仍需防滑点扫损,建议用策略测试器跑 EURUSD 15 分钟验证触发频率。
class="type">bool CSampleTrailing::CheckTrailingStopShort(CPositionInfo* position,class="type">class="kw">double& sl,class="type">class="kw">double& tp) { class=class="str">"cmt">//--- check of pointer if(position==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- check of parameter if(m_profit==class="num">0.0) class="kw">return(class="kw">false); class=class="str">"cmt">//--- already in a lossless zone? class="type">class="kw">double open=position.PriceOpen(); if(position.StopLoss()<=open) class="kw">return(class="kw">false); class=class="str">"cmt">//--- check of profit sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(open-m_symbol.Ask()>m_profit*m_adjusted_point) sl=m_symbol.NormalizePrice(open-m_stop_level*m_adjusted_point); class=class="str">"cmt">//--- class="kw">return(sl!=EMPTY_VALUE); }