作为技术分析工具的 MTF 指标·综合运用
(3/3)·当M1下单没空切H4看趋势,MTF组合指标如何替你守住三重滤网不漏信号
EA 输入参数与全局变量拆解
下面这段 MT5 专家顾问头部声明,把多指标共振策略的可调旋钮一次性摊开了。手数固定 0.1,止盈 150 点、止损 60 点,水平信号阈值 20 点,跟踪止损 25 点、偏移 5 点——这几个数字直接决定了仓位风险轮廓,外汇与贵金属杠杆环境下回撤可能放大,需自行压力测试。 信号层用了 MA(21)、MACD(12,26)、Stochastic(5,3) 三组默认值,周期偏日内波段。注意 InpTFreg 输入的是枚举值 2,对应更低一级周期做信号确认,和主图不是同一帧。 全局里 ExtTimeOut=10 限制了交易指令最小间隔为 10 秒,barsCalculated=3 意味着至少 3 根已成型 K 线才参与计算,避免实时毛刺。复制下面代码到 MQ5 文件头部即可直接编译验证。
class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="kw">input class="type">class="kw">string s0="class=class="str">"cmt">//--- 输入参数 手数+跟踪 ---//"; class="kw">input class="type">class="kw">double InpLots =class="num">0.1; class=class="str">"cmt">// 手数 class="kw">input class="type">int InpTakeProfit =class="num">150; class=class="str">"cmt">// 获利 (in pips) class="kw">input class="type">int InpStopLoss =class="num">60; class=class="str">"cmt">// 止损 (in pips) class="kw">input class="type">int InpLevel_S =class="num">20; class=class="str">"cmt">// 水平信号 class="kw">input compTF InpTFreg =class="num">2; class="kw">input class="type">class="kw">string s1="class=class="str">"cmt">//--- MA 输入参数 ---//"; class="kw">input class="type">int Signal_MA_PeriodMA =class="num">21; class=class="str">"cmt">// 平均周期数 class="kw">input class="type">class="kw">string s2="class=class="str">"cmt">//--- MACD 输入参数 ---//"; class="kw">input class="type">int Signal_MACD_PeriodFast =class="num">12; class=class="str">"cmt">// 快速 EMA 周期数 class="kw">input class="type">int Signal_MACD_PeriodSlow =class="num">26; class=class="str">"cmt">// 慢速 EMA 周期数 class="kw">input class="type">class="kw">string s3="class=class="str">"cmt">//--- Stochastic 输入参数 ---//"; class="kw">input class="type">int Signal_Stoch_PeriodK =class="num">5; class=class="str">"cmt">// K-period class="kw">input class="type">int Signal_Stoch_PeriodD =class="num">3; class=class="str">"cmt">// D-period class="kw">input class="type">class="kw">string s4="class=class="str">"cmt">//--- 跟踪止损输入参数 ---//"; class=class="str">"cmt">//--- 用于跟踪止损的输入参数 class="kw">input class="type">int InpTrailingStop =class="num">25; class=class="str">"cmt">// 跟踪止损水平 (in pips) class="kw">input class="type">int InpOffset =class="num">5; class=class="str">"cmt">// 与价格之间的距离 (in pips) class=class="str">"cmt">//--- class="type">int ExtTimeOut=class="num">10; class=class="str">"cmt">// 交易操作之间的时间间隔 class="type">int barsCalculated=class="num">3; class="type">class="kw">datetime t=class="num">0; class="type">class="kw">datetime time[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| AC 示例专家类 | class=class="str">"cmt">//+------------------------------------------------------------------+ class CSampleExpert { class="kw">protected:
「EA 类里的核心变量与句柄声明」
写 MT5 专家顾问时,把交易对象、品种信息和指标句柄集中挂在类成员里,后面每个回调函数都能直接复用,不用反复传参。下面这段声明来自一个多指标共振的样例 EA,覆盖了报价精度、账户、持仓、订单以及 MACD / MA / Stochastic 三类指标的句柄与缓冲区。 double m_adjusted_point 存的是 3 位或 5 位报价的小数位修正值,欧美类品种常为 5 位、日元类常为 3 位,下单时算止损距离要靠它。CTrade、CSymbolInfo、CPositionInfo、CAccountInfo、COrderInfo 五个标准库对象一次性实例化,省去在 OnTick 里反复构造的开销。 指标部分用 mas_tf[3] 存三个时间框架,handle_MACD / handle_MA / handle_Stochastic 分别接指标句柄,对应的 macd_buff / ma_buff / stoch_buff 动态数组用来 CopyBuffer 取数。close/open/low/high 四个数组直接拉 K 线数据,macd_ind_0、macd_ind_1 取 MACD 最近两根值做金叉死叉判断,stoch_ind_0/1 同理。 跟踪止损与止盈止损也提前留了成员:m_traling_stop、m_take_profit、m_stop_losse(原文拼写如此)。公开方法只留了构造、析构、Init、Deinit,逻辑全塞进类内,开 MT5 新建 EA 时照这个结构挂成员,编译能直接过。外汇和贵金属杠杆高,这类 EA 实盘前务必在策略测试器跑历史数据验证。
class="type">class="kw">double m_adjusted_point; class=class="str">"cmt">// class="num">3 或者 class="num">5 位小数值 CTrade m_trade; class=class="str">"cmt">// 交易对象 CSymbolInfo m_symbol; class=class="str">"cmt">// 交易品种信息 CPositionInfo m_position; class=class="str">"cmt">// 交易仓位 CAccountInfo m_account; class=class="str">"cmt">// 账户信息 COrderInfo m_order; class=class="str">"cmt">// 订单信息 class=class="str">"cmt">//--- 指标 ENUM_TIMEFRAMES mas_tf[class="num">3]; class=class="str">"cmt">// 时间框架的数组 class="type">int handle_MACD; class=class="str">"cmt">// MACD 指标句柄 class="type">int handle_MA; class=class="str">"cmt">// MA 指标句柄 class="type">int handle_Stochastic; class=class="str">"cmt">// Stochastic 指标句柄 class=class="str">"cmt">//--- 指标缓冲区 class="type">class="kw">double macd_buff[]; class=class="str">"cmt">// MACD 指标主缓冲区 class="type">class="kw">double ma_buff[]; class=class="str">"cmt">// MA 指标主缓冲区 class="type">class="kw">double stoch_buff[]; class=class="str">"cmt">// Stochastic 指标主缓冲区 class=class="str">"cmt">//--- class="type">class="kw">double close[]; class="type">class="kw">double open[]; class="type">class="kw">double low[]; class="type">class="kw">double high[]; class=class="str">"cmt">//--- 要处理的指标数据 class="type">class="kw">double macd_ind_0; class="type">class="kw">double macd_ind_1; class="type">class="kw">double ma_ind; class="type">class="kw">double stoch_ind_0; class="type">class="kw">double stoch_ind_1; class="type">int level_stoch; class=class="str">"cmt">//--- 要处理的跟踪止损数据 class="type">class="kw">double m_traling_stop; class="type">class="kw">double m_take_profit; class="type">class="kw">double m_stop_losse; class="kw">public: CSampleExpert(class="type">void); ~CSampleExpert(class="type">void); class="type">bool Init(class="type">void); class="type">void Deinit(class="type">void);
◍ EA 类骨架与构造函数初始化
这段声明把策略引擎的核心方法拆成了公开与保护两层。公开侧只留一个 Processing(void) 作为外部调用入口,其余建仓、改单、删单逻辑全部收在 protected 里,避免被外部误触。 从方法名能直接读出行为边界:LongOpened 与 ShortOpened 负责检查买卖触发条件,OpenBuyStop / OpenSellStop 挂止损单,OrderModifyBuyStop / OrderModifySellStop 做追踪改单,DellBuyStop / DellSellStop 清掉挂单。注意原文 DellSellStop 的注释写成了「删除止损卖出订单」,而 DellBuyStop 也标成「删除止损卖出订单」,这大概率是拷贝注释时的笔误,实际应按函数名区分买卖。 构造函数把关键句柄一次性置为 INVALID_HANDLE,MACD、Stochastic、MA 三大指标句柄若初始化失败就会保持这个值,后续 InitIndicators 里必须判空。m_adjusted_point 与各类 ind_0 / ind_1 缓冲区初值给 0,意味着首根 K 线前不假设任何指标偏移量。 [CODE] bool Processing(void); protected: bool InitCheckParameters(const int digits_adjust); bool Copy(void); bool InitIndicators(void); bool LongModified(void); bool ShortModified(void); bool LongOpened(void); // 检查买入条件 bool ShortOpened(void); // 检查卖出条件 bool OpenSellStop(void); // 设置止损卖出订单 bool OpenBuyStop(void); // 设置止损买入订单 bool OrderModifySellStop(void); // 修改止损卖出订单 bool OrderModifyBuyStop(void); // 修改止损买入订单 bool DellSellStop(void); // 删除止损卖出订单 bool DellBuyStop(void); // 删除止损卖出订单 }; //--- 全局的专家对象 CSampleExpert ExtExpert; CSampleExpert::CSampleExpert(void) : m_adjusted_point(0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(0), macd_ind_1(0), stoch_ind_0(0), stoch_ind_1(0), [/CODE] 逐行拆解:第1行公开方法 Processing 是 EA 每 tick 的主循环入口;protected 块里 InitCheckParameters 用 digits_adjust 校准小数点位差;Copy 负责把指标缓冲拷进本地数组;InitIndicators 申请指标句柄;Long/ShortModified 处理已持仓的追踪;Long/ShortOpened 是开仓信号判断;Open*Stop 与 OrderModify*Stop 管理挂单生命周期;Dell*Stop 做清理。全局对象 ExtExpert 在编译期即实例化,构造函数列表初始化把所有句柄与缓冲置零或无效值,杜绝野指针。 开 MT5 新建 EA 时,照这个分层把订单操作锁在 protected 内,能少踩很多外部重复调用的坑。外汇与贵金属杠杆高,这类自动挂单逻辑在滑点扩大时可能频繁改单失败,需实盘前用策略测试器跑至少 3 个月 tick 数据验证。
class="type">bool Processing(class="type">void); class="kw">protected: class="type">bool InitCheckParameters(class="kw">const class="type">int digits_adjust); class="type">bool Copy(class="type">void); class="type">bool InitIndicators(class="type">void); class="type">bool LongModified(class="type">void); class="type">bool ShortModified(class="type">void); class="type">bool LongOpened(class="type">void); class=class="str">"cmt">// 检查买入条件 class="type">bool ShortOpened(class="type">void); class=class="str">"cmt">// 检查卖出条件 class="type">bool OpenSellStop(class="type">void); class=class="str">"cmt">// 设置止损卖出订单 class="type">bool OpenBuyStop(class="type">void); class=class="str">"cmt">// 设置止损买入订单 class="type">bool OrderModifySellStop(class="type">void); class=class="str">"cmt">// 修改止损卖出订单 class="type">bool OrderModifyBuyStop(class="type">void); class=class="str">"cmt">// 修改止损买入订单 class="type">bool DellSellStop(class="type">void); class=class="str">"cmt">// 删除止损卖出订单 class="type">bool DellBuyStop(class="type">void); class=class="str">"cmt">// 删除止损卖出订单 }; class=class="str">"cmt">//--- 全局的专家对象 CSampleExpert ExtExpert; CSampleExpert::CSampleExpert(class="type">void) : m_adjusted_point(class="num">0), handle_MACD(INVALID_HANDLE), handle_Stochastic(INVALID_HANDLE), handle_MA(INVALID_HANDLE), macd_ind_0(class="num">0), macd_ind_1(class="num">0), stoch_ind_0(class="num">0), stoch_ind_1(class="num">0),
EA 初始化里的点位换算与多周期映射
这段 Init 函数干的事很直接:把外部输入参数落成可交易的内部变量,并顺手把报价精度抹平。MT5 里 3 位或 5 位小数品种(如 XAUUSD 的 5 位报价)和 2 位、4 位品种的点值差 10 倍,代码用 digits_adjust 做修正——3/5 位时取 10,否则取 1,再乘 SymbolPoint 得到 m_adjusted_point。 止损、止盈、 trailing stop 都不是直接读输入值,而是各自乘上 m_adjusted_point。比如 InpTrailingStop 设为 30,在 5 位黄金上实际距离是 30×0.00001×10 = 0.00300,即 300 点。偏差也按 3×digits_adjust 设,5 位品种就是 30 点容差。 周期映射由 InpTFreg 控制,switch 里 0~3 对应四组 M1/M5/M15 到 H1/H4/D1 的三周期组合,写进 mas_tf 数组。改这个输入就能让均线逻辑在短中长三档周期间切换,不用动核心代码。 最后两步 InitCheckParameters 和 InitIndicators 任一返回 false,Init 就整体失败,EA 不会继续跑。开 MT5 加载后若面板不反应,先查这两个函数打印的日志。
class="type">bool CSampleExpert::Init(class="type">void) { class=class="str">"cmt">//--- 初始化一般信息 m_symbol.Name(Symbol()); class=class="str">"cmt">// 交易品种 m_trade.SetExpertMagicNumber(EXPERT_MAGIC); class=class="str">"cmt">// 幻数 m_trade.SetMarginMode(); m_trade.SetTypeFillingBySymbol(Symbol()); class=class="str">"cmt">//--- 为3位或5位小数做调整 class="type">int digits_adjust=class="num">1; if(m_symbol.Digits()==class="num">3 || m_symbol.Digits()==class="num">5) digits_adjust=class="num">10; m_adjusted_point=m_symbol.Point()*digits_adjust; class=class="str">"cmt">//--- 设置用于交易的默认偏差 m_traling_stop =InpTrailingStop*m_adjusted_point; m_take_profit =InpTakeProfit*m_adjusted_point; m_stop_losse =InpStopLoss*m_adjusted_point; class=class="str">"cmt">//--- 设置交易默认偏差的点数 m_trade.SetDeviationInPoints(class="num">3*digits_adjust); class=class="str">"cmt">//--- class="type">int x=InpTFreg; class="kw">switch(x) { case class="num">0: {mas_tf[class="num">0]=PERIOD_M1;mas_tf[class="num">1]=PERIOD_M5;mas_tf[class="num">2]=PERIOD_M15;} break; case class="num">1: {mas_tf[class="num">0]=PERIOD_M5;mas_tf[class="num">1]=PERIOD_M15;mas_tf[class="num">2]=PERIOD_H1;} break; case class="num">2: {mas_tf[class="num">0]=PERIOD_M15;mas_tf[class="num">1]=PERIOD_H1;mas_tf[class="num">2]=PERIOD_H4;} break; case class="num">3: {mas_tf[class="num">0]=PERIOD_H1;mas_tf[class="num">1]=PERIOD_H4;mas_tf[class="num">2]=PERIOD_D1;} break; } class=class="str">"cmt">//--- if(!InitCheckParameters(digits_adjust)) class="kw">return(class="kw">false); if(!InitIndicators()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- 结果 class="kw">return(true); }
「参数校验与多周期指标句柄的初始化落点」
EA 启动阶段先卡死参数边界,比进场逻辑更该被盯紧。InitCheckParameters 里用 m_symbol.StopsLevel() 做底线:获利和跟踪止损乘上 digits_adjust 后若还小于平台止损级,直接返回 false 并 printf 报错,这意味着你的 InpTakeProfit 在 5 位数账号上填 10 点,若券商 StopsLevel 是 20,EA 连初始化都过不了。 手数检查分两道:先比对 LotsMin() 与 LotsMax() 的硬区间,再用 MathAbs(InpLots/LotsStep() - MathRound(...)) > 1e-10 验证是否踩中步长网格。很多市价单莫名被拒,根因就在手数不是 LotsStep 的整数倍,这套判定能提前拦掉。 InpTakeProfit <= InpTrailingStop 只打 warning 不阻断,属于逻辑隐患提醒而非致命错——实盘里跟踪止损吞掉获利的概率会显著抬升,建议手动改成 return(false)。外汇与贵金属杠杆高,参数越界可能瞬间放大回撤,校验层必须硬。 指标句柄走懒加载:handle_MACD / handle_MA / handle_Stochastic 仅在等于 INVALID_HANDLE 时才用 iCustom 去挂 MTF 系列自定义指标,路径如 "MTF\\Oscillators\\MTF_MACD",时间帧取 mas_tf[2]。任何句柄创建失败就 printf 并返回 false,EA 不会带着空指标硬跑。开 MT5 把这段贴进 CSampleExpert 类,改 mas_tf 数组就能验证多周期信号是否按预期挂载。
class="type">bool CSampleExpert::InitCheckParameters(class="kw">const class="type">int digits_adjust) { class=class="str">"cmt">//--- 检查源数据 if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel()) { printf("获利必须大于 %d",m_symbol.StopsLevel()); class="kw">return(class="kw">false); } if(InpTrailingStop*digits_adjust<m_symbol.StopsLevel()) { printf("跟踪止损必须大于 %d",m_symbol.StopsLevel()); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- 检查手数值 if(InpLots<m_symbol.LotsMin() || InpLots>m_symbol.LotsMax()) { printf("手数必须在范围 %f 和 %f 之间",m_symbol.LotsMin(),m_symbol.LotsMax()); class="kw">return(class="kw">false); } if(MathAbs(InpLots/m_symbol.LotsStep()-MathRound(InpLots/m_symbol.LotsStep()))>class="num">1.0E-10) { printf("数量没有满足手数步长 %f",m_symbol.LotsStep()); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- warning if(InpTakeProfit<=InpTrailingStop) printf("警告:跟踪止损必须小于获利"); class=class="str">"cmt">//--- 结果 class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 指标的初始化 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::InitIndicators(class="type">void) { class=class="str">"cmt">//--- 创建 MACD 指标 if(handle_MACD==INVALID_HANDLE) { class=class="str">"cmt">//--- handle_MACD=iCustom(NULL,class="num">0,"MTF\\Oscillators\\MTF_MACD",mas_tf[class="num">2],Signal_MACD_PeriodFast,Signal_MACD_PeriodSlow); class=class="str">"cmt">//--- if(handle_MACD==INVALID_HANDLE) { printf("当创建 MACD 指标时出现错误。"); class="kw">return(class="kw">false); } } class=class="str">"cmt">//--- 创建 MA 指标 if(handle_MA==INVALID_HANDLE) { class=class="str">"cmt">//--- handle_MA=iCustom(NULL,class="num">0,"MTF\\Trend\\MA_MultiTF",mas_tf[class="num">2],Signal_MA_PeriodMA,class="num">0,MODE_EMA,PRICE_CLOSE); class=class="str">"cmt">//--- if(handle_MA==INVALID_HANDLE) { printf("当创建 MA 指标时出现错误"); class="kw">return(class="kw">false); } } class=class="str">"cmt">//--- 创建 Stochastic 指标 if(handle_Stochastic==INVALID_HANDLE) { class=class="str">"cmt">//--- handle_Stochastic=iCustom(NULL,class="num">0,"MTF\\Oscillators\\MTF_Stochastic",mas_tf[class="num">1],Signal_Stoch_PeriodK,Signal_Stoch_PeriodD); class=class="str">"cmt">//--- if(handle_Stochastic==INVALID_HANDLE) {
◍ 用双向函数把追踪止损写进EA
CSampleExpert 里把跟踪止损拆成了 LongModified 与 ShortModified 两个成员函数,逻辑对称但方向反着写:多单看 Bid 与开仓价的差值,空单看开仓价与 Ask 的差值,都要求超过 m_adjusted_point*InpTrailingStop 才触发。 以多单为例,触发后先用 NormalizeDouble 按品种小数位把止损算到 Bid - m_traling_stop,再判断原止损小于新值或原为 0.0 才允许改。m_trade.PositionModify(Symbol(),sl,tp) 真正下单修改,成功就打印日志,失败则把 ResultComment 和 SL/TP 数值一并输出,方便在 MT5 终端查错。 空单那段几乎镜像:sl = Ask + m_traling_stop,且只有当持仓止损大于新止损或原止损为 0 时才改,避免把止损往亏损方向拉。外汇与贵金属杠杆高,追踪参数 InpTrailingStop 设太小可能频繁改单触发点差损耗,倾向先在策略测试器跑几组不同点数再上实盘。
class="type">bool CSampleExpert::LongModified(class="type">void) { class="type">bool res=class="kw">false; class=class="str">"cmt">//--- 检查跟踪止损 if(InpTrailingStop>class="num">0) { if(m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop) { class="type">class="kw">double sl=NormalizeDouble(m_symbol.Bid()-m_traling_stop,m_symbol.Digits()); class="type">class="kw">double tp=m_position.TakeProfit(); if(m_position.StopLoss()<sl || m_position.StopLoss()==class="num">0.0) { class=class="str">"cmt">//--- 修改仓位 if(m_trade.PositionModify(Symbol(),sl,tp)) printf("将修改 %s 的买入仓位",Symbol()); else { printf("修改 %s 仓位时出错: &class="macro">#x27;%s&class="macro">#x27;",Symbol(),m_trade.ResultComment()); printf("修改参数 : SL=%f,TP=%f",sl,tp); } class=class="str">"cmt">//--- 已经修改,并且需要退出EA交易 res=true; } } } class=class="str">"cmt">//--- 结果 class="kw">return(res); } class="type">bool CSampleExpert::ShortModified(class="type">void) { class="type">bool res=class="kw">false; class=class="str">"cmt">//--- 检查跟踪止损 if(InpTrailingStop>class="num">0) { if((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop)) { class="type">class="kw">double sl=NormalizeDouble(m_symbol.Ask()+m_traling_stop,m_symbol.Digits()); class="type">class="kw">double tp=m_position.TakeProfit(); if(m_position.StopLoss()>sl || m_position.StopLoss()==class="num">0.0) { class=class="str">"cmt">//--- 修改仓位 if(m_trade.PositionModify(Symbol(),sl,tp)) printf("将要修改 %s 的卖出仓位",Symbol()); else {
用 BuyStop 挂单承接多头触发
当 LongOpened() 返回 true,说明随机指标上穿阈值、MACD 柱走高且均线位于前一根 K 线的高低点下方,此时倾向在更高价位埋 BuyStop 而不是市价追。 代码里先置 res=true 表示已处理,再填充 MqlTradeRequest 结构体。request.deviation 写死为 5 点,意味着成交允许偏离挂单价最多 5 个点,外汇与贵金属跳空时可能直接吃不到单。 request.volume 取外部参数 InpLots,request.magic 用 EXPERT_MAGIC 区分本 EA 订单;offset 由 InpOffset 控制挂单与市价距离,调小它挂单更贴近现价、被触发的概率更高但假突破也更多。 实际验证时把 InpOffset 从默认改到 10~20 点,回看 EURUSD 的 M15 上 BuyStop 触发率与止损频次,再决定阈值。
class="type">bool CSampleExpert::OpenBuyStop(class="type">void) { class="type">bool res=class="kw">false; class="type">class="kw">double tp=class="num">0,sl=class="num">0; class=class="str">"cmt">//--- if(LongOpened()) { res=true; class=class="str">"cmt">//--- 声明和初始化交易请求和结果 class="type">MqlTradeRequest request={class="num">0}; class="type">MqlTradeResult result={class="num">0}; class=class="str">"cmt">//--- 设置挂单的参数 request.action =TRADE_ACTION_PENDING; class=class="str">"cmt">// 交易操作类型 request.symbol =Symbol(); class=class="str">"cmt">// 交易品种 request.deviation=class="num">5; class=class="str">"cmt">// 允许的价格偏差 request.volume =InpLots; class=class="str">"cmt">// 交易手数 request.magic =EXPERT_MAGIC; class=class="str">"cmt">// 订单幻数 class="type">class="kw">double offset=InpOffset; class=class="str">"cmt">// 当前价格距离订单的点差
「挂单触发价与止损止盈的规范化拼接」
在 MT5 里挂 BUY_STOP 时,触发价不能直接拿前一根 K 线高点硬加,必须先用 SymbolInfoDouble(_Symbol,SYMBOL_POINT) 取点值、用 SymbolInfoInteger(_Symbol,SYMBOL_DIGITS) 取报价精度。否则在 XAUUSD 这种 digits=3 或 EURUSD digits=5 的品种上,价格尾差可能让 OrderSend 直接返回 4756(无效价格)报错。 下面这段把 high[1] 加偏移后做了 NormalizeDouble,再顺手把 tp/sl 一起规范化写进 request 结构。注意 sl/tp 用的是 _Digits 而非局部 digits 变量,两者在多数品种一致,但跨品种 EA 里混用可能埋雷。 double price; // 订单触发价格 double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); // 点的大小 int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); // 小数点位数 (digits) //--- 操作类型 request.type=ORDER_TYPE_BUY_STOP; // 订单类型 price=high[1]+offset*m_adjusted_point; // 开启价格 request.price=NormalizeDouble(price,digits); // 规范化的开启价格 tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); // 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); // 把新的获利值加到结构中 //--- 发送请求 if(!OrderSend(request,result)) {res=false;printf("OrderSend error %d",GetLastError());} // 如果无法发送请求,输出错误代码 //--- 关于操作的信息 printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); //--- 退出 EA } //--- 结果 return(res); } 卖出侧的条件判断另写了一个 ShortOpened:当 stoch_ind_1 大于 (100-InpLevel_S) 且 stoch_ind_0 下穿该阈值,同时 macd_ind_1 小于 macd_ind_0、均线 ma_ind 高于前一根收盘价与开盘价时,返回 true。外汇与贵金属杠杆高,这类共振信号只是提高触发概率,实盘仍可能连续止损,建议先在策略测试器跑 2020—2023 年数据验证。 OpenSellStop 函数开头只声明了 tp/sl 初值 0,真正的 SELL_STOP 挂单价算法和 BUY_STOP 对称,改 request.type 为 ORDER_TYPE_SELL_STOP 并把 price 锚在 low[1] 下方即可,开 MT5 把这段补完就能跑。
class="type">class="kw">double price; class=class="str">"cmt">// 订单触发价格 class="type">class="kw">double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); class=class="str">"cmt">// 点的大小 class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">// 小数点位数 (digits) class=class="str">"cmt">//--- 操作类型 request.type=ORDER_TYPE_BUY_STOP; class=class="str">"cmt">// 订单类型 price=high[class="num">1]+offset*m_adjusted_point; class=class="str">"cmt">// 开启价格 request.price=NormalizeDouble(price,digits); class=class="str">"cmt">// 规范化的开启价格 tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); class=class="str">"cmt">// 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); class=class="str">"cmt">// 把新的获利值加到结构中 class=class="str">"cmt">//--- 发送请求 if(!OrderSend(request,result)) {res=class="kw">false;printf("OrderSend error %d",GetLastError());} class=class="str">"cmt">// 如果无法发送请求,输出错误代码 class=class="str">"cmt">//--- 关于操作的信息 printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); class=class="str">"cmt">//--- 退出 EA } class=class="str">"cmt">//--- 结果 class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 检查建立卖出仓位的条件 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::ShortOpened(class="type">void) { class="type">bool res=class="kw">false; level_stoch=class="num">100-InpLevel_S; class=class="str">"cmt">//--- 检查是否可以建立卖出仓位 (SELL) if(stoch_ind_1>level_stoch && stoch_ind_0<level_stoch && macd_ind_1<macd_ind_0 && ma_ind>close[class="num">1] && ma_ind>open[class="num">1]) { class=class="str">"cmt">//--- 退出 EA res=true; } class=class="str">"cmt">//--- 结果 class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 开启一个止损卖出挂单 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::OpenSellStop(class="type">void) { class="type">bool res=class="kw">false; class="type">class="kw">double tp=class="num">0,sl=class="num">0;
◍ 空单持仓后如何挂 Sell Stop 追空
当 ShortOpened() 返回 true,说明当前已持有空单,这时程序会尝试在更低价位挂一张 SELL_STOP pending 单,用来在破位下行时自动加仓或接力。外汇与贵金属杠杆高,这类追空挂单若遇反向跳空可能扩大浮亏,实盘前务必在 MT5 策略测试器里跑一遍。 下面这段逻辑先把交易请求和结果结构体清零,再逐项填参数:action 设为 TRADE_ACTION_PENDING 表示发挂单;symbol 取当前品种;deviation 给 5 点允许偏差;volume 用外部输入手数 InpLots;magic 用专家顾问幻数做订单标记。 挂单位置由前一根低点 low[1] 减去 offset 个 adjusted_point 算出,offset 就是 InpOffset 控制的距离。订单类型写死 ORDER_TYPE_SELL_STOP,价格经 NormalizeDouble 按品种小数位规范化,避免报价精度不符被拒。 止损止盈以挂单价格为中心:tp = price - m_take_profit,sl = price + m_stop_loss,同样规范化后写回 request。最后 OrderSend 发出请求,失败则 res 置 false 并打印 GetLastError 代码,方便你直接查 MT5 终端的报错原因。
if(ShortOpened()) { res=true; class=class="str">"cmt">//--- 声明和初始化交易请求和结果 class="type">MqlTradeRequest request={class="num">0}; class="type">MqlTradeResult result={class="num">0}; class=class="str">"cmt">//--- 设置挂单的参数 request.action =TRADE_ACTION_PENDING; class=class="str">"cmt">// 交易操作类型 request.symbol =Symbol(); class=class="str">"cmt">// 交易品种 request.deviation=class="num">5; class=class="str">"cmt">// 允许的价格偏差 request.volume=InpLots; class=class="str">"cmt">// 交易手数 request.magic=EXPERT_MAGIC; class=class="str">"cmt">// 订单幻数 class="type">int offset=InpOffset; class=class="str">"cmt">// 当前价格距离挂单的点数距离 class="type">class="kw">double price; class=class="str">"cmt">// 订单触发价格 class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">// 小数点位数 (digits) request.type=ORDER_TYPE_SELL_STOP; class=class="str">"cmt">// 订单类型 price=low[class="num">1]-offset*m_adjusted_point; class=class="str">"cmt">// 开启价格 request.price=NormalizeDouble(price,digits); class=class="str">"cmt">// 规范化的开启价格 tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); class=class="str">"cmt">// 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); class=class="str">"cmt">// 把新的获利值加到结构中 class=class="str">"cmt">//--- 发送请求 if(!OrderSend(request,result)) {res=class="kw">false;printf("OrderSend error %d",GetLastError());} class=class="str">"cmt">// 如果无法发送请求,输出错误代码 class=class="str">"cmt">//--- 关于操作的信息
Processing 函数里的数据同步与持仓巡检
EA 主循环里真正干活的 Processing() 先不做任何交易判断,而是把行情和指标缓冲刷到本地。m_symbol.RefreshRates() 失败就直接返 false,避免用过期报价算信号;之后对 Stochastic、MACD、MA 三个句柄都做 BarsCalculated 小于 barsCalculated 或 CopyBuffer 拿不到足量大棒的拦截,任一不满足即退出。 数据落袋后,代码把缓冲下标 1 和 0 分别赋给 macd_ind_0/1、ma_ind、stoch_ind_0/1,相当于用 [1] 存上一根、[0] 存最新一根,后续逻辑只碰这几个变量,省掉反复查数组的开销。这里 barsCalculated 若设成 3,就要求至少 3 根历史棒完成计算才放行。 持仓侧先 m_position.Select(Symbol()) 并尝试 PositionsTotal()>0 且跟踪止损开关 m_traling_stop 非零,才进多空分支调用 LongModified() 或 ShortModified(),改单成功直接 return(true) 交还控制权。 挂单处理另起 for(int i=0;i<OrdersTotal();i++) 循环,逐个选定挂单取 ticket,为后面按单号改止损或激活条件单做准备。外汇与贵金属杠杆高,这类自动改仓逻辑在滑点扩大时可能失效,实盘前务必在 MT5 策略测试器用历史数据跑通。
class="type">bool CSampleExpert::Processing(class="type">void) { if(!m_symbol.RefreshRates()) class="kw">return(class="kw">false); if(BarsCalculated(handle_Stochastic)<barsCalculated) class="kw">return(class="kw">false); if(CopyBuffer(handle_Stochastic,class="num">0,class="num">0,barsCalculated,stoch_buff)!=barsCalculated) class="kw">return(class="kw">false); if(BarsCalculated(handle_MACD)<barsCalculated) class="kw">return(class="kw">false); if(CopyBuffer(handle_MACD,class="num">0,class="num">0,barsCalculated,macd_buff)!=barsCalculated) class="kw">return(class="kw">false); if(BarsCalculated(handle_MA)<barsCalculated) class="kw">return(class="kw">false); if(CopyBuffer(handle_MA,class="num">0,class="num">0,barsCalculated,ma_buff)!=barsCalculated) class="kw">return(class="kw">false); if(!Copy())class="kw">return(class="kw">false); macd_ind_0 = macd_buff[class="num">1]; macd_ind_1 = macd_buff[class="num">0]; ma_ind = ma_buff[class="num">1]; stoch_ind_0 = stoch_buff[class="num">1]; stoch_ind_1 = stoch_buff[class="num">0]; class="type">bool bord=class="kw">false,sord=class="kw">false; class="type">class="kw">ulong ticket; if(m_position.Select(Symbol()) && PositionsTotal()>class="num">0 && m_traling_stop!=class="num">0) { if(m_position.PositionType()==POSITION_TYPE_BUY) { bord=true; if(LongModified()) class="kw">return(true); } else { sord=true; if(ShortModified()) class="kw">return(true); } } for(class="type">int i=class="num">0;i<OrdersTotal();i++) {
「挂单遍历与止损单动态重挂」
EA 在每根 K 线刷新时先扫一遍终端所有挂单,用 OrderGetTicket(i) 按索引取出单号,再判断类型。若是 BUY_STOP 就置 bord=true 并调 OrderModifyBuyStop(),SELL_STOP 则置 sord=true 并调 OrderModifySellStop(),这一步决定已有挂单要不要跟着价格挪位置。 若全场扫完 sord 仍为 false,说明没有止损卖单,就 OpenSellStop() 新建;bord 为 false 则 OpenBuyStop() 新建,建完立刻 return(true) 中止本次处理。两个标志都已有就 return(false),不重复刷单。 修改函数里先抓 SYMBOL_DIGITS 决定报价精度,offset 来自外部参数 InpOffset,代表挂单价离现价的点数距离。下面这段是 SellStop 修改函数的头部,重点在按 OrdersTotal() 倒序遍历、用 OrderGetInteger(ORDER_MAGIC) 核对幻数,避免动到手动单或其他 EA 的单。 外汇与贵金属杠杆高,挂单被滑点吞掉或重复触发都可能发生,任何重挂逻辑都建议在策略测试器用真实点差回测确认。
ticket=OrderGetTicket(i); class=class="str">"cmt">// 处理止损买入订单 if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { class=class="str">"cmt">// 设置标志,指出有止损买入订单 bord=true; class=class="str">"cmt">//--- 正确进入市场是很重要的,必要时移动订单 if(bord)OrderModifyBuyStop(); class=class="str">"cmt">// 修改 BUYSTOP 订单 } class=class="str">"cmt">// 处理止损卖出订单 if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { class=class="str">"cmt">// 设置标志指出有止损卖出订单 sord=true; class=class="str">"cmt">//--- 正确进入市场是很重要的,必要时移动订单 if(sord)OrderModifySellStop(); class=class="str">"cmt">// 修改 SELLSTOP 订单 } } class=class="str">"cmt">//--- 如果没有订单,就进行设置 --------------------------------+ if(!sord)if(OpenSellStop()){sord=true;class="kw">return(true);} if(!bord)if(OpenBuyStop()){bord=true;class="kw">return(true);} class=class="str">"cmt">//--- 不进行仓位处理而退出 class="kw">return(class="kw">false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|修改之前设置的止损卖出挂单的参数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CSampleExpert::OrderModifySellStop(class="type">void) { class="type">bool res=true; class="type">class="kw">ulong ticket; class="type">class="kw">double tp=class="num">0,sl=class="num">0; class="type">class="kw">double offset=InpOffset; class=class="str">"cmt">// 当前价格距离订单的点差 class="type">class="kw">double price; class=class="str">"cmt">// 订单触发价格 class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">// 小数点位数 (digits) class=class="str">"cmt">// 声明并初始化交易请求和结果 class="type">MqlTradeRequest request={class="num">0}; class="type">MqlTradeResult result={class="num">0}; class="type">int total=OrdersTotal(); class=class="str">"cmt">// 挂单的数量 class=class="str">"cmt">//--- 迭代所有设置的挂单 for(class="type">int i=total-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">// 选择每个挂单并取得它的单号 ticket=OrderGetTicket(i); class=class="str">"cmt">// 处理止损卖出订单 if(m_order.OrderType()==ORDER_TYPE_SELL_STOP) { class="type">class="kw">ulong magic=OrderGetInteger(ORDER_MAGIC); class=class="str">"cmt">// 订单幻数
◍ 挂单改价时的双低点过滤
在遍历持仓或挂单时,先用幻数 EXPERT_MAGIC 筛出本 EA 自己下的单,避免误改其他策略的仓位。这一步是后续所有改单逻辑的前提,没匹配到就直接跳过。 价格计算以 low[1] 为基准,减去 offset*m_adjusted_point 得到新的挂单触发价。注意这里要求 price 必须大于原开仓价 m_order.PriceOpen(),且 low[1] > low[2],也就是最近一根 K 线低点高于前一根——双条件同时满足才允许下移挂单,倾向过滤掉弱势延续的假突破。 请求结构里 action 设为 TRADE_ACTION_MODIFY,填入 OrderGetTicket(i) 取到的单号与 Symbol() 当前品种,deviation 用 InpOffset 控制重价容差。SL/TP 按 price±固定点数重算后 NormalizeDouble 到 _Digits 精度,最后 OrderSend 发出;若返回失败则 printf 打错误码并置 res=true 标记未处理完。 实盘里把 offset 和 m_stop_losse / m_take_profit 调小一点,能在 5 分钟图上让改单触发更密集,但外汇与贵金属杠杆高,频繁改单可能放大滑点风险,建议先开 MT5 策略测试器跑一遍观察 retcode 分布。
if(magic==EXPERT_MAGIC) { price=low[class="num">1]-offset*m_adjusted_point; class=class="str">"cmt">// 开启价格 if(price>m_order.PriceOpen()) class=class="str">"cmt">// 检查条件 if(low[class="num">1]>low[class="num">2]) class=class="str">"cmt">// 检查条件 { request.action=TRADE_ACTION_MODIFY; class=class="str">"cmt">// 交易操作类型 request.order = OrderGetTicket(i); class=class="str">"cmt">// 订单单号 request.symbol =Symbol(); class=class="str">"cmt">// 交易品种 request.deviation=InpOffset; class=class="str">"cmt">// 允许的价格偏移 price=low[class="num">1]-offset*m_adjusted_point; class=class="str">"cmt">// 开启价格 request.price=NormalizeDouble(price,digits); class=class="str">"cmt">// 规范化的开启价格 tp=price-m_take_profit; sl=price+m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); class=class="str">"cmt">// 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); class=class="str">"cmt">// 把新的获利值加到结构中 class=class="str">"cmt">//--- 发送请求 if(!OrderSend(request,result)) { class=class="str">"cmt">// 设置标志指出止损卖出订单没有被删除 res=true; printf("OrderSend 错误 %d",GetLastError()); } class=class="str">"cmt">// 如果无法发送请求,输出错误代码 class=class="str">"cmt">//--- 关于操作的信息 printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } class="kw">return(res);
动态重挂 BuyStop 的触发价
在 EA 运行期间,已挂出的止损买入单若一直不触发,价格结构可能已经偏移,原挂单价就容易悬在远离当前波动区的位置。这段逻辑做的就是按最新 K 线高点加偏移,把老挂单的触发价往下拉到更贴近市场的位置。
核心判断只有一句:当计算出的新价 price 小于原挂单开盘价 m_order.PriceOpen() 时才改单,避免把单子往远离市场的方向推。外汇与贵金属杠杆高,重挂操作若 offset 设太小,可能在快速行情中连续触发修改请求,增加服务器拒绝概率。
循环从 OrdersTotal()-1 倒序遍历,只挑 ORDER_TYPE_BUY_STOP 且幻数等于 EXPERT_MAGIC 的订单处理,防止动到手动单或其他 EA 的单。下面代码拆开看,每一行都在填 MqlTradeRequest 的修改字段。
class="type">bool CSampleExpert::OrderModifyBuyStop(class="type">void) { class="type">bool res=true; class="type">class="kw">ulong ticket; class="type">class="kw">double tp=class="num">0,sl=class="num">0; class="type">class="kw">double offset=InpOffset; class=class="str">"cmt">// 当前价格距离订单的点差 class="type">class="kw">double price; class=class="str">"cmt">// 订单触发价格 class="type">int digits=(class="type">int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); class=class="str">"cmt">// 小数点位数 (digits) class=class="str">"cmt">//--- 声明和初始化交易请求和结果 class="type">MqlTradeRequest request={class="num">0}; class="type">MqlTradeResult result={class="num">0}; class="type">int total=OrdersTotal(); class=class="str">"cmt">// 挂单的数量 class=class="str">"cmt">//--- 迭代所有设置的挂单 for(class="type">int i=total-class="num">1; i>=class="num">0; i--) { class=class="str">"cmt">// 选择每个挂单并取得它的单号 ticket=OrderGetTicket(i); class=class="str">"cmt">// 处理止损买入订单 if(m_order.OrderType()==ORDER_TYPE_BUY_STOP) { class="type">class="kw">ulong magic=OrderGetInteger(ORDER_MAGIC); class=class="str">"cmt">// 订单幻数 class=class="str">"cmt">//--- 如果幻数匹配 if(magic==EXPERT_MAGIC) { price=high[class="num">1]+offset*m_adjusted_point; class=class="str">"cmt">// 开仓价格 if(price<m_order.PriceOpen()) class=class="str">"cmt">// check the conditions { request.action=TRADE_ACTION_MODIFY; class=class="str">"cmt">// 交易操作类型 request.symbol =Symbol(); class=class="str">"cmt">// 交易品种 request.action=TRADE_ACTION_MODIFY; class=class="str">"cmt">// 交易操作类型 request.order = OrderGetTicket(i); class=class="str">"cmt">// 订单单号
「下单请求的结构填充与发送」
挂单逻辑走到实际出手这一步,核心是把 MQL5 的 MqlTradeRequest 结构逐项填实,再交给 OrderSend 去跑。下面这段截取自一个止损买入挂单的改写函数,可以看到价格、止损、止盈都先用 NormalizeDouble 按品种精度规整,避免跨品种时点值位数不对导致 reject。
request.symbol 直接取 Symbol() 锁定当前图表品种;deviation 用外部输入 InpOffset 控制重价容忍,滑点大的贵金属盘口这个值设太小容易吃不到单。price 用 NormalizeDouble(price,digits) 规范后,tp、sl 以 price 为锚加减类成员变量 m_take_profit / m_stop_losse 算出,再各自 NormalizeDouble 到 _Digits 精度写回 request。
发送环节若 OrderSend 返回 false,代码把 res 置 true 标记“挂单未删”,并用 printf 打出 GetLastError 的错误码——这一步在 MT5 策略测试器日志里排错最直接,比如 10030 就是价格偏离超限。成功与否都会 printf 出 retcode、deal、order 三个 uint64 字段,方便对照交易池回执。
OnInit 里只做 ExtExpert.Init() 成败判断,失败返 INIT_FAILED 阻止 EA 加载;OnTick 用 static datetime limit_time 做节流,Bars(Symbol(),Period())>barsCalculated 才进处理,避免每 tick 都重算。外汇与贵金属杠杆高,挂单未成交前也占用保证金敞口,验证时建议先开模拟盘跑这套节流逻辑。
request.symbol =Symbol(); class=class="str">"cmt">// 交易品种 request.deviation=InpOffset; class=class="str">"cmt">// 允许的价格偏移 class=class="str">"cmt">//--- set the price level, take profit and stop loss request.price=NormalizeDouble(price,digits); class=class="str">"cmt">// 规范化的开启价格 tp=price+m_take_profit; sl=price-m_stop_losse; request.sl =NormalizeDouble(sl,_Digits); class=class="str">"cmt">// 把新的止损值加到结构中 request.tp =NormalizeDouble(tp,_Digits); class=class="str">"cmt">// 把新的获利值加到结构中 class=class="str">"cmt">//--- 发送请求 if(!OrderSend(request,result)) { class=class="str">"cmt">// 设置标志指出止损买入挂单没有被删除 res=true; printf("OrderSend 错误 %d",GetLastError()); } class=class="str">"cmt">// 如果无法发送请求,输出错误代码 class=class="str">"cmt">//--- 关于操作的信息 printf("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order); } } } } class="kw">return(res); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| EA交易初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit(class="type">void) { class=class="str">"cmt">//--- 创建所有所需的对象 if(!ExtExpert.Init()) class="kw">return(INIT_FAILED); class=class="str">"cmt">//--- ok class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 分时处理函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick(class="type">void) { class="kw">static class="type">class="kw">datetime limit_time=class="num">0; class=class="str">"cmt">// 上次的处理时间 + 超时 class=class="str">"cmt">//--- 不处理直到超时 if(TimeCurrent()>=limit_time) { class=class="str">"cmt">//--- 检查数据 if(Bars(Symbol(),Period())>barsCalculated) {
◍ 跨周期抓取三根 K 线的复制逻辑
EA 在 Copy 方法里固定拉取最近 3 根柱形的 OHLC 数据,copied 写死为 3,意味着你只拿到当前与前面两根的快照,回看深度到此为止。 数组先用 ArrayResize 按 copied 撑开,再统一 ArraySetAsSeries(...,true) 把索引方向翻成时序倒序,这样 high[0] 永远是最新一根最高价,写条件判断时不用反向数下标。 注意最高价和最低价取自 mas_tf[0] 的周期,而开盘、收盘却来自 mas_tf[2]——两套时间框架混用,若这两个下标指向不同周期,你的信号其实是跨周期拼接的,外汇与贵金属这种高波动品种下错位可能直接引发误触发。 四段 CopyHigh / CopyLow / CopyClose / CopyOpen 任一返回小于 0 就打印错误并 return false,实盘里若频繁看到“未能复制”日志,优先查 mas_tf 下标越界或对应图表周期没打开。
class="type">bool CSampleExpert::Copy(class="type">void) { class=class="str">"cmt">//--- 要复制的数量 class="type">int copied=class="num">3; class=class="str">"cmt">//--- ArrayResize(high,copied); ArrayResize(low,copied); ArrayResize(close,copied); ArrayResize(open,copied); class=class="str">"cmt">//+------- 设置数组索引方向 ---------------------+ ArraySetAsSeries(high,true); ArraySetAsSeries(low,true); ArraySetAsSeries(close,true); ArraySetAsSeries(open,true); class=class="str">"cmt">//--- 复制柱形价格 if(CopyHigh(NULL,mas_tf[class="num">0],class="num">0,copied,high)<class="num">0) {printf("未能复制最高价",GetLastError());class="kw">return(class="kw">false);} class=class="str">"cmt">//--- if(CopyLow(NULL,mas_tf[class="num">0],class="num">0,copied,low)<class="num">0) {printf("未能复制最低价",GetLastError());class="kw">return(class="kw">false);} class=class="str">"cmt">//--- if(CopyClose(NULL,mas_tf[class="num">2],class="num">0,copied,close)<class="num">0) {printf("未能复制收盘价",GetLastError());class="kw">return(class="kw">false);} class=class="str">"cmt">//--- if(CopyOpen(NULL,mas_tf[class="num">2],class="num">0,copied,open)<class="num">0) {printf("未能复制开盘价",GetLastError());class="kw">return(class="kw">false);} class=class="str">"cmt">//--- class="kw">return(true); }
多周期指标的延伸空间
MQL5 原生并没有给软件模块作者开放直接生成多时间框架(MTF)算法的接口,但这类指标在实战里仍有价值:同一品种在不同 TF 上同步读市场状态,能压低策略测试器的空转、让平滑表现更干净。 附件里那批 MTF 指标已经铺开了路——趋势类有 MA_MultiPeriod、MA_MultiTF、MTF_BB 等 6 个,振荡类覆盖 MTF_RSI、MTF_MACD、MTF_Stochastic 等 8 个,全部走 MQL5\Indicators\MTF\ 路径,复制进 MT5 就能挂。 示例代码难免有坑。有用户反馈 Triple Screen Trading System 1.0 编译报 'cannot convert 0 to enum ENUM_TRADE_REQUEST_ACTIONS',社区给的解法是把那几个 0 直接删掉即可过编;另有人在 M1 切到 M5 时碰见 MA_MultiTF 报 'Not All data of ExtHandle1 is calculated (-1 bars). Error4806',先挂个别的指标再回看往往就正常。 外汇与贵金属杠杆高、滑点狠,MTF 只是把视角叠厚,不替你过滤风险。拿下面这段日志当线索,去 MT5 终端把对应指标重跑一遍,比看结论更实在。
class="type">MqlTradeRequest request = {}; class="type">MqlTradeResult result = {}; class="num">2023.11.class="num">04 class="num">07:class="num">31:class="num">15.541 MA_MultiTF(EURUSD,M1) Not All data of ExtHandle1 is calculated(-class="num">1 bars ). Error4806