价格行为分析工具包开发(第二十七部分):利用移动平均线进行流动性扫单·进阶篇
📉

价格行为分析工具包开发(第二十七部分):利用移动平均线进行流动性扫单·进阶篇

(2/3)·零售交易者总在支撑阻力假突破处被止损,本篇用MA过滤把聪明钱扫单看清

案例拆解新手友好 第 2/3 篇
把每次跌破前低都当真破位去止损,是散户账户被反复收割的主因。机构常借扫单触发你的止损再反向,肉眼很难在几根K线内分辨。这篇先把识别逻辑拆给你看,别再凭直觉扛单。

流动性扫单EA的骨架与信号闸门

这套 EA 的核心任务是在 MT5 任意图表上标出假突破式的流动性扫单:监测一根 K 线跌破前一根波段低点(或涨破高点)后以套牢盘姿态收盘,再叠加可选 MA 过滤对齐趋势。代码开头用 #property strict 强制编译器做类型与语法严检,未声明变量或类型不匹配会在运行前被拦下,而不是等到实盘报错。 输入参数分三组:MA 组可切 UseMAFilter、MALength(周期)及六种类型(SMA/EMA/LWMA/VWMA/RMA/HMA);判定组用 SignalStrict 枚举切换宽松/严格模式,宽松只要求 l<l1(看涨),严格额外要求 c>h1;显示组用 LabelType 控制 BS 短标或 Bull Sweep 全文,ArrowOffsetPoints 把箭头推离 K 线避免重叠。ColorChangeOnly 开启后,必须出现 K 线颜色反转才留信号。 全局变量 lastBarTime 存最近处理 K 线时间,每个 tick 用 iTime(Symbol(),Period(),0) 比对,不同才跑 DetectLiquiditySweep(1),保证每根闭合 K 线最多一次信号,从机制上掐掉重绘。MAHandle 初始化为 INVALID_HANDLE,OnInit 里若 MALength<1 直接返回 INIT_FAILED,防御性杜绝 0/负周期计算。 DetectLiquiditySweep 先算 requiredBars=shift+MALength,Bars() 不足就 return,防止数组越界;之后取 shift 与 shift+1 的 o/h/l/c 共 8 个值。宽松看涨条件为 c>o 且 l<l1 且 c>o1 且 c1!=o1,严格则加 c>h1。MA 过滤走 CopyBuffer 取 maValue,PriceAboveMA=true 时只留 c>maValue 的看涨信号。最后 DrawSignal 画图、Experts 日志写 "Bullish sweep detected at [time], price=[close]",并弹屏警报告知。

◍ 箭头与均线的解耦画法

DrawSignal 函数把图表标注和信号检测拆开处理。它先用 iTime 取指定 shift 的 K 线开盘时间 t,再按品种最小变动单位 _Point 算偏移:看涨箭头落在最低价减 ArrowOffsetPoints*_Point,看跌落在最高价加同样偏移。EURUSD 的 _Point 是 0.0001,USDJPY 是 0.01,用 _Point 就不用给每个品种手写偏移量。 对象名用 StringFormat("LS_%I64u", (long)t) 拼出来,因为 K 线开盘时间是唯一 64 位整数,重跑 EA 也不会撞名。画之前 ObjectFind 查重,有就先删,避免图表刷新叠一堆冗余标记。PlotArrow 为 true 就画彩色箭头;为 false 且 LblType 非空,就改画 "BS" 或 "Bull Sweep" 这类文本。 非标准均线藏在两个辅助函数里。CalcVWMA 跑 MALength 根 K 线,把每根收盘价乘成交量(iVolume 读成 64 位再转 double)累进 numerator,成交量累进 denominator,最后返回 numerator/denominator,denominator 为 0 时直接给 0.0 防崩。这就是标准成交量加权均价。 CalcHMA 分两段加权来压滞后:前半段 half=MALength/2,权重从 half 递减到 1 算 w1;全段权重从 MALength 递减到 1 算 w2;HMA = 2*w1 - w2。近期权重被放大,波动市里对拐点的反应可能比普通 MA 快。EA 只在 Bars() 大于 shift+MALength 时才调它,历史不够就跳过。 下方输入参数里 MALength 默认 20、MAType 默认 SMA、ArrowOffsetPoints 默认 10,改这几个值就能直接看标注位置和高低。外汇与贵金属杠杆高,回测结论只代表历史概率,实盘前请在 MT5 用策略测试器自跑一遍。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     Liquidity Sweep with MA filter|
class=class="str">"cmt">//|          Copyright class="num">2025, MetaQuotes Ltd.|
class=class="str">"cmt">//|       [MQL5官方文档]
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2025, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.0"
class="macro">#class="kw">property strict
class="macro">#include <Trade\Trade.mqh>
class=class="str">"cmt">//--- Inputs: Moving Average Filter
input class="type">bool   UseMAFilter       = class="kw">false;    class=class="str">"cmt">// Enable Moving Average Filter
input class="type">bool   ShowMA            = class="kw">false;    class=class="str">"cmt">// Show MA on chart
input class="type">int    MALength          = class="num">20;       class=class="str">"cmt">// MA period(must be >=class="num">1)
enum MA_Type {SMA=class="num">0, EMA, LWMA, VWMA, RMA, HMA};
input MA_Type MAType           = SMA;      class=class="str">"cmt">// Moving Average type
input class="type">bool   PriceAboveMA      = true;     class=class="str">"cmt">// Filter: price above MA?
class=class="str">"cmt">//--- Inputs: Sweep Definition Strictness
enum Strictness {LessStrict=class="num">0, Strict};
input Strictness SignalStrict  = LessStrict; class=class="str">"cmt">// Signal strictness
input class="type">bool   ColorChangeOnly   = class="kw">false;     class=class="str">"cmt">// Only on class="type">class="kw">color-change candles
class=class="str">"cmt">//--- Inputs: Label/Arrow & Color Customization
enum LabelType {None=class="num">0, Short, Full};
input LabelType LblType        = Full;     class=class="str">"cmt">// Label type(None/Short/Full)
input class="type">bool      PlotArrow      = true;     class=class="str">"cmt">// Draw arrow on signal
input class="type">int       ArrowOffsetPoints = class="num">10;    class=class="str">"cmt">// Offset(in points) above/below candles

「用新K线触发而非每跳重算」

MT5 上若把检测逻辑直接丢进 OnTick,每来一个报价就跑一遍,CPU 白白烧在重复计算上。上面这段把「当前 Bar 开盘时间」存进 lastBarTime,只有 iTime 返回的当前值跟上次不同,才调用一次 DetectLiquiditySweep(1)——也就是每根 K 线收盘切换时跑一次。 初始化阶段先卡一道 MALength:若外部输入小于 1,直接 Print 报错并返回 INIT_FAILED,避免后续数组越界。MAHandle 用 iMA 拿内置均线句柄,类型不是 VWMA/HMA 且开了过滤或显示时才建;建完若无效同样 INIT_FAILED,ShowMA 为真再用 ChartIndicatorAdd 挂到主图 0 号窗口。 OnDeinit 里用 IndicatorRelease 释放句柄,防止指标重载时句柄泄漏。DetectLiquiditySweep 开头先算 requiredBars = shift + MALength,Bars 总数不够就 return,这是实盘常见的「历史不足不交易」保护。 开 MT5 把这段贴进 EA 模板,把 MALength 设成 20 在 EURUSD 的 M15 跑一晚,你能直接看到日志里只在整点棒切换时打印检测,而非每跳刷屏。外汇与贵金属杠杆高,这类仅做结构触发的代码不预示方向,信号失效概率始终存在。

MQL5 / C++
input class="type">class="kw">color    BullishColor   = clrLime;    class=class="str">"cmt">// Color for bullish signals
input class="type">class="kw">color    BearishColor   = clrRed;     class=class="str">"cmt">// Color for bearish signals
class=class="str">"cmt">//--- Globals
class="type">class="kw">datetime  lastBarTime = class="num">0;                 class=class="str">"cmt">// Timestamp of the last processed bar
class="type">int       MAHandle    = INVALID_HANDLE;    class=class="str">"cmt">// Handle for built‐in MA indicator
class="type">int OnInit()
  {
  class=class="str">"cmt">// Validate MALength(cannot assign to an input directly)
  if(MALength < class="num">1)
    {
      Print("ERROR: MALength must be at least class="num">1. Current value = ", MALength);
      class="kw">return(INIT_FAILED);
    }
  class=class="str">"cmt">// Initialize timing so we only run when a new bar closes
  lastBarTime = iTime(Symbol(), Period(), class="num">0);
  class=class="str">"cmt">// … (rest of OnInit follows) …
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">// Create MA handle if class="kw">using a built-in MA(SMA, EMA, LWMA, RMA) 
if((MAType != VWMA && MAType != HMA) && (UseMAFilter || ShowMA))
  {
   ENUM_MA_METHOD method = (ENUM_MA_METHOD)MAType;
   MAHandle = iMA(Symbol(), Period(), MALength, class="num">0, method, PRICE_CLOSE);
   if(MAHandle == INVALID_HANDLE)
    {
     Print("Failed to create MA handle(type=", EnumToString(MAType),
           ", length=", MALength, ")");
     class="kw">return(INIT_FAILED);
    }
   if(ShowMA)
     ChartIndicatorAdd(class="num">0, class="num">0, MAHandle);
  }
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
   if(MAHandle != INVALID_HANDLE)
     IndicatorRelease(MAHandle);
  }
class="type">void OnTick()
  {
   class=class="str">"cmt">// Retrieve the current bar’s start time
   class="type">class="kw">datetime current = iTime(Symbol(), Period(), class="num">0);
   class=class="str">"cmt">// If the bar start time changed, call the detection routine once
   if(current != lastBarTime)
     {
      DetectLiquiditySweep(class="num">1);
      lastBarTime = current;
     }
  }
class="type">void DetectLiquiditySweep(class="type">int shift)
  {
   class=class="str">"cmt">// Ensure there are at least(shift + MALength) bars of history
   class="type">int requiredBars = shift + MALength;
   if(Bars(Symbol(), Period()) <= requiredBars)
     {
      class=class="str">"cmt">// Not enough bars to compute custom MA or compare prices
      class="kw">return;
     }
   class=class="str">"cmt">// … (next steps in the function) …
  }
class=class="str">"cmt">//--- Bar data for the current completed candle(index = shift)
class="type">class="kw">double o  = iOpen(Symbol(), Period(), shift);
class="type">class="kw">double c  = iClose(Symbol(), Period(), shift);

把流动性扫单信号接上过滤与告警

这段逻辑把前面算出的 K 线数据收口成可交易的扫单判定。先取当前与前置一根蜡烛的开高低收:当前柱用 shift 索引,前一根统一用 shift+1,避免手误写错偏移。 颜色反转标志很直白:bullCC 要求本根收大于开、且前一根收小于开;bearCC 反之。扫单本身分两档——LessStrict 只要求刺穿前低/前高并站回前一根开盘侧,Strict 则要求收盘直接越过前一根极端价(如 bullSweep 中 c > h1),假突破过滤更狠。 若勾了 ColorChangeOnly,就用 &= 把扫单与颜色反转绑定,信号必须是实体反转带动的扫单。UseMAFilter 打开后,VWMA/HMA 走自定义函数,其余类型用 CopyBuffer 取内置 MA 句柄;取不到数据直接 return,不画信号。 过滤完若 bullSweep 或 bearSweep 仍为真,调 DrawSignal 画图并用 PrintFormat 打日志,精度写到 %.5f(外汇五位数报价够用)。最后无论多空,只要触发就弹 Alert 报品种与周期。 外汇和贵金属杠杆高,扫单后延续概率不等于必然,建议先在 MT5 策略测试器用 2023 年 XAUUSD 小时图跑一遍 Strict 模式,看告警频率是否适配你的盯盘节奏。

MQL5 / C++
class="type">class="kw">double h  = iHigh(Symbol(), Period(), shift);
class="type">class="kw">double l  = iLow(Symbol(), Period(), shift);
class=class="str">"cmt">//--- Bar data for the prior candle(index = shift + class="num">1)
class="type">class="kw">double o1 = iOpen(Symbol(), Period(), shift + class="num">1);
class="type">class="kw">double c1 = iClose(Symbol(), Period(), shift + class="num">1);
class="type">class="kw">double h1 = iHigh(Symbol(), Period(), shift + class="num">1);
class="type">class="kw">double l1 = iLow(Symbol(), Period(), shift + class="num">1);
class=class="str">"cmt">// Compute class="type">class="kw">color-change flags
class="type">bool bullCC = (c > o && c1 < o1);
class="type">bool bearCC = (c < o && c1 > o1);
class=class="str">"cmt">// Liquidity sweep flags(LessStrict or Strict)
class="type">bool bullSweep, bearSweep;
if(SignalStrict == LessStrict)
  {
   bullSweep = (c > o && l < l1 && c > o1 && c1 != o1);
   bearSweep = (c < o && h > h1 && c < o1 && c1 != o1);
  }
else class=class="str">"cmt">// Strict
  {
   bullSweep = (c > o && l < l1 && c > h1 && c1 != o1);
   bearSweep = (c < o && h > h1 && c < l1 && c1 != o1);
  }
class=class="str">"cmt">// If only class="type">class="kw">color-change sweeps are desired, AND‐combine with the raw sweep flags
if(ColorChangeOnly)
  {
   bullSweep &= bullCC;      class=class="str">"cmt">// must also be a bullish class="type">class="kw">color reversal
   bearSweep &= bearCC;      class=class="str">"cmt">// must also be a bearish class="type">class="kw">color reversal
  }
if(UseMAFilter)
  {
   class="type">class="kw">double maValue = class="num">0.0;
   class=class="str">"cmt">// Compute MA value at the same &class="macro">#x27;shift&class="macro">#x27;
   if(MAType == VWMA)
      maValue = CalcVWMA(shift);
   else if(MAType == HMA)
      maValue = CalcHMA(shift);
   else
     {
      class=class="str">"cmt">// Built‐in MA handle(SMA, EMA, LWMA, RMA)
      class="type">class="kw">double buf[];
      if(CopyBuffer(MAHandle, class="num">0, shift, class="num">1, buf) != class="num">1)
         class="kw">return;   class=class="str">"cmt">// no valid MA data available
      maValue = buf[class="num">0];
     }
   class=class="str">"cmt">// Only allow bullish sweeps above MA or bearish sweeps below MA
   class="type">bool cond = PriceAboveMA ? (c > maValue) : (c < maValue);
   bullSweep &= cond;
   bearSweep &= !cond;
  }
class=class="str">"cmt">// If a bullish sweep remains true after all filters, draw and log it
if(bullSweep)
  {
   DrawSignal(shift, true);
   PrintFormat("Bullish sweep detected at %s, price=%.5f",
               TimeToString(iTime(Symbol(),Period(),shift)), c);
  }
class=class="str">"cmt">// If a bearish sweep remains true after all filters, draw and log it
if(bearSweep)
  {
   DrawSignal(shift, class="kw">false);
   PrintFormat("Bearish sweep detected at %s, price=%.5f",
               TimeToString(iTime(Symbol(),Period(),shift)), c);
  }
class=class="str">"cmt">// In either case, fire a pop‐up alert
if(bullSweep || bearSweep)
  {
   Alert("Liquidity Sweep detected on ", Symbol(), " ", EnumToString(Period()));
  }
class="type">void DrawSignal(class="type">int shift, class="type">bool bullish)
  {
把扫单标记交给小布盯盘
这些流动性扫单的箭头与MA确认信号,小布盯盘的AIGC已内置到对应品种页,打开即可看到实时标注,你只管判断跟不跟。

常见问题

shift=1 是刚收盘的当前测试K线,shift+1=2 是其前一根,EA每根新K线收盘时调用一次做比对。
宽松只要求新K线收高于开且刺破前低后回区间;严格会再卡K线颜色变化或MA方向,过滤假信号更狠。
无需自己编译,小布盯盘品种页已内置同源逻辑的可视化标记与警报,参数可切MA过滤。
机构吸收止损流动性后头寸方向本就顺原趋势,散户止损盘消失反而减轻反向阻力,概率上延续更强。