从新手到专家:对K线进行编程(基础篇)
📘

从新手到专家:对K线进行编程(基础篇)

第 1/3 篇

「把K线拆成可计算的数」

在 MT5 里,一根 K 线不是图形,而是一组带时间戳的结构体成员:开盘价、最高价、最低价、收盘价、成交量。理解这一点,是从肉眼看形态转向程序识别形态的第一步。 MQL5 的 MqlRates 结构体已经把单根 K 线的核心字段封装好,调用 CopyRates 就能把指定品种、周期、数量的 K 线批量拉进数组。新手常卡在索引方向——数组下标 0 是最近一根,越往后是越老的 K 线,写循环时方向反了就会读到未来数据。 外汇与贵金属属高杠杆品种,用程序识别形态只是降低主观误差,不消除价格反向跳空风险。先把单根 K 线的字段读熟,再谈组合形态,否则函数库写出来也是空中楼阁。

◍ 为什么要把K线形态交给代码认

我们记住了不少K线形态的名字,但靠肉眼在图上找,既耗精力又容易因为视差看歪。以看涨吞没为例:一根看跌K线后面跟着一根看涨K线,后者的开盘要低于前一根收盘、收盘要高于前一根开盘,实体把前面整根吞掉。人眼去卡‘开盘价相对前收盘’那条线,偶尔误判几乎不可避免,尤其长时间盯多周期图表时。 手动在历史数据里回测这类形态更痛苦——重复比对、记录、统计,错误会层层叠加。若用算法把形态判定和标记写进MT5,识别与回测的准确率和效率都会明显提升,外汇与贵金属市场波动剧烈、杠杆风险高,减少人工误读本身就是风控的一环。 本文要做的,是把形态判定背后的MQL5数学拆开讲,并逐步攒出一个可复用的形态库。市面上很多站点只列自然语言定义,少数碰算法结构;我认为形态创新仍有空间,你可以为自己在图上观察到的组合编码、命名。 先别急着写库,下一段我们会铺研究和关键概念,建议开着MT5对照着读,方便后面直接把自己的形态逻辑落进EA或指标。

把K线形态拆成可编码的原子条件

K线由开高低收四个价格定义,实体是开盘到收盘的区间,影线延伸到最高最低。最新一根K线索引是0,往过去走是1、2、3……这种反向索引的时间序列数组,是MT5里取历史数据的底层结构,写错索引号就会把形态判反。 实体大小必须取正值,不论涨跌。MQL5里用 MathAbs(Close[1]-Open[1]) 算绝对值:看涨时10-8=2,看跌时8-10=-2但绝对值还是2。这一步不处理,后面比较实体和ATR阈值会直接逻辑崩掉。 固定点数阈值在跨品种时失效。5分钟EUR/USD波动小,10点实体算大;日线GBP/JPY同样10点几乎看不见。用ATR做波动率调整后的动态阈值才站得住:锤头线要求实体小于ATR的0.3倍,且下影线≥实体2倍、上影线≤实体0.5倍,条件写成 MathAbs(Close[1]-Open[1])<0.3*ATR 即可。 单根与多根形态的区别在索引跨度。十字星看 open[1]≈close[1];看涨吞没要 close[2]<open[2] 且 close[1]>open[1],同时 open[1]<close[2] 且 close[1]>open[2] 把前一根实体吞掉。先用 Print() 把每根K线的判断结果打出来,比直接挂EA更省排查时间。 外汇与贵金属波动属性差异大,ATR阈值只是概率意义上的适配,实盘仍需警惕滑点和跳空风险。

「把形态识别拆成可复用头文件」

上面列的那些形态(三白兵、三只乌鸦、各类孕线吞没等)本质都是数组下标 + 价格关系的布尔判断。真要一个个写进指标里,代码会膨胀到没法维护;更合理的做法是把判定逻辑抽进一个 .mqh 头文件,后面任何 EA 或指标直接 #include 调用。 原文给出的 infinity_candlestick_pattern.mqh 思路就是这样:头文件里放 IsHammer、IsShootingStar 这类函数,入参是价格数组和 ATR 数组,通过引用传 patternATR 避免多文件调用时的重定义警告。判定时先校验索引有效,再算实体长度,最后拿影线跟实体、ATR 做比值——比如锤子线要求实体小于 0.3*ATR、下影大于等于 2 倍实体。 指标端只负责 OnInit 里建缓冲区和 ATR 句柄,OnCalculate 里取价后循环调库函数,把箭头画到对应 K 线。这种模块化让形态算法和绘图逻辑解耦,以后库里加了新形态,指标只需加缓冲区和几行调用。 下面这段代码片段演示了不在库里的几个轻量判定,可直接贴进脚本验证:十字星看收盘价与开盘价差是否小于 2 个点;看涨吞没要求 2 号阴线、1 号阳线且 1 号实体完全包住 2 号;锤子线则限制小实体加长下影。外汇与贵金属波动大,这类形态只提示概率倾向,实战须自担高风险。

MQL5 / C++
class=class="str">"cmt">// Check for Doji pattern
if( MathAbs(Open[class="num">1] - Close[class="num">1]) < Point * class="num">2 )
class=class="str">"cmt">// Open and close are almost equal
{
Print("Doji 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 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 open
{
    Print("Bullish engulfing pattern detected");
}
class=class="str">"cmt">// Check for Hammer pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.3 * ATR[class="num">1] &&     class=class="str">"cmt">// Small body
class=class="str">"cmt">// Long lower wick
(MathMin(Open[class="num">1], Close[class="num">1]) - Low[class="num">1]) >= class="num">2 * MathAbs(Close[class="num">1] - Open[class="num">1]) &&
class=class="str">"cmt">// Small or no upper wick

◍ 把K线形态判定直接写成MT5条件分支

上面这段逻辑把锤子线、流星线、各类十字星、光头光脚线以及纺锤线都收敛成可机器判定的不等式。核心思路是用 ATR[1] 当尺度基准,实体和影线一律按 ATR 的倍数做阈值比较,而不是写死点数,这样换到 XAUUSD 或 EURUSD 不同波动率品种时不用手动改常数。 以锤子线为例:上影线长度(High[1] 减实体上沿)不超过实体振幅的一半,即判定下影主导。流星线则反过来要求上影 ≥ 2 倍实体,且实体本身小于 0.3*ATR[1],属于小实体长上影的组合。

标准十字星要求Close[1]-Open[1]< 0.1*ATR[1];蜻蜓十字再加下影 > 0.5*ATR[1] 且上影可忽略,墓碑十字则上下影互换。光头光脚线(Marubozu)要求实体两头距高低点都 < 0.1*ATR[1],纺锤线则是小实体配上下影都 > 0.5*ATR[1]。

在 MT5 里把这些 if 块塞进 OnCalculate 或脚本主函数,跑历史品种就能在专家日志里看到形态命中记录。外汇与贵金属杠杆高、滑点跳空频繁,形态命中只代表概率倾斜,实盘前务必用策略测试器按真实点差回测。

MQL5 / C++
if(High[class="num">1] - MathMax(Open[class="num">1], Close[class="num">1]) <= class="num">0.5 * MathAbs(Close[class="num">1] - Open[class="num">1]))
{
   Print("Hammer pattern detected");
}
class=class="str">"cmt">// Check for Shooting Star pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.3 * ATR[class="num">1] &&     class=class="str">"cmt">// Small body
class=class="str">"cmt">// Long upper wick
(High[class="num">1] - MathMax(Open[class="num">1], Close[class="num">1]) >= class="num">2 * MathAbs(Close[class="num">1] - Open[class="num">1]) &&
class=class="str">"cmt">// Small or no lower wick
(MathMin(Open[class="num">1], Close[class="num">1]) - Low[class="num">1]) <= class="num">0.5 * MathAbs(Close[class="num">1] - Open[class="num">1]) )
{
   Print("Shooting Star pattern detected");
}
class=class="str">"cmt">// Check for Standard Doji pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.1 * ATR[class="num">1] )     class=class="str">"cmt">// Open and close are nearly equal
{
   Print("Standard Doji pattern detected");
}
class=class="str">"cmt">// Check for Dragonfly Doji pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.1 * ATR[class="num">1] &&     class=class="str">"cmt">// Small body
(MathMin(Open[class="num">1], Close[class="num">1]) - Low[class="num">1]) > class="num">0.5 * ATR[class="num">1] &&     class=class="str">"cmt">// Long lower wick
(High[class="num">1] - MathMax(Open[class="num">1], Close[class="num">1]) < class="num">0.1 * ATR[class="num">1] )     class=class="str">"cmt">// Little to no upper wick
{
   Print("Dragonfly Doji pattern detected");
}
class=class="str">"cmt">// Check for Gravestone Doji pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.1 * ATR[class="num">1] &&     class=class="str">"cmt">// Small body
(High[class="num">1] - MathMax(Open[class="num">1], Close[class="num">1]) > class="num">0.5 * ATR[class="num">1] &&     class=class="str">"cmt">// Long upper wick
(MathMin(Open[class="num">1], Close[class="num">1]) - Low[class="num">1]) < class="num">0.1 * ATR[class="num">1] )     class=class="str">"cmt">// Little to no lower wick
{
   Print("Gravestone Doji pattern detected");
}
class=class="str">"cmt">// Check for Bullish Marubozu pattern
if( Close[class="num">1] > Open[class="num">1] &&     class=class="str">"cmt">// Bullish candle
(High[class="num">1] - Close[class="num">1]) < class="num">0.1 * ATR[class="num">1] &&     class=class="str">"cmt">// Close is very close to high
(Open[class="num">1] - Low[class="num">1]) < class="num">0.1 * ATR[class="num">1] )     class=class="str">"cmt">// Open is very close to low
{
   Print("Bullish Marubozu pattern detected");
}
class=class="str">"cmt">// Check for Bearish Marubozu pattern
if( Close[class="num">1] < Open[class="num">1] &&     class=class="str">"cmt">// Bearish candle
(High[class="num">1] - Open[class="num">1]) < class="num">0.1 * ATR[class="num">1] &&     class=class="str">"cmt">// Open is very close to high
(Close[class="num">1] - Low[class="num">1]) < class="num">0.1 * ATR[class="num">1] )     class=class="str">"cmt">// Close is very close to low
{
   Print("Bearish Marubozu pattern detected");
}
class=class="str">"cmt">// Check for Spinning Top pattern
if( MathAbs(Close[class="num">1] - Open[class="num">1]) < class="num">0.3 * ATR[class="num">1] &&     class=class="str">"cmt">// Small body
(High[class="num">1] - MathMax(Open[class="num">1], Close[class="num">1]) > class="num">0.5 * ATR[class="num">1] &&     class=class="str">"cmt">// Long upper wick
(MathMin(Open[class="num">1], Close[class="num">1]) - Low[class="num">1]) > class="num">0.5 * ATR[class="num">1] )     class=class="str">"cmt">// Long lower wick
{
   Print("Spinning Top pattern detected");
}
class=class="str">"cmt">// Check for Morning Star pattern
if( Close[class="num">3] < Open[class="num">3] &&     class=class="str">"cmt">// Candle at index class="num">3 is bearish

常见问题

在代码里用 OHLC 数组取每根K线的四个价格,存成变量就能做加减和比较,先从单根K线练手。
把形态拆成实体长度、影线比例等原子条件,写成函数返回true/false,条件满足就报警或标记。
小布会直接对盘口做AI形态识别并在品种页标出候选形态,你只需复核信号强弱即可。
把判定写成独立头文件里的可复用函数,主图或EA直接#include调用,换周期只改参数。
多半是浮点比较没留容差或周期数据未刷新,给比例加阈值区间并用已成型K线计算即可。