价格行为分析工具包开发(第十六部分):引入四分之一理论(2) 侵入探测器智能交易系统(EA)·综合运用
(3/3)·手动盯四分位水平既耗神又易漏单,这篇把监控逻辑写成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,这是防报价未就绪的保底写法。外汇与贵金属波动剧烈,这类未过滤的占位逻辑上线前必须替换,否则可能误报。
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 上较快抓到这些微越界动作。
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 复位,下次再进可再提醒。
} 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 行,就能对照图表验证区域是否真有阻力。
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 调一次报警阈值,看它和你的手动判断差多远。