面向对象法建立多时间表及多货币面板(基础篇)
📘

面向对象法建立多时间表及多货币面板(基础篇)

第 1/3 篇

◍ 用面向对象搭多周期多货币监控面板

在 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,就能看到跨图事件流。外汇与贵金属多货币策略本身杠杆高、滑点风险大,事件漏处理可能让面板误判持仓。

MQL5 / C++
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 是真正的行仓库。外汇与贵金属行情跳空频繁,这类面板对象在实盘里可能因报价中断导致重绘延迟,属正常高风险现象。

MQL5 / C++
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 前缀批量清理图表文本对象,避免重复开图时残留标签——这部分接着写就能跑,属于面板内存自洁的落点。

MQL5 / C++
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前缀开始)

常见问题

用面向对象搭一个多周期多货币监控面板,把多个品种和周期的行对象塞进同一个表格组件,一套界面就能扫全。
骨架用CTable管组件,每行对应一个品种或周期的行情行对象,定时从行情源拉数据重绘,默认按网格参数排布。
可以。小布能按你设定的多品种多周期条件持续巡检,出现信号直接在对应品种页弹出提醒,不用自己守面板。
SpyAgent生成信号对象后调用面板公开方法,把品种、周期、方向等字段写入对应行情行,触发表格重绘。
一种直接传默认网格参数快速建空表,另一种先配行列再挂组件;默认网格适合标准布局,自定义路径适合特殊屏占比。