MQL5 酷宝书 宏观经济事件数据库·进阶篇
(2/3)· 面对上百条宏观事件记录,散落文件让回测和筛选都卡壳,本篇讲清分组与存储的实战结构
把国家基础数据落库时的回滚与提交
这段逻辑负责把遍历到的国家信息逐行写进 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 对齐,否则回滚会静默吞掉整批写入。
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)) —— 表存在且选中成功才进后续逻辑。
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)先验证返回行数。
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) <span class="number">11</span>| <span class="number">76</span> Brazil BR South America BRL <span class="number">4</span>_select_some_columns (EURUSD,H1) <span class="number">12</span>| <span class="number">484</span> Mexico MX North America MXN <span class="number">4</span>_select_some_columns (EURUSD,H1) <span class="number">13</span>| <span class="number">710</span> South Africa ZA Africa ZAR <span class="number">4</span>_select_some_columns (EURUSD,H1) <span class="number">14</span>| <span class="number">344</span> Hong Kong HK Asia HKD <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 回测环境验证再上实盘。
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();