价格行为分析工具包开发(第 24 部分):价格行为量化分析工具·进阶篇
📘

价格行为分析工具包开发(第 24 部分):价格行为量化分析工具·进阶篇

第 2/2 篇

「把四类K线形态交给EA自动认」

这套EA的核心目的,是把交易者常盯的长影线、十字星、吞没、光头光脚四类形态做成客观识别,不在图表上靠肉眼猜。它顺带记录形态出现后的下一根K线是否按预期方向移动,以此统计胜率,帮你在MT5里直接评估某形态在近200根K线内的参考价值。外汇与贵金属波动大,形态失效是常态,任何统计胜率都只是历史概率,不是方向保证。 输入参数先放开灵活性:InpLookbackBars=200 控制回溯根数,InpMinBodySizePts=10 过滤微小实体,InpWickBodyRatio=2.0 规定影线至少2倍于实体才算长影线,InpATRPeriod=14 把波动率拉进识别逻辑。OnInit里把这些数组按时间序列排好,保证新K线从索引0开始算;同时留了5格历史缓冲区记下最近信号。 OnTick用时间戳比对卡掉同根K线内的重复计算,每根新K线只跑一次AnalyzeAndDraw。该函数先清掉旧箭头和标签,再循环算每根K线的实体、上下影线、总区间和ATR。识别规则很硬:看跌长影线要求上影线≥2倍实体且下影线短;十字星实体≤总区间10%且两影线都长于实体;吞没要求当前实体吞掉前一根且方向反转;光头光脚则实体≥总区间90%、影线近乎没有。 命中即在图表画箭头并弹警报,回测结束或移除EA时输出总信号数、获胜次数与胜率。想验证就把自己常用的品种拖进策略测试器,把InpWickBodyRatio从2.0调到3.0,看长影线信号是变少还是假突破也跟着变少。

MQL5 / C++
class=class="str">"cmt">// Input parameters for customization
input class="type">int     InpLookbackBars   = class="num">200;   class=class="str">"cmt">// Bars to scan
input class="type">int     InpMinBodySizePts = class="num">10;     class=class="str">"cmt">// Min body size(points)
input class="type">class="kw">double  InpWickBodyRatio  = class="num">2.0;   class=class="str">"cmt">// Min wick-to-body ratio
input class="type">int     InpATRPeriod      = class="num">14;     class=class="str">"cmt">// ATR period
class="type">int OnInit()
  {
   ArraySetAsSeries(Body,true);
   ArraySetAsSeries(UpperWick,true);
   ArraySetAsSeries(LowerWick,true);
   ArraySetAsSeries(TotalRange,true);
   ArraySetAsSeries(ATR,true);
   ArrayResize(Body, InpLookbackBars+class="num">2);
   ArrayResize(UpperWick, InpLookbackBars+class="num">2);
   ArrayResize(LowerWick, InpLookbackBars+class="num">2);
   ArrayResize(TotalRange, InpLookbackBars+class="num">2);
   ArrayResize(ATR, InpLookbackBars+class="num">2);
   for(class="type">int i=class="num">0;i<class="num">5;i++)
      g_history[i]="";
   class="kw">return(INIT_SUCCEEDED);
  }
class="type">void OnTick()
  {
   class="kw">static class="type">class="kw">datetime lastTime=class="num">0;
   class="type">class="kw">datetime current = iTime(_Symbol, _Period, class="num">0);
   if(current == lastTime)
      class="kw">return; class=class="str">"cmt">// Only process once per new bar
   lastTime = current;
   AnalyzeAndDraw();
  }
class="type">void AnalyzeAndDraw()
  {
   class="type">int bars = MathMin(InpLookbackBars, (class="type">int)SeriesInfoInteger(_Symbol,_Period,SERIES_BARS_COUNT));
   ObjectsDeleteAll(class="num">0,class="num">0,OBJ_ARROW);
   ObjectsDeleteAll(class="num">0,class="num">0,OBJ_LABEL);
   for(class="type">int i=class="num">0; i<bars; i++)
     {

用 K 线构件识别 Pin Bar 与吞没形态

把单根 K 线拆成四个数值,是后续所有形态判定的地基。开高低收分别用 iOpen/iHigh/iLow/iClose 按索引 i 取数,实体、上下影线、总波幅全部由这四个值推导,不依赖任何外部指标缓冲。 double op = iOpen(_Symbol,_Period,i); double cl = iClose(_Symbol,_Period,i); double hi = iHigh(_Symbol,_Period,i); double lo = iLow(_Symbol,_Period,i); // Calculate candlestick components Body[i] = MathAbs(cl-op); UpperWick[i] = hi - MathMax(op,cl); LowerWick[i] = MathMin(op,cl) - lo; TotalRange[i] = hi - lo; // Copy ATR buffer if(CopyBuffer(iATR(_Symbol,_Period,InpATRPeriod),0,0,bars,ATR)<=0) return; // Exit if ATR data not available // assume Body[i], UpperWick[i], LowerWick[i] already computed if(InpShowPinBar && Body[i] > InpMinBodySizePts*_Point) { // Bearish: towering upper wick, stubby lower wick if( UpperWick[i] > Body[i]*InpWickBodyRatio && LowerWick[i] < Body[i]*0.5 ) type = "Bearish Pin Bar"; // Bullish: towering lower wick, stubby upper wick else if( LowerWick[i] > Body[i]*InpWickBodyRatio && UpperWick[i] < Body[i]*0.5 ) type = "Bullish Pin Bar"; } // TotalRange[i] = high – low if(InpShowDoji && Body[i] <= TotalRange[i] * InpDojiBodyRatio // body tiny vs full range && UpperWick[i] > Body[i] // upper shadow present && LowerWick[i] > Body[i]) // lower shadow present type = "Doji"; double po = iOpen(_Symbol,_Period,i+1), pc = iClose(_Symbol,_Period,i+1); double prevBody = MathAbs(pc - po), currBody = Body[i]; if(InpShowEngulfing && currBody > prevBody) { // Bullish: current body engulfs prior’s if(pc < po && op <= pc && cl >= po) type = "Bullish Engulfing"; // Bearish: current body engulfs prior’s else if(pc > po && op >= pc && cl <= po) type = "Bearish Engulfing"; } if(InpShowMarubozu && Body[i] >= TotalRange[i] * InpMarubozuBodyRatio && UpperWick[i] <= TotalRange[i] * (1 - InpMarubozuBodyRatio) && LowerWick[i] <= TotalRange[i] * (1 - InpMarubozuBodyRatio)) { type = (cl > op) ? "Bullish Marubozu" : "Bearish Marubozu"; } if(type!="") { lastIdx = i; lastCl = cl; lastIsBuy = (StringFind(type,"Bullish")>=0); latestSignal = type; g_totalSignals++; double nextC = iClose(_Symbol,_Period,i-1); if(lastIsBuy && nextC>cl) g_totalWins++; 上面这段逐行拆完,重点在 Pin Bar 判定:上影线大于实体乘 InpWickBodyRatio、下影线小于实体一半,才标记为看跌 Pin Bar;反过来则是看涨。InpMinBodySizePts*_Point 用点数卡掉那些太小没意义的碎 K。 吞没形态拿 i+1(前一根)和 i(当前根)比:当前实体必须比前一根大,且开盘收盘包住前一根的实体区间,才判为 Bullish/Bearish Engulfing。外汇与贵金属波动大,这类形态在 M15 以上周期出现时反转概率可能更高,但仍属概率事件,实盘前请在 MT5 策略测试器跑一遍确认信号频率。 Marubozu 的判定最硬:实体占满总波幅的 InpMarubozuBodyRatio 以上,且上下影线都不超过剩余比例,基本就是光头光脚。把 InpWickBodyRatio 设 2.0、InpDojiBodyRatio 设 0.1 这类参数直接塞进 EA 输入项,开 MT5 切 XAUUSD 的 H1 就能看到标记是否如预期。

MQL5 / C++
class="type">class="kw">double op = iOpen(_Symbol,_Period,i);
class="type">class="kw">double cl = iClose(_Symbol,_Period,i);
class="type">class="kw">double hi = iHigh(_Symbol,_Period,i);
class="type">class="kw">double lo = iLow(_Symbol,_Period,i);
class=class="str">"cmt">// Calculate candlestick components
Body[i]      = MathAbs(cl-op);
UpperWick[i] = hi - MathMax(op,cl);
LowerWick[i] = MathMin(op,cl) - lo;
TotalRange[i] = hi - lo;
class=class="str">"cmt">// Copy ATR buffer
if(CopyBuffer(iATR(_Symbol,_Period,InpATRPeriod),class="num">0,class="num">0,bars,ATR)<=class="num">0)
   class="kw">return; class=class="str">"cmt">// Exit if ATR data not available
class=class="str">"cmt">// assume Body[i], UpperWick[i], LowerWick[i] already computed
if(InpShowPinBar && Body[i] > InpMinBodySizePts*_Point)
{
  class=class="str">"cmt">// Bearish: towering upper wick, stubby lower wick
  if( UpperWick[i] > Body[i]*InpWickBodyRatio
    && LowerWick[i] < Body[i]*class="num">0.5 )
    type = "Bearish Pin Bar";
  class=class="str">"cmt">// Bullish: towering lower wick, stubby upper wick
  else if( LowerWick[i] > Body[i]*InpWickBodyRatio
        && UpperWick[i] < Body[i]*class="num">0.5 )
    type = "Bullish Pin Bar";
}
class=class="str">"cmt">// TotalRange[i] = high – low
if(InpShowDoji
  && Body[i] <= TotalRange[i] * InpDojiBodyRatio      class=class="str">"cmt">// body tiny vs full range
  && UpperWick[i] > Body[i]                            class=class="str">"cmt">// upper shadow present
  && LowerWick[i] > Body[i])                           class=class="str">"cmt">// lower shadow present
  type = "Doji";
class="type">class="kw">double po = iOpen(_Symbol,_Period,i+class="num">1),
       pc = iClose(_Symbol,_Period,i+class="num">1);
class="type">class="kw">double prevBody = MathAbs(pc - po),
       currBody = Body[i];
if(InpShowEngulfing && currBody > prevBody)
{
  class=class="str">"cmt">// Bullish: current body engulfs prior’s
  if(pc < po && op <= pc && cl >= po)
    type = "Bullish Engulfing";
  class=class="str">"cmt">// Bearish: current body engulfs prior’s
  else if(pc > po && op >= pc && cl <= po)
    type = "Bearish Engulfing";
}
if(InpShowMarubozu
  && Body[i] >= TotalRange[i] * InpMarubozuBodyRatio
  && UpperWick[i] <= TotalRange[i] * (class="num">1 - InpMarubozuBodyRatio)
  && LowerWick[i] <= TotalRange[i] * (class="num">1 - InpMarubozuBodyRatio))
{
  type = (cl > op) ? "Bullish Marubozu" : "Bearish Marubozu";
}
if(type!="")
{
   lastIdx = i;
   lastCl = cl;
   lastIsBuy = (StringFind(type,"Bullish")>=class="num">0);
   latestSignal = type;
   g_totalSignals++;
   class="type">class="kw">double nextC = iClose(_Symbol,_Period,i-class="num">1);
   if(lastIsBuy && nextC>cl)
       g_totalWins++;

◍ 把形态画在图上并算出胜率

上面这段逻辑干了两件事:一是扫描到符合条件的形态后在图表上打标记,二是退出时把整体命中情况打印出来。DrawPattern 函数负责可视化,OnDeinit 负责回测收尾统计。 DrawPattern 先用 iTime 取对应 K 线时间、iHigh 取最高价再叠加 15 个点作为箭头 Y 坐标,ObjectCreate 以 OBJ_ARROW 类型建箭头,买信号用箭头码 233 染绿、卖信号用 234 染红。紧跟一个 OBJ_LABEL 文字标签,下移 25 个点显示形态类型,颜色与箭头一致。 OnDeinit 里胜率算法很直白:信号数大于 0 时,胜率 = 100.0 × 总胜次 ÷ 总信号数,否则记 0。PrintFormat 输出类似 [PAQ] Backtest completed: Signals=120 Wins=71 WinRate=59.2% 的日志,你可以直接拿这个数在 MT5 策略测试器里比对。 外汇与贵金属波动受杠杆和消息面影响,历史形态胜率只代表过去样本,实盘触发概率可能明显偏离回测值,开 MT5 跑一遍自有品种的周期分布再下结论更稳妥。

MQL5 / C++
if(!lastIsBuy && nextC<cl)
      g_totalWins++;
   DrawPattern(i, type, lastIsBuy);
   class="kw">break;
}
class="type">void DrawPattern(class="type">int idx, class="kw">const class="type">class="kw">string type, class="type">bool isBuy)
  {
   class="type">class="kw">datetime t = iTime(_Symbol,_Period,idx);
   class="type">class="kw">double y = iHigh(_Symbol,_Period,idx) + class="num">15*_Point;
   class="type">class="kw">string an = StringFormat("PAQ_%s_%d",type,idx);
   ObjectCreate(class="num">0,an,OBJ_ARROW,class="num">0,t,y);
   ObjectSetInteger(class="num">0,an,OBJPROP_ARROWCODE,isBuy?class="num">233:class="num">234);
   ObjectSetInteger(class="num">0,an,OBJPROP_COLOR,isBuy?clrLime:clrRed);
   class="type">class="kw">string lbl = an+"_lbl";
   ObjectCreate(class="num">0,lbl,OBJ_LABEL,class="num">0,t,y-class="num">25*_Point);
   ObjectSetString(class="num">0,lbl,OBJPROP_TEXT,type);
   ObjectSetInteger(class="num">0,lbl,OBJPROP_COLOR,isBuy?clrLime:clrRed);
  }
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
   class="type">class="kw">double rate = g_totalSignals>class="num">0 ? class="num">100.0*g_totalWins/g_totalSignals : class="num">0.0;
   PrintFormat("[PAQ] Backtest completed: Signals=%I64d Wins=%I64d WinRate=%.1f%%",
               g_totalSignals, g_totalWins, rate);
  }

「实盘与回测里的形态命中」

把 EA 挂上 MT5 实时图,最先撞见的是看跌吞没:一根阴线实体把前一根阳线整根包住,EA 在图上标了出来,肉眼核对确实符合定义,不是近似。 另一处触发是带看跌长影线的 K 线,影线明显向下探、实体偏短,EA 同样识别并标注,说明它对影线类形态的判断逻辑是跑通的。 回测段更能说明问题:EA 把每一个识别到的形态名称写进了日志,翻 Experts 标签就能逐条核对,没有漏报或乱命名。 外汇与贵金属波动剧烈、滑点随机,这类形态识别只提高信号可读性,不预示涨跌,实盘前先用策略测试器跑一遍自己的品种周期。

形态识别之外的工具链延伸

这套EA在核心任务——四种关键K线形态识别上表现稳定,回测与实盘均准确标出目标形态,但不追求滤掉所有假信号,而是作为价格行为分析的补充层。外汇与贵金属市场高波动、高杠杆,信号失效概率始终存在,下单前仍需结合你自己的确认逻辑。 作者同期还放出24款配套工具,时间跨度从24年10月到25年5月:例如09/12/24的波动性导航工具用布林带+RSI+ATR判势,21/05/25的PAQ分析工具专做K线形态检测,27/02/25的侵入检测器在价格触四分位水平时报警。打开MT5文件目录把对应mq5拖进编辑器,就能按自己策略改输入参数。 工具再多也只是眼睛的延伸。真要落地,先拿EURUSD的M15跑一遍PAQ,看它标出的pin bar和engulfing是否跟你肉眼一致,再决定要不要接进实盘。

常见问题

用K线构件把实体、影线长度写成条件判断,EA扫描每根收盘K线并标记,省去肉眼翻图。
在EA里记录形态出现后的N根K线涨跌方向,统计盈利次数除以总次数,回测跑一遍即得胜率。
小布盯盘的AIGC已内置常见形态识别与统计,打开对应品种页即可直接看标记和概率,不用自己写EA。
滑点和假突破会让实盘胜率比回测低5%到15%,外汇贵金属高风险,需以实盘样本为准。
可延伸做波动率过滤、形态出现时的成交量佐证、以及把多个形态组合成共振信号再报警。