SQL 与 MQL5: 与 SQLite 数据库集成·综合运用
📘

SQL 与 MQL5: 与 SQLite 数据库集成·综合运用

第 3/3 篇

用 SQLite 把 MT5 历史成交落盘

在 MT5 里做自己的成交数据仓库,比反复调 HistoryDealsTotal() 更稳。下面这段 CSQLite3Base 的 Exec 方法是入口:先判连接,再判空串,最后把 string 转 uchar 数组丢给原生 sqlite3_exec。 [CODE] int CSQLite3Base::Exec(string query) { if(!IsConnected()) if(!Reconnect()) return(SQLITE_ERROR); if(StringLen(query)<=0) return(SQLITE_DONE); uchar str[]; StringToCharArray(query,str); int res=::sqlite3_exec(m_db,str,NULL,NULL,NULL); return(res); } [/CODE] 逐行看:第 3–5 行,没连上就尝试重连,重连失败直接返 SQLITE_ERROR;第 6–7 行,查询串长度 ≤0 视为空操作,返 SQLITE_DONE;第 8–9 行,StringToCharArray 把 MQL5 的 UTF-16 string 压成 C 端要的 uchar 缓冲;第 10 行调底层 sqlite3_exec,回调全传 NULL,适合无结果集的 DDL/DML。 真正导数据时在 OnStart 里走事务:Connect("Deals.db3") 打开库,CREATE TABLE IF NOT EXISTS 建表,字段含 ticket 主键、open_price、profit、comment。随后 Exec("BEGIN") 开事务,循环里用 QueryBind 做 REPLACE INTO,任一行失败就 Exec("ROLLBACK") 回滚。 实测若不包事务、逐条 INSERT 千笔成交可能卡好几秒;包 BEGIN/COMMIT 后同量写入通常亚秒级完成。外汇与贵金属杠杆高,历史 profit 仅作策略回看,不等于未来损益。

MQL5 / C++
class="type">int CSQLite3Base::Exec(class="type">class="kw">string query)
  {
   if(!IsConnected())
      if(!Reconnect())
         class="kw">return(SQLITE_ERROR);
   if(StringLen(query)<=class="num">0)
      class="kw">return(SQLITE_DONE);
   class="type">uchar str[];
   StringToCharArray(query,str);
   class="type">int res=::sqlite3_exec(m_db,str,NULL,NULL,NULL);
   class="kw">return(res);
  }

◍ 用 SQLite 给成交记录算盈亏分布

在 MT5 里直接调 SQLite 接口统计历史成交,比导出 csv 再拿 Excel 算要快得多,也方便写进 EA 的复盘模块。下面这段逻辑只做两件事:数盈利单数量、算总利润和均值利润。 代码先跑一条 COUNT 查询,筛出 profit 大于 0 的 Deals 行数;若返回不是 SQLITE_DONE 就打印错误并退出,避免脏数据继续往下走。拿到结果后从单元格 (0,0) 取 Int64 打印出来,这就是你的盈利单数。 紧接着一条 SELECT 同时拿 SUM(profit) 和 AVG(profit),别名分别叫 sumprof 和 avgprof。同样判错后,从 (0,0) 取双精度总和、从 (0,1) 取双精度均值并打印。实盘跑下来,若 avgprof 远低于单笔最大回撤,说明盈利单靠数量堆、每笔质量偏弱,外汇与贵金属杠杆高,这种结构在连亏期可能迅速侵蚀权益。 把这段塞进 OnDeinit 或独立脚本,每次退出终端就能留一份成交统计,比肉眼翻账户历史靠谱。

MQL5 / C++
  if(sql3.Query(tbl,"SELECT COUNT(*) FROM `Deals` WHERE(`profit`>class="num">0)")!=SQLITE_DONE)
    {
      Print(sql3.ErrorMsg());
      class="kw">return;
    }
  tbl.Cell(class="num">0,class="num">0,cell);
  Print("Count(*)=",cell.GetInt64());
class=class="str">"cmt">//---
  if(sql3.Query(tbl,"SELECT SUM(`profit`) AS `sumprof`, AVG(`profit`) AS `avgprof` FROM `Deals`")!=SQLITE_DONE)
    {
      Print(sql3.ErrorMsg());
      class="kw">return;
    }
  tbl.Cell(class="num">0,class="num">0,cell);
  Print("SUM(`profit`)=",cell.GetDouble());
  tbl.Cell(class="num">0,class="num">1,cell);
  Print("AVG(`profit`)=",cell.GetDouble());
  }

「自己动手编译 64 位 sqlite3_64.dll」

想在 MT5 64 位终端里直接调 SQLite,得先有一份匹配的 sqlite3_64.dll。从 SQLite amalgamation 包里取 sqlite3.c,再从 sqlite-dll-win32 包展开出 sqlite3.dll,目的是拿到 sqlite3.def 导出表。 在 DLL 文件夹里跑 LIB.EXE /DEF:sqlite3.def 生成导入库,注意 lib.exe 要么进了 PATH,要么临时指到 Visual Studio 的 VC 工具链路径,否则命令行直接报找不到。 建 DLL 工程时选 x64 发行配置,把 sqlite3.c 和 sqlite3.def 都加进去。若编译器对某些 def 里的函数签名不认,注释掉对应行即可,不影响核心 SQL 接口。 项目设置里把调试信息格式设为程序数据库 (/Zi),预编译头文件改为不使用 (/Yu),这两项是避开 MSVC 默认坑的关键。编完就能拿到 64 位 dll,丢进 MT5 的 Libraries 目录供 EA 调用。外汇与贵金属品种波动剧烈,接入本地数据库做 tick 存储属高风险自用方案,需先在模拟盘验证稳定性。

画得少,看得清

把前面三节的 SQLite 接入、表结构设计与查询封装收拢成一件事:在 MT5 里跑通一个轻量价格采集回路,比堆界面更有用。附件里的 MQL5.zip(790.12 KB)含类库与测试脚本,直接丢进 MetaEditor 编译即可验证。 建议你接的功课是写个即时价格收集器,把每个品种的 bid/ask 按时间戳插进表,再用 SQLite Studio 抽看分布。外汇与贵金属点差跳变频繁,这类本地库只作复盘与信号预筛,实盘仍受滑点和高风险影响。 真跑起来后,俄语文本乱码、ByteImg.mqh 报错这类社区反馈,本质都是编码与作用域的细节坑,遇到再查比预先背手册省时间。

常见问题

用 SQLite 建一张成交表,每次平仓后把品种、开平时间、手数、盈亏写进去,之后直接跑 SQL 查某段时间的交易就行。
把每笔平仓盈亏落盘到 SQLite,用一条 GROUP BY 盈亏区间的 SQL 就能出分布直方图数据,再丢给图表画出来。
小布可读取你的成交记录并自动归类统计,打开对应品种页就能看到盈亏分布和异常单提示,不用自己写库。
装好 MinGW-w64 并把 bin 加到 PATH,用 gcc -shared 命令编译 sqlite3.c 即可生成 dll,注意选 64 位目标。
只标出超过 2 倍均值的盈亏单和连亏段,其余灰显,视觉上立刻能抓到该复盘的那几笔。