从新手到专家:对K线进行编程·进阶篇
📊

从新手到专家:对K线进行编程·进阶篇

(2/3)·肉眼判吞没形态误差频发,手动回测更是噩梦,算法化才是解脱路径

案例拆解 第 2/3 篇
用眼睛在图表上找看涨吞没,开盘价是否低于前收常常看走眼,视差误差让误判概率陡增。若再把这套动作搬到历史数据手动回测,错误会成倍放大。把识别逻辑写成代码,才能绕开人肉分析的先天短板。

用 ATR 卡位识别四类 K 线形态

在 MT5 的裸 K 策略里,用 ATR[1] 作动态阈值比固定点数更抗不同品种波动差异。下面这套判定把星线类和小兵类全绑在最近 4 根蜡烛(索引 1~3)上,直接可在 EA 的 OnCalculate 里跑。 晨星要求索引 2 实体小于 0.3 倍 ATR[1],索引 1 为阳线且收在索引 3 实体的中点上方;暮星对称,索引 1 为阴线且收在中点下方。这类形态在 EURUSD 的 H1 上,若 ATR[1] 约 12 点,则中间星线实体需小于约 3.6 点才触发。 三白兵与三黑鸦更苛刻:连续三根都要实体大于 1 倍 ATR[1],且上/下影小于 0.3 倍 ATR[1];后一根开盘嵌在前一根实体内并收盘更高(或更低)。XAUUSD 的 M15 里 ATR[1] 常到 8 美元,实体门槛就是 8 美元以上,过滤掉大量毛刺。 外汇与贵金属杠杆高,形态触发只代表概率倾向反转,真要下单得叠自己的止损与波动率过滤。

MQL5 / C++
MathAbs(Close[class="num">2] - Open[class="num">2]) < class="num">0.3 * ATR[class="num">1] && class=class="str">"cmt">// Candle at index class="num">2 has a small body
Close[class="num">1] > Open[class="num">1] &&     class=class="str">"cmt">// Candle at index class="num">1 is bullish
Close[class="num">1] > (Open[class="num">3] + Close[class="num">3]) / class="num">2 ) class=class="str">"cmt">// Candle at index class="num">1 closes above midpoint of candle class="num">3
{
Print("Morning Star pattern detected");
}
class=class="str">"cmt">// Check for Evening Star pattern
if( Close[class="num">3] > Open[class="num">3] &&     class=class="str">"cmt">// Candle at index class="num">3 is bullish
MathAbs(Close[class="num">2] - Open[class="num">2]) < class="num">0.3 * ATR[class="num">1] && class=class="str">"cmt">// Candle at index class="num">2 has a small body
Close[class="num">1] < Open[class="num">1] &&     class=class="str">"cmt">// Candle at index class="num">1 is bearish
Close[class="num">1] < (Open[class="num">3] + Close[class="num">3]) / class="num">2 ) class=class="str">"cmt">// Candle at index class="num">1 closes below midpoint of candle class="num">3
{
Print("Evening Star pattern detected");
}
class=class="str">"cmt">// Check for Three White Soldiers pattern
class=class="str">"cmt">// Candle class="num">3 is bullish with large body and small upper wick
if( Close[class="num">3] > Open[class="num">3] && (Close[class="num">3] - Open[class="num">3]) > ATR[class="num">1] && (High[class="num">3] - Close[class="num">3]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 is bullish with large body and small upper wick
Close[class="num">2] > Open[class="num">2] && (Close[class="num">2] - Open[class="num">2]) > ATR[class="num">1] && (High[class="num">2] - Close[class="num">2]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">1 is bullish with large body and small upper wick
Close[class="num">1] > Open[class="num">1] && (Close[class="num">1] - Open[class="num">1]) > ATR[class="num">1] && (High[class="num">1] - Close[class="num">1]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 opens within candle class="num">3&class="macro">#x27;s body and closes higher
Open[class="num">2] > Open[class="num">3] && Open[class="num">2] < Close[class="num">3] && Close[class="num">2] > Close[class="num">3] &&
class=class="str">"cmt">// Candle class="num">1 opens within candle class="num">2&class="macro">#x27;s body and closes higher
Open[class="num">1] > Open[class="num">2] && Open[class="num">1] < Close[class="num">2] && Close[class="num">1] > Close[class="num">2] )
{
Print("Three White Soldiers pattern detected");
}
class=class="str">"cmt">// Check for Three Black Crows pattern
class=class="str">"cmt">// Candle class="num">3 is bearish with large body and small lower wick
if( Close[class="num">3] < Open[class="num">3] && (Open[class="num">3] - Close[class="num">3]) > ATR[class="num">1] && (Open[class="num">3] - Low[class="num">3]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 is bearish with large body and small lower wick
Close[class="num">2] < Open[class="num">2] && (Open[class="num">2] - Close[class="num">2]) > ATR[class="num">1] && (Open[class="num">2] - Low[class="num">2]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">1 is bearish with large body and small lower wick
Close[class="num">1] < Open[class="num">1] && (Open[class="num">1] - Close[class="num">1]) > ATR[class="num">1] && (Open[class="num">1] - Low[class="num">1]) < class="num">0.3 * ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 opens within candle class="num">3&class="macro">#x27;s body and closes lower
Open[class="num">2] < Open[class="num">3] && Open[class="num">2] > Close[class="num">3] && Close[class="num">2] < Close[class="num">3] &&
class=class="str">"cmt">// Candle class="num">1 opens within candle class="num">2&class="macro">#x27;s body and closes lower
Open[class="num">1] < Open[class="num">2] && Open[class="num">1] > Close[class="num">2] && Close[class="num">1] < Close[class="num">2] )
{

◍ 吞没与孕线的代码化判定边界

在 MT5 的 K 线数组里,最新一根是索引 0,往前依次是 1、2、3。上面这段逻辑用 Close[]、Open[]、High[]、Low[] 直接比对,把几种经典反转形态写成了可编译的 if 判断,省掉了任何指标依赖。 以 Bullish Harami 为例:要求索引 2 的阴线实体大于前一根 ATR[1],即 (Open[2]-Close[2]) > ATR[1],而索引 1 的阳线完全包在 2 号阴线实体内部(Open[1] > Close[2] 且 Close[1] < Open[2])。这种写法把「孕线」的视觉定义转成了硬阈值,回测时你能直接改 ATR 倍数调灵敏度。 Three Inside Up 比 Harami 多一根确认:索引 3 是大阴线,索引 2 阳线孕于其内,索引 1 阳线还得收在 2 号高点之上(Close[1] > High[2])。外汇与贵金属波动大,ATR 阈值设太小容易在震荡里刷出一堆信号,实盘前建议在 EURUSD 的 M15 上跑一遍打印日志看命中率。 别把正态当圣经:代码里只判了形态骨架,没滤掉影线噪音和成交量,信号出现后价格倾向反转但也可能假突破,开 MT5 把这段贴进 EA 的 OnTick 里验证最直观。

MQL5 / C++
Print("Three Black Crows pattern detected");
}
class=class="str">"cmt">// Check for Bullish Harami pattern
class=class="str">"cmt">// Candle class="num">2 is bearish with large body
if( Close[class="num">2] < Open[class="num">2] && (Open[class="num">2] - Close[class="num">2]) > ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">1 is bullish and within candle class="num">2&class="macro">#x27;s body
Close[class="num">1] > Open[class="num">1] && Open[class="num">1] > Close[class="num">2] && Close[class="num">1] < Open[class="num">2] )
{
Print("Bullish Harami pattern detected");
}
class=class="str">"cmt">// Check for Bearish Harami pattern
class=class="str">"cmt">// Candle class="num">2 is bullish with large body
if( Close[class="num">2] > Open[class="num">2] && (Close[class="num">2] - Open[class="num">2]) > ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">1 is bearish and within candle class="num">2&class="macro">#x27;s body
Close[class="num">1] < Open[class="num">1] && Open[class="num">1] < Close[class="num">2] && Close[class="num">1] > Open[class="num">2] )
{
Print("Bearish Harami pattern detected");
}
class=class="str">"cmt">// Check for Bullish Engulfing pattern
if( Close[class="num">2] < Open[class="num">2] &&     class=class="str">"cmt">// Candle at index class="num">2 is bearish
Close[class="num">1] > Open[class="num">1] &&     class=class="str">"cmt">// Candle at index class="num">1 is bullish
Open[class="num">1] < Close[class="num">2] &&     class=class="str">"cmt">// Candle at index class="num">1 opens below candle at index class="num">2&class="macro">#x27;s close
Close[class="num">1] > Open[class="num">2] )     class=class="str">"cmt">// Candle at index class="num">1 closes above candle at index class="num">2&class="macro">#x27;s open
{
Print("Bullish Engulfing pattern detected");
}
class=class="str">"cmt">// Check for Bearish Engulfing pattern
if( Close[class="num">2] > Open[class="num">2] && class=class="str">"cmt">// Candle at index class="num">2 is bullish
Close[class="num">1] < Open[class="num">1] &&   class=class="str">"cmt">// Candle at index class="num">1 is bearish
Open[class="num">1] > Close[class="num">2] &&  class=class="str">"cmt">// Candle at index class="num">1 opens above candle at index class="num">2&class="macro">#x27;s close
Close[class="num">1] < Open[class="num">2] )   class=class="str">"cmt">// Candle at index class="num">1 closes below candle at index class="num">2&class="macro">#x27;s open
{
Print("Bearish Engulfing pattern detected");
}
class=class="str">"cmt">// Check for Three Inside Up pattern
class=class="str">"cmt">// Candle class="num">3 is bearish with large body
if( Close[class="num">3] < Open[class="num">3] && (Open[class="num">3] - Close[class="num">3]) > ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 is bullish and within candle class="num">3&class="macro">#x27;s body
Close[class="num">2] > Open[class="num">2] && Open[class="num">2] > Close[class="num">3] && Close[class="num">2] < Open[class="num">3] &&
class=class="str">"cmt">// Candle class="num">1 is bullish and closes above candle class="num">2&class="macro">#x27;s high
Close[class="num">1] > Open[class="num">1] && Close[class="num">1] > High[class="num">2] )
{
Print("Three Inside Up pattern detected");
}
class=class="str">"cmt">// Check for Three Inside Down pattern
class=class="str">"cmt">// Candle class="num">3 is bullish with large body
if( Close[class="num">3] > Open[class="num">3] && (Close[class="num">3] - Open[class="num">3]) > ATR[class="num">1] &&
class=class="str">"cmt">// Candle class="num">2 is bearish and within candle class="num">3&class="macro">#x27;s body
Close[class="num">2] < Open[class="num">2] && Open[class="num">2] < Close[class="num">3] && Close[class="num">2] > Open[class="num">3] &&
class=class="str">"cmt">// Candle class="num">1 is bearish and closes below candle class="num">2&class="macro">#x27;s low
Close[class="num">1] < Open[class="num">1] && Close[class="num">1] < Low[class="num">2] )
{
Print("Three Inside Down pattern detected");
}
class=class="str">"cmt">// Check for Tweezer Bottom pattern
if( Close[class="num">2] < Open[class="num">2] &&     class=class="str">"cmt">// Candle at index class="num">2 is bearish

「镊子与跳空踢脚的形态判定代码」

在 MT5 的自定义指标或 EA 里,镊子底(Tweezer Bottom)可以靠两根 K 线的低点近似重合来识别:要求前一根(index 2)收阴、紧邻一根(index 1)收阳,且两者低点差的绝对值小于当时 ATR 的 0.1 倍。 镊子顶则反过来,前一根收阳、后一根收阴,高点差同样压在 0.1*ATR[1] 以内。这种阈值把「视觉上平齐」转成了可回测的数值条件,比肉眼框选更不容易漏。 看涨踢脚(Bullish Kicker)的写法是:index 2 为阴线、index 1 为阳线,且阳线开盘价高于前一根开盘价,中间不留小缺口也能算强反转信号;看跌踢脚对称处理,阳线后阴线且开盘更低。 下面这段摘出来的判定块可以直接丢进脚本验证,把 Print 换成你自己的报警或下单钩子即可。外汇与贵金属波动大,这类形态只代表概率倾向,实盘须自行加风控。

MQL5 / C++
Close[class="num">1] > Open[class="num">1] &&     class=class="str">"cmt">// Candle at index class="num">1 is bullish
class=class="str">"cmt">// Lows of both candles are nearly equal
MathAbs(Low[class="num">2] - Low[class="num">1]) < class="num">0.1 * ATR[class="num">1] )
{
Print("Tweezer Bottom pattern detected");
}
class=class="str">"cmt">// Check for Tweezer Top pattern
if( Close[class="num">2] > Open[class="num">2] &&     class=class="str">"cmt">// Candle at index class="num">2 is bullish
Close[class="num">1] < Open[class="num">1] &&     class=class="str">"cmt">// Candle at index class="num">1 is bearish
class=class="str">"cmt">// Highs of both candles are nearly equal
MathAbs(High[class="num">2] - High[class="num">1]) < class="num">0.1 * ATR[class="num">1] )
{
Print("Tweezer Top pattern detected");
}
class=class="str">"cmt">// Check for Bullish Kicker pattern
if( Close[class="num">2] < Open[class="num">2] && class=class="str">"cmt">// Candle at index class="num">2 is bearish
class=class="str">"cmt">// Candle at index class="num">1 is bullish
Close[class="num">1] > Open[class="num">1] &&
class=class="str">"cmt">// Candle at index class="num">1 opens above candle at index class="num">2&class="macro">#x27;s open
Open[class="num">1] > Open[class="num">2] )
{
Print("Bullish Kicker pattern detected");
}
class=class="str">"cmt">// Check for Bearish Kicker pattern
class=class="str">"cmt">// Candle at index class="num">2 is bullish
if( Close[class="num">2] > Open[class="num">2] &&
class=class="str">"cmt">// Candle at index class="num">1 is bearish
Close[class="num">1] < Open[class="num">1] &&
class=class="str">"cmt">// Candle at index class="num">1 opens below candle at index class="num">2&class="macro">#x27;s open
Open[class="num">1] < Open[class="num">2]
{
Print("Bearish Kicker pattern detected");
}

锤子线与流星线的代码判定逻辑

在 MT5 里把锤子线和流星线写成可复用的布尔函数,核心是先算实体大小再比上下影线。两个函数都吃 open/high/low/close 数组加一份 patternATR,靠 index 定位 K 线,避免越界直接返回 false。 锤子线要求下影线不小于实体的 2 倍、上影线不超过 0.5 倍实体;流星线反过来,上影线 ≥2 倍实体、下影线 ≤0.5 倍实体。两者都先用 body >= 0.3 * patternATR[index] 做过滤,实体超过 ATR 三成就不算这两类形态,等于用波动率给形态加了动态门槛。 想扩库也很直接:照着 IsHammer / IsShootingStar 的形参写新函数,比如 IsDoji 就比 open 和 close 是否近似;多根蜡烛的形态把 index 检查改成 index < 1 之类即可。外汇和贵金属波动大,这类形态信号在消息面行情里失效概率偏高,只适合当概率参考。

MQL5 / C++
if (lowerWick >= class="num">2 * body && upperWick <= class="num">0.5 * body)
      class="kw">return true;

   class="kw">return false;
}
class=class="str">"cmt">// Shooting Star Pattern
class="type">bool IsShootingStar(class="type">class="kw">double &open[], class="type">class="kw">double &high[], class="type">class="kw">double &low[], class="type">class="kw">double &close[], class="type">class="kw">double &patternATR[], class="type">int index)
{
   if (index < class="num">0) class="kw">return false; class=class="str">"cmt">// Ensure the index is valid

   class=class="str">"cmt">// Calculate the body size(absolute difference between close and open)
   class="type">class="kw">double body = MathAbs(close[index] - open[index]);

   class=class="str">"cmt">// Check if the body is small relative to ATR(less than class="num">30% of ATR)
   if (body >= class="num">0.3 * patternATR[index]) class="kw">return false;

   class=class="str">"cmt">// Calculate wick lengths
   class="type">class="kw">double upperWick = high[index] - MathMax(open[index], close[index]);
   class="type">class="kw">double lowerWick = MathMin(open[index], close[index]) - low[index];

   class=class="str">"cmt">// Shooting Star conditions: class="type">long upper wick(2x body), small lower wick(≤class="num">0.5x body)
   if (upperWick >= class="num">2 * body && lowerWick <= class="num">0.5 * body)
      class="kw">return true;

   class="kw">return false;
}

◍ 把箭头和 ATR 句柄接进指标骨架

这段初始化把两个箭头缓冲画到了图表上:缓冲 0 用 242 号字符(↓)标卖、蓝色 0x0000FF;缓冲 1 用 241 号字符(↑)标买、红色 0xFF0000。EMPTY_VALUE 设为空值,避免无信号处画出零轴噪点。 ATR 句柄在 OnInit 里用 iATR(_Symbol, _Period, 14) 建好,周期 14 是默认参数,回测 EURUSD H1 时平均真实波幅约在 8~15 点区间跳动。若返回 INVALID_HANDLE 直接 INIT_FAILED,防止后续计算崩在空指针上。 OnCalculate 开头先 CopyOpen 把开盘价拉进数组,ArraySetAsSeries(Open, true) 把序列倒序,使 Open[0] 永远是当前未收线柱。外汇与贵金属杠杆高,这类信号仅作概率参考,实盘须自担回撤风险。

MQL5 / C++
class="macro">#class="kw">property indicator_color1 0x0000FF
class="macro">#class="kw">property indicator_label1 "Sell "
class="macro">#class="kw">property indicator_type2 DRAW_ARROW
class="macro">#class="kw">property indicator_color2 0xFF0000
class="macro">#class="kw">property indicator_label2 "Buy "
class=class="str">"cmt">//--- Indicator buffers and variables
class="type">class="kw">double Buffer1[];
class="type">class="kw">double Buffer2[];
class="type">class="kw">double Open[];
class="type">class="kw">double High[];
class="type">class="kw">double Low[];
class="type">class="kw">double Close[];
class="type">class="kw">double atr[];
class="type">int atrHandle;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator initialization function                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, Buffer1);
   PlotIndexSetDouble(class="num">0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(class="num">0, PLOT_ARROW, class="num">242);
   SetIndexBuffer(class="num">1, Buffer2);
   PlotIndexSetDouble(class="num">1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(class="num">1, PLOT_ARROW, class="num">241);
   class=class="str">"cmt">// Create ATR handle
   atrHandle = iATR(_Symbol, _Period, class="num">14);
   if (atrHandle == INVALID_HANDLE)
   {
      Print("Failed to create ATR handle");
      class="kw">return(INIT_FAILED);
   }
   class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime& time[],
                class="kw">const class="type">class="kw">double& open[],
                class="kw">const class="type">class="kw">double& high[],
                class="kw">const class="type">class="kw">double& low[],
                class="kw">const class="type">class="kw">double& close[],
                class="kw">const class="type">long& tick_volume[],
                class="kw">const class="type">long& volume[],
                class="kw">const class="type">int& spread[])
{
   class=class="str">"cmt">// Copy price data and ATR
   if (CopyOpen(Symbol(), PERIOD_CURRENT, class="num">0, rates_total, Open) <= class="num">0) class="kw">return(rates_total);
   ArraySetAsSeries(Open, true);
让小布替你跑这套
这些形态标记与回测流程,小布盯盘的 AIGC 看盘模块已内置常用模板,打开对应品种页即可直接调用,你只需定规则、做决策。

常见问题

通常比较收盘价与开盘价:Close>Open 视为看涨,反之为看跌,可封装成基础函数供后续形态调用。
实体可取 Abs(Close-Open),影线用 High-Low 减去实体部分,再按点数或ATR归一,便于跨品种比较。
可以,将形态条件以规则或脚本接入后,小布会在对应品种页标记并推送,省去肉眼巡图。
多根涉及跨根相对位置与顺序约束,人工核对时容易漏掉某一根的开收关系,算法化后一致性更高。
关于形态库落地与策略接线的完整讨论见《从新手到专家:对K线进行编程·实战篇》。