利用标准库类创建您自己的市场报价·综合运用
(3/3)·从基类扩展到期现报价面板,把标准库类串成一套可复用的图表文本输出方案
信息结构标题的显示 - 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>: <span class="keyword">long</span> chart_id; <span class="keyword">int</span> sub_window; <span class="keyword">long</span> chart_width; <span class="comment">// 图表的像素点数宽度</span> <span class="keyword">long</span> chart_height; <span class="comment">// 图表的像素点数高度</span> <span class="keyword">long</span> chart_width_step; <span class="comment">// 水平方向上网格坐标距离</span> <span class="keyword">long</span> chart_height_step; <span class="comment">// 垂直方向上网格坐标距离</span> <span class="keyword">int</span> columns_number; <span class="comment">// 列数</span> <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 用这类容器管理挂单或信号对象时,注意列表膨胀带来的内存与遍历开销,属于高风险环境下的资源管控点,建议实盘前在策略测试器里跑一遍增删压力。
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 就能跑。外汇和贵金属波动快,这类悬浮表只作辅助盯盘,信号误读可能引发较大回撤,实盘前先在策略测试器验证刷新逻辑。
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 里有完整版,还带了标题配色和表格定位的外部参数可改。
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 里得手动删。
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 标准库里的图形对象类,用面向对象方式拼出二维文本表并不费事,熟练后改个传入品种数或时间窗就能复用。 外汇与贵金属波动剧烈、杠杆风险高,这类行情速览只作盘前扫描,不构成任何方向暗示;真要上实盘,先在策略测试器里跑一遍附件代码确认逻辑再说。