价格行为分析工具包开发(第十八部分):四分位理论(3)四分位看板·进阶篇
用 50 周期均线给盘面贴趋势标签
这段逻辑干的事很直接:抓当前报价,再拉一条 50 周期收盘价 SMA,两者比大小判定趋势方向,最后把结果写进左上角文本对象。外汇与贵金属波动快,这种判定只反映中短周期偏向,不构成方向保证,实际共振失效概率不低。 核心在 UpdateTrendComment()。先用 SymbolInfoDouble(_Symbol, SYMBOL_BID) 取卖价,再 iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_SMA, PRICE_CLOSE) 建均线句柄;CopyBuffer 从第 1 根往回取 1 根填进 buffer,buffer[0] 即为上一根 SMA 值。currentPrice 大于 smaValue 标 Uptrend,小于标 Downtrend,相等标 Sideways。 标签位置由 PanelY + 10 + 3*(ButtonHeight+ButtonSpacing) + ButtonHeight + ButtonSpacing 算出,首次用 ObjectFind 查不到 TREND_LABEL 才 ObjectCreate,之后只改 OBJPROP_TEXT。开 MT5 把这段塞进 EA,改 50 为 20 或 200,能立刻看出不同周期下趋势标签翻转频率的差异。
class="type">void UpdateTrendComment() { class="type">class="kw">double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); class="type">class="kw">double smaValue = class="num">0.0; class="type">int handle = iMA(_Symbol, PERIOD_CURRENT, class="num">50, class="num">0, MODE_SMA, PRICE_CLOSE); if(handle != INVALID_HANDLE) { class="type">class="kw">double buffer[]; if(CopyBuffer(handle, class="num">0, class="num">1, class="num">1, buffer) > class="num">0) smaValue = buffer[class="num">0]; IndicatorRelease(handle); } class="type">class="kw">string trendComment = (currentPrice > smaValue) ? "Uptrend" : (currentPrice < smaValue) ? "Downtrend" : "Sideways"; class="type">int trendLabelY = PanelY + class="num">10 + class="num">3 * (ButtonHeight + ButtonSpacing) + ButtonHeight + ButtonSpacing; if(ObjectFind(class="num">0, TREND_LABEL) == -class="num">1) { ObjectCreate(class="num">0, TREND_LABEL, OBJ_LABEL, class="num">0, class="num">0, class="num">0); ObjectSetInteger(class="num">0, TREND_LABEL, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, TREND_LABEL, OBJPROP_XDISTANCE, PanelX + class="num">10); ObjectSetInteger(class="num">0, TREND_LABEL, OBJPROP_YDISTANCE, trendLabelY); ObjectSetInteger(class="num">0, TREND_LABEL, OBJPROP_COLOR, clrWhite); ObjectSetInteger(class="num">0, TREND_LABEL, OBJPROP_FONTSIZE, class="num">14); } ObjectSetString(class="num">0, TREND_LABEL, OBJPROP_TEXT, "Trend Direction: " + trendComment); }
「把整数位与四分位画进MT5面板」
做价格行为分析时,整数位(如 1.1000、1.2000)和四分位(大四分 0.2500、小四分 0.0625 这类细分)是肉眼最容易忽略却最常被触碰的隐性支撑阻力。下面这段脚本把这类层级直接渲染到图表左侧面板,交易者点一下按钮就能切换显示,不用手动拉横线。 代码里的 MajorStep 默认设 0.1000,意味着主整数位每 0.1 个报价单位画一条;LargeQuarter 用暗红点线、SmallQuarter 用深蓝实线但默认关闭(g_DrawSmallQuarters=false),说明作者把细分层级当作可选项,避免图表过脏。外汇与贵金属波动受杠杆放大,层级触碰仅代表概率性反应,不代表必然反转。 面板本身用 PANEL_NAME 固定对象名,左上角偏移 PanelX=10、PanelY=10,宽 250 像素,四个按钮高 30、间距 5,这套参数在 1920×1080 屏上不挡 K 线主体。OnInit 里先 CreatePanel 再 CreateButtons,最后 DrawQuarterLines,初始化顺序错了按钮会浮在图层外。 别把默认参数当最优解:XAUUSD 用 0.1000 步进太大,改 MajorStep=1.000 才贴合每 1 美元整数关;切换时直接改 input 重编译,比运行时硬调全局变量稳。
class="macro">#class="kw">property strict class=class="str">"cmt">//---- Input parameters for drawing levels ---------------------------- input class="type">class="kw">double MajorStep = class="num">0.1000; class=class="str">"cmt">// Difference between major whole numbers 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 styles 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">//---- Panel and button settings -------------------------------------- input class="type">int PanelX = class="num">10; input class="type">int PanelY = class="num">10; input class="type">int PanelWidth = class="num">250; input class="type">int ButtonHeight = class="num">30; input class="type">int ButtonSpacing= class="num">5; class=class="str">"cmt">//---- Global toggle variables ---------------------------------------- class="type">bool g_DrawLargeQuarters = true; class="type">bool g_DrawSmallQuarters = false; class="type">bool g_DrawOvershootAreas = true; class="type">bool g_DrawTrendDirection = false; class=class="str">"cmt">//---- Object names for panel and buttons ----------------------------- class="macro">#define PANEL_NAME "LevelsPanel" class="macro">#define BUTTON_LARGE "btnLargeQuarters" class="macro">#define BUTTON_SMALL "btnSmallQuarters" class="macro">#define BUTTON_OVERSHOOT "btnOvershoot" class="macro">#define BUTTON_TREND "btnTrendDirection" class="macro">#define TREND_LABEL "TrendDirectionLabel" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">// Create panel background and buttons CreatePanel(); CreateButtons(); class=class="str">"cmt">// Draw quarter lines initially DrawQuarterLines(); class=class="str">"cmt">// If trend commentary is toggled on, update it if(g_DrawTrendDirection)
◍ 面板对象的销毁与点击事件分发
EA 退出时要把自己画上去的图形对象全部清掉,否则切换周期或重加载会留下残影。OnDeinit 里依次删掉主面板、四个功能按钮(大分位 / 小分位 / 过冲区 / 趋势注释),再调用 DeleteQuarterLines 和 DeleteTrendComment 清理动态线条与文字,reason 参数虽传入但未参与分支判断。 行情每跳动一次,OnTick 就重绘四分位线;仅当全局开关 g_DrawTrendDirection 为真才刷新趋势注释,避免无谓计算。 按钮交互走 OnChartEvent,用 CHARTEVENT_OBJECT_CLICK 拦截,再比 sparam 与预设按钮名。点大分位按钮即翻转 g_DrawLargeQuarters 并调 UpdateButtonColors 和 DrawQuarterLines,小分位、过冲区、趋势按钮逻辑同构,都是翻转对应布尔量后重绘。 外汇与贵金属波动剧烈,这类可视化 EA 仅辅助读图,信号误触或对象未删都可能让图表状态紊乱,实盘前务必在策略测试器里反复开关按钮验证对象生命周期。
class="type">void OnDeinit(const class="type">int reason) { class=class="str">"cmt">// Delete panel, buttons, quarter lines, and trend commentary ObjectDelete(class="num">0, PANEL_NAME); ObjectDelete(class="num">0, BUTTON_LARGE); ObjectDelete(class="num">0, BUTTON_SMALL); ObjectDelete(class="num">0, BUTTON_OVERSHOOT); ObjectDelete(class="num">0, BUTTON_TREND); DeleteQuarterLines(); DeleteTrendComment(); } class="type">void OnTick() { class=class="str">"cmt">// Redraw quarter lines on every tick DrawQuarterLines(); class=class="str">"cmt">// Update trend commentary if enabled if(g_DrawTrendDirection) UpdateTrendComment(); } class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == BUTTON_LARGE) { g_DrawLargeQuarters = !g_DrawLargeQuarters; UpdateButtonColors(); DrawQuarterLines(); } else if(sparam == BUTTON_SMALL) { g_DrawSmallQuarters = !g_DrawSmallQuarters; UpdateButtonColors(); DrawQuarterLines(); } else if(sparam == BUTTON_OVERSHOOT) { g_DrawOvershootAreas = !g_DrawOvershootAreas; UpdateButtonColors(); DrawQuarterLines(); } else if(sparam == BUTTON_TREND) { g_DrawTrendDirection = !g_DrawTrendDirection; UpdateButtonColors();
用矩形标签和按钮搭出盯盘面板
想在 MT5 图表左上角放一个常驻面板,不用画复杂控件,用 OBJ_RECTANGLE_LABEL 做底、OBJ_BUTTON 做交互就够了。下面这段逻辑先判断趋势注释开关,再决定刷新或删除文字,随后创建面板背景与两个季度切换按钮。 CreatePanel() 里把面板锚点设成 CORNER_LEFT_UPPER,XDISTANCE/YDISTANCE 取全局变量 PanelX、PanelY(例如常见设 10/10),颜色用 clrDarkGray,边框类型 BORDER_RAISED 让底色有凸起感。文字只塞了 "\n\n\n\n",靠换行把矩形撑高,省去算高度。 CreateButtons() 里按钮从左上角偏移 10 像素起排,宽 = PanelWidth-20,高用 ButtonHeight。第一个按钮名 BUTTON_LARGE 文本 "Large Quarters";第二个按钮 y 再叠加 ButtonHeight+ButtonSpacing,避免重叠。外汇与贵金属波动剧烈,这类面板只做辅助观察,信号误触可能带来高频操作风险。 直接把代码丢进 EA 的 OnInit 调一次,就能在图上看到灰底双按钮;改 PanelX/PanelY 可挪位置,改 ButtonSpacing 可调间距。
if(g_DrawTrendDirection) UpdateTrendComment(); else DeleteTrendComment(); } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create panel background | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CreatePanel() { if(ObjectCreate(class="num">0, PANEL_NAME, OBJ_RECTANGLE_LABEL, class="num">0, class="num">0, class="num">0)) { ObjectSetInteger(class="num">0, PANEL_NAME, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, PANEL_NAME, OBJPROP_XDISTANCE, PanelX); ObjectSetInteger(class="num">0, PANEL_NAME, OBJPROP_YDISTANCE, PanelY); ObjectSetInteger(class="num">0, PANEL_NAME, OBJPROP_COLOR, clrDarkGray); class=class="str">"cmt">// Simulate a larger panel class="kw">using newlines in the text. class="type">class="kw">string panelText = "\n\n\n\n"; ObjectSetString(class="num">0, PANEL_NAME, OBJPROP_TEXT, panelText); ObjectSetInteger(class="num">0, PANEL_NAME, OBJPROP_BORDER_TYPE, BORDER_RAISED); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create buttons on the panel | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CreateButtons() { class="type">int x = PanelX + class="num">10; class="type">int y = PanelY + class="num">10; class="type">int btnWidth = PanelWidth - class="num">20; class=class="str">"cmt">// Button for Large Quarters if(!ObjectCreate(class="num">0, BUTTON_LARGE, OBJ_BUTTON, class="num">0, class="num">0, class="num">0)) Print("Failed to create button ", BUTTON_LARGE); else { ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_XDISTANCE, x); ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_YDISTANCE, y); ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_XSIZE, btnWidth); ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_YSIZE, ButtonHeight); ObjectSetString(class="num">0, BUTTON_LARGE, OBJPROP_TEXT, "Large Quarters"); } class=class="str">"cmt">// Button for Smaller Quarters y += ButtonHeight + ButtonSpacing; if(!ObjectCreate(class="num">0, BUTTON_SMALL, OBJ_BUTTON, class="num">0, class="num">0, class="num">0))
「按钮堆叠与状态着色的收尾写法」
在连续创建图形按钮时,纵坐标靠累加推进是最省心的做法:每画完一个,就把 y 加上 ButtonHeight 与 ButtonSpacing,下一个按钮自然排在下方,不会出现重叠。上面这段对 Smaller Quarters、Overshoot/Undershoot、Trend Direction 三个按钮都套用了同一套 OBJPROP_CORNER / XDISTANCE / YDISTANCE / XSIZE / YSIZE / TEXT 赋值,区别只在 y 的偏移量和显示文字。 创建失败时用 Print 把按钮名打进日志,能在 MT5 Experts 标签里直接看到哪一行 ObjectCreate 返回了 false,省去猜哪颗按钮没出来。 所有按钮画完后调用一次 UpdateButtonColors(),按开关变量给 BUTTON_LARGE 等对象写 OBJPROP_COLOR:开态 clrGreen、关态 clrRed。这样界面颜色跟内部布尔量同步,交易者扫一眼就知道当前哪些图层在跑。外汇与贵金属波动剧烈,这类按钮只负责切换视图,不构成任何方向暗示,实际下单前仍需自担高风险。
Print("Failed to create button ", BUTTON_SMALL); else { ObjectSetInteger(class="num">0, BUTTON_SMALL, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, BUTTON_SMALL, OBJPROP_XDISTANCE, x); ObjectSetInteger(class="num">0, BUTTON_SMALL, OBJPROP_YDISTANCE, y); ObjectSetInteger(class="num">0, BUTTON_SMALL, OBJPROP_XSIZE, btnWidth); ObjectSetInteger(class="num">0, BUTTON_SMALL, OBJPROP_YSIZE, ButtonHeight); ObjectSetString(class="num">0, BUTTON_SMALL, OBJPROP_TEXT, "Smaller Quarters"); } class=class="str">"cmt">// Button for Overshoot/Undershoot y += ButtonHeight + ButtonSpacing; if(!ObjectCreate(class="num">0, BUTTON_OVERSHOOT, OBJ_BUTTON, class="num">0, class="num">0, class="num">0)) Print("Failed to create button ", BUTTON_OVERSHOOT); else { ObjectSetInteger(class="num">0, BUTTON_OVERSHOOT, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, BUTTON_OVERSHOOT, OBJPROP_XDISTANCE, x); ObjectSetInteger(class="num">0, BUTTON_OVERSHOOT, OBJPROP_YDISTANCE, y); ObjectSetInteger(class="num">0, BUTTON_OVERSHOOT, OBJPROP_XSIZE, btnWidth); ObjectSetInteger(class="num">0, BUTTON_OVERSHOOT, OBJPROP_YSIZE, ButtonHeight); ObjectSetString(class="num">0, BUTTON_OVERSHOOT, OBJPROP_TEXT, "Overshoot/Undershoot"); } class=class="str">"cmt">// Button for Trend Direction y += ButtonHeight + ButtonSpacing; if(!ObjectCreate(class="num">0, BUTTON_TREND, OBJ_BUTTON, class="num">0, class="num">0, class="num">0)) Print("Failed to create button ", BUTTON_TREND); else { ObjectSetInteger(class="num">0, BUTTON_TREND, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(class="num">0, BUTTON_TREND, OBJPROP_XDISTANCE, x); ObjectSetInteger(class="num">0, BUTTON_TREND, OBJPROP_YDISTANCE, y); ObjectSetInteger(class="num">0, BUTTON_TREND, OBJPROP_XSIZE, btnWidth); ObjectSetInteger(class="num">0, BUTTON_TREND, OBJPROP_YSIZE, ButtonHeight); ObjectSetString(class="num">0, BUTTON_TREND, OBJPROP_TEXT, "Trend Direction"); } UpdateButtonColors(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Update button colors based on toggle state | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void UpdateButtonColors() { class="type">class="kw">color onColor = clrGreen; class="type">class="kw">color offColor = clrRed; ObjectSetInteger(class="num">0, BUTTON_LARGE, OBJPROP_COLOR, g_DrawLargeQuarters ? onColor : offColor);