价格行为分析工具包开发(第十八部分):四分位理论(3)四分位看板·进阶篇
📘

价格行为分析工具包开发(第十八部分):四分位理论(3)四分位看板·进阶篇

第 2/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,能立刻看出不同周期下趋势标签翻转频率的差异。

MQL5 / C++
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 重编译,比运行时硬调全局变量稳。

MQL5 / C++
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 仅辅助读图,信号误触或对象未删都可能让图表状态紊乱,实盘前务必在策略测试器里反复开关按钮验证对象生命周期。

MQL5 / C++
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 可调间距。

MQL5 / C++
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。这样界面颜色跟内部布尔量同步,交易者扫一眼就知道当前哪些图层在跑。外汇与贵金属波动剧烈,这类按钮只负责切换视图,不构成任何方向暗示,实际下单前仍需自担高风险。

MQL5 / C++
   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);

常见问题

取50周期收盘价均线,价格在其上标“多”、在下标“空”;复盘时可先肉眼核对几根K线确认标签不反。
用独立面板子窗口或右上角文本标签列出整数位与四分位值,避免在主图叠矩形;颜色调低透明度防干扰。
可以。小布盯盘的AIGC已内置趋势标签与关键位标注,打开对应品种页即可直接看,不用手搭面板。
多为点击事件没分发到对应ID,或对象销毁后还绑旧ID;检查CHARTEVENT_OBJECT_CLICK里的sparam匹配。
用枚举存按钮状态,重绘时统一走一个ColorByState函数;别在事件里散写颜色赋值,后期难维护。