MQL5 交易工具(第五部分):创建滚动行情条,实现交易品种实时监控·综合运用
「面板标签与自适应的绘制落点」
多货币 dashboard 的每段横向区块,靠 i * SectionHorizontalSpacing 控制水平位移,文本纵坐标则根据是否显示点差动态叠加:ShowSpread 为真时,基准 Y 额外多出 SpreadFontSize + 2 像素,否则少算这一段。百分比标签固定在横向偏移 105 处,箭头用 Wingdings 字体画在偏移 35 处,Bid 价文本从偏移 50 开始,三者纵向均落在基准 Y 再叠加 SectionFontSize + 2 的位置。 后台 UpdateBackground() 里面板高度不是写死的:ShowSpread 为真取 4 层字号最大值加 2 再 +40,否则取 3 层,这意味着关闭点差显示能直接砍掉一整行高度。宽度则实时读 ChartGetInteger(0, CHART_WIDTH_IN_PIXELS),随窗口缩放走。 想验证这套坐标逻辑,把 SectionHorizontalSpacing 从默认改到 200,重加载指标,看各货币块是否横向拉开;再把 ShowSpread 切 false,面板底部会肉眼可见地上缩一行。外汇与贵金属波动剧烈,此类 HUD 仅作盘口速读,不构成方向判断。
createText(percentChangeName, percentText, (i * SectionHorizontalSpacing) + class="num">105, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14), prices[i].percent_color, SectionFontSize, SectionFont); class=class="str">"cmt">//--- Create percent change text label class=class="str">"cmt">// Section: Arrow(below currency, right of image, Wingdings) class="type">class="kw">string arrowName = dashboardName + "_Arrow_" + IntegerToString(i); class=class="str">"cmt">//--- Define arrow object name createText(arrowName, prices[i].arrow_char, (i * SectionHorizontalSpacing) + class="num">35, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14) + SectionFontSize + class="num">2, prices[i].arrow_color, SectionFontSize, "Wingdings"); class=class="str">"cmt">//--- Create arrow text label class=class="str">"cmt">// Section: Bid Price(next to arrow, horizontal) class="type">class="kw">string bidName = dashboardName + "_Bid_" + IntegerToString(i); class=class="str">"cmt">//--- Define bid price object name createText(bidName, StringFormat("%.5f", prices[i].bid), (i * SectionHorizontalSpacing) + class="num">50, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14) + SectionFontSize + class="num">2, prices[i].bid_color, SectionFontSize, SectionFont); class=class="str">"cmt">//--- Create bid price text label } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Update background function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void UpdateBackground() { class="type">int width = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS); class=class="str">"cmt">//--- Get current chart width class="type">int height = (ShowSpread ? class="num">4 : class="num">3) * (MathMax(MathMax(MathMax(SymbolFontSize, AskFontSize), SpreadFontSize), SectionFontSize) + class="num">2) + class="num">40; class=class="str">"cmt">//--- Recalculate panel height ObjectSetInteger(class="num">0, backgroundName, OBJPROP_XSIZE, width); class=class="str">"cmt">//--- Update panel width ObjectSetInteger(class="num">0, backgroundName, OBJPROP_YSIZE, height); class=class="str">"cmt">//--- Update panel height } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Update dashboard function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void UpdateDashboard() { class="kw">static class="type">class="kw">double symbolOffset = class="num">0; class=class="str">"cmt">//--- Track symbol line offset class="kw">static class="type">class="kw">double askOffset = class="num">0; class=class="str">"cmt">//--- Track ask line offset class="kw">static class="type">class="kw">double spreadOffset = class="num">0; class=class="str">"cmt">//--- Track spread line offset class="kw">static class="type">class="kw">double sectionOffset = class="num">0; class=class="str">"cmt">//--- Track section offset
多品种面板的横向排布与越界回收
做跨品种盯盘面板时,最容易被忽略的是横向像素分配。上面这段代码把每个品种的显示列宽拆成四组:symbol、ask、spread、section,分别乘以品种数得到总宽。以 20 个品种、各间距 60 像素算,单 symbol 行就占 1200 像素,远超多数笔记本的图表可视区。 循环里对每个品种算 x 坐标,一旦小于负间距就加总宽「绕回」左侧,相当于把超出右边界的对象从左边重新进场。配合 ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) 取到的右边界,用 OBJPROP_HIDDEN 把屏幕外的文本直接隐藏,避免 MT5 对象树里堆出几百个不可见标签拖慢刷新。 注意 ask 行和 spread 行的 y 偏移是用字体大小累加出来的:SymbolFontSize+2 再叠 AskFontSize+2。如果你改了字体号却没同步这几个常量,三行文字会叠在一起。开 MT5 把 SymbolFontSize 从 10 调到 14,肉眼就能验证行距是否还对齐。
class="type">int totalWidthSymbol = totalSymbols * SymbolHorizontalSpacing; class=class="str">"cmt">//--- Calculate total symbol line width class="type">int totalWidthAsk = totalSymbols * AskHorizontalSpacing; class=class="str">"cmt">//--- Calculate total ask line width class="type">int totalWidthSpread = totalSymbols * SpreadHorizontalSpacing; class=class="str">"cmt">//--- Calculate total spread line width class="type">int totalWidthSection = totalSymbols * SectionHorizontalSpacing; class=class="str">"cmt">//--- Calculate total section width class="type">int rightEdge = (class="type">int)ChartGetInteger(class="num">0, CHART_WIDTH_IN_PIXELS); class=class="str">"cmt">//--- Get chart right boundary class=class="str">"cmt">//--- Update text and image objects for(class="type">int i = class="num">0; i < totalSymbols; i++) class=class="str">"cmt">//--- Iterate through all symbols { class=class="str">"cmt">// Symbol line(first line) class="type">class="kw">string symbolName = dashboardName + "_Symbol_" + IntegerToString(i); class=class="str">"cmt">//--- Define symbol object name class="type">class="kw">double symbolXPos = (i * SymbolHorizontalSpacing) - symbolOffset; class=class="str">"cmt">//--- Calculate symbol x-position if(symbolXPos < -SymbolHorizontalSpacing) symbolXPos += totalWidthSymbol; class=class="str">"cmt">//--- Wrap around if off-screen createText(symbolName, StringFormat("%-10s", symbolArray[i]), (class="type">int)symbolXPos, Y_Position, FontColor, SymbolFontSize, SymbolFont); class=class="str">"cmt">//--- Update symbol text ObjectSetInteger(class="num">0, symbolName, OBJPROP_HIDDEN, symbolXPos > rightEdge || symbolXPos < class="num">0); class=class="str">"cmt">//--- Hide if off-screen class=class="str">"cmt">// Ask line(second line) class="type">class="kw">string askName = dashboardName + "_Ask_" + IntegerToString(i); class=class="str">"cmt">//--- Define ask object name class="type">class="kw">double askXPos = (i * AskHorizontalSpacing) - askOffset; class=class="str">"cmt">//--- Calculate ask x-position if(askXPos < -AskHorizontalSpacing) askXPos += totalWidthAsk; class=class="str">"cmt">//--- Wrap around if off-screen createText(askName, StringFormat("%.5f", prices[i].ask), (class="type">int)askXPos, Y_Position + SymbolFontSize + class="num">2, clrMagenta, AskFontSize, AskFont); class=class="str">"cmt">//--- Update ask text ObjectSetInteger(class="num">0, askName, OBJPROP_HIDDEN, askXPos > rightEdge || askXPos < class="num">0); class=class="str">"cmt">//--- Hide if off-screen class=class="str">"cmt">// Spread line(third line) if(ShowSpread) class=class="str">"cmt">//--- Check if spread display is enabled { class="type">class="kw">string spreadName = dashboardName + "_Spread_" + IntegerToString(i); class=class="str">"cmt">//--- Define spread object name class="type">class="kw">double spreadXPos = (i * SpreadHorizontalSpacing) - spreadOffset; class=class="str">"cmt">//--- Calculate spread x-position if(spreadXPos < -SpreadHorizontalSpacing) spreadXPos += totalWidthSpread; class=class="str">"cmt">//--- Wrap around if off-screen createText(spreadName, StringFormat("%.1f", prices[i].spread), (class="type">int)spreadXPos, Y_Position + SymbolFontSize + class="num">2 + AskFontSize + class="num">2, clrAqua, SpreadFontSize, SpreadFont); class=class="str">"cmt">//--- Update spread text
◍ 仪表盘板块的环绕隐藏与坐标排布
这段逻辑管的是 MT5 图表上那个多品种仪表盘的每个板块(图片、货币名、涨跌幅、箭头、买价)怎么随图表宽度滚动。核心思路是:算出板块横坐标 sectionXPos,若它小于一个负间距就整体加 totalWidthSection 实现左出右进的环绕;一旦超出右边界或小于 0,立刻用 OBJPROP_HIDDEN 藏掉,避免对象堆在可视区外吃资源。 具体坐标依赖一个链式偏移:Y 方向先叠 SymbolFontSize+2 与 AskFontSize,若开了点差显示再叠 SpreadFontSize+14,否则只补 14 像素行距。货币名固定在图片右移 35 像素处,涨跌幅再右移 105 像素,箭头在货币名下一行同 35 像素偏移并用 Wingdings 字体画方向符。 你在 EA 里改 SectionHorizontalSpacing 就能压扁或拉长板块间距;把 rightEdge 换成 ChartGetInteger(0,CHART_WIDTH_IN_PIXELS) 实时值,隐藏触发会更跟手。外汇与贵金属品种波动大,这类悬浮对象在快速翻页时可能闪跳,建议在实盘前用策略测试器先跑一轮可见性逻辑。
ObjectSetInteger(class="num">0, spreadName, OBJPROP_HIDDEN, spreadXPos > rightEdge || spreadXPos < class="num">0); class=class="str">"cmt">//--- Hide if off-screen } class=class="str">"cmt">// Section(Image, Currency, Percent Change, Arrow, Bid Price) class="type">class="kw">double sectionXPos = (i * SectionHorizontalSpacing) - sectionOffset; class=class="str">"cmt">//--- Calculate section x-position if(sectionXPos < -SectionHorizontalSpacing) sectionXPos += totalWidthSection; class=class="str">"cmt">//--- Wrap around if off-screen class=class="str">"cmt">// Image(left) class="type">class="kw">string imageName = dashboardName + "_Image_" + IntegerToString(i); class=class="str">"cmt">//--- Define image object name ObjectSetInteger(class="num">0, imageName, OBJPROP_XDISTANCE, (class="type">int)sectionXPos); class=class="str">"cmt">//--- Update image x-coordinate ObjectSetInteger(class="num">0, imageName, OBJPROP_YDISTANCE, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14)); class=class="str">"cmt">//--- Update image y-coordinate ObjectSetInteger(class="num">0, imageName, OBJPROP_HIDDEN, sectionXPos > rightEdge || sectionXPos < class="num">0); class=class="str">"cmt">//--- Hide if off-screen class=class="str">"cmt">// Currency(top, right of image) class="type">class="kw">string currencyName = dashboardName + "_Currency_" + IntegerToString(i); class=class="str">"cmt">//--- Define currency object name createText(currencyName, StringFormat("%-10s", symbolArray[i]), (class="type">int)sectionXPos + class="num">35, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14), FontColor, SectionFontSize, "Arial Bold"); class=class="str">"cmt">//--- Update currency text class=class="str">"cmt">// Percent Change(next to currency, horizontal) class="type">class="kw">string percentChangeName = dashboardName + "_PercentChange_" + IntegerToString(i); class=class="str">"cmt">//--- Define percent change object name class="type">class="kw">string percentText = prices[i].percent_change >= class="num">0 ? StringFormat("+%.2f%%", prices[i].percent_change) : StringFormat("%.2f%%", prices[i].percent_change); class=class="str">"cmt">//--- Format percent change createText(percentChangeName, percentText, (class="type">int)sectionXPos + class="num">105, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14), prices[i].percent_color, SectionFontSize, SectionFont); class=class="str">"cmt">//--- Update percent change text class=class="str">"cmt">// Arrow(below currency, right of image, Wingdings) class="type">class="kw">string arrowName = dashboardName + "_Arrow_" + IntegerToString(i); class=class="str">"cmt">//--- Define arrow object name createText(arrowName, prices[i].arrow_char, (class="type">int)sectionXPos + class="num">35, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14) + SectionFontSize + class="num">2, prices[i].arrow_color, SectionFontSize, "Wingdings"); class=class="str">"cmt">//--- Update arrow text class=class="str">"cmt">// Bid Price(next to arrow, horizontal)
「滚动偏移与跨日开盘价的定时器接管」
面板里的 bid 文本对象名由前缀、固定串和索引拼出,位置 Y 值根据是否显示点差在 SymbolFontSize+2+AskFontSize+14 与再叠加 SpreadFontSize+14 之间切换,说明布局高度是条件驱动的。 四个滚动偏移量都用 fmod 对各自总宽取余:symbolOffset 每次加 SymbolScrollSpeed 后对 totalWidthSymbol 取模,ask/spread/section 同理。这样能制造无缝循环滚动的视觉效果,改 Speed 参数即可调快慢。 OnTimer 里先用 TimeCurrent()/86400 算当前「天序号」,若大于 lastDay 就遍历全部品种用 iOpen(symbolArray[i], PERIOD_D1, 0) 重写 daily_open,随后更新 lastDay。外汇与贵金属为高杠杆品种,Daily Open 重置逻辑只反映日历日切换,不预示任何方向。 定时器由 EventSetMillisecondTimer(UpdateInterval) 拉起,周期以毫秒计;每次触发先 UpdateBackground 防 resize 变形,再 UpdateDashboard 重绘。想验证滚动是否顺滑,把 UpdateInterval 从默认调小到 50 看 CPU 占用变化。
class="type">class="kw">string bidName = dashboardName + "_Bid_" + IntegerToString(i); class=class="str">"cmt">//--- Define bid object name createText(bidName, StringFormat("%.5f", prices[i].bid), (class="type">int)sectionXPos + class="num">50, Y_Position + (ShowSpread ? SymbolFontSize + class="num">2 + AskFontSize + class="num">2 + SpreadFontSize + class="num">14 : SymbolFontSize + class="num">2 + AskFontSize + class="num">14) + SectionFontSize + class="num">2, prices[i].bid_color, SectionFontSize, SectionFont); class=class="str">"cmt">//--- Update bid price text } class=class="str">"cmt">//--- Increment offsets for scrolling effect symbolOffset = fmod(symbolOffset + SymbolScrollSpeed, totalWidthSymbol); class=class="str">"cmt">//--- Update symbol line offset askOffset = fmod(askOffset + AskScrollSpeed, totalWidthAsk); class=class="str">"cmt">//--- Update ask line offset spreadOffset = fmod(spreadOffset + SpreadScrollSpeed, totalWidthSpread); class=class="str">"cmt">//--- Update spread line offset sectionOffset = fmod(sectionOffset + SectionScrollSpeed, totalWidthSection); class=class="str">"cmt">//--- Update section offset class=class="str">"cmt">//--- Redraw chart ChartRedraw(); class=class="str">"cmt">//--- Refresh chart display } class=class="str">"cmt">//--- Set timer EventSetMillisecondTimer(UpdateInterval); class=class="str">"cmt">//--- Set timer for updates class=class="str">"cmt">//--- Initialize last day lastDay = TimeCurrent() / class="num">86400; class=class="str">"cmt">//--- Set current day for daily open tracking class="type">void OnTimer() { class=class="str">"cmt">//--- Check for new day to update daily open class="type">class="kw">datetime currentDay = TimeCurrent() / class="num">86400; class=class="str">"cmt">//--- Calculate current day if(currentDay > lastDay) class=class="str">"cmt">//--- Check if new day { for(class="type">int i = class="num">0; i < totalSymbols; i++) class=class="str">"cmt">//--- Iterate through symbols { prices[i].daily_open = iOpen(symbolArray[i], PERIOD_D1, class="num">0); class=class="str">"cmt">//--- Update daily open price } lastDay = currentDay; class=class="str">"cmt">//--- Update last day } class=class="str">"cmt">//--- Update background size in case chart is resized UpdateBackground(); class=class="str">"cmt">//--- Update background dimensions class=class="str">"cmt">//--- Update dashboard display UpdateDashboard(); class=class="str">"cmt">//--- Update dashboard visuals } class="type">void UpdatePrices() {
逐tick刷色与日变幅的计算落点
多品种盯盘面板的核心循环,就是拿上一tick的 bid 和这一tick的 bid 做比较,决定箭头和文字颜色。下面这段代码跑在 for 循环里,totalSymbols 是你要监控的货币对或贵金属个数,symbolArray 存的是品种名数组。 价格取数先调 SymbolInfoDouble 拿 SYMBOL_BID 和 SYMBOL_ASK。若返回 0,说明该品种当前无报价或权限不足,直接 LogError 并 continue 跳到下一个,避免把脏数据写进面板。外汇与贵金属报价受流动性影响,0 值在非农前后出现概率会明显升高,需留意外盘休市时段。 bid 大于 prev_bid 时箭头用字符 236(▲)、着 UpColor;小于则用 238(▼)、着 DownColor;相等或首tick用默认色和 236。注意 prev_bid 为 0 时强制走默认分支,这是防止开盘首帧误染色的保护。 日变幅按 (bid - daily_open) / daily_open * 100 算,daily_open 为 0 时强制返回 0;变幅 ≥0 用 UpColor,否则 DownColor。把这套逻辑直接贴进 EA 的 OnTick,就能在 MT5 上验证多品种面板的实时刷色效果。
for(class="type">int i = class="num">0; i < totalSymbols; i++) class=class="str">"cmt">//--- Iterate through all symbols { class="type">class="kw">double bid = SymbolInfoDouble(symbolArray[i], SYMBOL_BID); class=class="str">"cmt">//--- Retrieve current bid price class="type">class="kw">double ask = SymbolInfoDouble(symbolArray[i], SYMBOL_ASK); class=class="str">"cmt">//--- Retrieve current ask price class=class="str">"cmt">//--- Validate prices if(bid == class="num">0 || ask == class="num">0) class=class="str">"cmt">//--- Check for invalid prices { LogError("UpdatePrices: Failed to retrieve prices for " + symbolArray[i]); class=class="str">"cmt">//--- Log price retrieval failure class="kw">continue; class=class="str">"cmt">//--- Skip to next symbol } class=class="str">"cmt">//--- Update class="type">class="kw">color and arrow based on price change(tick-to-tick for bid and arrow) if(bid > prices[i].prev_bid && prices[i].prev_bid != class="num">0) class=class="str">"cmt">//--- Check if bid increased { prices[i].bid_color = UpColor; class=class="str">"cmt">//--- Set bid class="type">class="kw">color to up class="type">class="kw">color prices[i].arrow_char = CharToString(class="num">236); class=class="str">"cmt">//--- Set up arrow character prices[i].arrow_color = ArrowUpColor; class=class="str">"cmt">//--- Set arrow to up class="type">class="kw">color } else if(bid < prices[i].prev_bid && prices[i].prev_bid != class="num">0) class=class="str">"cmt">//--- Check if bid decreased { prices[i].bid_color = DownColor; class=class="str">"cmt">//--- Set bid class="type">class="kw">color to down class="type">class="kw">color prices[i].arrow_char = CharToString(class="num">238); class=class="str">"cmt">//--- Set down arrow character prices[i].arrow_color = ArrowDownColor; class=class="str">"cmt">//--- Set arrow to down class="type">class="kw">color } else class=class="str">"cmt">//--- Handle no change or first tick { prices[i].bid_color = FontColor; class=class="str">"cmt">//--- Set bid class="type">class="kw">color to class="kw">default prices[i].arrow_char = CharToString(class="num">236); class=class="str">"cmt">//--- Set class="kw">default up arrow prices[i].arrow_color = FontColor; class=class="str">"cmt">//--- Set arrow to class="kw">default class="type">class="kw">color } class=class="str">"cmt">//--- Calculate daily percentage change prices[i].percent_change = prices[i].daily_open != class="num">0 ? ((bid - prices[i].daily_open) / prices[i].daily_open) * class="num">100 : class="num">0; class=class="str">"cmt">//--- Compute percentage change prices[i].percent_color = prices[i].percent_change >= class="num">0 ? UpColor : DownColor; class=class="str">"cmt">//--- Set percent class="type">class="kw">color based on change class=class="str">"cmt">//--- Update data prices[i].bid = bid; class=class="str">"cmt">//--- Store current bid prices[i].ask = ask; class=class="str">"cmt">//--- Store current ask }
◍ 实时刷新与退出清理的实现落点
在 OnTick 里直接调 UpdatePrices(),意味着每个报价到来都会重算各品种 spread 与 prev_bid,MT5 上 EURUSD 在常规行情下每秒可能触发 5~20 次 tick,图形对象随之连续重绘。 OnDeinit 用倒序遍历 objManager 删除自建对象,先 ObjectFind 确认存在再 ObjectDelete,失败就记错误码。最后 EventKillTimer 停掉定时器,避免残留句柄。 把下面这段直接塞进 EA 框架就能在 MT5 验证:OnTick 持续推价格、OnDeinit 干净拆场。外汇与贵金属点差跳动频繁,实盘跑这套逻辑需注意高频重绘带来的资源占用风险。
prices[i].spread = (ask - bid) * MathPow(class="num">10, SymbolInfoInteger(symbolArray[i], SYMBOL_DIGITS)); class=class="str">"cmt">//--- Calculate spread prices[i].prev_bid = bid; class=class="str">"cmt">//--- Update previous bid } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- Update prices on every tick for live changes UpdatePrices(); class=class="str">"cmt">//--- Update symbol prices } 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">//--- Clean up objects for(class="type">int i = objManager.Total() - class="num">1; i >= class="num">0; i--) class=class="str">"cmt">//--- Iterate through all managed objects { class="type">class="kw">string name = objManager.At(i); class=class="str">"cmt">//--- Get object name if(ObjectFind(class="num">0, name) >= class="num">0) class=class="str">"cmt">//--- Check if object exists { if(!ObjectDelete(class="num">0, name)) class=class="str">"cmt">//--- Delete object LogError("OnDeinit: Failed to class="kw">delete object: " + name + ", Error: " + IntegerToString(GetLastError())); class=class="str">"cmt">//--- Log deletion failure } objManager.Delete(i); class=class="str">"cmt">//--- Remove object from manager } EventKillTimer(); class=class="str">"cmt">//--- Stop timer }
「别急着下结论」
这套滚动行情条把买价、点差和日内涨跌幅压进一条横向滚动带,靠 SymbolData 结构管和 UpdateDashboard、UpdatePrices 两个函数刷新,MT5 里同时盯十几个品种不再靠切窗口。它自带字体、颜色、速度参数,波动一来视觉上先撞你一眼,比看静态报价表反应快半拍。 外汇和贵金属杠杆高、跳空频繁,这种工具只解决“看见”的问题,不替你判断方向,实盘里该错过的还是可能错过。 真要落地,把附带的 ROLLING_TICKER_TIMER_EA.mq5 拖进 MT5 编辑器编译,先调 ScrollSpeed 到你能跟上的节奏,再按品种波动改颜色阈值,剩下的就是盯盘时少分心。