价格行为分析工具包开发(第十六部分):引入四分之一理论(2) 侵入探测器智能交易系统(EA)·综合运用
🤖

价格行为分析工具包开发(第十六部分):引入四分之一理论(2) 侵入探测器智能交易系统(EA)·综合运用

(3/3)·手动盯四分位水平既耗神又易漏单,这篇把监控逻辑写成EA并给出回测结果

含代码示例 第 3/3 篇
价格碰到小四分位还是大四分位,靠肉眼守着图表多半会错过前几根K线的反应。把容差扫描交给EA,你只在它报警时回来读市场语言,效率差出几个量级。

「用标签和生命周期钩子搭检测器骨架」

这段逻辑给「侵入检测器」EA 铺了底:先在每段行情里按 smallQuarter 步长画水平线,线名用段号 j 拼出来,方便后续按名删改。 画完线之后,CreateOrUpdateLabel 负责在图表角落挂一个等宽字体的文本标签;它先 ObjectFind 查重,查不到才 ObjectCreate,避免每 tick 重复建对象导致句柄泄漏。 OnInit 里把标签名写死成 IntrusionCommentary,丢在左上角 (10,10) 像素位,白字 14 号,返回 INIT_SUCCEEDED 即代表 EA 加载成功。 OnDeinit 只做一件事:ObjectDelete 清掉那条标签,防止切换周期或卸载后图表残留垃圾对象。 OnTick 开头把 intrusionDetected 写死为 true 只是占位,真正判定要后面补;currentPrice 取 SYMBOL_BID,若为 0 直接 return,这是防报价未就绪的保底写法。外汇与贵金属波动剧烈,这类未过滤的占位逻辑上线前必须替换,否则可能误报。

MQL5 / C++
class="type">class="kw">double level = segStart + j * smallQuarter;
class="type">class="kw">string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j);
DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle);
 }
 }
 }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CreateOrUpdateLabel: Creates or updates a label with given text  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CreateOrUpdateLabel(class="type">class="kw">string name, class="type">class="kw">string text, class="type">int corner, class="type">int xdist, class="type">int ydist, class="type">class="kw">color txtColor, class="type">int fontSize)
  {
  if(ObjectFind(class="num">0, name) == -class="num">1)
    {
      ObjectCreate(class="num">0, name, OBJ_LABEL, class="num">0, class="num">0, class="num">0);
      ObjectSetInteger(class="num">0, name, OBJPROP_CORNER, corner);
      ObjectSetInteger(class="num">0, name, OBJPROP_XDISTANCE, xdist);
      ObjectSetInteger(class="num">0, name, OBJPROP_YDISTANCE, ydist);
      class=class="str">"cmt">// Set a monospaced font for tabular display(Courier New)
      ObjectSetString(class="num">0, name, OBJPROP_FONT, "Courier New");
    }
  ObjectSetString(class="num">0, name, OBJPROP_TEXT, text);
  ObjectSetInteger(class="num">0, name, OBJPROP_COLOR, txtColor);
  ObjectSetInteger(class="num">0, name, OBJPROP_FONTSIZE, fontSize);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnInit: Initialization function for the EA                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">// Create a persistent commentary label in the top-left corner
  CreateOrUpdateLabel("IntrusionCommentary", "Intrusion Detector Initialized", CORNER_LEFT_UPPER, class="num">10, class="num">10, clrWhite, class="num">14);
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnDeinit: Deinitialization function for the EA                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
  {
class=class="str">"cmt">// Remove the commentary label on exit
  ObjectDelete(class="num">0, "IntrusionCommentary");
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| OnTick: Main function called on every tick                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnTick()
  {
  class="type">bool intrusionDetected = true;
  class="type">class="kw">double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  if(currentPrice == class="num">0)
      class="kw">return;
class=class="str">"cmt">// Draw the quarter lines first

千点区间内的支撑阻力与越界扫描

在 MT5 脚本里,先把当前价格锚定到 1000 点(MajorStep)整数区间:下边界用 MathFloor(currentPrice / MajorStep) * MajorStep 取整,上边界直接加 MajorStep。这样无论 EURUSD 还是 XAUUSD,都能立刻框出「当前千点笼子」的上下沿。 随后用一张表格式字符串把触发区打印出来:表头占 18 字符宽留给 Zone 名,价格留 8 字符,Reason 写触发逻辑。若现价距下边界 ≤ AlertTolerance,记 Major Support 并置 intrusionDetected=true;距上边界同理记 Major Resistance。外汇与贵金属波动剧烈,这种边界触碰只是概率信号,不代表反转必现。 开启 DrawLargeQuarters 后,代码把千点区间四等分(LQIncrement = MajorStep/4.0),遍历 i=1~3 的中间三个大四分之一位。每个位若被现价贴近就记 Large Quarter。 若再开 DrawOvershootAreas,会在每个大四分之一位上下各偏移 MajorStep/40.0(约 25 点)生成越界区:overshootUp 向上、undershootDown 向下。现价蹭到就分别记 Overshoot / Undershoot。把 AlertTolerance 设成 10 点左右,能在 M15 上较快抓到这些微越界动作。

MQL5 / C++
  DrawQuarters(currentPrice);
class=class="str">"cmt">// Calculate boundaries of the current class="num">1000-PIP Range
  class="type">class="kw">double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
  class="type">class="kw">double upperMajor = lowerMajor + MajorStep;
class=class="str">"cmt">// Build a tabular commentary class="type">class="kw">string with a header.
  class="type">class="kw">string header = StringFormat("%-18s | %-8s | %s\n", "Zone", "Price", "Reason");
  class="type">class="kw">string separator = "----------------------------------------------\n";
  class="type">class="kw">string table = header + separator;
class=class="str">"cmt">// Check for Major Support
  if(MathAbs(currentPrice - lowerMajor) <= AlertTolerance)
    {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Support", DoubleToString(lowerMajor,class="num">4), MajorSupportReason);
      intrusionDetected = true;
    }
class=class="str">"cmt">// Check for Major Resistance
  if(MathAbs(currentPrice - upperMajor) <= AlertTolerance)
    {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Resistance", DoubleToString(upperMajor,class="num">4), MajorResistanceReason);
      intrusionDetected = true;
    }
class=class="str">"cmt">// Check Large Quarter Levels and Overshoot/Undershoot Zones
  if(DrawLargeQuarters)
    {
      class="type">class="kw">double LQIncrement = MajorStep / class="num">4.0;
      for(class="type">int i = class="num">1; i < class="num">4; i++)
        {
         class="type">class="kw">double level = lowerMajor + i * LQIncrement;
         if(MathAbs(currentPrice - level) <= AlertTolerance)
           {
             table += StringFormat("%-18s | %-8s | %s\n", "Large Quarter", DoubleToString(level,class="num">4), LargeQuarterReason);
             intrusionDetected = true;
           }
         if(DrawOvershootAreas)
           {
             class="type">class="kw">double offset = MajorStep / class="num">40.0; class=class="str">"cmt">// ~class="num">25 pips
             class="type">class="kw">double overshootUp = level + offset;
             class="type">class="kw">double undershootDown = level - offset;
             if(MathAbs(currentPrice - overshootUp) <= AlertTolerance)
               {
                table += StringFormat("%-18s | %-8s | %s\n", "Overshoot", DoubleToString(overshootUp,class="num">4), OvershootReason);
                intrusionDetected = true;
               }
             if(MathAbs(currentPrice - undershootDown) <= AlertTolerance)
               {
                table += StringFormat("%-18s | %-8s | %s\n", "Undershoot", DoubleToString(undershootDown,class="num">4), UndershootReason);
                intrusionDetected = true;
               }
           }
        }
    }

◍ 小季度位触发与无侵入时的兜底输出

这段逻辑承接大区间判定之后,只在 DrawSmallQuarters 开关打开时才计算小季度位。它将 MajorStep 先除以 10 得到 segStep,再除以 4 得到 smallQuarter,相当于把每个大区间切成 10 段、每段再分 4 份,定位 0.25、0.5、0.75 三个内部分位。 双层循环里,外层 seg 跑 0~9 覆盖十个子段,内层 j 跑 1~3 跳过整段起点只取三个小季度价。若当前价与某个 level 的距离小于等于 AlertTolerance,就拼入表格并置 intrusionDetected 为 true,说明价格侵入了敏感分位。 若整轮跑完没有任何触发,代码不留空表,而是写一句「No significant intrusion detected」并附当前价,提示市场可能在已有季度框内震荡。外汇与贵金属波动剧烈,这种无侵入状态随时可能被一根大阳/大阴破坏,仅作概率参考。 最后更新左上角标签显示表格,且 intrusionDetected 为真、intrusionAlerted 为假时才弹 Alert,保证同一次侵入只响一次;价格离开敏感区后把 intrusionAlerted 复位,下次再进可再提醒。

MQL5 / C++
   }
class=class="str">"cmt">// Check Small Quarter Levels(if enabled)
   if(DrawSmallQuarters)
   {
      class="type">class="kw">double segStep = MajorStep / class="num">10.0;
      class="type">class="kw">double smallQuarter = segStep / class="num">4.0;
      for(class="type">int seg = class="num">0; seg < class="num">10; seg++)
      {
       class="type">class="kw">double segStart = lowerMajor + seg * segStep;
       for(class="type">int j = class="num">1; j < class="num">4; j++)
         {
          class="type">class="kw">double level = segStart + j * smallQuarter;
          if(MathAbs(currentPrice - level) <= AlertTolerance)
            {
             table += StringFormat("%-18s | %-8s | %s\n", "Small Quarter", DoubleToString(level,class="num">4), SmallQuarterReason);
             intrusionDetected = true;
            }
         }
      }
   }
class=class="str">"cmt">// If no zones were triggered, still provide full information
   if(!intrusionDetected)
   {
    table = StringFormat("No significant intrusion detected.\nCurrent Price: %s\nMarket consolidating within established quarters.\n", DoubleToString(currentPrice,class="num">4));
   }
class=class="str">"cmt">// Update the label with the commentary table.
   CreateOrUpdateLabel("IntrusionCommentary", table, CORNER_LEFT_UPPER, class="num">10, class="num">10, clrWhite, class="num">14);
class=class="str">"cmt">// Trigger an alert only once per intrusion event.
   if(intrusionDetected && !intrusionAlerted)
   {
    Alert(table);
    class=class="str">"cmt">// Alternatively, you could use: PlaySound("alert.wav");
    intrusionAlerted = true;
   }
   if(!intrusionDetected)
    intrusionAlerted = false;
   ChartRedraw();
   }

「模拟盘里跑出来的警报长什么样」

下面这组记录来自 MT5 的 Experts 选项卡,是 NZDUSD 的 H1 周期下用模拟账户测「侵入检测 EA」留下的真实日志。价格走到 0.5725 时触发了下探(Undershoot)警报,系统同时给了一句判断:看涨动能不足,存在看跌反转的可能。 外汇与贵金属杠杆高、滑点随机,模拟结论不能直接当实盘依据。但这条日志的价值在于——它把「区域识别 + 具体价位 + 动能评语」压成了一行,省掉了人工盯盘去翻裸 K 的时间。 同一套逻辑在 USD/CAD 上也测过,GIF 里能看到两个上冲区域和大四分位价位的反应区间被标出来。如果你手头有 MT5,直接把 EA 挂上 H1,等 Experts 里冒出 Undershoot / Overshoot 行,就能对照图表验证区域是否真有阻力。

MQL5 / C++
class="num">2025.02.class="num">25 class="num">16:class="num">55:class="num">37.188 Intrusion Detector EA(NZDUSD,H1)     Alert: Zone           | Price    | Reason
class="num">2025.02.class="num">25 class="num">16:class="num">55:class="num">37.188 Intrusion Detector EA(NZDUSD,H1)     ----------------------------------------------
class="num">2025.02.class="num">25 class="num">16:class="num">55:class="num">37.188 Intrusion Detector EA(NZDUSD,H1)     Undershoot       | class="num">0.5725  | Insufficient bullish force; possible bearish reversal.
class="num">2025.02.class="num">25 class="num">16:class="num">55:class="num">37.188 Intrusion Detector EA(NZDUSD,H1)     

把工具请下神坛

从 01/10/24 的图表展示器到 27/02/25 的侵入探测器,16 个工具串起了一条自动化脉络:前 15 个在做绘制、预测、指标整合,最后一个才真正落到「价格触到四分位水平就报警」的实时监测。EA 在回测环境里对关键区间的捕捉和提醒延迟都表现稳定,但外汇与贵金属自带高杠杆和高波动,信号漏报或假触发的概率始终存在。 把它当助手可以,当判官不行。侵入探测器只在价格碰线时给一句背景说明,真正的进出场还得你用自己的策略过滤一遍。个人经验加自动化预警,才更接近靠谱的决策链路。 这一代四分位监测算踏出了自动化的一步,后面大概率还会有更细的价位交互逻辑,但眼下你能做的,就是挂上 EA 调一次报警阈值,看它和你的手动判断差多远。

让小布替你跑这套
这些诊断小布盯盘的AIGC已内置,打开对应品种页即可看到四分位触碰提醒,把重复劳动交给小布,你专注决策。

常见问题

容差取决于品种波动率和图表周期,常见从2到10点不等;回测时应按品种分批标定,避免把噪音当成有效侵入。
通常另设偏移区间并采用不同线条样式与颜色,代码层用独立数组存储,绘制时按类型遍历避免互相覆盖。
原理论以货币对为基准验证,贵金属如XAUUSD波动结构不同,直接套用可能失真,建议先在小周期做样本比对。
小布盯盘内置的AIGC看盘页已覆盖四分位触碰预警,不必自己部署EA也能收到同级提醒,适合不想写代码的交易者。
在重要整数位附近滑点可能让成交价越过容差带,回测需加入真实点差与延迟,否则实盘信号质量可能倾向下降。