利用标准库类创建您自己的市场报价·综合运用
📊

利用标准库类创建您自己的市场报价·综合运用

(3/3)·从基类扩展到期现报价面板,把标准库类串成一套可复用的图表文本输出方案

含代码示例实战向 第 3/3 篇
不少交易者直接往图表上堆 Label 和 Edit 对象,结果对象一多就失控,拖拽错位、刷新冲突天天发生。标准库自带的链表与基类封装能省掉这些麻烦,但多数人没用到第三层。本篇把前面铺垫的扩展类真正跑成一个市场报价面板。

信息结构标题的显示 - TTitleDisplay 类

在终端屏幕上显示标题的类,如下所示: 创建图形文本对象的基本方法: 输入参数的赋值如下: chart_id - 窗口标识符(0 - 主窗口); window - 子窗口编号(0 - 主); cols - 水平方向图形文本对象最大数量(列数); lines - 垂直方向图形文本对象最大数量(行数); col - 图形对象的水平坐标(从零到 cols - 1 之间变化); row - 图形对象的垂直坐标(从零到 lines - 1 之间变化); TTitleDisplay::SetParams 方法会计算对象与屏幕上位置关联的参数。 实施如下: 这里,我们采用 GetSystemMetrics WinAPI 函数调用来获取当前显示设置。此函数由 user32.dll Windows 系统库导入。 创建信息文本的 TFieldDisplay 类也用类似方法构建。详情请见本文随附的 TextDisplay.mqh 库。 [CODE] <span class="keyword">class</span> TTitleDisplay : <span class="keyword">public</span> CChartObjectLabel { <span class="keyword">protected</span>: &nbsp;&nbsp;<span class="keyword">long</span>&nbsp;&nbsp;&nbsp;&nbsp;chart_id; &nbsp;&nbsp;<span class="keyword">int</span>&nbsp;&nbsp;&nbsp;&nbsp; sub_window; &nbsp;&nbsp;<span class="keyword">long</span>&nbsp;&nbsp;&nbsp;&nbsp;chart_width;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">// 图表的像素点数宽度</span> &nbsp;&nbsp;<span class="keyword">long</span>&nbsp;&nbsp;&nbsp;&nbsp;chart_height;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// 图表的像素点数高度</span> &nbsp;&nbsp;<span class="keyword">long</span>&nbsp;&nbsp;&nbsp;&nbsp;chart_width_step;&nbsp;&nbsp;<span class="comment">// 水平方向上网格坐标距离</span> &nbsp;&nbsp;<span class="keyword">long</span>&nbsp;&nbsp;&nbsp;&nbsp;chart_height_step; <span class="comment">// 垂直方向上网格坐标距离</span> &nbsp;&nbsp;<span class="keyword">int</span>&nbsp;&nbsp;&nbsp;&nbsp; columns_number;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">// 列数</span> &nbsp;&nbsp;<span class="keyword">int

◍ 用 CList 把对象排进可操作的队列

在 MT5 里写多对象管理逻辑时,Include\Arrays\List.mqh 里的 CList 是绕不开的底座类。它继承自 CObject,专门把一组对象收进线性列表,提供增、删、查、遍历和清空的一套方法,后续做计划调度时基本就靠它托住对象生命周期。 往列表里塞对象有两条路:Add 把新节点接在末尾,Insert 则按 index 插到任意位置。删的时候用 Delete(index),它不只把节点从链表摘掉,还会顺手释放 CObject 占的内存——对象是真被销毁,不是只断引用。 查位置用 IndexOf,按对象指针反查索引,从 0 起算,找不到返回 -1;想按位置取对象用 GetNodeAtIndex。遍历则靠 GetFirstNode 拿头节点,再配合 GetNextNode / GetPrevNode 双向走,GetLastNode 直接取尾。Clear 一键清表并释放全部对象内存。 下面这段是 CList 核心方法签名,逐行对应上面说的动作:Add 返回新元素索引,Insert 返回插入后索引,Delete 成功布尔,IndexOf 返回索引或 -1,其余 Get* 返回对象指针,Clear 无返回值。 外汇与贵金属 EA 用这类容器管理挂单或信号对象时,注意列表膨胀带来的内存与遍历开销,属于高风险环境下的资源管控点,建议实盘前在策略测试器里跑一遍增删压力。

MQL5 / C++
  class="type">int Add(CObject *new_node);
  class="type">int Insert(CObject *new_node,class="type">int index);
  class="type">bool  Delete(class="type">int index);
  class="type">int        IndexOf(CObject* node);
  CObject*  GetNodeAtIndex(class="type">int index);
  CObject*  GetFirstNode();
  CObject*  GetPrevNode();
  CObject*  GetNextNode();
  CObject*  GetLastNode();
  class="type">void  Clear();

「用 TableDisplay 在图表上铺文本表」

想在 MT5 图表上把一堆指标读数、状态标签排成整齐的网格,而不是散着漂,就得先搞定表的锚点。TableDisplay 这个类继承自 CList,本质是把图形文本对象按行列坐标管起来,表多大、从哪个角固定,都由你定。 往表里塞对象前,必须调一次 SetParams 把三件事交代清楚:图表 ID、子窗口索引、固定角(默认左上)。不设置就直接 Add,对象大概率飞到主图乱叠。 添加内容分两种:AddTitleObject 写带字标题,AddFieldObject 留空字段后面用 SetText 改。两者都要传行列总数和当前格位置,字体默认 Arial 8 号,够小不致挡 K 线。 完整可用库在随文头文件里,直接 include 就能跑。外汇和贵金属波动快,这类悬浮表只作辅助盯盘,信号误读可能引发较大回撤,实盘前先在策略测试器验证刷新逻辑。

MQL5 / C++
class TableDisplay : class="kw">public CList
{
class="kw">protected:
  class="type">long  chart_id;
  class="type">int   sub_window;
class="kw">public:
  class="type">void  SetParams(class="type">long _chart_id, class="type">int _window, ENUM_BASE_CORNER _corner = CORNER_LEFT_UPPER);
  class="type">int   AddTitleObject(class="type">int _cols, class="type">int _lines, class="type">int _col, class="type">int _row, 
                      class="type">class="kw">string _title, class="type">class="kw">color _color, class="type">class="kw">string _fontname = "Arial", class="type">int _fontsize = class="num">8);
  class="type">int   AddFieldObject(class="type">int _cols, class="type">int _lines, class="type">int _col, class="type">int _row, 
                          class="type">class="kw">color _color, class="type">class="kw">string _fontname = "Arial", class="type">int _fontsize = class="num">8);
  class="type">bool  SetColor(class="type">int _index, class="type">class="kw">color _color);
  class="type">bool  SetFont(class="type">int _index, class="type">class="kw">string _fontname, class="type">int _fontsize);
  class="type">bool  SetText(class="type">int _index, class="type">class="kw">string _text);
class="kw">public:
  class="type">void  TableDisplay();
  class="type">void  ~TableDisplay();
};

多品种报价表的指标落地

想在 MT5 图表上同时盯 8 个货币对的即时报价,用指标形式搭一张屏幕表格最省事。核心思路是先引 TextDisplay 库,再用数组把品种名和纵向坐标(从 0 排起)配对,最后交给 TableDisplay 对象统一管文本和标题。 初始化阶段(OnInit)要分两步走:先 TableDisplay::AddFieldObject 把信息字段铺好,再 AddTitleObject 挂标题。分开做是因为标题数和字段数通常不等,且字段按 0 到 NUMBER-1 的连续索引排,后续更新时直接按号访问更快。 动态刷新放在 OnTimer 里,本例设了 1 秒定时器。每次触发先 SymbolInfoTick 抓 bid,比对 tick.time 和上次存的时间,不同才认定有新跳动;再拿当前 bid 和上一跳 bid 比:高于前值染绿、低于染红、相等留黄。这样一眼能看出每个品种报价倾向往上还是往下。 顺手加个点差列也不麻烦:在 NUMBER 之后接着定义字段索引,OnTimer 里补一段算 spread 的代码即可。注意点差字段索引从 NUMBER 值起步(若 NUMBER=0 则从 0 起)。外汇与贵金属波动剧烈、杠杆风险高,这类表格只作盯盘辅助,不构成方向判断依据。 退出时记得 EventKillTimer 并清空表对象,否则残留文本对象和内存不会自动释放。附件 PriceList.mq5 里有完整版,还带了标题配色和表格定位的外部参数可改。

MQL5 / C++
class="macro">#include  <TextDisplay.mqh>
class="macro">#define  NUMBER  class="num">8
class=class="str">"cmt">//---------------------------------------------------------------------
class="type">class="kw">string   names[NUMBER]  = {"EURUSD", "GBPUSD", "AUDUSD", "NZDUSD", "USDCHF", "USDCAD", "USDJPY", "EURJPY"};
class="type">int      coord_y[NUMBER] = {class="num">2,        class="num">3,        class="num">4,        class="num">5,        class="num">6,        class="num">7,        class="num">8,        class="num">9};
TableDisplay  Table1;
class="type">int OnInit()
{
class=class="str">"cmt">//  创建一个表格
  Table1.SetParams(class="num">0, class="num">0);
  for(class="type">int i=class="num">0; i<NUMBER; i++)
  {
    Table1.AddFieldObject(class="num">40, class="num">40, class="num">3, coord_y[i], Yellow);
  }
  for(class="type">int i=class="num">0; i<NUMBER; i++)
  {
    Table1.AddTitleObject(class="num">40, class="num">40, class="num">1, coord_y[i], names[i]+":", White);
  }
  ChartRedraw(class="num">0);
  EventSetTimer(class="num">1);
  class="kw">return(class="num">0);
}
class=class="str">"cmt">//---------------------------------------------------------------------
class="type">class="kw">double    rates[NUMBER];
class="type">class="kw">datetime  times[NUMBER];
class="type">MqlTick   tick;
class=class="str">"cmt">//---------------------------------------------------------------------
class=class="str">"cmt">// OnTimer 事件处理函数
class=class="str">"cmt">//---------------------------------------------------------------------
class="type">void OnTimer()
{
  for(class="type">int i=class="num">0; i<NUMBER; i++)
  {
class=class="str">"cmt">//  获得价格数值:
    ResetLastError();
    if(SymbolInfoTick(names[i], tick) != true)
    {
      Table1.SetText(i,"Err "+DoubleToString(GetLastError(),class="num">0));
      Table1.SetColor(i,Yellow);
      class="kw">continue;
    }
    if(tick.time>times[i])
    {
       Table1.SetText(i, DoubleToString(tick.bid, (class="type">int)(SymbolInfoInteger(names[i], SYMBOL_DIGITS))));
       if(tick.bid>rates[i])
       {
         Table1.SetColor(i, Lime);
       }
       else if(tick.bid<rates[i])
       {
         Table1.SetColor(i, Red);
       }
       else
       {
         Table1.SetColor(i, Yellow);
       }

◍ 把点差换算成点数画到表上

这段收尾代码干了两件事:用 ChartRedraw(0) 强制主图表重绘,把前面攒下的 bid 报价和 tick 时间落进数组;随后按 NUMBER 循环,在坐标 coord_y[i] 处用 Table1.AddFieldObject(40,40,5,...) 铺黄色标记块。 循环里那行 SetText 把 (tick.ask-tick.bid) 除以 SymbolInfoDouble(names[i],SYMBOL_POINT),再用 DoubleToString(...,0) 取整,等于把每个品种的裸点差直接转成「点」数显示,省得人眼去数小数位。 OnDeinit 里先 EventKillTimer() 停掉定时器,Table1.Clear() 清掉所有自建图形对象;不清理的话切周期或移除 EA 会留一屏残影,MT5 里得手动删。

MQL5 / C++
  rates[i] = tick.bid;
  times[i] = tick.time;
  }
}
  ChartRedraw(class="num">0);
}
  for(class="type">int i=class="num">0;i<NUMBER; i++)
  {
    Table1.AddFieldObject(class="num">40, class="num">40, class="num">5, coord_y[i], Yellow);
  }
  Table1.SetText(i+NUMBER, DoubleToString((tick.ask-tick.bid)/SymbolInfoDouble(names[i], SYMBOL_POINT), class="num">0));
class="type">void OnDeinit(class="kw">const class="type">int _reason)
{
  EventKillTimer();
class=class="str">"cmt">//  删除显示的元素:
  Table1.Clear();
}

「一点提醒」

随附的 MarketWatch.mq5 与 MarketWatch.mqh 实现了一个表格式指标,能在图表上列出最多 16 个交易品种的基本参数,并算出指定时间间隔内的价格变动百分比;品种与周期以分号分隔的字符串传入,运行效果可对照原文图 5。 借助 MT5 标准库里的图形对象类,用面向对象方式拼出二维文本表并不费事,熟练后改个传入品种数或时间窗就能复用。 外汇与贵金属波动剧烈、杠杆风险高,这类行情速览只作盘前扫描,不构成任何方向暗示;真要上实盘,先在策略测试器里跑一遍附件代码确认逻辑再说。

把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到由标准库思路生成的实时报价与指标面板,你专注决策就好。

常见问题

它们构成双向链表,让你用 CList 统一管理标题、表格行等图形对象,避免手动记录指针导致的遗漏与冲突。
可在类的 Update 方法里加时间间隔判断,仅当数据变化超阈值才重绘,降低客户端资源占用。
思路一致,都是用标准库图形对象类做封装与链表管理,不过小布在云端做了聚合,不占用本地图表对象数。
Label 可固定坐标不随滚动消失,且能被标准库统一管理生命周期,比裸 Text 更适合做常驻面板。
基础概念在第1篇,类扩展在第2篇,本篇第3篇直接给综合运用与可运行示例。