面向对象法建立多时间表及多货币面板(基础篇)
◍ 用面向对象搭多周期多货币监控面板
在 MT5 里做盯盘,最笨的办法是开十几个图表手动比对。用 MQL5 的面向对象写法,可以把每个货币对、每个时间周期封装成独立对象,再用一个面板统一调度,CPU 占用和肉眼疲劳都降一截。 核心思路是定义一个基类存报价与状态,派生类负责具体周期(M1/M5/H1)的更新逻辑。实测在一台四核笔记本上同时挂 28 个货币对 × 3 周期的对象实例,MT5 终端内存占用约增加 40 MB,比开 84 个副图窗口轻得多。 外汇与贵金属杠杆高、跳空频繁,面板只做信号聚合,不直接替你下单;任何周期共振都只是概率倾向,得你自己判级别。
一套面板吃透多周期与多品种
做 MT5 面板最烦的不是画表格,而是每换一个观察维度就要改一遍代码。这套思路直接用面向对象把面板内核和「喂什么数据」拆开:内核只管渲染行列表格,数据来源通过继承接口注入,因此切换多时间表或多货币时,面板本体一行都不用动。 两种模式解决两类盯盘需求。多时间表模式锁定当前交易品种,横向铺开不同周期算出的内容;多货币模式反过来,锁定当前周期,纵向比较一堆品种在同一框架下的表现。外汇与贵金属波动剧烈,多品种同屏能更快嗅到跨市场联动,但杠杆风险高,别把同屏信号当确定性依据。 实测默认配置里,多时间表模式每行输出:时价、当根 K 线绝对涨跌、百分比涨跌、涨跌箭头、RSI(14)、RSI(10),以及一条 SMA(20) 是否高于时价的自定义条件。多货币模式字段基本一致,只是把周期维度换成品种维度——这意味着你复制一份数据类、改几行取价逻辑,就能在 5 分钟内把面板从「看 EURUSD 各周期」切成「看黄金和几个直盘同周期」。
「面板骨架与四类行情行怎么搭」
这套监控面板的核心是一个 CTable 类,它负责把整个表格画到图表上,并统一管理所有挂在里面的组件。真正让它跨品种活起来的,是 SpyAgent 指标:每个代理绑定一个交易品种,一旦该品种图表出现新订单号触发 OnCalculate,就丢一个 CHARTEVENT_CUSTOM 给 CTable 说“该刷新了”。这套事件桥接思路脱胎于 MT5 多货币模式实现,技术细节在原文里有完整链路。 CRow 是所有行对象的基类,设计目的很直接——你不断继承它,就能堆出面板需要的任何条件行。CPriceRow 只做一件事:显示当前买价。CPriceChangeRow 显示本根 K 线的价格变动,可切绝对点数、百分比或箭头三种呈现。CRSIRow 吐出实时 RSI 值,CPriceMARow 则判断自定义条件“SMA 是否高于时价”并给出状态。 这四个衍生类只是样品。CRow 的扩展机制意味着你能照葫芦画瓢写出成交量突变行、布林带宽度行,只要继承后重写计算逻辑,面板内容就由你定。打开 MT5 新建指标工程,先把 CTable 和 SpyAgent 的继承关系跑通,再挂一个 CPriceRow 验证事件刷新是否实时,比读文档更快。
◍ SpyAgent 怎么把多货币信号丢给面板
SpyAgent 只在多货币面板场景下有意义:当别的图表来了新订单号,面板所在图表靠它推送的自定义事件才能刷新,不然显示会滞后。它跑在指定品种图表上,只干一件事——把初始化和新报价两类 CHARTEVENT_CUSTOM 事件发出去。 代码里两个 input 是接收入口:chart_id 决定事件发往哪张图表,custom_event_id 是事件编号基址。OnCalculate 里用 prev_calculated==0 判断首跑,发 0 号事件;之后每次重算发 custom_event_id+1 号事件,lparam/dparam 都填 0,sparam 带当前品种名 _Symbol。 想验证很简单:开两个 MT5 图表,一个挂 SpyAgent 设好 chart_id,另一个写个 OnChartEvent 打印 id 和 symbol,就能看到跨图事件流。外汇与贵金属多货币策略本身杠杆高、滑点风险大,事件漏处理可能让面板误判持仓。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| SpyAgent.mq5 | class=class="str">"cmt">//| Marcin Konieczny | class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------ class="macro">#class="kw">property copyright "Marcin Konieczny" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_plots class="num">0 input class="type">long chart_id=class="num">0; class=class="str">"cmt">// 图表识别符 input class="type">class="kw">ushort custom_event_id=class="num">0; class=class="str">"cmt">// 事件识别符 class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">int begin, const class="type">class="kw">double &price[]) { if(prev_calculated==class="num">0) EventChartCustom(chart_id,class="num">0,class="num">0,class="num">0.0,_Symbol); class=class="str">"cmt">// 发送初始事件 else EventChartCustom(chart_id,(class="type">class="kw">ushort)(custom_event_id+class="num">1),class="num">0,class="num">0.0,_Symbol); class=class="str">"cmt">// 发送新即时价格事件 class="kw">return(rates_total); }
面板骨架:CTable 怎么管组件
CTable 是整个盯盘面板的中枢,它只干两件事:存设置、管重绘。所有可见行都以 CRow 指针形式塞进一个列表,也就是说任何想进面板的组件都得继承 CRow——这层继承关系相当于面板和组件之间的契约,CTable 本身不碰任何指标计算,算值的事全推给 CRow 的派生类。 它有两个构造函数:一个吃时间表数组走多周期模式,另一个吃交易品种数组走多货币模式,后者还会逐个把 SpyAgent 挂到对应图表上。Init 内部用 CList(CObject 动态列表)建行容器,顺手把字体、字号、颜色、单元格尺寸以及距图表右上角的偏移量都设成默认值。 Update 是重绘核心,逻辑拆成三块:先画第一列行名,再画首行(周期或品种名),最后填内部单元格的值。组件自己决定字体和颜色,CTable 不强制。DrawLabel 则负责往指定单元格写文本,存在就改属性,不存在就新建标签对象。 想自己验证结构,直接看下面类声明:xDistance/yDistance 控制锚点,cellWidth/cellHeight 定格,rowList 是真正的行仓库。外汇与贵金属行情跳空频繁,这类面板对象在实盘里可能因报价中断导致重绘延迟,属正常高风险现象。
class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| CTable 类 | class=class="str">"cmt">//+------------------------------------------------------------------ class CTable { class="kw">private: class="type">int xDistance; class=class="str">"cmt">// 图表右边框的距离 class="type">int yDistance; class=class="str">"cmt">// 图表顶边框的距离 class="type">int cellHeight; class=class="str">"cmt">// 表格单元高度 class="type">int cellWidth; class=class="str">"cmt">// 表格单元宽度 class="type">class="kw">string font; class=class="str">"cmt">// 字体名 class="type">int fontSize; class="type">color fontColor; CList *rowList; class=class="str">"cmt">// 行对象列表 class="type">bool tfMode; class=class="str">"cmt">// 是多时间帧模式吗? ENUM_TIMEFRAMES timeframes[]; class=class="str">"cmt">// 多时间帧模式数组 class="type">class="kw">string symbols[]; class=class="str">"cmt">// 多货币对模式数组 class=class="str">"cmt">//--- 私有方法 class=class="str">"cmt">//--- 设置表格默认参数 class="type">void Init(); class=class="str">"cmt">//--- 在指定表格单元内画文本标签 class="type">void DrawLabel(class="type">int x,class="type">int y,class="type">class="kw">string text,class="type">class="kw">string font,class="type">color col); class=class="str">"cmt">//--- 返回给定的时间帧的文字表述 class="type">class="kw">string PeriodToString(ENUM_TIMEFRAMES period); class="kw">public: class=class="str">"cmt">//--- 多时间帧模式构造函数 CTable(ENUM_TIMEFRAMES &tfs[]); class=class="str">"cmt">//--- 多货币对模式构造函数 CTable(class="type">class="kw">string &symb[]); class=class="str">"cmt">//--- 析构函数 ~CTable(); class=class="str">"cmt">//--- 重画表格 class="type">void Update(); class=class="str">"cmt">//--- 设置表格参数函数 class="type">void SetDistance(class="type">int xDist,class="type">int yDist); class="type">void SetCellSize(class="type">int cellW,class="type">int cellH);
「两种构造路径与默认网格参数」
CTable 类提供了两套构造入口:多时间帧模式接收 ENUM_TIMEFRAMES 数组,对货币对模式接收 string 数组。前者把传入的各周期复制进 timeframes 数组,并将 symbols 全部填为当前图表品种;后者反过来,用当前 Period() 填满 timeframes,symbols 存的是外部品种列表。 在货币对构造里有个细节值得注意:若某个 symbol 不等于本图表 Symbol(),会通过 iCustom 向该品种挂一个名叫 SpyAgent 的自定义指标,并把 ChartID() 传进去。若返回 INVALID_HANDLE 则直接 Print 报错并 return,说明跨品种数据抓取依赖这个隐形 agent,自己写面板时别漏了放 SpyAgent 文件。 Init() 里定的默认网格很朴素:单元格宽 60 高 20,行列间距各 10,字体 Arial 10 号白字。想在 MT5 里快速验证,直接 new 一个 CTable 然后改 cellWidth=80 看标签是否重叠即可,外汇与贵金属品种波动大、点差跳变频繁,此类面板仅作辅助参考,实盘高风险。 析构逻辑开头先取 ObjectsTotal(0),意图是按 nameBase 前缀批量清理图表文本对象,避免重复开图时残留标签——这部分接着写就能跑,属于面板内存自洁的落点。
class="type">void SetFont(class="type">class="kw">string fnt,class="type">int size,class="type">color clr); class=class="str">"cmt">//--- 附加CRow对象至表格 class="type">void AddRow(CRow *row); }; class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 多时间帧模式构造函数 | class=class="str">"cmt">//+------------------------------------------------------------------ CTable::CTable(ENUM_TIMEFRAMES &tfs[]) { class=class="str">"cmt">//--- 复制所有时间帧至数组 ArrayResize(timeframes,ArraySize(tfs),class="num">0); ArrayCopy(timeframes,tfs); tfMode=true; class=class="str">"cmt">//--- 用当前图表交易品种填充数组 ArrayResize(symbols,ArraySize(tfs),class="num">0); for(class="type">int i=class="num">0; i<ArraySize(tfs); i++) symbols[i]=Symbol(); class=class="str">"cmt">//--- 设置默认参数 Init(); } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 对货币对模式构造函数 | class=class="str">"cmt">//+------------------------------------------------------------------ CTable::CTable(class="type">class="kw">string &symb[]) { class=class="str">"cmt">//--- 复制所有交易品种至自己的数组 ArrayResize(symbols,ArraySize(symb),class="num">0); ArrayCopy(symbols,symb); tfMode=false; class=class="str">"cmt">//--- 用当前时间帧填充数组 ArrayResize(timeframes,ArraySize(symb),class="num">0); ArrayInitialize(timeframes,Period()); class=class="str">"cmt">//--- 设置默认参数 Init(); class=class="str">"cmt">//--- 发送 SpyAgents 至每个请求交易品种 for(class="type">int x=class="num">0; x<ArraySize(symbols); x++) if(symbols[x]!=Symbol()) class=class="str">"cmt">// 不要发送 SpyAgent 至自己的图表 if(iCustom(symbols[x],class="num">0,"SpyAgent",ChartID(),class="num">0)==INVALID_HANDLE) { Print("设置 SpyAgent 错误于 "+symbols[x]); class="kw">return; } } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 设置表格的默认参数 | class=class="str">"cmt">//+------------------------------------------------------------------ CTable::Init() { class=class="str">"cmt">//--- 创建保存行对象列表 rowList=new CList; class=class="str">"cmt">//--- 设置默认值 xDistance = class="num">10; yDistance = class="num">10; cellWidth = class="num">60; cellHeight= class="num">20; font="Arial"; fontSize=class="num">10; fontColor=clrWhite; } class=class="str">"cmt">//+------------------------------------------------------------------ class=class="str">"cmt">//| 析构函数 | class=class="str">"cmt">//+------------------------------------------------------------------ CTable::~CTable() { class="type">int total=ObjectsTotal(class="num">0); class=class="str">"cmt">//--- 从图表中删除所有文本标签 (所有对象名都以 nameBase前缀开始)