如何创建自己的追踪止损·综合运用
(3/3)·从手动拖止损到用 EA 自动跟随,多数人只用了终端内置的一半能力
用按钮切换的追踪止损托管EA
想把不同品种的持仓交给不同追踪逻辑管,又不想让EA自己去开仓,可以做一个纯托管型的辅助EA:它只负责挂按钮、调类实例,开仓动作仍由你或别的策略完成。 在 MetaEditor 里新建 Sample_TrailingStop 后,先 include 那个追踪止损头文件,再把 SAR 步长、NRTR 周期等做成外部参数。代码里默认盯 EURUSD、GBPUSD、USDCHF、USDJPY 四个品种,数组写死,你要加品种直接改 Symbols[] 就行。 OnInit 里按品种数量用 ArrayResize 扩两个指针数组,再循环 new 出 CParabolicStop 和 CNRTRStop 实例。Init 时左边按钮留给抛物线止损、右边留给 NRTR,横坐标错开 17 像素避免叠在一起。 编译挂图后,指标和电源按钮出现在图上,此时EA不建仓。你手动开仓后点对应按钮,DoStoploss 才在 OnTick 里生效,外汇和贵金属波动大,追踪止损只降风险不保盈利,实际触发概率随点差和滑点变化。 下面这段是EA核心装配逻辑,逐行拆一下关键行: #include <Sample_TrailingStop.mqh> // 包含追踪止损类 input double SARStep=0.02; // 抛物线步长,越小跟得越紧 input double SARMaximum=0.02; // 抛物线最大步长 input int NRTRPeriod=40; // NRTR看40根K input double NRTRK=2; // NRTR倍率因子 string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"}; // 托管品种清单 CParabolicStop *SARTrailing[]; // 抛物线实例指针数组 CNRTRStop *NRTRTrailing[]; // NRTR实例指针数组 ArrayResize(SARTrailing,ArraySize(Symbols)); // 按品种数扩数组 ArrayResize(NRTRTrailing,ArraySize(Symbols)); for(int i=0;i<ArraySize(Symbols);i++) // 遍历品种 { SARTrailing[i]=new CParabolicStop(); // 新建抛物线类 SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,5,15+i*17,Silver,Blue); // 按钮x=5,y逐品种+17 if(!SARTrailing[i].SetParameters(SARStep,SARMaximum)) // 塞入参数 { Alert("trailing error"); return(-1); } // 失败报警退出 SARTrailing[i].StartTimer(); // 开定时器 NRTRTrailing[i]=new CNRTRStop(); NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,127,15+i*17,Silver,Blue); // 按钮x=127 if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK)) { Alert("trailing error"); return(-1); } NRTRTrailing[i].StartTimer(); } for(int i=0;i<ArraySize(Symbols);i++) // 每Tick跑止损 { SARTrailing[i].DoStoploss(); NRTRTrailing[i].DoStoploss(); } void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { for(int i=0;i<ArraySize(Symbols);i++) // 按钮事件分发 { SARTrailing[i].EventHandle(id,lparam,dparam,sparam); NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam); } } for(int i=0;i<ArraySize(Symbols);i++) // 卸载清对象 { SARTrailing[i].Deinit(); NRTRTrailing[i].Deinit(); delete(SARTrailing[i]); delete(NRTRTrailing[i]); }
class="macro">#include <Sample_TrailingStop.mqh> class=class="str">"cmt">// 包含追踪止损类 input class="type">class="kw">double SARStep=class="num">0.02; class=class="str">"cmt">// 抛物线指标的步长 input class="type">class="kw">double SARMaximum=class="num">0.02; class=class="str">"cmt">// 抛物线指标的最大值 input class="type">int NRTRPeriod=class="num">40; class=class="str">"cmt">// NRTR 周期 input class="type">class="kw">double NRTRK=class="num">2; class=class="str">"cmt">// NRTR 因子 class="type">class="kw">string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"}; CParabolicStop *SARTrailing[]; CNRTRStop *NRTRTrailing[]; ArrayResize(SARTrailing,ArraySize(Symbols)); class=class="str">"cmt">// 根据用到的交易品种的数量,重新设置数组大小 ArrayResize(NRTRTrailing,ArraySize(Symbols)); class=class="str">"cmt">// 根据用到的交易品种的数量,重新设置数组大小 for(class="type">int i=class="num">0;i<ArraySize(Symbols);i++) { class=class="str">"cmt">// 针对所有交易品种 SARTrailing[i]=new CParabolicStop(); class=class="str">"cmt">// 创建CParabolicStop类的实例 SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,class="kw">false,true,true,class="num">5,class="num">15+i*class="num">17,Silver,Blue); class=class="str">"cmt">// 初始化CParabolicStop类的实例 if(!SARTrailing[i].SetParameters(SARStep,SARMaximum)) { class=class="str">"cmt">// 设置CParabolicStop类实例的参数 Alert("trailing error"); class="kw">return(-class="num">1); } SARTrailing[i].StartTimer(); class=class="str">"cmt">// 启动计时器 class=class="str">"cmt">//---- NRTRTrailing[i]=new CNRTRStop(); class=class="str">"cmt">// 创建CNRTRStop类的实例 NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,class="kw">false,true,true,class="num">127,class="num">15+i*class="num">17,Silver,Blue); class=class="str">"cmt">// 初始化CNRTRStop类的实例 if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK)) { class=class="str">"cmt">// 设置CNRTRcStop类实例的参数 Alert("trailing error"); class="kw">return(-class="num">1); } NRTRTrailing[i].StartTimer(); class=class="str">"cmt">// 启动计时器 } for(class="type">int i=class="num">0;i<ArraySize(Symbols);i++) { SARTrailing[i].DoStoploss(); NRTRTrailing[i].DoStoploss(); } class="type">void OnChartEvent(class="kw">const class="type">int id, class="kw">const class="type">long &lparam, class="kw">const class="type">class="kw">double &dparam, class="kw">const class="type">class="kw">string &sparam ) { for(class="type">int i=class="num">0;i<ArraySize(Symbols);i++) { SARTrailing[i].EventHandle(id,lparam,dparam,sparam); NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam); } } for(class="type">int i=class="num">0;i<ArraySize(Symbols);i++) { SARTrailing[i].Deinit(); NRTRTrailing[i].Deinit(); class="kw">delete(SARTrailing[i]); class=class="str">"cmt">// 删除对象 class="kw">delete(NRTRTrailing[i]); class=class="str">"cmt">// 删除对象 }
◍ 给 EA 挂上追踪止损的七步接法
给手工交易逻辑接一套追踪止损,用 CTrailingStop 类最快。流程是固定的七步:先 include 那个 Sample_TrailingStop.mqh;再把指标参数用 input 外部变量声明出来;接着 new 一个类实例;OnInit 里依次调 Init()、SetParameters()、StartTimer()、On();OnTimer 里只放 Refresh();OnTick 里调 DoStopLoss();OnDeinit 里调 Deinit()。 实测从空 EA 到跑通,熟练后不到 5 分钟就能挂上。配套 mqh 文件 27.36 KB,样例 EA 在 21 KB 上下,直接丢进 MT5 的 MQL5/Include 和 Experts 目录就能编译。 类内部用了一组 protected 成员管状态,下面这段是核心字段定义,逐行对应了追踪止损跑起来要记住的上下文:符号名、周期、是否每 tick 触发、是否画指标线和按钮、按钮坐标与配色、偏移量、开关、指标句柄、上次执行时间,以及下单用的请求和结果结构。 有读者把 OnTimer 里的刷新频率从每秒改成每 60 秒一次,CPU 占用明显下降,功能不受影响——若你的品种不刷得那么快,调这个间隔比改逻辑更划算。外汇和贵金属波动剧烈,追踪止损只管离场节奏,不保证不回撤,实盘前请在策略测试器用历史数据验一遍。
class="kw">protected: class="type">class="kw">string m_symbol; class=class="str">"cmt">// 符号 ENUM_TIMEFRAMES m_timeframe; class=class="str">"cmt">// 时限 class="type">bool m_eachtick; class=class="str">"cmt">// 运行于每个刻度线 class="type">bool m_indicator; class=class="str">"cmt">// 在图表上显示指标 class="type">bool m_button; class=class="str">"cmt">// 显示开/关按钮 class="type">int m_button_x; class=class="str">"cmt">// 按钮的 x 坐标 class="type">int m_button_y; class=class="str">"cmt">// 按钮的坐标 class="type">class="kw">color m_bgcolor; class=class="str">"cmt">// 按钮颜色 class="type">class="kw">color m_txtcolor; class=class="str">"cmt">// 按钮标题颜色 class="type">int m_shift; class=class="str">"cmt">// 条形偏移 class="type">bool m_onoff; class=class="str">"cmt">// 开/关 class="type">int m_handle; class=class="str">"cmt">// 指标句柄 class="type">class="kw">datetime m_lasttime; class=class="str">"cmt">// 最后一次执行追踪止损的时间 class="type">MqlTradeRequest m_request; class=class="str">"cmt">// 交易请求结构 class="type">MqlTradeResult m_result; class=class="str">"cmt">// 交易请求结果的结构 class="type">int m_digits; class=class="str">"cmt">// 价格的小数位数 class="type">class="kw">double m_point; class=class="str">"cmt">// 项目值 class="type">class="kw">string m_objname; class=class="str">"cmt">// 按钮名称 class="type">class="kw">string m_typename; class=class="str">"cmt">// 追踪止损类型名称
「收束」
把铭文变量塞进面板只是收尾的一步,真正要在 MT5 里跑通,还得在 OnChartEvent 里把 m_caption 绑到按钮对象上,否则纽扣永远空白。 这套三步拆下来,从建类、挂面板到补铭文,核心就是 CAppDialog 的派生与控件生命周期;外汇与贵金属波动剧烈,拿去实盘前先在策略测试器用 2023 年 EURUSD 的 M15 数据验一遍控件响应。 代码层没有魔法,缺哪一行编译器都会红,补上就能见着那颗带字的纽扣。
class="type">class="kw">string m_caption; class=class="str">"cmt">// 纽扣铭文