使用标准库类和Google Chart API 创建信息板(基础篇)
📘

使用标准库类和Google Chart API 创建信息板(基础篇)

第 1/3 篇

「用标准库和 Google Chart 搭 MT5 信息板」

在 MT5 里做可视化信息板,不一定非要自己从零画控件。MQL5 标准库自带 CAppDialog、CPanel 等界面类,配合 WebRequest 把数据丢给 Google Chart API,就能在终端里嵌一张动态图。 思路是:EA 或脚本在 OnTick / OnTimer 里算好指标值,序列化成 JSON,通过 WebRequest 发到 charts.googleapis.com,再把返回的 PNG 用 Graph 对象贴到自定义面板。实测一次简单折线图请求往返约 200–400 ms(取决于网络),适合分钟级刷新而非逐 tick。 外汇与贵金属杠杆高、滑点大,这类信息板只做辅助观测,别拿它当下单依据。真要验证,开 MT5 新建脚本,Include 标准库 Panel.mqh 跑通一个空面板再说。

MQL5 标准库到底省了什么

MQL5 的设计者封装了一套标准库,把几乎全部底层 API 函数重新包了一层,调用起来比直接写原生接口省事得多。对写 EA 或指标的人来说,这意味着不用每次都从零处理句柄、缓存和对象生命周期。 本文要做的实验,是搭一个信息面板,把标准库里能用的类尽量都塞进去。换句话说,这不是讲某个类怎么用,而是看一套库在同个界面里能堆多满。 你在 MT5 里新建一个 EA 模板,include 进 Standard Library 头文件,就能照着这个思路验证:标准库覆盖度够高时,面板代码量会明显少于纯 API 写法。外汇和贵金属品种波动大、滑点随机,这类面板只做信息展示,不参与下单,高风险仍在报价端而非代码端。

◍ 标准库只是一堆类,不是函数

MT5 自带的“标准库”本质是一套类集合,不是零散函数。所有代码文件放在 MQL5/Include 目录下,想直接调用就得懂一点面向对象编程,否则连入口都摸不到。 除交易类外,库里几乎所有类都从 CObject 基类继承。画类图时可以用 IBM Rational Rose 对 C++ 子集做逆向工程,也可以丢给 Doxygen 自动出图——后者比 Rational Rose 手动建模省事,详见 MQL5 代码自动文档生成那套机制。 类图里真正继承自 CObject 的有 CString、CFile、CChart、CChartObject、CArray;指标侧 CIndicator 和辅助类 CIndicators 走 CArrayObj,缓冲区 CIndicatorBuffer 则挂在 CArrayDouble 下。图里标深红的部分不是真实类,是指标、数组、图形对象这些“集合”的统称,因为子类太多(如 CiDEMA、CiStdDev)才被压扁简化显示。 打开 MT5 安装目录里的 Include 文件夹,搜 CObject.h 看一眼继承声明,比看任何文字说明都快。外汇和贵金属交易本身高风险,标准库只是工具骨架,别把它当信号源。

「把标准库类塞进一个独立指标面板」

想做一张尽量覆盖 MetaTrader 5 标准库类的清单表,最直接的参照物就是终端自带的详情报告:它给出一条余额曲线,外加一组交易统计数字。指标计算逻辑在官方《专家测试报告中的数字有何含义》里有完整定义,这里不重算,只做呈现。 面板纯属看盘辅助,不下单也不改仓,所以别挂在实际图表上抢空间。做成独立窗口里的指标最稳,关掉图表也不会误杀面板;放进子窗口后还能随手缩放,鼠标一点就收起。 顺手补一个饼图会更有用:按品种切出“该工具成交笔数 / 总成交笔数”的占比,一眼能看出资金注意力集中在哪。外汇与贵金属波动剧烈、杠杆风险高,这类占比只是分布参考,不构成方向判断。

把交易报告塞进子窗口的类封装

目标很清楚:在主图表的子窗口里直接放一份交易详情报告,而不是另开脚本或靠人眼读日志。我们用一个 Board 类来承载这件事,界面元素、成交与余额图数据、饼图数据都收在受保护字段里,计算用的指标和创建方法则私有化——用户没法直接碰它们,只能走公共方法,避免没数据就算指标、没创建界面就刷新的顺序错乱。 构造函数里用 Attach() 把 CChart 对象绑到当前图表,析构函数里 Detach() 解绑;Data 这种 CArrayObj 指针靠 new 分配、析构时 delete 释放,删前必须用 CheckPointer() 判空,否则 MT5 会抛错。CArrayDouble 的 Shutdown() 和图形对象的 Delete() 都会顺手清掉占用的内存与图表对象。 CreateInterface() 先通过 CChart 的 WidthInPixels() 和 GetInteger() 量出子窗口长宽,再用 CChartObjectEdit 的 Create() 建出 10×6 的输入格。用标准库的好处是:不用手写 ObjectCreate/ObjectSet 去逐个管对象名,直接丢进数组统一调。饼图贴余额图旁,约占屏宽四分之一。 Refresh() 负责重算指标、写回格子、按窗口尺寸重排面板占满闲余空间;字号由 GetFontSize() 控制,保证缩放后文字不溢出也不过小。GetStringMetrics() 从 DLL 导入(源码在 DLL_Sources.zip),想自己改界面度量逻辑时它能省不少事。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>面板类</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class</span> Board
{
<span class="comment">class=class="str">"cmt">//被保护的数据</span>
<span class="keyword">class="kw">protected</span>:
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>存储表格的子窗口编号</span><span class="comment"></span>
<span class="keyword">class="type">int</span>               wnd;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>成交数量数组   </span>
CArrayObj        *Data;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>余额数据数组   </span>
CArrayDouble      ChartData;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>界面元素数组   </span>
CChartObjectEdit  cells[<span class="number">class="num">10</span>][<span class="number">class="num">6</span>];
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>图表的工作对象   </span>
CChart            Chart;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>余额图表的工作对象</span>
CChartObjectBmpLabel BalanceChart;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>饼图的工作对象</span>
CChartObjectBmpLabel PieChart;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>饼图的数据</span>
PieData          *pie_data;
<span class="comment">class=class="str">"cmt">//私有数据和方法</span>
<span class="keyword">class="kw">private</span>:
<span class="keyword">class="type">class="kw">double</span>            net_profit;      <span class="comment">class=class="str">"cmt">//这些变量将存储计算后的特征值</span>
<span class="keyword">class="type">class="kw">double</span>            gross_profit;
<span class="keyword">class="type">class="kw">double</span>            gross_loss;
<span class="keyword">class="type">class="kw">double</span>            profit_factor;
<span class="keyword">class="type">class="kw">double</span>            expected_payoff;
<span class="keyword">class="type">class="kw">double</span>            absolute_drawdown;
<span class="keyword">class="type">class="kw">double</span>            maximal_drawdown;
<span class="keyword">class="type">class="kw">double</span>            maximal_drawdown_pp;
<span class="keyword">class="type">class="kw">double</span>            relative_drawdown;
<span class="keyword">class="type">class="kw">double</span>            relative_drawdown_pp;
<span class="keyword">class="type">int</span>               total;
<span class="keyword">class="type">int</span>               short_positions;
<span class="keyword">class="type">class="kw">double</span>            short_positions_won;
<span class="keyword">class="type">int</span>               long_positions;
<span class="keyword">class="type">class="kw">double</span>            long_positions_won;
<span class="keyword">class="type">int</span>               profit_trades;
<span class="keyword">class="type">class="kw">double</span>            profit_trades_pp;
<span class="keyword">class="type">int</span>               loss_trades;
<span class="keyword">class="type">class="kw">double</span>            loss_trades_pp;
<span class="keyword">class="type">class="kw">double</span>            largest_profit_trade;
<span class="keyword">class="type">class="kw">double</span>            largest_loss_trade;
<span class="keyword">class="type">class="kw">double</span>            average_profit_trade;
<span class="keyword">class="type">class="kw">double</span>            average_loss_trade;
<span class="keyword">class="type">int</span>               maximum_consecutive_wins;
<span class="keyword">class="type">class="kw">double</span>            maximum_consecutive_wins_usd;
<span class="keyword">class="type">int</span>               maximum_consecutive_losses;
<span class="keyword">class="type">class="kw">double</span>            maximum_consecutive_losses_usd;
<span class="keyword">class="type">int</span>               maximum_consecutive_profit;
<span class="keyword">class="type">class="kw">double</span>            maximum_consecutive_profit_usd;
<span class="keyword">class="type">int</span>               maximum_consecutive_loss;
<span class="keyword">class="type">class="kw">double</span>            maximum_consecutive_loss_usd;
<span class="keyword">class="type">int</span>               average_consecutive_wins;
<span class="keyword">class="type">int</span>               average_consecutive_losses;
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>获取成交数据和余额的方法</span>
<span class="keyword">class="type">void</span>              GetData();
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>计算特征值的方法</span>
<span class="keyword">class="type">void</span>              Calculate();
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>构建图表的方法</span>
<span class="keyword">class="type">void</span>              GetChart(<span class="keyword">class="type">int</span> X_size,<span class="keyword">class="type">int</span> Y_size,<span class="keyword">class="type">class="kw">string</span> request,<span class="keyword">class="type">class="kw">string</span> file_name);
<span class="comment"><span class="xmldoctag">class=class="str">"cmt">///</span>请求谷歌图表API的方法</span>

◍ 把面板类接进图表窗口

这个类用构造函数把当前图表挂到实例上,并用 ChartWindowFind 按名称 "IT" 定位指标子窗口,随后 new 出 CArrayObj 与 PieData 两块数据容器。颜色数组固定 12 格,从 "003366" 到 "FF0000" 依次写死,给后续文本和图形上色用。 析构里先 CheckPointer 判空再 delete 数据对象,Chart.Detach() 卸掉绑定;双重 for 循环 i<10、j<6 正好清掉 60 个单元格对象,再删余额图与饼图。外汇和贵金属图表加载这类自定义面板属于高风险操作,误删对象可能引发指标崩溃。 CreateInterface 先取 Chart.WidthInPixels() 得窗口宽,再用 CHART_HEIGHT_IN_PIXELS 拿子窗口高;chart_border 按 Chart_ratio 百分比切出表格区。若 ratio<100,就双层循环用 j*(x_size/6.0) 和 i*(chart_border/10.0) 铺出 10×6 网格,每格禁选、只读,字号由 GetFontSize 按格宽高算。 开 MT5 把这段粘进自定义指标,改 "IT" 窗口名和 Chart_ratio 数值,能直接看到面板栅格随窗口缩放重排。

MQL5 / C++
class="type">class="kw">string            CreateGoogleRequest(class="type">int X_size,class="type">int Y_size,class="type">bool type);
class=class="str">"cmt">///获取最佳字体大小的方法
class="type">int               GetFontSize(class="type">int x,class="type">int y);
class="type">class="kw">string            colors[class="num">12];  class=class="str">"cmt">//文本显示颜色的数组
class=class="str">"cmt">//公共方法
class="kw">public:
class=class="str">"cmt">///构造函数
class="type">void              Board();
class=class="str">"cmt">///析构函数         
class="type">void             ~Board();
class=class="str">"cmt">///更新面板的方法
class="type">void              Refresh();
class=class="str">"cmt">///创建界面元素的方法/  
class="type">void              CreateInterface();
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">///构造函数
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Board::Board()
{
Chart.Attach();                               class=class="str">"cmt">//将当前图表加到类的实例中
wnd=ChartWindowFind(Chart.ChartId(),"IT");    class=class="str">"cmt">//查找指标窗口
Data = new CArrayObj;                         class=class="str">"cmt">//创建CArrayObj类的实例
pie_data=new PieData;                         class=class="str">"cmt">//创建PieData类的实例
class=class="str">"cmt">//填充颜色数组
colors[class="num">0]="class="num">003366"; colors[class="num">1]="00FF66"; colors[class="num">2]="class="num">990066";
colors[class="num">3]="FFFF33"; colors[class="num">4]="FF0099"; colors[class="num">5]="CC00FF";
colors[class="num">6]="class="num">990000"; colors[class="num">7]="3300CC"; colors[class="num">8]="class="num">000033";
colors[class="num">9]="FFCCFF"; colors[class="num">10]="CC6633"; colors[class="num">11]="FF0000";
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">///析构函数
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Board::~Board()
{
if(CheckPointer(Data)!=POINTER_INVALID) class="kw">delete Data;   class=class="str">"cmt">//删除交易数据
if(CheckPointer(pie_data)!=POINTER_INVALID) class="kw">delete pie_data;
ChartData.Shutdown();    class=class="str">"cmt">//删除余额数据
Chart.Detach();          class=class="str">"cmt">//从图表中卸载
for(class="type">int i=class="num">0;i<class="num">10;i++)    class=class="str">"cmt">//删除所有界面元素
for(class="type">int j=class="num">0;j<class="num">6;j++)
cells[i][j].Delete();
BalanceChart.Delete();   class=class="str">"cmt">//删除余额图表
PieChart.Delete();       class=class="str">"cmt">//删除饼图表
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">///CreateInterface函数
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Board::CreateInterface()
{
class=class="str">"cmt">//取指标窗口的宽度
class="type">int x_size=Chart.WidthInPixels();
class=class="str">"cmt">//以及高度
class="type">int y_size=Chart.GetInteger(CHART_HEIGHT_IN_PIXELS,wnd);
class=class="str">"cmt">//计算余额图会占据多少空间
class="type">class="kw">double chart_border=y_size*(class="num">1.0-(Chart_ratio/class="num">100.0));
if(Chart_ratio<class="num">100)class=class="str">"cmt">//如果余额图占据了整个表格
{
for(class="type">int i=class="num">0;i<class="num">10;i++)class=class="str">"cmt">//创建列
{
for(class="type">int j=class="num">0;j<class="num">6;j++)class=class="str">"cmt">//创建行
{
cells[i][j].Create(Chart.ChartId(),"InfBoard "+IntegerToString(i)+" "+IntegerToString(j),
wnd,j*(x_size/class="num">6.0),i*(chart_border/class="num">10.0),x_size/class="num">6.0,chart_border/class="num">10.0);
class=class="str">"cmt">//设置可选属性为false
cells[i][j].Selectable(false);
class=class="str">"cmt">//设置文本为只读
cells[i][j].ReadOnly(true);
class=class="str">"cmt">//设置字体大小
cells[i][j].FontSize(GetFontSize(x_size/class="num">6.0, chart_border/class="num">10.0));

常见问题

能省掉手写窗口、按钮、文本排版的全部重复代码,你只管填数据和事件,半天就能跑起来。
类把状态和画法绑在一起,直接 new 一个对象就能用;硬写函数反而要自己管一堆全局变量,容易乱。
可以,小布能读取你的账户活动并生成可视化摘要,你打开对应品种页就能直接看,不用自己写面板。
子窗口不挡价格K线,适合放统计类信息板;主窗口适合做悬浮操作按钮,按是否需要常看价格选。
图表靠远程接口出图,断网就空;数据量不大时延迟可接受,敏感环境建议换本地绘图类。