利用解析入场点为指标的技术创建新的交易策略·综合运用
📘

利用解析入场点为指标的技术创建新的交易策略·综合运用

第 3/3 篇

◍ 用 MACD 阈值卡住多空触发点

这段逻辑把 MACD 柱值夹在 ±0.0015 到 ±0.0035 的窄区间里做信号过滤,区间外的动量被认为太弱或太极端,不进场。 多头分支在 macd_data[0] 落在 [0.0015, 0.0035] 时,以 ask 开买、bid-sl 挂止损、ask+tp 挂止盈;空头分支对称处理,macd_data[0] 在 [-0.0035, -0.0015] 时以 bid 开卖。 每次 new CDeal 后都用 CheckPointer 确认对象有效,再 Deals.Add 入列,并调 IndicatorsStatic.SaveNewValues(Deals.Total()-1) 把当前指标快照存进对应成交槽。 后面接了 Chaikin 振荡器(iChaikin)做二次确认:买需 cho_data[1]-cho_data[0] < 0(动能拐头向下但仍处正 MACD 区),卖需差值为正。外汇与贵金属杠杆高,这种窄阈值策略在震荡市可能频繁假触发,建议先上 MT5 用策略测试器跑 EURUSD 的 M15 验证命中率。

MQL5 / C++
deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
if(CheckPointer(deal)!=POINTER_INVALID)
  if(Deals.Add(deal))
    IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
 }
 if(macd_data[class="num">0]<=(-class="num">0.0015) && macd_data[class="num">0]>=(-class="num">0.0035))
 {
   deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
   if(CheckPointer(deal)!=POINTER_INVALID)
     if(Deals.Add(deal))
       IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
 }
class="type">int cho;
 handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period2, Ch_Slow_Period2, Ch_Method, Ch_Volume);
 name="CHO("+IntegerToString(Ch_Slow_Period2)+","+IntegerToString(Ch_Fast_Period2)+") "+tf_name;
 if(handle<class="num">0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
   class="kw">return false;
 if(timeframe==TimeFrame3)
   cho=handle;
 class="type">class="kw">double cho_data[];
 if(CopyBuffer(atr,class="num">0,class="num">1,class="num">1,atrs)<=class="num">0 || CopyBuffer(force,class="num">0,class="num">1,class="num">1,force_data)<=class="num">0 || CopyBuffer(macd,class="num">0,class="num">1,class="num">1,macd_data)<=class="num">0
   || CopyBuffer(cho,class="num">0,class="num">1,class="num">2,cho_data)<class="num">2)
   class="kw">return;
 if(macd_data[class="num">0]>=class="num">0.0015 && macd_data[class="num">0]<=class="num">0.0035 && (cho_data[class="num">1]-cho_data[class="num">0])<class="num">0)
  {
   deal = new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
   if(CheckPointer(deal)!=POINTER_INVALID)
     if(Deals.Add(deal))
       IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
  }
 if(macd_data[class="num">0]<=(-class="num">0.0015) && macd_data[class="num">0]>=(-class="num">0.0035) && (cho_data[class="num">1]-cho_data[class="num">0])>class="num">0)
  {
   deal = new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
   if(CheckPointer(deal)!=POINTER_INVALID)
     if(Deals.Add(deal))
       IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
  }
class="type">int cci;
 name="CCI("+IntegerToString(CCI_Period1)+") "+tf_name;

多指标共振的开仓判定逻辑

下面这段 MT5 EA 片段展示了如何用 CCI 配合 MACD、CHO、ATR、Force 做多指标共振过滤。核心思路是:MACD 柱值被限制在一个窄区间(0.0015~0.0035 或 -0.0035~-0.0015),同时 CHO 与 CCI 出现反向斜率,才触发挂单。 先看指标句柄获取与缓冲拷贝。iCCI 创建 CCI 句柄后,若失败或无法加入缓冲就直接返回 false;当周期等于 TimeFrame3 时把句柄存到 cci 变量。随后用 CopyBuffer 一次性拉取 atr、force、macd、cho、cci 各 1~2 根历史值,任一拷贝长度不达标就退出,避免脏数据进场。 开多条件写得很死:macd_data[0] 必须在 0.0015 到 0.0035 之间,cho_data[1]-cho_data[0] 小于 0(CHO 向下),cci_data[1]-cci_data[0] 大于 0(CCI 向上)。满足后 new 一个 CDeal 买价单,sl/tp 用 ask±变量算好,指针有效就加入 Deals 并保存指标值。 开空完全对称:macd_data[0] 在 -0.0015 到 -0.0035,CHO 向上、CCI 向下,生成卖价单。外汇与贵金属波动剧烈,这种窄区间共振在震荡市可能频繁信号、趋势市可能踏空,上 MT5 用策略测试器跑 EURUSD 15M 看命中率再决定是否放宽 MACD 区间。

MQL5 / C++
  class="type">int handle = iCCI(_Symbol, timeframe, CCI_Period1, CCI_Price);
  if(handle<class="num">0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      class="kw">return false;
  if(timeframe==TimeFrame3)
      cci=handle;
  class="type">class="kw">double cci_data[];
  if(CopyBuffer(atr,class="num">0,class="num">1,class="num">1,atrs)<=class="num">0 || CopyBuffer(force,class="num">0,class="num">1,class="num">1,force_data)<=class="num">0 || CopyBuffer(macd,class="num">0,class="num">1,class="num">1,macd_data)<=class="num">0
      || CopyBuffer(cho,class="num">0,class="num">1,class="num">2,cho_data)<class="num">2 || CopyBuffer(cci,class="num">0,class="num">1,class="num">2,cci_data)<class="num">2)
      class="kw">return;
  if(macd_data[class="num">0]>=class="num">0.0015 && macd_data[class="num">0]<=class="num">0.0035 && (cho_data[class="num">1]-cho_data[class="num">0])<class="num">0 && (cci_data[class="num">1]-cci_data[class="num">0])>class="num">0)
   {
     deal  =  new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
     if(CheckPointer(deal)!=POINTER_INVALID)
       if(Deals.Add(deal))
         IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
   }
  if(macd_data[class="num">0]<=(-class="num">0.0015) && macd_data[class="num">0]>=(-class="num">0.0035) && (cho_data[class="num">1]-cho_data[class="num">0])>class="num">0 && (cci_data[class="num">1]-cci_data[class="num">0])<class="num">0)
   {
     deal  =  new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
     if(CheckPointer(deal)!=POINTER_INVALID)
       if(Deals.Add(deal))
         IndicatorsStatic.SaveNewValues(Deals.Total()-class="num">1);
   }

「把四指标策略塞进EA跑回测」

策略定了四个指标做决策,再用 ATR 管止损止盈,下一步就是写成 EA 丢进测试器看真实表现。这一阶段不碰资金管理,所有订单固定手数,输入参数里先把指标周期、手数、报酬风险比都暴露出来方便调。 EA 骨架很直接:全局里放交易类实例、指标句柄、记录已处理柱线和上次交易日的辅助变量;OnInit 初始化指标并赋初值,OnDeinit 释放句柄。OnTick 开头先判新柱,只在开盘 10 秒内且未超最大时延才允许开仓,等于一根 K 线只吃一个信号。 指标取数若报错直接退出;力度指数不满足滤波就等下一根。多头信号来了先查持仓——空单先平,多单浮亏则忽略。参数算完发单,空头对称处理。完整代码随文附上,复制就能编。 为避开曲线拟合,测试拉长到 2015.01.01–2017.01.12,初始本金 10000 美元、1 手交易。结果净值获利 74.8%,最大余额回撤 12.4%、净值回撤 23.8%,共 44 笔(多空各 22),但盈利单占比仅 18.2%——根子在那 15:1 的报酬风险比定太高,留了明显优化口子。外汇与贵金属杠杆品种回撤波动剧烈,这类回测仅作逻辑验证,实盘仍属高风险。

MQL5 / C++
class=class="str">"cmt">//--- 输入参数
input class="type">class="kw">double            Lot                =  class="num">0.1            ;
input class="type">class="kw">double            Reward_Risk         =  class="num">15.0           ;
input ENUM_TIMEFRAMES   ATR_TimeFrame       =  PERIOD_M5      ;
input class="type">int               ATR_Period          =  class="num">288            ;
input class="type">class="kw">string            s1                  =  "CCI"          ; class=class="str">"cmt">//---
input ENUM_TIMEFRAMES   CCI_TimeFrame       =  PERIOD_D1      ;
input class="type">uint              CCI_Period          =  class="num">14             ;
input ENUM_APPLIED_PRICE CCI_Price           =  PRICE_TYPICAL ;
input class="type">class="kw">string            s2                  =  "Chaikin"      ; class=class="str">"cmt">//---
input ENUM_TIMEFRAMES   Ch_TimeFrame        =  PERIOD_D1      ;
input class="type">uint              Ch_Fast_Period      =  class="num">6              ;
input class="type">uint              Ch_Slow_Period      =  class="num">28             ;
input ENUM_MA_METHOD    Ch_Method           =  MODE_EMA       ;
input ENUM_APPLIED_VOLUME Ch_Volume         =  VOLUME_TICK    ;
input class="type">class="kw">string            s3                  =  "Force Index"  ; class=class="str">"cmt">//---

◍ 指标句柄的初始化与周期边界

把 ATR、Force、MACD、Chaikin、CCI 五个指标一次性挂到 EA 上,核心在 OnInit 里用 iATR / iForce / iMACD / iChaikin / iCCI 拿句柄,任一句柄返回 INVALID_HANDLE 就直接 INIT_FAILED,MT5 会拒绝加载。 输入参数里 Force 默认 PERIOD_M5、周期 56、SMA、VOLUME_TICK;MACD 默认快 12 / 慢 26 / 信号 9、收盘价。这些数字直接决定后续信号计算的采样粒度,调参时先动 Force_Period 比动 MACD 更能改变趋势敏感度。 最后一段用 fmax / fmin 在四个时间帧里挑出 MaxPeriod 与 MinPeriod,这是给后面控制 K 线同步用的——若你切到 M15 图表但指标最小帧是 M5,同步逻辑就得靠这两个值卡边界。外汇与贵金属杠杆高,句柄初始化失败往往意味着品种不可交易或时段无数据,上真实账户前先在策略测试器跑一遍。

MQL5 / C++
input ENUM_TIMEFRAMES   Force_TimeFrame   = PERIOD_M5       ;
input class="type">uint                Force_Period      = class="num">56               ;
input ENUM_MA_METHOD      Force_Method      = MODE_SMA         ;
input ENUM_APPLIED_VOLUME Force_Volume      = VOLUME_TICK      ;
input class="type">class="kw">string              s4                = "MACD"           ; class=class="str">"cmt">//---
input ENUM_TIMEFRAMES     MACD_TimeFrame    = PERIOD_M5        ;
input class="type">uint                MACD_Fast         = class="num">12               ;
input class="type">uint                MACD_Slow         = class="num">26               ;
input class="type">uint                MACD_Signal       = class="num">9                ;
input ENUM_APPLIED_PRICE  MACD_Price        = PRICE_CLOSE      ;
class="type">int OnInit()
  {
class=class="str">"cmt">//---
   last_bar=class="num">0;
   last_deal=class="num">0;
class=class="str">"cmt">//---
   atr=iATR(_Symbol,ATR_TimeFrame,ATR_Period);
   if(atr==INVALID_HANDLE)
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   force=iForce(_Symbol,Force_TimeFrame,Force_Period,Force_Method,Force_Volume);
   if(force==INVALID_HANDLE)
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   macd=iMACD(_Symbol,MACD_TimeFrame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price);
   if(macd==INVALID_HANDLE)
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   cho=iChaikin(_Symbol,Ch_TimeFrame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume);
   if(cho==INVALID_HANDLE)
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   cci=iCCI(_Symbol,CCI_TimeFrame,CCI_Period,CCI_Price);
   if(cci==INVALID_HANDLE)
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//---
   MaxPeriod=fmax(Force_TimeFrame,MACD_TimeFrame);
   MaxPeriod=fmax(MaxPeriod,Ch_TimeFrame);
   MaxPeriod=fmax(MaxPeriod,CCI_TimeFrame);
   MinPeriod=fmin(Force_TimeFrame,MACD_TimeFrame);
   MinPeriod=fmin(MinPeriod,Ch_TimeFrame);
   MinPeriod=fmin(MinPeriod,CCI_TimeFrame);
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }

指标句柄释放与多周期触发门槛

EA 卸载时若不清掉自定义指标句柄,MT5 会在策略测试器里留下僵尸资源,反复加载回测时内存占用可能线性爬升。OnDeinit 里对 atr、force、macd、cho、cci 五个句柄逐个判 INVALID_HANDLE 再 IndicatorRelease,是低成本防漏的标准动作。 OnTick 的入口过滤决定了实盘触发频率。代码用 cur_bar<=last_bar 封掉同根 K 线重复计算,并用 (cur_time-cur_bar)>10 把「距当前小周期 bar 开盘超 10 秒」的延迟 tick 拒之门外;cur_max<=last_deal 则卡住大周期未换柱时的无效扫描。这三道门槛能把无谓的 CopyBuffer 调用砍掉大半。 数据拷贝环节有个隐藏坑:cho 和 cci 取的是 2 根柱(索引 1 和 0),其余取 1 根。若 CopyBuffer 返回值小于 2 直接 return,说明大周期数据没齐,此时硬算价差 (cho_data[1]-cho_data[0]) 会越界或失真。 force 的归一化用 _Point*1000 当步长,MathAbs 后小于 0.01 就退出——相当于力指标绝对值不足 1 个点(标准点对大多数品种已极小)时不参与信号。macd 则先除 _Point*50 取整再乘回,把 0.0015~0.0035 区间当作「弱多头动量带」,叠上 cho 向下、cci 向上的背离形态才放行。外汇与贵金属杠杆高,这类多指标共振仅提高概率,不代表方向必现。

MQL5 / C++
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
   if(atr!=INVALID_HANDLE)
       IndicatorRelease(atr);
class=class="str">"cmt">//---
   if(force==INVALID_HANDLE)
       IndicatorRelease(force);
class=class="str">"cmt">//---
   if(macd==INVALID_HANDLE)
       IndicatorRelease(macd);
class=class="str">"cmt">//---
   if(cho==INVALID_HANDLE)
       IndicatorRelease(cho);
class=class="str">"cmt">//---
   if(cci==INVALID_HANDLE)
       IndicatorRelease(cci);
  }
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   class="type">class="kw">datetime cur_bar=(class="type">class="kw">datetime)SeriesInfoInteger(_Symbol,MinPeriod,SERIES_LASTBAR_DATE);
   class="type">class="kw">datetime cur_max=(class="type">class="kw">datetime)SeriesInfoInteger(_Symbol,MaxPeriod,SERIES_LASTBAR_DATE);
   class="type">class="kw">datetime cur_time=TimeCurrent();
   if(cur_bar<=last_bar || (cur_time-cur_bar)>class="num">10 || cur_max<=last_deal)
       class="kw">return;
   last_bar=cur_bar;
   class="type">class="kw">double atrs[];
   class="type">class="kw">double force_data[];
   class="type">class="kw">double macd_data[];
   class="type">class="kw">double cho_data[];
   class="type">class="kw">double cci_data[];
   if(CopyBuffer(atr,class="num">0,class="num">1,class="num">1,atrs)<=class="num">0 || CopyBuffer(force,class="num">0,class="num">1,class="num">1,force_data)<=class="num">0 || CopyBuffer(macd,class="num">0,class="num">1,class="num">1,macd_data)<=class="num">0
       || CopyBuffer(cho,class="num">0,class="num">1,class="num">2,cho_data)<class="num">2 || CopyBuffer(cci,class="num">0,class="num">1,class="num">2,cci_data)<class="num">2)
     {
       class="kw">return;
     }
   class="type">class="kw">double force_Step=_Point*class="num">1000;
   if(MathAbs(NormalizeDouble(force_data[class="num">0]/force_Step,class="num">0)*force_Step)<=class="num">0.01)
       class="kw">return;
   class="type">class="kw">double macd_Step=_Point*class="num">50;
   macd_data[class="num">0]=NormalizeDouble(macd_data[class="num">0]/macd_Step,class="num">0)*macd_Step;
   if(macd_data[class="num">0]>=class="num">0.0015 && macd_data[class="num">0]<=class="num">0.0035 && (cho_data[class="num">1]-cho_data[class="num">0])<class="num">0 && (cci_data[class="num">1]-cci_data[class="num">0])>class="num">0)
     {
       if(PositionSelect(_Symbol))
         {
          class="kw">switch((class="type">int)PositionGetInteger(POSITION_TYPE))
            {
             case POSITION_TYPE_BUY:
               if(PositionGetDouble(POSITION_PROFIT)<=class="num">0)
                 class="kw">return;
            }
         }
     }
  }

「双向触发下的平仓与挂单逻辑」

这段逻辑把 MACD 区间与 CHO、CCI 的斜率组合当作双向触发器。当 MACD 落在 -0.0015 到 -0.0035 之间、CHO 差值由正转负、CCI 差值反向时,系统判定为偏空信号;对应地,前文 MACD 在 +0.0015~+0.0035 且 CHO 上行、CCI 下行时走偏多分支。 持仓处理用 switch 按 POSITION_TYPE 分流:若已持有同向单且浮动利润 ≤0 则直接 return 不动作;若持反向单则 Trade.PositionClose(_Symbol) 先平掉。止损距离取 2 倍 ATR 与经纪商 SYMBOL_TRADE_STOPS_LEVEL 点值的较大者,再 NormalizeDouble 到品种精度。 止盈按 Reward_Risk*(stops+ask-bid) 推算,买单价差补偿用 ask-bid。外汇与贵金属杠杆高,这类自动平仓重开逻辑在滑点扩大时可能频繁触发,实盘前务必在 MT5 策略测试器用历史数据跑一遍验证触发频率。

MQL5 / C++
         break;
         case POSITION_TYPE_SELL:
            Trade.PositionClose(_Symbol);
            break;
         }
      }
      last_deal=cur_max;
      class="type">class="kw">double stops=MathMax(class="num">2*atrs[class="num">0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point);
      class="type">class="kw">double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      class="type">class="kw">double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
      class="type">class="kw">double sl=NormalizeDouble(stops,_Digits);
      class="type">class="kw">double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits);
      class="type">class="kw">double SL=NormalizeDouble(bid-sl,_Digits);
      class="type">class="kw">double TP=NormalizeDouble(ask+tp,_Digits);
      if(!Trade.Buy(Lot,_Symbol,ask,SL,TP,"New Strategy"))
         Print("Error of open BUY ORDER "+Trade.ResultComment());
   }
   if(macd_data[class="num">0]<=( -class="num">0.0015) && macd_data[class="num">0]>=( -class="num">0.0035) && (cho_data[class="num">1]-cho_data[class="num">0])>class="num">0 && (cci_data[class="num">1]-cci_data[class="num">0])<class="num">0)
   {
      if(PositionSelect(_Symbol))
      {
       class="kw">switch((class="type">int)PositionGetInteger(POSITION_TYPE))
       {
         case POSITION_TYPE_SELL:
            if(PositionGetDouble(POSITION_PROFIT)<=class="num">0)
               class="kw">return;
            break;
         case POSITION_TYPE_BUY:
            Trade.PositionClose(_Symbol);
            break;
       }
      }
      last_deal=cur_max;
      class="type">class="kw">double stops=MathMax(class="num">2*atrs[class="num">0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point);
      class="type">class="kw">double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      class="type">class="kw">double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
      class="type">class="kw">double sl=NormalizeDouble(stops,_Digits);
      class="type">class="kw">double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits);

◍ 卖单止损止盈的挂单计算

在 MT5 的 EA 逻辑里,卖单的止损和止盈不能直接用裸价格,必须经过点位偏移再按品种精度规整。下面这段片段把 ask 加上 sl 点数、bid 减去 tp 点数,再用 NormalizeDouble 对齐 _Digits,避免报价小数位不匹配导致的下单失败。 double SL=NormalizeDouble(ask+sl,_Digits); double TP=NormalizeDouble(bid-tp,_Digits); if(!Trade.Sell(Lot,_Symbol,bid,SL,TP,"New Strategy")) Print("Error of open SELL ORDER "+Trade.ResultComment()); 这里 SL 放在 ask 上方、TP 放在 bid 下方,符合卖单盈利空间向下的结构。若 Trade.Sell 返回 false,直接打印 ResultComment 能拿到经纪商返回的具体拒单原因,比盲猜高效。 实际验证时把 sl、tp 设成 300、600 这类常见点数,在 XAUUSD 的 5 位平台上跑回测,能看到 NormalizeDouble 后价格尾数恒为经纪商 _Digits 位,外汇与贵金属杠杆高,点位计算错一位就可能触发异常平仓,建议先开策略测试器单步跟一遍。

MQL5 / C++
class="type">class="kw">double SL=NormalizeDouble(ask+sl,_Digits);
class="type">class="kw">double TP=NormalizeDouble(bid-tp,_Digits);
if(!Trade.Sell(Lot,_Symbol,bid,SL,TP,"New Strategy"))
   Print("Error of open SELL ORDER "+Trade.ResultComment());

把入场逻辑拆成指标再用起来

把入场条件反向解析成独立指标,是这套思路真正能落地的关键。原文用三年回测说明,按此法搭建的策略有长期盈利的可能,但标准 MetaTrader 自带指标生成的信号,和文献里写的经典形态常常差得很远,不能拿书本结论直接套。 技术价值在于不绑定某个指标:任意用户指标或其变体都能接进来,用同一套解析框架评估信号质量。外汇与贵金属波动受杠杆和消息驱动,高风险,回测结论只代表历史样本,实盘前务必在 MT5 用自己品种重跑。 想验证就开 MT5 建个自定义指标,把现有入场规则逐条输出成缓冲数组,再比对着图表看信号偏移在哪。

「随包附带的源码与回测素材」

这套四阶段策略不是只给思路,作者把可跑的 MT5 工程也一并打了包。New_Strategy_Gizlyk.zip 里是 4 个分阶段 EA(NewStrategy1~4.mq5)加一个统测用的 NewStrategy_Final.mq5,另有 16 个 .mqh 类库,分别管 ADX、Alligator、MACD 三大指标的状态、历史与静态统计。 Common.zip 则塞了 9 份 html 测试报告,覆盖 2016–2017 年、盈利/风险比分别取 1/1 与 15/1 的各阶段结果,以及最终统测页。两个压缩包体量分别为 1374.2 KB 与 1455.62 KB,直接丢进 MT5 的 MQL5 目录就能编译。 回测报告里 1/1 与 15/1 两种盈亏比对照,能直观看出冒同样风险时激进入场和收紧离场的权益曲线差异。外汇与贵金属杠杆高、滑点跳空频繁,报告结论只代表历史样本,实盘复制前务必用自己的品种重跑一遍。

常见问题

用柱状体正负穿越零轴配合阈值过滤杂讯,阈值建议取近期波动均值的 0.5~1 倍,在回测里调参确认。
至少叠加趋势、动量、波动率三类指标同向,且价格突破近期结构位才开仓,避免单指标误触。
小布可加载你的指标逻辑并跑多周期回测,直接输出胜率与回撤,你只需核对参数边界。
会造成内存泄漏与多周期调用报错,应在 OnDeinit 释放全部句柄,并设最小触发间隔防重复。
用独立标志位锁方向,平仓成交前禁止同侧挂单,反向触发仅改挂单价不新建仓。