从新手到专家:对K线进行编程·进阶篇
(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 美元以上,过滤掉大量毛刺。 外汇与贵金属杠杆高,形态触发只代表概率倾向反转,真要下单得叠自己的止损与波动率过滤。
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 里验证最直观。
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 换成你自己的报警或下单钩子即可。外汇与贵金属波动大,这类形态只代表概率倾向,实盘须自行加风控。
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 之类即可。外汇和贵金属波动大,这类形态信号在消息面行情里失效概率偏高,只适合当概率参考。
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] 永远是当前未收线柱。外汇与贵金属杠杆高,这类信号仅作概率参考,实盘须自担回撤风险。
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);