MQL5交易工具(第四部分):为多周期扫描仪表盘添加动态定位与切换功能·进阶篇
🖱️

MQL5交易工具(第四部分):为多周期扫描仪表盘添加动态定位与切换功能·进阶篇

(2/3)· 图表被指标面板占满、切窗口手忙脚乱?这篇教你用代码让仪表盘随手拖、一键收。

含代码示例偏理论 第 2/3 篇
很多交易者把多周期扫描面板钉死在图表角落,行情一多就被挡住关键 K 线。其实在 MQL5 里加几段坐标与按钮逻辑,面板就能拖着走、还能折叠成一条,屏幕空间立刻松绑。

多周期面板的格子是怎么排出来的

在 MT5 里手搓一个跨周期信号面板,核心是先算清楚每个表头方块和单元格的屏幕坐标。下面这段逻辑用 panel_x、panel_y 作为面板锚点,WIDTH_TIMEFRAME 和 WIDTH_SIGNAL/WIDTH_INDICATOR 控制列宽,HEIGHT_RECTANGLE 控制行高,靠循环把矩形和文字标签逐个画出来。 表头部分对前 2 列(信号类)和后几列(指标类)用了不同的宽度与偏移算法:header_index<2 时按 SIGNAL 宽度累加,否则从 2*WIDTH_SIGNAL 起跳按 INDICATOR 宽度排。当前图表周期所在的行会被涂成 clrLimeGreen、文字改黑色,其余行灰底白字,一眼能定位自己在哪根时间框架。 单元格背景也分了类:header_index<2(买/卖摘要列)用浅灰,其余指标列用黑底。代码里用 switch 把第 0 列绑 BUY_RECTANGLE/BUY_TEXT、第 1 列绑 SELL_RECTANGLE/SELL_TEXT,后续列留作指标格子。你开 MT5 把下列代码塞进面板初始化函数,调一下 panel_y+35 和 HEIGHT_RECTANGLE 就能看到行列间距的实际渲染效果,外汇贵金属波动快,这类面板仅作辅助、杠杆风险高。

MQL5 / C++
class="type">int x_offset = panel_x - WIDTH_TIMEFRAME - (header_index < class="num">2 ? header_index * WIDTH_SIGNAL : class="num">2 * WIDTH_SIGNAL + (header_index - class="num">2) * WIDTH_INDICATOR) + (class="num">1 + header_index); class=class="str">"cmt">//--- Calculate x position
class="type">int width = (header_index < class="num">2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); class=class="str">"cmt">//--- Set width based on header type
create_rectangle(HEADER_RECTANGLE + IntegerToString(header_index), x_offset, panel_y + class="num">35, width, HEIGHT_RECTANGLE, clrGray); class=class="str">"cmt">//--- Create header rectangle
create_label(HEADER_TEXT + IntegerToString(header_index), header_names[header_index], x_offset - width/class="num">2, panel_y + class="num">45, class="num">11, COLOR_WHITE); class=class="str">"cmt">//--- Create header label
 }

 class=class="str">"cmt">// Create timeframe rectangles and labels, and summary/indicator cells
 for(class="type">int timeframe_index = class="num">0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { class=class="str">"cmt">//--- Loop through timeframes
  class=class="str">"cmt">// Highlight current timeframe
  class="type">class="kw">color timeframe_background = (timeframes_array[timeframe_index] == _Period) ? clrLimeGreen : clrGray; class=class="str">"cmt">//--- Set background class="type">class="kw">color for current timeframe
  class="type">class="kw">color timeframe_text_color = (timeframes_array[timeframe_index] == _Period) ? COLOR_BLACK : COLOR_WHITE; class=class="str">"cmt">//--- Set text class="type">class="kw">color for current timeframe
  
  create_rectangle(TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), panel_x - class="num">2, (panel_y + class="num">35 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index), WIDTH_TIMEFRAME, HEIGHT_RECTANGLE, timeframe_background); class=class="str">"cmt">//--- Create timeframe rectangle
  create_label(TIMEFRAME_TEXT + IntegerToString(timeframe_index), truncate_timeframe_name(timeframe_index), panel_x - class="num">47, (panel_y + class="num">45 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index), class="num">11, timeframe_text_color); class=class="str">"cmt">//--- Create timeframe label
  
  class=class="str">"cmt">// Create summary and indicator cells
  for(class="type">int header_index = class="num">0; header_index < ArraySize(header_names); header_index++) { class=class="str">"cmt">//--- Loop through headers for cells
    class="type">class="kw">string cell_rectangle_name, cell_text_name;       class=class="str">"cmt">//--- Declare cell name and label variables
    class="type">class="kw">color cell_background = (header_index < class="num">2) ? COLOR_LIGHT_GRAY : COLOR_BLACK; class=class="str">"cmt">//--- Set cell background class="type">class="kw">color
    class="kw">switch(header_index) {                            class=class="str">"cmt">//--- Select cell type
      case class="num">0: cell_rectangle_name = BUY_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = BUY_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Buy cell
      case class="num">1: cell_rectangle_name = SELL_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = SELL_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Sell cell

「多周期信号格的命名与坐标落位」

面板里 RSI、Stochastic、CCI、ADX、AO 这几类信号格,靠 switch 里的 case 2~6 来指派对象名。每个格子的矩形与文字对象名都用指标前缀拼接 timeframe_index,这样不同周期的同名指标不会互相覆盖,MT5 对象树里一眼能分清。 x 坐标不是写死的:header_index 小于 2 时按 WIDTH_SIGNAL 步进,否则先占满 2 个信号宽再按 WIDTH_INDICATOR 排指标格,最后补 1+header_index 像素缝隙。高度方向用 panel_y+35 起算,每个 timeframe_index 叠 HEIGHT_RECTANGLE 并回扣 1+index 做 1px 描边补偿。 create_rectangle 调用时格子底色传 cell_background,文字先写死 "-/-" 占位,后面刷数据时再改。标签字号 10、白色,x 偏移取 x_offset - width/2 让文字居左内缩,避免贴边。 最小化面板走另一套:头部矩形宽 617、高 27,底色 C'60,60,60',图标用 Wingdings 的 char 91,关闭钮用 Webdings 'r' 放 x-600,最大化钮用 Wingdings 'o' 放 x-570。整套画完都跟一句 ChartRedraw(0),否则 MT5 上可能看不到即时重绘。

MQL5 / C++
case class="num">2: cell_rectangle_name = RSI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = RSI_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- RSI cell
case class="num">3: cell_rectangle_name = STOCH_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = STOCH_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Stochastic cell
case class="num">4: cell_rectangle_name = CCI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = CCI_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- CCI cell
case class="num">5: cell_rectangle_name = ADX_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = ADX_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- ADX cell
case class="num">6: cell_rectangle_name = AO_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = AO_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- AO cell
}
class="type">int x_offset = panel_x - WIDTH_TIMEFRAME - (header_index < class="num">2 ? header_index * WIDTH_SIGNAL : class="num">2 * WIDTH_SIGNAL + (header_index - class="num">2) * WIDTH_INDICATOR) + (class="num">1 + header_index); class=class="str">"cmt">//--- Calculate x position
class="type">int width = (header_index < class="num">2 ? WIDTH_SIGNAL : WIDTH_INDICATOR); class=class="str">"cmt">//--- Set width based on cell type
create_rectangle(cell_rectangle_name, x_offset, (panel_y + class="num">35 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index), width, HEIGHT_RECTANGLE, cell_background); class=class="str">"cmt">//--- Create cell rectangle
create_label(cell_text_name, "-/-", x_offset - width/class="num">2, (panel_y + class="num">45 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index), class="num">10, COLOR_WHITE); class=class="str">"cmt">//--- Create cell label
}
}
ChartRedraw(class="num">0);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create minimized dashboard UI                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void create_minimized_dashboard() {
  create_rectangle(HEADER_PANEL, panel_x, panel_y, class="num">617, class="num">27, C&class="macro">#x27;class="num">60,class="num">60,class="num">60&class="macro">#x27;, BORDER_FLAT); class=class="str">"cmt">//--- Create header panel background
  create_label(HEADER_PANEL_ICON, CharToString(class="num">91), panel_x - class="num">12, panel_y + class="num">14, class="num">18, clrAqua, "Wingdings"); class=class="str">"cmt">//--- Create header icon
  create_label(HEADER_PANEL_TEXT, "TimeframeScanner", panel_x - class="num">105, panel_y + class="num">12, class="num">13, COLOR_WHITE); class=class="str">"cmt">//--- Create header title
  create_label(CLOSE_BUTTON, CharToString(&class="macro">#x27;r&class="macro">#x27;), panel_x - class="num">600, panel_y + class="num">14, class="num">18, clrYellow, "Webdings"); class=class="str">"cmt">//--- Create close button
  create_label(TOGGLE_BUTTON, CharToString(&class="macro">#x27;o&class="macro">#x27;), panel_x - class="num">570, panel_y + class="num">14, class="num">18, clrYellow, "Wingdings"); class=class="str">"cmt">//--- Create maximize button(+)
  ChartRedraw(class="num">0);
}

◍ 清盘时如何干净拆除仪表盘

做多品种监控面板最怕卸载脚本后留下一堆孤儿对象,图表上飘着看不见点击区的矩形和文字,下次加载直接重名冲突。下面这段清理函数把面板拆得比较彻底:先按名字逐个删主控件,再用前缀批量删动态生成的标签。 ObjectDelete 针对的是编译期写死的 6 个控件——主面板、表头、图标、标题、关闭与折叠按钮,每个都靠唯一常量名定位。剩下的 10 类用 ObjectsDeleteAll 按前缀 SYMBOL_ / TIMEFRAME_ / HEADER_ / RSI_ / STOCH_ 成组清除,覆盖矩形和文字两套对象,合计 16 行调用。 在 MT5 里跑 EA 或指标前,建议先把这段挂到 OnDeinit 里。外汇与贵金属波动剧烈、高风险,图表对象堆积会拖慢终端响应,清干净再重载参数才不容易出鬼影。

MQL5 / C++
class="type">void delete_all_objects() {
   ObjectDelete(class="num">0, MAIN_PANEL);                                                          class=class="str">"cmt">//--- Delete main panel
   ObjectDelete(class="num">0, HEADER_PANEL);                                                        class=class="str">"cmt">//--- Delete header panel
   ObjectDelete(class="num">0, HEADER_PANEL_ICON);                                                   class=class="str">"cmt">//--- Delete header icon
   ObjectDelete(class="num">0, HEADER_PANEL_TEXT);                                                   class=class="str">"cmt">//--- Delete header title
   ObjectDelete(class="num">0, CLOSE_BUTTON);                                                        class=class="str">"cmt">//--- Delete close button
   ObjectDelete(class="num">0, TOGGLE_BUTTON);                                                       class=class="str">"cmt">//--- Delete toggle button
   ObjectsDeleteAll(class="num">0, SYMBOL_RECTANGLE);                                                class=class="str">"cmt">//--- Delete all symbol rectangles
   ObjectsDeleteAll(class="num">0, SYMBOL_TEXT);                                                     class=class="str">"cmt">//--- Delete all symbol labels
   ObjectsDeleteAll(class="num">0, TIMEFRAME_RECTANGLE);                                             class=class="str">"cmt">//--- Delete all timeframe rectangles
   ObjectsDeleteAll(class="num">0, TIMEFRAME_TEXT);                                                  class=class="str">"cmt">//--- Delete all timeframe labels
   ObjectsDeleteAll(class="num">0, HEADER_RECTANGLE);                                                class=class="str">"cmt">//--- Delete all header rectangles
   ObjectsDeleteAll(class="num">0, HEADER_TEXT);                                                     class=class="str">"cmt">//--- Delete all header labels
   ObjectsDeleteAll(class="num">0, RSI_RECTANGLE);                                                   class=class="str">"cmt">//--- Delete all RSI rectangles
   ObjectsDeleteAll(class="num">0, RSI_TEXT);                                                        class=class="str">"cmt">//--- Delete all RSI labels
   ObjectsDeleteAll(class="num">0, STOCH_RECTANGLE);                                                 class=class="str">"cmt">//--- Delete all Stochastic rectangles
   ObjectsDeleteAll(class="num">0, STOCH_TEXT);                                                      class=class="str">"cmt">//--- Delete all Stochastic labels
}

清场与面板重定位的底层调用

指标或 EA 在重绘前必须先把旧图形对象清干净,否则 MT5 图表会堆叠上一轮留下的矩形和文字标签。下面这段用 ObjectsDeleteAll 按前缀分组删除,CCI、ADX、AO 三类指标区加上买卖信号区,各自矩形与文字分开清,共 10 次调用覆盖全部面板元素。 清场之后紧接着是 update_panel_positions 函数,它用 ObjectSetInteger 改写对象的 OBJPROP_XDISTANCE 与 OBJPROP_YDISTANCE 来重定位。注意 header 图标 x 坐标是 panel_x - 12,标题文字却是 panel_x - 105,关闭按钮更拉到 panel_x - 600,这种非对称偏移说明面板各部件并非等距排列,而是按视觉锚点硬编码。 外汇与贵金属图表上跑这类面板脚本,高风险在于对象 ID 前缀若与其他指标冲突,ObjectsDeleteAll 可能误删别家物件;实盘前建议在策略测试器里先挂一个货币对验证删除范围。

MQL5 / C++
ObjectsDeleteAll(class="num">0, CCI_RECTANGLE);                                                                       class=class="str">"cmt">//--- Delete all CCI rectangles
ObjectsDeleteAll(class="num">0, CCI_TEXT);                                                                                class=class="str">"cmt">//--- Delete all CCI labels
ObjectsDeleteAll(class="num">0, ADX_RECTANGLE);                                                                           class=class="str">"cmt">//--- Delete all ADX rectangles
ObjectsDeleteAll(class="num">0, ADX_TEXT);                                                                                class=class="str">"cmt">//--- Delete all ADX labels
ObjectsDeleteAll(class="num">0, AO_RECTANGLE);                                                                            class=class="str">"cmt">//--- Delete all AO rectangles
ObjectsDeleteAll(class="num">0, AO_TEXT);                                                                                 class=class="str">"cmt">//--- Delete all AO labels
ObjectsDeleteAll(class="num">0, BUY_RECTANGLE);                                                                           class=class="str">"cmt">//--- Delete all buy rectangles
ObjectsDeleteAll(class="num">0, BUY_TEXT);                                                                                class=class="str">"cmt">//--- Delete all buy labels
ObjectsDeleteAll(class="num">0, SELL_RECTANGLE);                                                                          class=class="str">"cmt">//--- Delete all sell rectangles
ObjectsDeleteAll(class="num">0, SELL_TEXT);                                                                               class=class="str">"cmt">//--- Delete all sell labels
}

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update panel object positions                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void update_panel_positions() {
  class=class="str">"cmt">// Update header and buttons
  ObjectSetInteger(class="num">0, HEADER_PANEL, OBJPROP_XDISTANCE, panel_x); class=class="str">"cmt">//--- Set header panel x position
  ObjectSetInteger(class="num">0, HEADER_PANEL, OBJPROP_YDISTANCE, panel_y); class=class="str">"cmt">//--- Set header panel y position
  ObjectSetInteger(class="num">0, HEADER_PANEL_ICON, OBJPROP_XDISTANCE, panel_x - class="num">12); class=class="str">"cmt">//--- Set header icon x position
  ObjectSetInteger(class="num">0, HEADER_PANEL_ICON, OBJPROP_YDISTANCE, panel_y + class="num">14); class=class="str">"cmt">//--- Set header icon y position
  ObjectSetInteger(class="num">0, HEADER_PANEL_TEXT, OBJPROP_XDISTANCE, panel_x - class="num">105); class=class="str">"cmt">//--- Set header title x position
  ObjectSetInteger(class="num">0, HEADER_PANEL_TEXT, OBJPROP_YDISTANCE, panel_y + class="num">12); class=class="str">"cmt">//--- Set header title y position
  ObjectSetInteger(class="num">0, CLOSE_BUTTON, OBJPROP_XDISTANCE, panel_x - class="num">600); class=class="str">"cmt">//--- Set close button x position

「面板坐标的逐对象刷新逻辑」

在 MT5 自定义面板里,所有图形对象的位置都靠 ObjectSetInteger 配合 OBJPROP_XDISTANCE / OBJPROP_YDISTANCE 来重算。上面这段只处理未最小化状态:关闭按钮和折叠按钮的 Y 偏移固定为 panel_y+14,而折叠按钮的 X 直接锁在 panel_x-570,意味着它始终贴在面板左侧外部 570 像素处。 主面板本体与符号区是分开刷的。MAIN_PANEL 用 panel_x / panel_y 原值,SYMBOL_RECTANGLE 则取 panel_x-2 与 panel_y+35,SYMBOL_TEXT 再往左挪到 panel_x-47、下沉到 panel_y+45,形成符号框与文字的层叠错位。 表头循环是这段最易算错的地方。header_names 有 7 项(BUY/SELL/RSI/STOCH/CCI/ADX/AO),前两项按 WIDTH_SIGNAL 步进、后五项按 WIDTH_INDICATOR 步进,x_offset 里还夹了 (1+header_index) 的 1 像素缝隙补偿。若你改了 WIDTH_SIGNAL 或 WIDTH_INDICATOR 的宏定义,必须同步核对这里,否则表头会重叠或留出怪缝。 外汇与贵金属图表上挂这类面板属于高频重绘操作,过多 ObjectSetInteger 调用在 tick 级刷新时可能拖慢终端;建议仅在参数变更或定时器触发时跑这段,而非每笔报价都重排。

MQL5 / C++
ObjectSetInteger(class="num">0, CLOSE_BUTTON, OBJPROP_YDISTANCE, panel_y + class="num">14); class=class="str">"cmt">//--- Set close button y position
ObjectSetInteger(class="num">0, TOGGLE_BUTTON, OBJPROP_XDISTANCE, panel_x - class="num">570); class=class="str">"cmt">//--- Set toggle button x position
ObjectSetInteger(class="num">0, TOGGLE_BUTTON, OBJPROP_YDISTANCE, panel_y + class="num">14); class=class="str">"cmt">//--- Set toggle button y position
if (!panel_minimized) {
   class=class="str">"cmt">// Update main panel
   ObjectSetInteger(class="num">0, MAIN_PANEL, OBJPROP_XDISTANCE, panel_x); class=class="str">"cmt">//--- Set main panel x position
   ObjectSetInteger(class="num">0, MAIN_PANEL, OBJPROP_YDISTANCE, panel_y); class=class="str">"cmt">//--- Set main panel y position
   class=class="str">"cmt">// Update symbol rectangle and label
   ObjectSetInteger(class="num">0, SYMBOL_RECTANGLE, OBJPROP_XDISTANCE, panel_x - class="num">2); class=class="str">"cmt">//--- Set symbol rectangle x position
   ObjectSetInteger(class="num">0, SYMBOL_RECTANGLE, OBJPROP_YDISTANCE, panel_y + class="num">35); class=class="str">"cmt">//--- Set symbol rectangle y position
   ObjectSetInteger(class="num">0, SYMBOL_TEXT, OBJPROP_XDISTANCE, panel_x - class="num">47); class=class="str">"cmt">//--- Set symbol text x position
   ObjectSetInteger(class="num">0, SYMBOL_TEXT, OBJPROP_YDISTANCE, panel_y + class="num">45); class=class="str">"cmt">//--- Set symbol text y position
   class=class="str">"cmt">// Update header rectangles and labels
   class="type">class="kw">string header_names[] = {"BUY", "SELL", "RSI", "STOCH", "CCI", "ADX", "AO"};
   for(class="type">int header_index = class="num">0; header_index < ArraySize(header_names); header_index++) { class=class="str">"cmt">//--- Loop through headers
      class="type">int x_offset = panel_x - WIDTH_TIMEFRAME - (header_index < class="num">2 ? header_index * WIDTH_SIGNAL : class="num">2 * WIDTH_SIGNAL + (header_index - class="num">2) * WIDTH_INDICATOR) + (class="num">1 + header_index); class=class="str">"cmt">//--- Calculate x position
      ObjectSetInteger(class="num">0, HEADER_RECTANGLE + IntegerToString(header_index), OBJPROP_XDISTANCE, x_offset); class=class="str">"cmt">//--- Set header rectangle x position
      ObjectSetInteger(class="num">0, HEADER_RECTANGLE + IntegerToString(header_index), OBJPROP_YDISTANCE, panel_y + class="num">35); class=class="str">"cmt">//--- Set header rectangle y position
      ObjectSetInteger(class="num">0, HEADER_TEXT + IntegerToString(header_index), OBJPROP_XDISTANCE, x_offset - (header_index < class="num">2 ? WIDTH_SIGNAL/class="num">2 : WIDTH_INDICATOR/class="num">2)); class=class="str">"cmt">//--- Set header text x position
      ObjectSetInteger(class="num">0, HEADER_TEXT + IntegerToString(header_index), OBJPROP_YDISTANCE, panel_y + class="num">45); class=class="str">"cmt">//--- Set header text y position
   }
   class=class="str">"cmt">// Update timeframe rectangles, labels, and cells
   for(class="type">int timeframe_index = class="num">0; timeframe_index < ArraySize(timeframes_array); timeframe_index++) { class=class="str">"cmt">//--- Loop through timeframes

◍ 把多周期面板的坐标落进对象属性

这段逻辑负责把面板里每一行(对应一个 timeframe_index)的矩形和文字按像素距离摆正。y_offset 以 panel_y 为基准,加上 35 像素顶距与行高 HEIGHT_RECTANGLE,再按 timeframe_index 逐行下移,减去 (1 + timeframe_index) 做 1px 像素纠偏,避免 MT5 对象坐标累计漂移。 时间框架标签本身用 OBJPROP_XDISTANCE / OBJPROP_YDISTANCE 写入:矩形左边距 panel_x - 2,文字左边距 panel_x - 47、上边距比矩形多 10px,肉眼看是标签左对齐贴在行首。 内层 for 循环遍历 header_names 数组(长度由 ArraySize 决定,本例为 7:买、卖、RSI、Stoch、CCI、ADX、AO),用 switch 给每种单元格拼出独立对象名(如 BUY_RECTANGLE+序号)。x_offset 的计算区分了前 two 列信号列(WIDTH_SIGNAL)与后五列指标列(WIDTH_INDICATOR),公式里 + (1 + header_index) 同样是 1px 级修正。 开 MT5 把这段塞进你面板的绘制函数,改 HEIGHT_RECTANGLE 或 WIDTH_INDICATOR 数值,就能直接看到行距和列宽变化;外汇与贵金属品种波动大,面板刷新频率过高可能拖累终端响应,调参时留意资源占用。

MQL5 / C++
class="type">int y_offset = (panel_y + class="num">35 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index); class=class="str">"cmt">//--- Calculate y position
ObjectSetInteger(class="num">0, TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), OBJPROP_XDISTANCE, panel_x - class="num">2); class=class="str">"cmt">//--- Set timeframe rectangle x position
ObjectSetInteger(class="num">0, TIMEFRAME_RECTANGLE + IntegerToString(timeframe_index), OBJPROP_YDISTANCE, y_offset); class=class="str">"cmt">//--- Set timeframe rectangle y position
ObjectSetInteger(class="num">0, TIMEFRAME_TEXT + IntegerToString(timeframe_index), OBJPROP_XDISTANCE, panel_x - class="num">47); class=class="str">"cmt">//--- Set timeframe text x position
ObjectSetInteger(class="num">0, TIMEFRAME_TEXT + IntegerToString(timeframe_index), OBJPROP_YDISTANCE, y_offset + class="num">10); class=class="str">"cmt">//--- Set timeframe text y position
for(class="type">int header_index = class="num">0; header_index < ArraySize(header_names); header_index++) { class=class="str">"cmt">//--- Loop through cells
  class="type">class="kw">string cell_rectangle_name, cell_text_name;
  class="kw">switch(header_index) { class=class="str">"cmt">//--- Select cell type
    case class="num">0: cell_rectangle_name = BUY_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = BUY_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Buy cell
    case class="num">1: cell_rectangle_name = SELL_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = SELL_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Sell cell
    case class="num">2: cell_rectangle_name = RSI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = RSI_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- RSI cell
    case class="num">3: cell_rectangle_name = STOCH_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = STOCH_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- Stochastic cell
    case class="num">4: cell_rectangle_name = CCI_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = CCI_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- CCI cell
    case class="num">5: cell_rectangle_name = ADX_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = ADX_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- ADX cell
    case class="num">6: cell_rectangle_name = AO_RECTANGLE + IntegerToString(timeframe_index); cell_text_name = AO_TEXT + IntegerToString(timeframe_index); class="kw">break; class=class="str">"cmt">//--- AO cell
  }
  class="type">int x_offset = panel_x - WIDTH_TIMEFRAME - (header_index < class="num">2 ? header_index * WIDTH_SIGNAL : class="num">2 * WIDTH_SIGNAL + (header_index - class="num">2) * WIDTH_INDICATOR) + (class="num">1 + header_index); class=class="str">"cmt">//--- Calculate x position
让小布替你跑这套
这些定位与切换的诊断逻辑小布盯盘已内置,打开对应品种页即可看到折叠态面板,你专注决策而不是调界面。

常见问题

通常基于 ChartMouseEvent 读取鼠标 x/y,配合标题栏悬停状态与拖拽标志位计算偏移,再重绘面板控件位置。
合理写法下只隐藏 UI 对象、不注销指标句柄,后台计算继续,展开后数值无缝衔接。
脚本级全局变量随图表卸载清空;若需持久化要用终端全局变量或文件,本篇架构默认会话内有效。
可以,小布盯盘的 AIGC 面板支持折叠与位置记忆,逻辑思路与本文 MQL5 实现一致,但无需自己写代码。
外汇贵金属波动快、高风险,同屏常开多品种多周期,折叠能降低视觉噪声,但信号仍只是概率参考。