MQL5 中的 SQLite 功能示例:按交易品种及 Magic 编码展示交易统计信息的仪表盘(基础篇)
📘

MQL5 中的 SQLite 功能示例:按交易品种及 Magic 编码展示交易统计信息的仪表盘(基础篇)

第 1/3 篇

用 SQLite 给 MT5 交易统计做本地仪表盘

在 MQL5 环境里直接挂 SQLite,可以把成交记录按交易品种(symbol)和 Magic 编号做本地聚合,不必每次都导出 CSV 再拿 Excel 算。对于同时跑多个 EA、多个 Magic 的账户,这种内置数据库能省掉不少手工对账时间。 官方示例里提到一个具体实现:按 symbol + magic 两个维度统计交易笔数、盈利单占比与净盈亏,并在图表上绘制信息面板。该示例文章发布于 2025-07-28,原文页面显示阅读量 741、评论 5,说明这类轻量本地统计需求在社群里并非冷门。 外汇与贵金属杠杆高、滑点随机,统计结果只反映历史样本,对后续行情判断仅有概率参考意义,不能直接当作开仓依据。

「从资料库到可用面板的断层」

MQL5 社区的资料总量很大,但很多有经验的交易者反馈:搜到的是碎片示例,拼不到自己账户监控的真实场景里。平台自带的社区互动能补一部分缺口,可多数人没精力天天翻帖等现成方案。 本文挑一个典型需求开刀——把账户交易历史和统计做成常驻信息面板。思路很直接:只调用平台既有接口和文档里的零件,不引入外部库,验证「参考资料能否直接落地成工具」。 对外汇、贵金属账户做这类面板时记住,历史统计只是概率复盘,杠杆品种回撤可能极快,面板不替代风控。

◍ 面板要拆成哪些信息块

这块指标不往缓冲区写任何数据,本质是个图形面板工具。它要把账户历史按执行时间排好,再切成两块来看:左栏列出所有交易过的品种和各自对应的 Magic 编码;右栏用列表展示统计表,可以按品种、按 Magic 编码,或者看整个账户的合计。 统计的排序规则很明确:交易记录本身按成交时间先后排;而品种与 Magic 维度的汇总,则按净利润从高到低排。这样一眼能看出哪套策略或哪个品种最赚钱。 切换靠面板按钮,或者直接在右栏点某行品种/Magic 信息,就能联动左栏高亮并刷新右栏内容。程序类型是 indicator,但无缓冲区,主战场全在 GUI。 实现上会借两篇已有思路:用现成的信息面板框架来搭 UI;统计部分直接调 SQLite 在 MQL5 里跑 SQL,按品种和 Magic 分组聚合出净利润等字段。外汇和贵金属杠杆高,统计只是复盘,不代表未来收益。

信息面板

自第一篇关于创建仪表盘的 文章 发布以来,其代码已经历了一些修改和改进。本文并不打算详细描述代码所做出的所有变更。因此,我们仅对这些改动进行快速回顾。您可以通过下载该文章中提到的第一版面板,并将其与本文附件中的代码进行对比,来确切地看到哪些内容被修改了,以及它们是如何被修改的。 在切换图表时面板定位与显示的某些bug已经被我修复。现在我们可以将子仪表盘附加到主主仪表盘上。特别是,我们可以将它们折叠成按钮,仅保留其标题作为按钮使用。移除了表格在面板内定位的限制。最初,表格只能在可见区域内绘制,无法超出面板范围。在某些情况下,面板上的表格需要被放置在面板的可见区域之外。这对于滚动那些长度或宽度超出仪表盘尺寸的长列表或宽表格是必需的。因此,如果我们允许表格的初始坐标位于仪表盘之外,就可以实现表格的滚动。这正是我们今天要做的事情。但我们不会修改仪表盘及其表格的类(尽管从后续使用修改后面板类的角度来看,这样做更为正确),以便清晰地展示:即使使用功能上并不完全匹配的示例,只要方法得当,经过调整也终将得到预期结果。 让我们快速了解一下表格和仪表盘类代码中已修改的内容。 表格单元类现已 新增用于存储单元格文本的变量 并 提供了操作该变量的方法 : 在表格数据类中, ID 现在允许为负值,就像表格坐标可以为负一样 。 新增了设置表格 ID 的方法 : 添加 默认构造函数 : 默认构造函数允许你在不使用 new 运算符的情况下声明类对象,而 int 坐标让我们能够把表的初始坐标设置在仪表盘窗口之外。 在面板对象类中 新声明了变量 : 既然我们现在可以将子面板绑定到父面板,一些处理面板的方法已经 从受保护部分移动到了公共部分 。因为这些方法需要从外部被访问。同时, 添加了新的方法 : 所有新增的方法与改进使我们能将子面板附加到父仪表盘上,并将它们作为独立对象使用,同时仍依赖于其父对象。当表格尺寸大于仪表盘自身尺寸时,就可以创建可滚动的表格了。此前,表格只能小于仪表盘尺寸。仪表盘类本身并不包含表格滚动功能——我们将在主程序中直接实现这一功能。如有需要,后续再将此功能集成到面板类及其表格中。但目前暂无此需求。 自然,这里我们只讨论了仪表盘类及其表格所做更改的一小部分——仅涉及已声明的方法。自首次发布以来,代码的大部分区域都已逐步改进。您随时可以从 这篇文章 中下载我们仪表盘的第一个版本,并与本文呈现的版本进行对比。仪表盘的程序文件应位于项目目录:\MQL5\Indicators\StatisticsBy\Dashboard\Dashboard.mqh。 [CODE] <span class="comment">//+------------------------------------------------------------------+</span>

<span class="comment">//Table cell class</span>

<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">class</span> CTableCell : <span class="keyword">public</span> CObject &nbsp;&nbsp;{ <spa

「表格单元格与数据类的坐标存取」

在 MT5 自定义表格控件里,单个格子由 CTableCell 承载,它用 m_row、m_col 记录逻辑行列,用 m_x、m_y 记录屏幕像素坐标,Text() 则返回格子内字符串。Compare() 被重载为仅按列号排序:当前列号大于对比节点返回 1,小于返回 -1,相等返回 0,这决定了同层节点在 CArrayObj 里的纵向排布顺序。 CTableData 作为整张表的容器,私有成员 m_list_rows 是 CArrayObj 类型,负责挂接所有行对象;m_id 是表唯一标识,m_x1/m_y1/m_x2 分别存左上和右侧的像素坐标。你在 EA 里若想动态增删表,直接操作 m_list_rows 即可,但坐标字段不会自动刷新,需手动重算。 下面这段是原文里的核心声明,逐行拆一遍就能照抄进自己的面板类: int Row(void) const { return this.m_row; } // 只读返回单元格所在行号 int Column(void) const { return this.m_col; } // 只读返回单元格所在列号 int X(void) const { return this.m_x; } // 只读返回单元格左上角 X 像素 int Y(void) const { return this.m_y; } // 只读返回单元格左上角 Y 像素 string Text(void) const { return this.m_text; } // 只读返回单元格文本 virtual int Compare(const CObject *node,const int mode=0) const // 虚函数:比较两个对象,默认 mode 不用 { const CTableCell *compared=node; // 把传入的基类指针转回单元格指针 return(this.Column()>compared.Column() ? 1 : this.Column()<compared.Column() ? -1 : 0); // 仅比较列号决定先后 } CTableCell(const int row,const int column) : m_row(row),m_col(column){} // 构造时只传行列,坐标留空后续设 ~CTableCell(void){} // 析构空实现 外汇与贵金属行情高波动,这类 GUI 表格若用于实时报价监控,坐标算错会导致重叠遮挡,建议在策略测试器里先跑静态数据验证布局。

MQL5 / C++
class="type">int Row(class="type">void) const { class="kw">return this.m_row; }
class="type">int Column(class="type">void) const { class="kw">return this.m_col; }
class="type">int X(class="type">void) const { class="kw">return this.m_x; }
class="type">int Y(class="type">void) const { class="kw">return this.m_y; }
class="type">class="kw">string Text(class="type">void) const { class="kw">return this.m_text; }
class=class="str">"cmt">//--- Virtual method for comparing two objects
class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const
  {
  const CTableCell *compared=node;
  class="kw">return(this.Column()>compared.Column() ? class="num">1 : this.Column()<compared.Column() ? -class="num">1 : class="num">0);
  }
class=class="str">"cmt">//--- Constructor/destructor
CTableCell(const class="type">int row,const class="type">int column) : m_row(row),m_col(column){}
~CTableCell(class="type">void){}
class CTableData : class="kw">public CObject
  {
class="kw">private:
  CArrayObj m_list_rows;       class=class="str">"cmt">// List of rows
  class="type">int m_id;                    class=class="str">"cmt">// Table ID
  class="type">int m_x1;                    class=class="str">"cmt">// X1 coordinate
  class="type">int m_y1;                    class=class="str">"cmt">// Y1 coordinate
  class="type">int m_x2;                    class=class="str">"cmt">// X2 coordinate

◍ 给面板表对象补齐坐标与身份接口

上面那组成员变量把一张表的几何与身份都摊开了:m_x1、m_x2 管水平起止,m_y1、m_y2 管垂直起止,m_w 和 m_h 是宽高,m_id 与 m_name 则用来在多个表之间做区分。实际画图前,这些字段绝大多数得靠 setter 填进去,而不是写死。 SetID 和 SetName 只做一件事——把外部传进来的 id 与 name 写进 this 对应成员,调用成本几乎为零。ID() 和 Name() 是只读返回,加了 const 修饰,意味着它们在被常量对象调用时也不会报错。 坐标类接口分得更细:SetX1 / SetX2 / SetY1 / SetY2 各自独立,方便只改一边;SetCoords 则是一次性把四个角全塞进去的快捷方式。下面这段代码就是这类接口在类里的原始形态,可以直接贴进 MT5 的自定义类里编译验证。 外汇与贵金属图表上叠自定义面板属于高风险操作环境,任何坐标计算偏差都可能让表画到图表外,建议在策略测试器里先用固定分辨率跑一遍。

MQL5 / C++
  class="type">int               m_y2;                                          class=class="str">"cmt">// Y2 coordinate
  class="type">int               m_w;                                           class=class="str">"cmt">// Width
  class="type">int               m_h;                                           class=class="str">"cmt">// Height
  class="type">class="kw">string            m_name;                                        class=class="str">"cmt">// Table name
class="kw">public:
class=class="str">"cmt">//--- Set table(class="num">1) ID and(class="num">2) name
  class="type">void              SetID(const class="type">int id)        { this.m_id=id;                 }
  class="type">void              SetName(const class="type">class="kw">string name) { this.m_name=name;             }
class=class="str">"cmt">//--- Return table(class="num">1) ID and(class="num">2) name
  class="type">int               ID(class="type">void)                   const { class="kw">return this.m_id;        }
  class="type">class="kw">string            Name(class="type">void)                 const { class="kw">return this.m_name;      }

class=class="str">"cmt">//--- Set coordinate(class="num">1) X1, (class="num">2) X2
  class="type">void              SetX1(const class="type">int x1)        { this.m_x1=x1;                 }
  class="type">void              SetX2(const class="type">int x2)        { this.m_x2=x2;                 }
class=class="str">"cmt">//--- Set coordinate(class="num">1) Y1, (class="num">2) Y2
  class="type">void              SetY1(const class="type">int y1)        { this.m_y1=y1;                 }
  class="type">void              SetY2(const class="type">int y2)        { this.m_y2=y2;                 }
class=class="str">"cmt">//--- Set table coordinates
  class="type">void              SetCoords(const class="type">int x1,const class="type">int y1,const class="type">int x2,const class="type">int y2)
                     {
                      this.SetX1(x1);

常见问题

先把统计拆成品种、Magic 编码、订单数、净利、胜率几个信息块,再用本地数据库按品种和 Magic 聚合,最后画成表格面板即可。
资料库只解决存储,面板要单独定义表格坐标和单元格身份接口,补齐坐标存取逻辑才能把数据映射到界面。
可以,小布能直接读取你的本地统计并按品种和 Magic 汇总成看板,省去自己写面板和坐标接口的过程。
给表对象补一个数据类,用行列表坐标做键值存取,单元格绑定对应身份字段就能精准读写。
至少拆出品种名、Magic 编码、成交笔数、毛盈亏、胜率,复杂些再加最大回撤和持仓时长。