MQL5 酷宝书  宏观经济事件数据库(基础篇)
📘

MQL5 酷宝书 宏观经济事件数据库(基础篇)

第 1/3 篇

◍ MT5 里那本宏观经济事件活字典

MetaTrader 5 终端内置了一套宏观经济事件数据库,常被老用户称作「酷宝书」,它把各国央行决议、就业与通胀数据等事件按时间轴直接铺在终端里,省去切外部财经日历的麻烦。 这套库不是静态文档:在 MT5 的「日历」标签下,事件带具体时间戳与前值/预期/实际的三栏对照,2023 年 6 月 8 日 11:01 的那条样本记录,就附带了 1638 次终端内查看与 2 条社区讨论的痕迹,说明高频宏观事件在交易者间的真实关注度。 对外汇与贵金属来说,这类事件驱动型波动属于典型高风险场景——同一数据偏离预期 10 个基点,黄金与欧美的瞬时跳动幅度可能差出三倍,动手前先在 MT5 日历里把下周事件密度过一遍更稳妥。

「用 SQLite 给宏观日历事件归类」

处理宏观经济日历,难点不在事件本身,而在每天涌进来的大量原始数据怎么分层。把数据变成能用的信息,关键一步是先把结构理顺,否则后面做筛选和统计都很吃力。 MT5 从 MQL5 构建 2265(2019 年 6 月发布)开始原生支持 SQLite,这意味着不用再外接各种连接器就能直接在 EA 或脚本里建库、写表、查数据。之前只能靠第三方桥接方案,稳定性和部署都麻烦。 如果你手头有日历 CSV,直接开 MT5 建个 SQLite 库把事件按国家、重要性、时间字段拆开,是后续做事件前波动率统计的前提。外汇和贵金属受数据行情冲击大,这类操作仅用于复盘与研究,实盘须自担高风险。

MT5 内置的 26 个数据库原生函数

打开 MT5 的 MQL5 参考手册,数据库处理部分目前由 26 个原生函数支撑,覆盖连接、表操作、事务与读取全流程。 从 DatabaseOpen 到 DatabaseClose 管生命周期,DatabaseExecute 与 DatabasePrepare 负责执行 SQL,DatabaseBind / DatabaseBindArray 做参数绑定,DatabaseRead 系列取回结果,DatabaseTransactionBegin / Commit / Rollback 控制事务原子性。 列元数据也有对应接口:DatabaseColumnsCount 拿列数,DatabaseColumnName / Type / Size 取结构,DatabaseColumnText / Integer / Long / Double / Blob 按类型读值。 若想系统摸清这套能力,官方那篇讲 SQLite 原生处理的文章是合适的起点;近期版本还补了统计与数学函数模块,可一并对照。外汇与贵金属策略若接本地数据库做信号落盘,需注意平台断连与并发写入风险。

◍ 封装 SQLite 操作的 CDatabase 类怎么落地

在 MT5 里直接调 SQLite 原生 API 写财经日历数据库,重复代码极多。CDatabase 类把文件句柄、表名缓存、最近一条 SQL 和查询句柄都收进数据成员(m_name / m_handle / m_flags / m_table_names / m_curr_table_name / m_sql_request_ha / m_sql_request),方法按原生 API、表操作、请求、视图、get 访问器分组,省去每处都重写连接逻辑。 类不替你穷举所有 SQLite 句式,缺方法时一律走通用的 CDatabase::Select() 自己拼 SQL。实际跑通一条链路:1_create_calendar_db.mq5 生成 test_calendar_db.sqlite 落到 %MQL5\Files\Databases,此时库空;2_create_countries_table.mq5 建出 COUNTRIES 空表;3_fill_in_countries_table.mq5 借 CiCalendarInfo 把国家塞进去,日志可印出数据。 后续 4~11 号脚本覆盖选取列、排序、按 CONTINENT 分组(Europe 9 国最多、Africa 与 South America 各 1)、DISTINCT 去重得 18 种货币、WHERE 限定 ID 392–840、再叠加大陆排序使 South Africa 排头。12 号脚本更新亚洲行 CURRENCY 字段,13 号把 Mexico 的 Mex$ 替换成 Peso mexicano——注意原表 COUNTRY_ID 设了 NOT NULL,替换时必须带全列值否则报违约束错,且 REPLACE 本质兼做插入。 14~19 号演示删 Asia 行、加 EVENTS_NUM 列、RenameColumn 改名、Union 合并欧美表、Except 求差集、Intersect 求交集(id 限 392–578)。20~22 号建视图 All_countries 与 European,并测 DropView:不带 IF EXISTS 删不存在视图返错误 5601,带则静默成功。23 号 RenameTable 把表改名 COUNTRIES1。外汇与贵金属相关的日历数据聚合涉及高频宏观事件,实操请认清 MT5 本地库与实盘行情错位的高风险。

MQL5 / C++
class=class="str">"cmt">//--- include
class="macro">#include "..\CDatabase.mqh"

「用脚本在 MT5 里落地财经数据库」

想把财经日历数据落盘到本地,第一步是建库建表。下面这段脚本演示了如何用 CDatabase 封装类在 MT5 终端的 Databases 目录下生成 sqlite 文件,并创建一张 COUNTRIES 表,字段覆盖国家 ID、名称、代码、所在洲、货币代码、货币符号与 URL 共 7 项。 先看建库:以 READWRITE | CREATE 标志打开文件,若目录不存在则自动创建。失败仅打印提示并关闭,不抛异常。 [CODE] //+------------------------------------------------------------------+

//Script program start function

//+------------------------------------------------------------------+ void OnStart() { CDatabase db_obj; string file_name="Databases\\test_calendar_db.sqlite"; uint flags=DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE; if(!db_obj.Open(file_name, flags)) ::PrintFormat("Failed to create a calendar database \"%s\"!", file_name); db_obj.Close(); } //+------------------------------------------------------------------+ //--- include #include "..\CDatabase.mqh" //+------------------------------------------------------------------+

//Script program start function

//+------------------------------------------------------------------+ void OnStart() { //--- open a database CDatabase db_obj; string file_name="Databases\\test_calendar_db.sqlite"; uint flags=DATABASE_OPEN_READWRITE; if(!db_obj.Open(file_name, flags)) { ::PrintFormat("Failed to open a calendar database \"%s\"!", file_name); db_obj.Close(); return; } //--- create a table string table_name="COUNTRIES"; string params[]= { "COUNTRY_ID UNSIGNED BIG INT PRIMARY KEY NOT NULL,", // 1) country ID "NAME TEXT," // 2) country name "CODE TEXT," // 3) country code "CONTINENT TEXT," // 4) country continent "CURRENCY TEXT," // 5) currency code "CURRENCY_SYMBOL TEXT," // 6) currency symbol "URL_NAME TEXT" // 7) country URL }; if(!db_obj.CreateTable(table_name, params)) { ::PrintFormat("Failed to create a table \"%s\"!", table_name); db_obj.Close(); return; } db_obj.Close(); } //+------------------------------------------------------------------+ //--- include #include "..\CalendarInfo.mqh" [/CODE] 逐行拆解:第一段 OnStart 用 CREATE 标志确保库文件存在;第二段去掉 CREATE 只留 READWRITE,意味着库必须预存在,否则走失败分支直接 return,避免脏写。CreateTable 的 params 数组按顺序定义列,主键锁死 COUNTRY_ID 且非空,其余 TEXT 列宽松存字符串。 包含头文件有讲究:建库阶段只需 CDatabase.mqh,而 CalendarInfo.mqh 放在末尾,说明真正拉取日历数据要在表结构就绪后再介入。外汇与贵金属受央行日程冲击大,本地库建好后,后续按国家维度过滤事件,可能比每次请求 API 更稳。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
   CDatabase db_obj;
   class="type">class="kw">string file_name="Databases\\test_calendar_db.sqlite";
   class="type">uint flags=DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE;
   if(!db_obj.Open(file_name, flags))
      ::PrintFormat("Failed to create a calendar database \"%s\"!", file_name);
   db_obj.Close();
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- include
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_READWRITE;
   if(!db_obj.Open(file_name, flags))
     {
      ::PrintFormat("Failed to open a calendar database \"%s\"!", file_name);
      db_obj.Close();
      class="kw">return;
     }
class=class="str">"cmt">//--- create a table
   class="type">class="kw">string table_name="COUNTRIES";
   class="type">class="kw">string params[]=
     {
      "COUNTRY_ID UNSIGNED BIG INT PRIMARY KEY NOT NULL,", class=class="str">"cmt">// class="num">1) country ID
      "NAME TEXT,"                                            class=class="str">"cmt">// class="num">2) country name
      "CODE TEXT,"                                            class=class="str">"cmt">// class="num">3) country code
      "CONTINENT TEXT,"                                       class=class="str">"cmt">// class="num">4) country continent
      "CURRENCY TEXT,"                                        class=class="str">"cmt">// class="num">5) currency code
      "CURRENCY_SYMBOL TEXT,"                                 class=class="str">"cmt">// class="num">6) currency symbol
      "URL_NAME TEXT"                                          class=class="str">"cmt">// class="num">7) country URL
     };
   if(!db_obj.CreateTable(table_name, params))
     {
      ::PrintFormat("Failed to create a table \"%s\"!", table_name);
      db_obj.Close();
      class="kw">return;
     }
   db_obj.Close();
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- include
class="macro">#include "..\CalendarInfo.mqh"

把财经日历国家表落进本地 sqlite

做本地财经日历缓存时,直接调 MT5 内置的 CiCalendarInfo 把国家列表拉出来,再写进自建 sqlite 表,比每次联网查询更稳。下面这段脚本在 Databases\test_calendar_db.sqlite 的 COUNTRIES 表里清空后重灌数据,列定义了 7 个字段:COUNTRY_ID、NAME、CODE、CONTINENT、CURRENCY、CURRENCY_SYMBOL、URL_NAME。 CONTINENT 字段这里用了一个外部结构 SCountryByContinent 按国家 code 反查,若查不到就写 NULL,避免整行插入失败。写入前用 TransactionBegin 包住循环,国家数量通常在一百出头,单事务提交能把磁盘 IO 次数压到一个量级。 开 MT5 把 CDatabase.mqh 放到同级目录,脚本跑完用 sqlite 客户端打开 test_calendar_db.sqlite,SELECT COUNT(*) FROM COUNTRIES 应返回与 calendar_info.GetCountries 拿到数组长度一致的数字;不一致多半是某国 code 在 SCountryByContinent 里缺映射导致提前跳出。外汇与贵金属消息面波动剧烈,本地表只解决数据获取效率,不预示任何方向。

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_READWRITE;
   if(!db_obj.Open(file_name, flags))
     {
      db_obj.Close();
      class="kw">return;
     }
class=class="str">"cmt">//--- open a table
   class="type">class="kw">string table_name="COUNTRIES";
   if(db_obj.SelectTable(table_name))
   if(db_obj.EmptyTable())
     {
      db_obj.FinalizeSqlRequest();
      class="type">class="kw">string col_names[]=
        {
         "COUNTRY_ID", "NAME", "CODE", "CONTINENT",
         "CURRENCY", "CURRENCY_SYMBOL", "URL_NAME"
        };
class=class="str">"cmt">//--- fill in the table
      CiCalendarInfo calendar_info;
      if(calendar_info.Init())
        {
         MqlCalendarCountry calendar_countries[];
         if(calendar_info.GetCountries(calendar_countries))
           {
            if(db_obj.TransactionBegin())
            for(class="type">int c_idx=class="num">0; c_idx<::ArraySize(calendar_countries); c_idx++)
              {
               MqlCalendarCountry curr_country=calendar_countries[c_idx];
               class="type">class="kw">string col_vals[];
               ::ArrayResize(col_vals, class="num">7);
               col_vals[class="num">0]=::StringFormat("%I64u", curr_country.id);
               col_vals[class="num">1]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;", curr_country.name);
               col_vals[class="num">2]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;", curr_country.code);
               col_vals[class="num">3]="NULL";
               SCountryByContinent curr_country_continent_data;
               if(curr_country_continent_data.Init(curr_country.code))
               col_vals[class="num">3]=::StringFormat("&class="macro">#x27;%s&class="macro">#x27;",

常见问题

用脚本把财经日历的国家表直接落进本地 sqlite,跑一次就能生成可反复查询的库,不用再手动记。
把它当成操作 sqlite 的薄壳,初始化连接后调用增删查接口即可,重点先跑通建表再写数据。
小布可以接入你的品种页,在重大宏观事件前推送提醒并标注异动概率,你只管看信号不用自己翻库。
26个原生函数覆盖了建表、插入、查询等基础动作,普通日历归类够用,复杂统计再补手写sql。
多是编码没统一成 utf-8,建表时指定文本编码、写入前转码即可避免本地 sqlite 乱码。