价格行为分析工具包开发(第十六部分):引入四分之一理论(2) 侵入探测器智能交易系统(EA)·进阶篇
「千点区间的四分位画线逻辑」
这段 DrawQuarters 函数是把当前价格锁定在一个 1000 点(MajorStep=0.1000)区间内做水平线分割的核心。先用 MathFloor(currentPrice / MajorStep) * MajorStep 求出区间下边界,再加 MajorStep 得到上边界,两条主界线就框出了整段千点范围。 大四分位把千点区间四等分,循环 i=1 到 3 生成三个内部分割线;若开启 DrawOvershootAreas,会在每条大四分位上下各偏移 MajorStep/40.0(约 25 点)画超调/欠调参考线,用来观察价格触碰四分位后的刺穿幅度。 小四分位进一步把区间切成 10 段、每段再四等分,共 30 条细线,适合做更密的价格行为参照。外汇与贵金属波动剧烈,这类辅助线仅作结构观察,实际突破概率仍受基本面与流动性影响。 开 MT5 把 MajorStep 改成 0.0500 可切换为 500 点区间视图,肉眼对比黄金与欧美对四分位的反应差异。
class="type">void DrawQuarters(class="type">class="kw">double currentPrice) { class=class="str">"cmt">// Calculate the 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">// Draw Major Whole Number lines(defining the class="num">1000-PIP Range) DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle); DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle); class=class="str">"cmt">// Draw Large Quarter lines and their overshoot/undershoot areas if enabled 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; class="type">class="kw">string objName = "LargeQuarter_" + IntegerToString(i); DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle); if(DrawOvershootAreas) { class="type">class="kw">double offset = MajorStep / class="num">40.0; class=class="str">"cmt">// approximately class="num">25 pips if MajorStep=class="num">0.1000 DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle); DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle); } } } class=class="str">"cmt">// Draw Small Quarter lines if enabled(optional, finer divisions) 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; class="type">class="kw">string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j); DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle); } } } }
把价位入侵画到图表左上角
想在 MT5 图表上实时盯住价格对关键位的「入侵」,第一步是把提示信息钉在固定角落。下面这段用 OBJ_LABEL 建一个持久标签,初始化时放在左上角距边 10 像素处,白色 14 号字,等宽字体保证表格列对齐不串位。 ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0); // 在图表0号子窗口建标签对象,坐标参数对LABEL无效 ObjectSetInteger(0, name, OBJPROP_CORNER, corner); // 锚定角落,如CORNER_LEFT_UPPER=0 ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xdist); // 距左边界像素 ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ydist); // 距上边界像素 ObjectSetString(0, name, OBJPROP_FONT, "Courier New"); // 等宽字体,数字表才不会错位 OnInit 里只调一次 CreateOrUpdateLabel("IntrusionCommentary", ... , 10, 10, clrWhite, 14),标签就常驻了。OnTick 中先取 SYMBOL_BID,若为 0 直接 return 避免假信号;随后算 lowerMajor = MathFloor(price/MajorStep)*MajorStep,上下大位差一个 MajorStep。
| 当 | price - lowerMajor | ≤ AlertTolerance 判定 Major Support 入侵,同理上边界判 Major Resistance;若开 DrawLargeQuarters,再把 MajorStep 四等分,i=1~3 的 level 命中即记 Large Quarter。外汇与贵金属杠杆高,这类入侵只是概率性参考,真触发不代表反转。 |
|---|
画 overshoot 区时 offset = MajorStep/40.0,原文注释约 25 点,用来标 level 上下各一档的穿透带;价格落进 overshootUp 附近也会被捕获。复制代码后把 MajorStep、AlertTolerance 按品种波动调一调,就能在 MT5 里跑起来验证。
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="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="type">void OnTick() { class="type">bool intrusionDetected = false; 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 DrawQuarters(currentPrice); class="type">class="kw">double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep; class="type">class="kw">double upperMajor = lowerMajor + MajorStep; 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; } 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)
◍ 侵入判定与提示标签的收口逻辑
上面这段收尾代码负责把价格对四分位区间的侵入结果汇总成表,并控制 alert 只响一次。外汇与贵金属波动大,AlertTolerance 设太小会频繁误报,设太大则漏掉有效刺穿,实盘里建议先在 XAUUSD 的 M15 上用 0.0003~0.0008 试。 当价格落在 overshootUp 或 undershootDown 的容差内,代码把对应行写进 table 并置 intrusionDetected=true;小四分位则用 MajorStep/10 再除 4 得到 smallQuarter,遍历 10 段每段 3 个内点做同样判定。若一轮下来没有任何触发,table 会被替换成「No significant intrusion detected」并提示当前价与盘整状态。 最后 CreateOrUpdateLabel 把 table 画在左上角(偏移 10,10,白色 14 号字),Alert 仅在 intrusionDetected 由 false 变 true 时发一次,价格退出容差后 intrusionAlerted 复位。这样你开 MT5 挂上 EA,就能在图上直接读侵入 commentary,不用自己算四分位边界。
{
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;
}
}
}
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);
intrusionAlerted = true;
}
if(!intrusionDetected)
intrusionAlerted = false;
ChartRedraw();
}「把整数位网格的参数和配色先钉死」
这套 MT5 指标把「大整数区间」切成可配置的网格:MajorStep 默认 0.1000,也就是每 1000 点画一条主整数线,框出所谓的 1000-PIP Range。 LargeQuarter 和 SmallQuarter 是主区间里的细分线,前者默认开、后者默认关;OvershootAreas 默认开,用来标出大四分位线被刺穿但没站住的上下影区。AlertTolerance 设 0.0025,对常规 1 点=0.0001 的货币对约等于 25 点容差,价格蹭到线就触发判定。 配色和线型也全暴露成 input:主线和字色用暗石板灰(0x2F4F4F)实线宽 4;大四分位暗红点线宽 3;刺穿区红虚线宽 1。注释文本也能改,比如 LargeQuarterReason 写的是「决断性破位可能引出后续 250 点波动」——外汇和贵金属波动随机,这类表述只描述概率倾向,实操高风险。 全局布尔 intrusionAlerted 用来防重复报警:价格赖在线上不走时,不会每 tick 都刷提示,这点直接决定你手机会不会被 alert 轰爆。
class="macro">#class="kw">property strict class=class="str">"cmt">//---- Input parameters ------------------------------------------------- input class="type">class="kw">double MajorStep = class="num">0.1000; class=class="str">"cmt">// Difference between Major Whole Numbers(defines the class="num">1000-PIP Range) input class="type">bool DrawLargeQuarters = true; class=class="str">"cmt">// Draw intermediate Large Quarter lines. input class="type">bool DrawSmallQuarters = false; class=class="str">"cmt">// Draw Small Quarter lines. input class="type">bool DrawOvershootAreas = true; class=class="str">"cmt">// Mark overshoot/undershoot areas for Large Quarter lines. input class="type">class="kw">double AlertTolerance = class="num">0.0025; class=class="str">"cmt">// Tolerance for detecting a "touch" (e.g., ~class="num">25 pips for a pair where class="num">1 pip=class="num">0.0001) class=class="str">"cmt">//---- Color settings --------------------------------------------------- input class="type">class="kw">color MajorColor = 0x2F4F4F; class=class="str">"cmt">// Dark Slate Gray for Major lines. input class="type">class="kw">color LargeQuarterColor = 0x8B0000; class=class="str">"cmt">// Dark Red for Large Quarter lines. input class="type">class="kw">color SmallQuarterColor = 0x00008B; class=class="str">"cmt">// Dark Blue for Small Quarter lines. input class="type">class="kw">color OvershootColor = clrRed; class=class="str">"cmt">// Red for overshoot/undershoot lines. class=class="str">"cmt">//---- Line style and thickness settings ------------------------------- input ENUM_LINE_STYLE MajorLineStyle = STYLE_SOLID; input class="type">int MajorLineWidth = class="num">4; input ENUM_LINE_STYLE LargeQuarterLineStyle = STYLE_DOT; input class="type">int LargeQuarterLineWidth = class="num">3; input ENUM_LINE_STYLE OvershootLineStyle = STYLE_DASH; input class="type">int OvershootLineWidth = class="num">1; input ENUM_LINE_STYLE SmallQuarterLineStyle = STYLE_SOLID; input class="type">int SmallQuarterLineWidth = class="num">1; class=class="str">"cmt">//---- Commentary Messages(customizable) ----------------------------- input class="type">class="kw">string MajorSupportReason = "Key support level. Break below signals range shift."; input class="type">class="kw">string MajorResistanceReason = "Pivotal resistance. Breakout above may start new range."; input class="type">class="kw">string LargeQuarterReason = "Decisive break could trigger next class="num">250-PIP move."; input class="type">class="kw">string OvershootReason = "Test of breakout; reversal likely if momentum fails."; input class="type">class="kw">string UndershootReason = "Insufficient bullish force; possible bearish reversal."; input class="type">class="kw">string SmallQuarterReason = "Minor fluctuation."; class=class="str">"cmt">// Global flag to avoid repeated alerts class="kw">while price lingers at a level class="type">bool intrusionAlerted = false; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| DrawHorizontalLine: Creates or replaces a horizontal line | class=class="str">"cmt">//+------------------------------------------------------------------+
把千点区间拆成可交易的四分位线
千点整数区(MajorStep=0.1000 这类跨度)只是个容器,真正能拿来挂单的是它内部的四分位。下面这段逻辑先按 MathFloor 把现价落到下边界,再 +MajorStep 得出上边界,两个边界线框出当前千点区间。 大四分位用 MajorStep/4.0 做步长,循环 i=1 到 3 画出三条内部水平线;若开启 DrawOvershootAreas,会在每条线上下各偏移 MajorStep/40.0(约 25 点)画穿透/跌破参考线,外汇与贵金属波动下这些区域常出现假突破,概率上更值得警惕。 小四分位进一步把每段再十等分后取四分之一,粒度更细但画线数量陡增;MT5 里对象过多会拖慢刷新,实盘前建议先用策略测试器跑一遍确认卡顿情况。 DrawHorizontalLine 每次先 ObjectFind 删旧再建新,避免重复对象名报错,这是挂 EA 时最容易忽略的清理动作。
class="type">void DrawHorizontalLine(class="type">class="kw">string name, class="type">class="kw">double price, class="type">class="kw">color lineColor, class="type">int width, ENUM_LINE_STYLE style) { if(ObjectFind(class="num">0, name) != -class="num">1) ObjectDelete(class="num">0, name); if(!ObjectCreate(class="num">0, name, OBJ_HLINE, class="num">0, class="num">0, price)) { Print("Failed to create line: ", name); class="kw">return; } ObjectSetInteger(class="num">0, name, OBJPROP_COLOR, lineColor); ObjectSetInteger(class="num">0, name, OBJPROP_STYLE, style); ObjectSetInteger(class="num">0, name, OBJPROP_WIDTH, width); ObjectSetInteger(class="num">0, name, OBJPROP_RAY_RIGHT, true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| DrawQuarters: Draws all quarter lines based on the current price | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void DrawQuarters(class="type">class="kw">double currentPrice) { class=class="str">"cmt">// Calculate the 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">// Draw Major Whole Number lines(defining the class="num">1000-PIP Range) DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle); DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle); class=class="str">"cmt">// Draw Large Quarter lines and their overshoot/undershoot areas if enabled 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; class="type">class="kw">string objName = "LargeQuarter_" + IntegerToString(i); DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle); if(DrawOvershootAreas) { class="type">class="kw">double offset = MajorStep / class="num">40.0; class=class="str">"cmt">// approximately class="num">25 pips if MajorStep=class="num">0.1000 DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle); DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle); } } } class=class="str">"cmt">// Draw Small Quarter lines if enabled(optional, finer divisions) 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++) {