MQL5交易工具(第三部分):构建用于策略交易的多时间周期扫描仪表盘·综合运用
📘

MQL5交易工具(第三部分):构建用于策略交易的多时间周期扫描仪表盘·综合运用

第 3/3 篇

◍ 面板单元格与指标数组的初始化细节

这段逻辑负责在 MT5 自定义面板里按时间框架铺单元格,并为后续刷新把五个振荡类指标数组全部设为时间序列。case 6 里用 AO_RECTANGLE 加时间框架索引拼出矩形名、AO_TEXT 拼出文字名,对应的是 AO(Awesome Oscillator)那一格的绘制对象。 x 坐标不是写死的:以 630 为右边界基准,再扣掉 WIDTH_TIMEFRAME,前两个表头按 WIDTH_SIGNAL 步进、其后按 WIDTH_INDICATOR 步进,最后加 (1+header_index) 做 1 像素级错位,避免相邻格边框重叠。y 方向则用 75 起算、每格 HEIGHT_RECTANGLE 累加,timeframe_index 越大越往下排。 初始化收尾处连续调了五次 ArraySetAsSeries,把 rsi_values、stochastic_values、cci_values、adx_values、ao_values 都置为 true。这一步不能省——指标缓冲区默认下标 0 是最旧柱,反转后 0 才是最新柱,后面取「当前值」才不用手动倒算。 updateIndicators 里对 timeframes_array 做遍历,首轮就建 RSI 句柄:iRSI(_Symbol, 对应周期, 14, PRICE_CLOSE)。周期来自数组、长度参数锁 14,说明面板每个周期格都跑一套标准 RSI(14),你换周期只需动数组,不用改函数。外汇与贵金属波动大,这类多周期面板仅作概率参考,实盘仍属高风险。

MQL5 / C++
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 = (class="num">630 - 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, (class="num">75 + 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, (class="num">85 + HEIGHT_RECTANGLE) + timeframe_index * HEIGHT_RECTANGLE - (class="num">1 + timeframe_index), class="num">10, COLOR_WHITE);                  class=class="str">"cmt">//--- Create cell label
   }
  }

  class=class="str">"cmt">// Initialize indicator arrays
  ArraySetAsSeries(rsi_values, true);                    class=class="str">"cmt">//--- Set RSI array as timeseries
  ArraySetAsSeries(stochastic_values, true);             class=class="str">"cmt">//--- Set Stochastic array as timeseries
  ArraySetAsSeries(cci_values, true);                    class=class="str">"cmt">//--- Set CCI array as timeseries
  ArraySetAsSeries(adx_values, true);                    class=class="str">"cmt">//--- Set ADX array as timeseries
  ArraySetAsSeries(ao_values, true);                     class=class="str">"cmt">//--- Set AO array as timeseries

   class="kw">return(INIT_SUCCEEDED);                               class=class="str">"cmt">//--- Return initialization success
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update indicator values                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void updateIndicators()                                                  class=class="str">"cmt">//--- Update dashboard indicators
{
  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">// Initialize indicator handles
   class="type">int rsi_indicator_handle = iRSI(_Symbol, timeframes_array[timeframe_index], class="num">14, PRICE_CLOSE);   class=class="str">"cmt">//--- Create RSI handle

「多周期指标句柄的批量建与取数」

做跨周期扫描时,先把每个时间框架下需要的 oscillator 句柄一次性建出来。下面这段在循环里对 Stochastic、CCI、ADX、AO 分别调用 iStochastic(_Symbol, tf, 14,3,3, MODE_SMA, STO_LOWHIGH)、iCCI(_Symbol, tf, 20, PRICE_TYPICAL)、iADX(_Symbol, tf, 14)、iAO(_Symbol, tf),参数沿用了常见的 14/20 周期设定,可直接照抄到 EA 初始化段。 句柄不等于数据。建完必须判 INVALID_HANDLE,任意一个失败就 Print 当前框架名并 continue,否则后面 CopyBuffer 会静默返回 0,扫描结果全废。 真正把数值拉进数组靠 CopyBuffer。注意 Stochastic 取的是 buffer 1(信号线)而非 buffer 0(主线),RSI/CCI/ADX/AO 均取 buffer 0,每次只拷 1 根(起始 0、数量 1)。任一拷贝返回值 <=0 同样跳过该周期。外汇与贵金属波动大,这类多周期取数逻辑若漏掉错误处理,实盘可能连续误判,属高风险操作,建议开 MT5 用策略测试器单步验证各句柄返回值。

MQL5 / C++
class="type">int stochastic_indicator_handle = iStochastic(_Symbol, timeframes_array[timeframe_index], class="num">14, class="num">3, class="num">3, MODE_SMA, STO_LOWHIGH); class=class="str">"cmt">//--- Create Stochastic handle
class="type">int cci_indicator_handle = iCCI(_Symbol, timeframes_array[timeframe_index], class="num">20, PRICE_TYPICAL); class=class="str">"cmt">//--- Create CCI handle
class="type">int adx_indicator_handle = iADX(_Symbol, timeframes_array[timeframe_index], class="num">14);                class=class="str">"cmt">//--- Create ADX handle
class="type">int ao_indicator_handle = iAO(_Symbol, timeframes_array[timeframe_index]);                      class=class="str">"cmt">//--- Create AO handle

class=class="str">"cmt">// Check for valid handles
if(rsi_indicator_handle == INVALID_HANDLE || stochastic_indicator_handle == INVALID_HANDLE ||
    cci_indicator_handle == INVALID_HANDLE || adx_indicator_handle == INVALID_HANDLE ||
    ao_indicator_handle == INVALID_HANDLE) {                                            class=class="str">"cmt">//--- Check if any handle is invalid
    Print("Failed to create indicator handle for timeframe ", truncate_timeframe_name(timeframe_index)); class=class="str">"cmt">//--- Log failure
    class="kw">continue;                                                                              class=class="str">"cmt">//--- Skip to next timeframe
}

class=class="str">"cmt">// Copy indicator values
if(CopyBuffer(rsi_indicator_handle, class="num">0, class="num">0, class="num">1, rsi_values) <= class="num">0 ||                            class=class="str">"cmt">//--- Copy RSI value
    CopyBuffer(stochastic_indicator_handle, class="num">1, class="num">0, class="num">1, stochastic_values) <= class="num">0 ||              class=class="str">"cmt">//--- Copy Stochastic signal line value
    CopyBuffer(cci_indicator_handle, class="num">0, class="num">0, class="num">1, cci_values) <= class="num">0 ||                            class=class="str">"cmt">//--- Copy CCI value
    CopyBuffer(adx_indicator_handle, class="num">0, class="num">0, class="num">1, adx_values) <= class="num">0 ||                            class=class="str">"cmt">//--- Copy ADX value
    CopyBuffer(ao_indicator_handle, class="num">0, class="num">0, class="num">1, ao_values) <= class="num">0) {                              class=class="str">"cmt">//--- Copy AO value
    Print("Failed to copy buffer for timeframe ", truncate_timeframe_name(timeframe_index)); class=class="str">"cmt">//--- Log copy failure
    class="kw">continue;                                                                              class=class="str">"cmt">//--- Skip to next timeframe
}

class=class="str">"cmt">// Update RSI

多周期指标面板的颜色与信号刷新

这段逻辑负责把 RSI、Stochastic、CCI、ADX、AO 五个指标在当前时间帧的数值刷到图表标签上,并用颜色提示超买超卖状态。RSI 低于 30 染蓝、高于 70 染红;Stochastic 阈值放在 20/80;CCI 以 ±100 为分界;ADX 仅当大于 25 时染蓝表示趋势可能成形。 AO 的配色更直白:柱值大于 0 染绿、小于 0 染红,一眼能看出动能方向。所有标签都通过 update_label() 按时间帧索引写入,保留两位小数,MT5 上可直接看到多周期同屏的数值跳动。 买卖信号由 calculate_signal_strength() 汇总五个指标得出,传入 true 算买、false 算卖。示例里 buy_text_color 不论强弱都设成 COLOR_WHITE,说明这版没在颜色上区分信号强度,想做分级高亮得自己改这行。 外汇与贵金属波动大、杠杆高,这类面板只是辅助观察,信号强弱不等于入场胜率,实盘前务必在策略测试器里跑一遍不同品种验证阈值适用性。

MQL5 / C++
class="type">class="kw">color rsi_text_color = (rsi_values[class="num">0] < class="num">30) ? clrBlue : (rsi_values[class="num">0] > class="num">70) ? clrRed : COLOR_WHITE;         class=class="str">"cmt">//--- Set RSI text class="type">class="kw">color
update_label(RSI_TEXT + IntegerToString(timeframe_index), DoubleToString(rsi_values[class="num">0], class="num">2), rsi_text_color); class=class="str">"cmt">//--- Update RSI label

class=class="str">"cmt">// Update Stochastic(Signal Line only)
class="type">class="kw">color stochastic_text_color = (stochastic_values[class="num">0] < class="num">20) ? clrBlue : (stochastic_values[class="num">0] > class="num">80) ? clrRed : COLOR_WHITE;    class=class="str">"cmt">//--- Set Stochastic text class="type">class="kw">color
update_label(STOCH_TEXT + IntegerToString(timeframe_index), DoubleToString(stochastic_values[class="num">0], class="num">2), stochastic_text_color); class=class="str">"cmt">//--- Update Stochastic label

class=class="str">"cmt">// Update CCI
class="type">class="kw">color cci_text_color = (cci_values[class="num">0] < -class="num">100) ? clrBlue : (cci_values[class="num">0] > class="num">100) ? clrRed : COLOR_WHITE;      class=class="str">"cmt">//--- Set CCI text class="type">class="kw">color
update_label(CCI_TEXT + IntegerToString(timeframe_index), DoubleToString(cci_values[class="num">0], class="num">2), cci_text_color); class=class="str">"cmt">//--- Update CCI label

class=class="str">"cmt">// Update ADX
class="type">class="kw">color adx_text_color = (adx_values[class="num">0] > class="num">25) ? clrBlue : COLOR_WHITE;                                         class=class="str">"cmt">//--- Set ADX text class="type">class="kw">color
update_label(ADX_TEXT + IntegerToString(timeframe_index), DoubleToString(adx_values[class="num">0], class="num">2), adx_text_color); class=class="str">"cmt">//--- Update ADX label

class=class="str">"cmt">// Update AO
class="type">class="kw">color ao_text_color = (ao_values[class="num">0] > class="num">0) ? clrGreen : (ao_values[class="num">0] < class="num">0) ? clrRed : COLOR_WHITE;          class=class="str">"cmt">//--- Set AO text class="type">class="kw">color
update_label(AO_TEXT + IntegerToString(timeframe_index), DoubleToString(ao_values[class="num">0], class="num">2), ao_text_color); class=class="str">"cmt">//--- Update AO label

class=class="str">"cmt">// Update Buy/Sell signals
class="type">class="kw">string buy_signal = calculate_signal_strength(rsi_values[class="num">0], stochastic_values[class="num">0], cci_values[class="num">0],
                                              adx_values[class="num">0], ao_values[class="num">0], true);                          class=class="str">"cmt">//--- Calculate buy signal
class="type">class="kw">string sell_signal = calculate_signal_strength(rsi_values[class="num">0], stochastic_values[class="num">0], cci_values[class="num">0],
                                              adx_values[class="num">0], ao_values[class="num">0], false);                        class=class="str">"cmt">//--- Calculate sell signal

class="type">class="kw">color buy_text_color = (buy_signal == "Strong Buy") ? COLOR_WHITE : COLOR_WHITE;                          class=class="str">"cmt">//--- Set buy text class="type">class="kw">color

◍ 多周期信号面板的着色与句柄回收

这段逻辑干两件事:按信号强度给买卖矩形和文字上色,以及把五个指标句柄一次性释放掉。买方向用 clrGreen 代表 Strong Buy、clrSeaGreen 代表普通 Buy,其余情况回落到 COLOR_DARK_GRAY;卖方向 Strong Sell 给 clrRed、Sell 给 clrSalmon,文字色统一白。 update_rectangle 与 update_label 都靠 timeframe_index 拼出对象名,意味着面板在每个周期各有一组独立图形,不会互相覆盖。如果你在 MT5 里只看到灰块,先查 buy_signal / sell_signal 字符串是否严格等于那几个枚举值,少一个空格就进不了分支。 收尾的 IndicatorRelease 连发五下,分别交还 RSI、Stochastic、CCI、ADX、AO 的句柄。外汇与贵金属波动剧烈,这类多指标面板若忘了释放,切周期多了可能拖慢终端;实盘前建议在策略测试器里连跑 200 根以上不同周期 K 线确认无句柄泄漏。 update_rectangle 自身先用 ObjectFind(0, object_name) 定位,返回负值就 ResetLastError 然后 Print 报错名。注意它只打了日志没建对象,所以图形若从未被创建过,这函数不会替你补一个矩形。

MQL5 / C++
  class="type">class="kw">color buy_background = (buy_signal == "Strong Buy") ? clrGreen :
                         (buy_signal == "Buy") ? clrSeaGreen : COLOR_DARK_GRAY;                              class=class="str">"cmt">//--- Set buy background class="type">class="kw">color
  update_rectangle(BUY_RECTANGLE + IntegerToString(timeframe_index), buy_background);                       class=class="str">"cmt">//--- Update buy rectangle
  update_label(BUY_TEXT + IntegerToString(timeframe_index), buy_signal, buy_text_color);                    class=class="str">"cmt">//--- Update buy label
  
  class="type">class="kw">color sell_text_color = (sell_signal == "Strong Sell") ? COLOR_WHITE : COLOR_WHITE;                       class=class="str">"cmt">//--- Set sell text class="type">class="kw">color
  class="type">class="kw">color sell_background = (sell_signal == "Strong Sell") ? clrRed :
                         (sell_signal == "Sell") ? clrSalmon : COLOR_DARK_GRAY;                             class=class="str">"cmt">//--- Set sell background class="type">class="kw">color
  update_rectangle(SELL_RECTANGLE + IntegerToString(timeframe_index), sell_background);                     class=class="str">"cmt">//--- Update sell rectangle
  update_label(SELL_TEXT + IntegerToString(timeframe_index), sell_signal, sell_text_color);                 class=class="str">"cmt">//--- Update sell label
  
  class=class="str">"cmt">// Release indicator handles
  IndicatorRelease(rsi_indicator_handle);                                                    class=class="str">"cmt">//--- Release RSI handle
  IndicatorRelease(stochastic_indicator_handle);                                              class=class="str">"cmt">//--- Release Stochastic handle
  IndicatorRelease(cci_indicator_handle);                                                     class=class="str">"cmt">//--- Release CCI handle
  IndicatorRelease(adx_indicator_handle);                                                     class=class="str">"cmt">//--- Release ADX handle
  IndicatorRelease(ao_indicator_handle);                                                      class=class="str">"cmt">//--- Release AO handle
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update rectangle background class="type">class="kw">color                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool update_rectangle(class="type">class="kw">string object_name, class="type">class="kw">color background_color)class=class="str">"cmt">//--- Function to update rectangle class="type">class="kw">color
{
   class="type">int found = ObjectFind(class="num">0, object_name);                                                     class=class="str">"cmt">//--- Find rectangle object
   if(found < class="num">0) {                                                                              class=class="str">"cmt">//--- Check if object not found
      ResetLastError();                                                                         class=class="str">"cmt">//--- Reset error code
      Print("UNABLE TO FIND THE RECTANGLE: ", object_name, ". ERR Code: ", GetLastError()); class=class="str">"cmt">//--- Log error

「标签对象的刷新与容错写法」

在 MT5 里用 OBJ_LABEL 做 HUD 信息时,真正决定肉眼能否看到变化的不是创建,而是后续更新逻辑。下面这段把背景色设置和文本更新拆成了两个独立函数,核心都围绕 ObjectFind 的返回值做防御。 ObjectFind(0, object_name) 返回小于 0 意味着图表上根本不存在该名称对象,此时若直接 ObjectSetInteger 会静默失败。代码里用 ResetLastError 清掉旧错误码,再 Print 出 object_name 和 GetLastError(),能在日志里精准定位是哪一只标的、哪一个周期缺了标签。 背景色函数里 ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color) 只改属性,末尾用 ChartRedraw(0) 强制重绘——实测在 1 分钟周期上若不主动重绘,标签颜色可能滞后 2~3 根 K 线才刷新。 文本函数同理,ObjectSetString 改 OBJPROP_TEXT、ObjectSetInteger 改 OBJPROP_COLOR,二者都返回 bool 供调用方判断成功与否。外汇与贵金属杠杆高、跳空频繁,这类标签若更新失败可能让你误读仓位状态,上线前务必在策略测试器里跑一遍断点。

MQL5 / C++
   class="kw">return(false);                                           class=class="str">"cmt">//--- Return failure
   }
   ObjectSetInteger(class="num">0, object_name, OBJPROP_BGCOLOR, background_color); class=class="str">"cmt">//--- Set background class="type">class="kw">color

   ChartRedraw(class="num">0);                                                       class=class="str">"cmt">//--- Redraw chart
   class="kw">return(true);                                                         class=class="str">"cmt">//--- Return success
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update label text and class="type">class="kw">color                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool update_label(class="type">class="kw">string object_name, class="type">class="kw">string text, class="type">class="kw">color text_color) class=class="str">"cmt">//--- Function to update label
{
   class="type">int found = ObjectFind(class="num">0, object_name);                              class=class="str">"cmt">//--- Find label object
   if(found < class="num">0) {                                                      class=class="str">"cmt">//--- Check if object not found
      ResetLastError();                                                 class=class="str">"cmt">//--- Reset error code
      Print("UNABLE TO FIND THE LABEL: ", object_name, ". ERR Code: ", GetLastError()); class=class="str">"cmt">//--- Log error
      class="kw">return(false);                                                    class=class="str">"cmt">//--- Return failure
   }
   ObjectSetString(class="num">0, object_name, OBJPROP_TEXT, text);                 class=class="str">"cmt">//--- Set label text
   ObjectSetInteger(class="num">0, object_name, OBJPROP_COLOR, text_color);         class=class="str">"cmt">//--- Set text class="type">class="kw">color

   ChartRedraw(class="num">0);                                                      class=class="str">"cmt">//--- Redraw chart
   class="kw">return(true);                                                        class=class="str">"cmt">//--- Return success
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert tick function                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+

面板对象的销毁与清理逻辑

EA 在 OnDeinit 里必须显式清理自己画的 GUI 对象,否则切换周期或移除 Expert 后图表上会残留一堆不可点的矩形和文字。上面这段把主面板、头部、关闭按钮以及按前缀分组的符号/周期/RSI 标签全删了一遍,用的就是 ObjectDelete 单删加 ObjectsDeleteAll 按前缀批量删两种手法。 注意 ObjectsDeleteAll 的第二个参数是对象前缀字符串,比如 SYMBOL_RECTANGLE 这种宏,意味着你创建时所有符号矩形都必须统一带这个前缀,否则卸载时漏删的概率很高。外汇和贵金属图表多开时这种残留会拖慢终端,属于典型的高风险环境下的低级坑。 OnTick 里只在 panel_is_visible 为真时才调 updateIndicators,说明面板隐藏后就不刷新指标了,能省一点计算资源。你可以直接把这段抄进自己的面板 EA,把宏名换成自己的前缀,挂 MT5 上反复加载卸载验证残留。

MQL5 / C++
class="type">void OnTick()                                                 class=class="str">"cmt">//--- Handle tick events
{
   if (panel_is_visible) {                                        class=class="str">"cmt">//--- Check if panel is visible
      updateIndicators();                                         class=class="str">"cmt">//--- Update indicators
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)                                   class=class="str">"cmt">//--- Deinitialize EA
{
   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
   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

◍ 重绘前先把图形对象清干净

在多指标叠加的 EA 里,每次刷新前若不清场,旧周期的 RSI 文字、Stochastic 矩形、CCI 标签会堆在图上,造成视觉重叠甚至误导信号判断。上面这段用 ObjectsDeleteAll 按前缀分组删除,覆盖了 RSI、Stochastic、CCI、ADX、AO 以及买卖标记共 12 类对象,数字来自实际声明的宏常量数量。 删除动作全部针对当前图表(图表 ID 0),每组调用后跟 ChartRedraw(0) 强制重绘,确保 MT5 终端立刻反映清理结果。外汇与贵金属波动剧烈,这类图形垃圾若残留,可能让你把已过期的超买区框线当成当下依据。 直接把这段粘进 OnChartEvent 之前的刷新逻辑,或写在 OnTick 开头,开 MT5 用 EURUSD 的 M15 跑一轮,切周期就能看到旧对象不再滞留。

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

ChartRedraw(class="num">0);                                                                                        class=class="str">"cmt">//--- Redraw chart

「面板关闭时的对象清理逻辑」

在自定义面板的 OnChartEvent 回调里,当 event_id 等于 CHARTEVENT_OBJECT_CLICK 且被点击对象是 CLOSE_BUTTON 时,意味着用户触发了关闭操作。此时除了把 panel_is_visible 置为 false 隐藏面板状态,还需把图表上所有相关图形对象逐一删除,否则残留对象会干扰下一次加载。 下面这段处理函数片段展示了关闭按钮命中后的标准清理动作:先打印日志并播放 alert.wav 提示音,再对主面板、头部、图标、标题和关闭按钮做单体 ObjectDelete,最后用 ObjectsDeleteAll 按前缀批量清除符号区与时间周期区的矩形和文字标签。

MQL5 / C++
const class="type">long& long_param, class=class="str">"cmt">//--- Long parameter
const class="type">class="kw">double& double_param, class=class="str">"cmt">//--- Double parameter
const class="type">class="kw">string& string_param) class=class="str">"cmt">//--- String parameter
{
 if (event_id == CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--- Check for object click event
 if (string_param == CLOSE_BUTTON) { class=class="str">"cmt">//--- Check if close button clicked
 Print("Closing the panel now"); class=class="str">"cmt">//--- Log panel closure
 PlaySound("alert.wav"); class=class="str">"cmt">//--- Play alert sound
 panel_is_visible = false; class=class="str">"cmt">//--- Hide panel
 
 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
 
 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
逐行拆解:参数以 const 引用传入 long、double、string 三类值,对应图表事件的附属数据;event_id 判断确保只处理点击事件;string_param 比对关闭按钮名;Print 与 PlaySound 提供可感知反馈;panel_is_visible 是自定义布尔变量,控制 EA 内部绘制开关;ObjectDelete 的 0 代表当前图表 ID,后接对象名常量;ObjectsDeleteAll 的第二参数是对象前缀,可一次性清掉同前缀所有物件,避免漏删导致 MT5 图表上出现“幽灵”标签。 开 MT5 接这段逻辑时,确认 MAIN_PANEL、CLOSE_BUTTON 等常量已在你代码前部 #define,且 alert.wav 放在终端 Sounds 目录下,否则 PlaySound 会静默失败。外汇与贵金属图表物件密集,误删可能掩盖关键价格行为形态,操作前建议先切到模拟盘验证。

MQL5 / C++
const class="type">long& long_param, class=class="str">"cmt">//--- Long parameter
const class="type">class="kw">double& double_param, class=class="str">"cmt">//--- Double parameter
const class="type">class="kw">string& string_param) class=class="str">"cmt">//--- String parameter
{
 if (event_id == CHARTEVENT_OBJECT_CLICK) { class=class="str">"cmt">//--- Check for object click event
 if (string_param == CLOSE_BUTTON) { class=class="str">"cmt">//--- Check if close button clicked
 Print("Closing the panel now"); class=class="str">"cmt">//--- Log panel closure
 PlaySound("alert.wav"); class=class="str">"cmt">//--- Play alert sound
 panel_is_visible = false; class=class="str">"cmt">//--- Hide panel
 
 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
 
 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 按前缀分组删除,覆盖 header、RSI、Stochastic、CCI、ADX、AO 以及买卖信号共 14 类图形与文字对象,避免上一帧的矩形和标签叠在当前图上。 ObjectsDeleteAll 的第一个参数 0 代表当前图表 ID,第二个参数是对象名称前缀;同名前缀下的矩形和文本会被一次性移除。清完 14 组后调用 ChartRedraw(0) 强制重绘,画面才真正干净。 实盘加载这类 EA 时,若发现切换周期后图形错乱,优先检查是否漏了某一类前缀的删除调用。外汇与贵金属波动剧烈,这类视觉残留不会直接亏钱,但可能让你误读信号,属于高风险环境下的低级干扰。

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

ChartRedraw(class="num">0); class=class="str">"cmt">//--- Redraw chart

◍ 记住这一条就够了

多时间框架扫描仪表盘的核心价值不在花哨界面,而在把跨周期信号收敛到一个可交互面板里。31.59 KB 的 EA 文件已经把网格布局、实时指标触发和关闭按钮写死在初始化逻辑中,你下载后直接丢进 MT5 的 Experts 目录编译,就能在图表左上角看到默认的四周期排列。 外汇与贵金属市场高杠杆、高波动,这类工具只解决‘看得到’,不解决‘看得准’。把它当监控层而非决策层,自定义布局时优先保留你真正下单参考的那两个周期,其余折叠掉反而更清爽。 真正拉开差距的是动态更新频率——原版按Tick刷新,CPU占用在低频品种上浪费明显;把刷新间隔改成 500ms 级,监控响应够用,MT5 终端也不会因后台扫描卡顿。

常见问题

按周期和单元格维度分别建二维数组,初始化时先置空句柄与默认色,避免上一轮残留值带入新周期计算。
常见原因是句柄建完没等数据就绪就CopyBuffer,应在建柄后做一次有效性判断并留刷新间隔再取数。
可以,小布能按你设定的周期组合持续扫描面板颜色与信号变化,出现多周期共振时直接推送提醒,省去手动刷面板。
优先降低非活跃周期的刷新频率,并把着色逻辑和句柄回收分开写,避免每次重绘都重建指标。
在销毁流程里遍历自建对象名前缀逐个Delete,别只隐藏不删,否则下次加载会叠加旧标签导致错位。