MQL5交易工具(第三部分):构建用于策略交易的多时间周期扫描仪表盘·综合运用
◍ 面板单元格与指标数组的初始化细节
这段逻辑负责在 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),你换周期只需动数组,不用改函数。外汇与贵金属波动大,这类多周期面板仅作概率参考,实盘仍属高风险。
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 用策略测试器单步验证各句柄返回值。
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,说明这版没在颜色上区分信号强度,想做分级高亮得自己改这行。 外汇与贵金属波动大、杠杆高,这类面板只是辅助观察,信号强弱不等于入场胜率,实盘前务必在策略测试器里跑一遍不同品种验证阈值适用性。
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 报错名。注意它只打了日志没建对象,所以图形若从未被创建过,这函数不会替你补一个矩形。
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 供调用方判断成功与否。外汇与贵金属杠杆高、跳空频繁,这类标签若更新失败可能让你误读仓位状态,上线前务必在策略测试器里跑一遍断点。
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 上反复加载卸载验证残留。
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 跑一轮,切周期就能看到旧对象不再滞留。
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 按前缀批量清除符号区与时间周期区的矩形和文字标签。
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 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 时,若发现切换周期后图形错乱,优先检查是否漏了某一类前缀的删除调用。外汇与贵金属波动剧烈,这类视觉残留不会直接亏钱,但可能让你误读信号,属于高风险环境下的低级干扰。
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 终端也不会因后台扫描卡顿。