MQL5 酷宝书  宏观经济事件数据库·进阶篇
🗄️

MQL5 酷宝书 宏观经济事件数据库·进阶篇

(2/3)· 面对上百条宏观事件记录,散落文件让回测和筛选都卡壳,本篇讲清分组与存储的实战结构

实战向进阶 第 2/3 篇
多数人把宏观日历事件直接写进 CSV 或全局数组,等到要按国家、影响级别筛选时就得重跑整段逻辑。用 MQL5 内置 SQLite 做结构化存储,查询和分组只是一条 SQL 的事,不用每次都重新解析原始数据。

把国家基础数据落库时的回滚与提交

这段逻辑负责把遍历到的国家信息逐行写进 SQLite 表,写入前用 StringFormat 把 currency、currency_symbol、url_name 包成带单引号的字符串,分别对应 col_vals[4]~[6]。若 InsertSingleRow 返回失败,立即 TransactionRollback 并 Close 后 return,避免脏数据残留。 循环结束后统一 TransactionCommit,失败则用 PrintFormat 打出 "Failed to complete transaction execution, error %d" 与 GetLastError 值;随后 PrintTable 把表内容输出到专家日志便于核对。 从 EURUSD,H1 实测日志看,表头含 COUNTRY_ID、NAME、CODE、CONTINENT、CURRENCY、CURRENCY_SYMBOL、URL_NAME 七列,首行写入为 ID=1、NAME='554 New Zealand'、CODE='NZ'、CONTINENT='Australia/Oceania'、CURRENCY='NZD'、符号'$'、URL='new-zealand'。第二行欧洲联盟 CODE='EU'、CURRENCY='EUR'、符号'€',第三行日本 CODE='JP'、CURRENCY='JPY'、符号'¥',说明国家枚举已覆盖大洋洲、欧洲、亚洲主要交易体。 开 MT5 跑这段脚本时,若日志出现 error 5xxx 级别的数据库报错,优先查 InsertSingleRow 前的列名数组长度是否和 col_vals 对齐,否则回滚会静默吞掉整批写入。

MQL5 / C++
curr_country_continent_data.ContinentDescription());
      col_vals[class="num">4]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;", curr_country.currency);
      col_vals[class="num">5]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;", curr_country.currency_symbol);
      col_vals[class="num">6]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;", curr_country.url_name);
      if(!db_obj.InsertSingleRow(col_names, col_vals))
        {
        db_obj.TransactionRollback();
        db_obj.Close();
        class="kw">return;
        }
      db_obj.FinalizeSqlRequest();
      }
    if(!db_obj.TransactionCommit())
      ::PrintFormat("Failed to complete transaction execution, error %d", ::GetLastError());
      }
    class=class="str">"cmt">//--- print
    if(db_obj.PrintTable()<class="num">0)
      ::PrintFormat("Failed to print the table \"%s\", error %d", table_name, ::GetLastError());
      }
    }
  db_obj.Close();
  }

◍ 国家映射表的打印输出长这样

在 EURUSD 的 H1 周期下调用 _fill_in_countries_table,终端里会吐出一张带行号的国家清单。每一行左侧是函数名加品种周期,中间数字是从 4 到 14 的连续序号,右侧才是真实落库的数据。 拿打印结果看,第 4 行是 Canada(数字代码 124,缩写 CA,北美,本币 CAD,符号 $);第 6 行 China 代码 156、CNY 带 ¥;第 13 行 South Africa 代码 710、ZAR 符号 R。这些数字代码就是 ISO 3166-1 三位数值,后面写过滤逻辑时直接拿它当主键比用字符串稳。 如果你在 MT5 里跑这段代码,把品种切到 XAUUSD 同样能打印,只是函数名前缀会变成 _fill_in_countries_table (XAUUSD,H1)。外汇和贵金属杠杆高,这类基础表只是辅助分类,别拿它当方向依据。

「从 SQLite 里捞国家字典表」

把经济日历里的国家维度单独落进 COUNTRIES 表后,MT5 脚本能在只读模式下直接拉出整张字典。上面那串终端打印就是 fill_in_countries_table 在 EURUSD,H1 下的实际输出:从行号 15 到 23 一共 9 条记录,数字前缀是 ISO 3166 三位码——印度 356、挪威 578、美国 840、英国 826、瑞士 756、韩国 410、西班牙 724、瑞典 752,外加一条 0 代表的 Worldwide。 这些码不是装饰。后续做按央行事件过滤、或把新闻冲击按国别归因时,直接用 840 锁美元区、用 826 锁英镑区,比用字符串匹配快且不易错。下面这段 Open + SelectTable 是验证表是否落地的起点,跑不通就说明 sqlite 文件没在 Databases 目录下。 [CODE] 见 code 字段,逐行拆解: #include "..\CDatabase.mqh" —— 引入封装好的数据库操作类,路径指向上级目录。 void OnStart() —— 脚本入口,MT5 加载即跑。 CDatabase db_obj; —— 声明一个数据库对象实例。 string file_name="Databases\\test_calendar_db.sqlite"; —— 指定相对 MT5 数据文件夹的库文件路径。 uint flags=DATABASE_OPEN_READONLY; —— 以只读 flag 打开,避免误写。 if(!db_obj.Open(file_name, flags)) —— 打开失败则进分支。 { db_obj.Close(); return; } —— 关对象并退出,不往下走。 string table_name="COUNTRIES"; —— 要查的表名。 if(db_obj.SelectTable(table_name)) —— 表存在且选中成功才进后续逻辑。

MQL5 / C++
class="macro">#include "..\CDatabase.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//--- open a database
   CDatabase db_obj;
   class="type">class="kw">string file_name="Databases\\test_calendar_db.sqlite";
   class="type">uint flags=DATABASE_OPEN_READONLY;
   if(!db_obj.Open(file_name, flags))
     {
      db_obj.Close();
      class="kw">return;
     }
class=class="str">"cmt">//--- check a table
   class="type">class="kw">string table_name="COUNTRIES";
   if(db_obj.SelectTable(table_name))
     {

指定字段的查询与回显

在 MT5 的 SQLite 封装里,想只捞需要的列,就先声明一个字符串数组把列名写死。下面这段把 COUNTRY_ID、NAME、CODE、CONTINENT、CURRENCY 五个字段塞进 col_names_to_select,再交给 db_obj.SelectFrom 执行。 if(!db_obj.SelectFrom(...)) 一旦失败立刻 Close 并 return,避免后续空指针式误操作;PrintSqlRequest 用来把实际发出的 SQL 打到日志,返回小于 0 才用 GetLastError 报具体码。 跑出来的终端记录显示,10 个国家/地区被选出:新西兰(COUNTRY_ID 554, NZD)、欧盟(999, EUR)、日本(392, JPY)、加拿大(124, CAD)、澳大利亚(36, AUD)、中国(156, CNY)、意大利(380, EUR)、新加坡(702, SGD)、德国(276, EUR)、法国(250, EUR)。欧洲阵营里 EUR 出现 5 次,占半数。 外汇与贵金属品种叠加这类基础数据做过滤时,注意关联表可能随券商环境变动,实盘前应在策略测试器用样本品种(如 EURUSD,H1)先验证返回行数。

MQL5 / C++
class="type">class="kw">string col_names_to_select[]=
  {
  "COUNTRY_ID", "NAME", "CODE",
  "CONTINENT", "CURRENCY"
  };
if(!db_obj.SelectFrom(col_names_to_select))
  {
  db_obj.Close();
  class="kw">return;
  }
class=class="str">"cmt">//--- print the SQL request
if(db_obj.PrintSqlRequest()<class="num">0)
  ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();
}
db_obj.Close();
}

◍ 正文

<span class="number">4</span>_select_some_columns (EURUSD,H1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">11</span>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">76</span> Brazil&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BR&nbsp;&nbsp; South America&nbsp;&nbsp;&nbsp;&nbsp; BRL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">4</span>_select_some_columns (EURUSD,H1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">12</span>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="number">484</span> Mexico&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MX&nbsp;&nbsp; North America&nbsp;&nbsp;&nbsp;&nbsp; MXN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">4</span>_select_some_columns (EURUSD,H1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">13</span>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="number">710</span> South Africa&nbsp;&nbsp; ZA&nbsp;&nbsp; Africa&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ZAR&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">4</span>_select_some_columns (EURUSD,H1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">14</span>|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="number">344</span> Hong Kong&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HK&nbsp;&nbsp; Asia&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HKD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">4

「按序捞取指定列并核对输出」

在 MT5 里用 CDatabase 做本地查询时,先判 Open 成败再决定后续流程,能避免文件锁或路径错误导致的静默崩溃。下面这段逻辑打开库后只挑 COUNTRIES 表里的 5 个字段,并按 COUNTRY_ID 升序取数。 if(!db_obj.Open(file_name, flags)) { db_obj.Close(); return; } //--- check a table string table_name="COUNTRIES"; if(db_obj.SelectTable(table_name)) { string col_names_to_select[]= { "COUNTRY_ID", "NAME", "CODE", "CONTINENT", "CURRENCY" }; string ord_names[1]; ord_names[0]=col_names_to_select[0]; if(!db_obj.SelectFromOrderedBy(col_names_to_select, ord_names)) { db_obj.Close(); return; } //--- print the SQL request if(db_obj.PrintSqlRequest()<0) ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError()); db_obj.FinalizeSqlRequest(); } db_obj.Close(); 逐行拆解:第 1 行尝试以 flags 模式打开数据库文件,失败立刻 Close 并退出;第 8 行指定表名为 COUNTRIES;第 10–15 行声明要选的 5 列数组;第 16–17 行把排序键设为 COUNTRY_ID(即数组第 0 项);第 18 行执行带 ORDER BY 的查询,失败同样收尾返回;第 22–24 行把生成的 SQL 打到日志,便于在策略测试器里核对请求文本,最后 Finalize 并 Close。 实跑日志里能看到有序结果:COUNTRY_ID 从 0(Worldwide/ALL)走到 344(Hong Kong/HKD),共 8 行样本,其中欧洲区 FR 与 DE 的 CURRENCY 都是 EUR。外汇与贵金属品种叠加这类本地映射表做币种过滤时,注意离线数据滞后可能引发信号偏差,属高风险用法,建议先在 EURUSD,H1 回测环境验证再上实盘。

MQL5 / C++
if(!db_obj.Open(file_name, flags))
  {
  db_obj.Close();
  class="kw">return;
  }
class=class="str">"cmt">//--- check a table
class="type">class="kw">string table_name="COUNTRIES";
if(db_obj.SelectTable(table_name))
  {
  class="type">class="kw">string col_names_to_select[]=
    {
    "COUNTRY_ID", "NAME", "CODE",
    "CONTINENT", "CURRENCY"
    };
  class="type">class="kw">string ord_names[class="num">1];
  ord_names[class="num">0]=col_names_to_select[class="num">0];
  if(!db_obj.SelectFromOrderedBy(col_names_to_select, ord_names))
    {
    db_obj.Close();
    class="kw">return;
    }
  class=class="str">"cmt">//--- print the SQL request
  if(db_obj.PrintSqlRequest()<class="num">0)
    ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
  db_obj.FinalizeSqlRequest();
  }
db_obj.Close();
把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到宏观事件命中情况,你只需把本文的库结构接进自己的 EA 即可省去手动维护日历。

常见问题

自构建 2265(2019 年 6 月发布)起,MQL5 提供了 DatabaseOpen 等 26 个原生函数,无需再借助外部连接器。
包括数据库文件名 m_name、句柄 m_handle、标志组合 m_flags、表名集合 m_table_names,以及当前表相关成员,方便统一管理和复用。
正确建表并定义字段类型后,用 SELECT 按日期、国家、影响等级聚合,能把流水记录转成策略可读取的结构化信号。
目前小布内置的是云端宏观事件命中视图,若要把自有库接入,需要以 EA 输出标准化字段,再映射到品种页的诊断模块。
前者绑定单值参数到预处理语句占位符,后者支持把数组一次性绑入,适合批量写入事件记录时减少循环调用。