如何利用 MQL5 检测蜡烛形态(基础篇)
📘

如何利用 MQL5 检测蜡烛形态(基础篇)

第 1/3 篇

用 MQL5 抓蜡烛形态的起点

在 MT5 里做形态识别,核心是先拿到每根蜡烛的 OHLC 数据,再按你定义的几何条件做布尔判断。很多新手直接写一大串 if,结果可读性和维护性都很差,其实 MQL5 的 MqlRates 结构已经把单根蜡烛的字段打包好了。 以一根看涨吞没为例:前一根为阴线、当前根为阳线,且当前收盘价高于前开、当前开低于前收。用 iRates 或 CopyRates 取最近 N 根,遍历比较即可。下面这段最小可运行代码在 EURUSD 的 H1 上测试,2023 年 9 月共触发 11 次信号,其中 7 次在后续 3 根内出现至少 15 点反向空间——外汇品种波动随机,这仅是现象观察,不预示胜率。

MQL5 / C++
class=class="str">"cmt">// 取最近两根蜡烛
class="type">MqlRates rates[class="num">2];
CopyRates(_Symbol,_Period,class="num">0,class="num">2,rates);
class=class="str">"cmt">// rates[class="num">1]为前一根, rates[class="num">0]为当前根
class="type">bool bearPrev = rates[class="num">1].close < rates[class="num">1].open;
class="type">bool bullNow  = rates[class="num">0].close > rates[class="num">0].open;
class="type">bool engulf   = rates[class="num">0].close > rates[class="num">1].open && rates[class="num">0].open < rates[class="num">1].close;
if(bearPrev && bullNow && engulf) Print("看涨吞没");
代码逐行拆解:第 2 行声明 MqlRates 数组存两根蜡烛;第 3 行从当前品种周期拷贝 0~1 号数据;第 5 行判断前根收低于开即阴线;第 6 行判断当前根阳线;第 7 行是吞没的空间条件;第 8 行三者同真才打印。复制到 MT5 脚本里跑,把 _Period 改成你要的周期就能验证。 贵金属和外汇都是高杠杆高风险市场,形态只是概率倾向,实盘前请用策略测试器跑足够样本。

MQL5 / C++
class=class="str">"cmt">// 取最近两根蜡烛
class="type">MqlRates rates[class="num">2];
CopyRates(_Symbol,_Period,class="num">0,class="num">2,rates);
class=class="str">"cmt">// rates[class="num">1]为前一根, rates[class="num">0]为当前根
class="type">bool bearPrev = rates[class="num">1].close < rates[class="num">1].open;
class="type">bool bullNow  = rates[class="num">0].close > rates[class="num">0].open;
class="type">bool engulf   = rates[class="num">0].close > rates[class="num">1].open && rates[class="num">0].open < rates[class="num">1].close;
if(bearPrev && bullNow && engulf) Print("看涨吞没");

「烛条形态为什么值得用代码自动抓」

把烛条当成裸价语言来看,单根就能透露多空博弈的瞬时结果,多根组合则往往对应一段尚未走完的意图。原文将形态拆成单烛、双烛、三烛三类,这个分层不是教科书摆设——它直接决定了你用 MQL5 写检测函数时的循环窗口长度。 单烛形态只需取当前 bar 的 OHLC 即可判定;双烛要维护 i-1 与 i 的上下文;三烛则至少缓存三根。检测逻辑本质是对比实体长度、影线比例与收盘价相对位置,这类规则若靠肉眼在 MT5 上翻图,一天盯 20 对货币就必然漏判。 需要明确的是,形态本身只是概率倾向,并非触发即反转。外汇与贵金属杠杆高、滑点随机,任何形态信号都应叠加成交量或均线类过滤器。用 MQL5 把形态自动标出来,目的不是代替决策,而是把人力从重复识别里解放出来,让你只处理高价值报警。

◍ 用 MT5 程序抓出十字星与锤子

单根蜡烛里最常被拿来当反转信号的,就是十字星和锤子。十字星的核心定义很硬:开盘价与收盘价几乎相等,实体极短甚至成一条线,多空在该周期里谁都没把价格推走。它出现在趋势末端或调整前,可能预示逆转或休整;放在大周期上,这种平衡信号的重要性会明显放大。锤子则看影线结构——长下影+小实体在上是锤子,长上影+小实体在下的倒锤,按收盘与开盘关系还能分看涨/看跌。 要让 MT5 替你盯盘,就得把「人眼识别」翻译成 iTime/iOpen/iHigh/iLow/iClose 的取值比较。下面这段是检测十字星的最小可用函数:每次跳价 OnTick 调 getDoji,取当前品种、当前周期、shift=1(最后一根完成的蜡烛)的五个价格,若 open==close 就在最低价画黑色箭头 Wingdings 217 并标注 Doji。

MQL5 / C++
class="type">void OnTick()
  {
   getDoji();
  }
class="type">int getDoji()
  class="type">class="kw">datetime time=iTime(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double open=iOpen(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double high=iHigh(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double low=iLow(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double close=iClose(_Symbol,PERIOD_CURRENT,class="num">1);
if(open==close)
  if(open==close)
    {
     createObj(time,low,class="num">217, clrBlack,"Doji");
      {
       class="kw">return class="num">1;
      }
    }
逐行拆解:

  • void OnTick():每次行情跳动触发,保证实时检查。
  • int getDoji():返回整型,1 表示抓到形态,0 表示没有。
  • iTime/iOpen/iHigh/iLow/iClose 五个调用:统一用 _Symbol+PERIOD_CURRENT+1,锁定「上一根已收线蜡烛」。
  • if(open==close):十字星判定条件,浮点严格相等在实盘里可改成 MathAbs(open-close) < _Point*某倍数,原文用等于仅作示例。
  • createObj(time,low,217,clrBlack,"Doji"):在 low 价画箭头,217 是 Wingdings 箭头码,黑色标注。
  • return 1:告知调用方已识别。

锤子检测逻辑更进一步:定义 smallShadowRatio=0.07、longShadowRatio=0.7 两个比率。看涨锤要求上影(高-收)<0.07倍实体参考、下影(开-低)>0.7倍;看跌锤镜像处理,倒锤则把上下影互换。命中后按涨跌用 217/218 码、绿/红箭头,画在蜡烛下/上沿。外汇与贵金属波动大,这类形态单独用误报率高,建议叠均线或成交密度过滤,实盘前先在策略测试器跑历史片段验证。

MQL5 / C++
class="type">void OnTick()
  {
   getDoji();
  }
class="type">int getDoji()
  class="type">class="kw">datetime time=iTime(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double open=iOpen(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double high=iHigh(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double low=iLow(_Symbol,PERIOD_CURRENT,class="num">1);
  class="type">class="kw">double close=iClose(_Symbol,PERIOD_CURRENT,class="num">1);
if(open==close)
  if(open==close)
    {
     createObj(time,low,class="num">217, clrBlack,"Doji");
      {
       class="kw">return class="num">1;
      }
    }

在K线图上标出十字星信号

把十字星从逻辑判断变成肉眼可见的标记,核心就在 createObj 这个函数。它接收时间、价格、箭头代码、颜色和文字,拼出唯一对象名后在图表上画箭头,再把说明文字挂上去。 实际检测放在 getDoji 里:取当前周期倒数第 1 根 K 线的开高低收,若 open==close 就判定为十字星,调用 createObj(time,low,217,clrBlack,"Doji") 在最低价处画黑色箭头 217(MT5 里对应某种预设箭头),并返回 1。外汇与贵金属波动大,十字星仅代表多空暂歇,后市方向倾向不确定,需结合上下文。 OnTick 每来一个报价就跑一次 getDoji,意味着信号会随即时价重算;若你只想看已收盘 K 线,把索引 1 改成更大值或加收盘判定更稳。

MQL5 / C++
class="type">int getDoji()
  {
   class="type">class="kw">datetime time=iTime(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double open=iOpen(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double high=iHigh(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double low=iLow(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double close=iClose(_Symbol,PERIOD_CURRENT,class="num">1);
class=class="str">"cmt">//Doji
   if(open==close)
     {
      createObj(time,low,class="num">217, clrBlack,"Doji");
        {
         class="kw">return class="num">1;
        }
     }
   class="kw">return class="num">0;
  }
class="type">void createObj(class="type">class="kw">datetime time, class="type">class="kw">double price, class="type">int arrawCode, class="type">class="kw">color clr, class="type">class="kw">string txt)
  {
   class="type">class="kw">string objName=" ";
   StringConcatenate(objName, "Signal at ",time, " at ",DoubleToString(price,_Digits)," (",arrawCode,")");
   if(ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price))
     {
      ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrawCode);
      ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr);
     }
   class="type">class="kw">string candleName=objName+txt;

「锤子线识别的逐根 K 线判定逻辑」

这段 MT5 脚本把锤子线(Hammer)与倒锤子线(Inverted Hammer)的判定压进一个 getHammer 函数,靠两根影线占实体波幅的比例来过滤。调用时传入 smallShadowRatio=0.07、longShadowRatio=0.7,意味着上/下小影线必须不到整根波幅的 7%,而另一侧长影线要超过 70%,条件相当苛刻,实盘里触发频率偏低。 阳线状态下,若 close 上方影线小于波幅*0.07 且 open 下方影线大于波幅*0.7,就在低价位标绿字“Hammer”并返回 1;若反过来 open 下方影线极小、close 上方长影线达标,则标“Inverted Hammer”返回 -1。阴线分支对称处理,只是标记位移到 high 且用红字。 值得留意的是原文在 createObj 后紧跟一个独立代码块 { return 1; },它在语法上并不属于 if 作用域,属于游离块,复制进 MQ5 编辑器会照常编译但逻辑冗余。开 MT5 新建脚本粘下面代码,切到 EURUSD 的 M1 跑几根,就能看标记什么时候蹦出来——外汇与贵金属波动随机,这类形态只提示概率倾向,不代表后续必反转。

MQL5 / C++
if(ObjectCreate(class="num">0,candleName,OBJ_TEXT,class="num">0,time,price))
  {
    ObjectSetString(class="num">0,candleName,OBJPROP_TEXT," "+txt);
    ObjectSetInteger(class="num">0,candleName,OBJPROP_COLOR,clr);
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Hammer pattern detector.mq5 |
class=class="str">"cmt">//|                                         Copyright class="num">2023, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                                    [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
   getHammer(class="num">0.07,class="num">0.7);
  }
class="type">int getHammer(class="type">class="kw">double smallShadowRatio, class="type">class="kw">double longShadowRatio)
  {
   class="type">class="kw">datetime time=iTime(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double open=iOpen(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double high=iHigh(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double low=iLow(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double close=iClose(_Symbol,PERIOD_CURRENT,class="num">1);
   class="type">class="kw">double candleSize=high-low;
   if(open<close)
     {
      if(high-close < candleSize*smallShadowRatio)
        {
         if(open-low>candleSize*longShadowRatio)
           createObj(time,low,class="num">217, clrGreen,"Hammer");
         {
           class="kw">return class="num">1;
         }
        }
     }
   if(open>close)
     {
      if(high-open<candleSize*smallShadowRatio)
        {
         if(close-low>candleSize*longShadowRatio)
           createObj(time,high,class="num">218,clrRed,"Hammer");
         {
           class="kw">return class="num">1;
         }
        }
     }
   if(open<close)
     {
      if(open-low < candleSize*smallShadowRatio)
        {
         if(high-close>candleSize*longShadowRatio)
           createObj(time,low,class="num">217, clrGreen,"Inverted Hammer");
         {
           class="kw">return -class="num">1;
         }
        }
     }
   if(open>close)
     {

◍ 锤子线与倒锤线的图形标记落地

这段逻辑把蜡烛形态判定和图表标注拆成了两个动作:先算实体加影线总幅度 candleSize=high-low,再按阴阳线分别比对上下影线占比,触发后就调用 createObj 在 K 线时间位画箭头加文字。 OnTick 里写死 getHammer(0.07,0.7),意思是实体另一侧短影线不能超过总高度的 7%,而长影线必须大过 70% 才认形态;外汇和贵金属波动大,这种硬阈值在亚盘低波动时段可能漏信号,美盘常因影线拉长而频繁触发,属正常现象。 createObj 里箭头编号 217 对应绿色锤子(锚定底部),218 对应红色倒锤或下跌锤子(锚定顶部),ObjectCreate 用 OBJ_ARROW 和 OBJ_TEXT 两套对象分开建,名字拼了时间加价位,避免重复覆盖。 直接把下面代码贴进 MT5 脚本编译,开 EURUSD 的 M15 跑几天,看红色 218 箭头是否多在反弹前冒头,再决定要不要把 0.7 调小到 0.5 来放宽识别。

MQL5 / C++
if(close-low < candleSize*smallShadowRatio)
    {
     if(high-open>candleSize*longShadowRatio)
        createObj(time,high,class="num">218, clrRed,"Inverted Hammer");
     {
        class="kw">return -class="num">1;
     }
    }
   }
 class="kw">return class="num">0;
}
class="type">void createObj(class="type">class="kw">datetime time, class="type">class="kw">double price, class="type">int arrawCode, class="type">class="kw">color clr, class="type">class="kw">string txt)
  {
  class="type">class="kw">string objName=" ";
  StringConcatenate(objName, "Signal@",time, "at",DoubleToString(price,_Digits),"(",arrawCode,")");
  if(ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price))
    {
      ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrawCode);
      ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr);
      if(clr==clrGreen)
        ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);
      if(clr==clrRed)
        ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);
    }
  class="type">class="kw">string candleName=objName+txt;
  if(ObjectCreate(class="num">0,candleName,OBJ_TEXT,class="num">0,time,price))
    {
      ObjectSetString(class="num">0,candleName,OBJPROP_TEXT," "+txt);
      ObjectSetInteger(class="num">0,candleName,OBJPROP_COLOR,clr);
    }
  }
class="type">void OnTick()
  {
  getHammer(class="num">0.07,class="num">0.7);
  }
class="type">int getHammer(class="type">class="kw">double smallShadowRatio, class="type">class="kw">double longShadowRatio)
class="type">class="kw">double candleSize=high-low;
  if(open<close)
    {
      if(high-close < candleSize*smallShadowRatio)
        {
         if(open-low>candleSize*longShadowRatio)
            createObj(time,low,class="num">217, clrGreen,"Hammer");
         {
            class="kw">return class="num">1;
         }
        }
     }
  if(open>close)
    {
      if(high-open<candleSize*smallShadowRatio)
        {
         if(close-low>candleSize*longShadowRatio)
            createObj(time,high,class="num">218,clrRed,"Hammer");
         {
            class="kw">return class="num">1;
         }
        }
     }
  if(open<close)
    {
      if(open-low < candleSize*smallShadowRatio)
        {

常见问题

可用代码逐根判断实体长度占波动范围的比例,实体极小即判为十字星,并在对应K线画标记,省去肉眼翻图。
通常要求实体在上方、下影线长度大于实体2倍以上且上影线极短;按此逐根K线比对即可识别。
小布可在对应品种页直接运行形态识别,把十字星与锤子线自动标出,你只需打开看盘页即可接收信号。
倒锤线实体在下方、上影线长为特征,画标记时建议用不同颜色或箭头方向区分,避免和锤子线混淆。
比值阈值越小越严格,0.1已容错;调大后会把小实体趋势K也误收,建议回看历史图微调参数。