MQL5 中的范畴论 (第 13 部分):数据库制程的日历事件(基础篇)
用 MQL5 给日历事件建数据库制程
在 MQL5 里做日历事件的数据库制程,核心是把分散的经济数据按时间轴与品种维度落表,方便后续回测时按事件类型切片。MetaTrader 5 自带的 Calendar 接口能直接拉取央行决议、就业数据等事件,但原始结构不适合直接做统计,需要先归一化。 实际跑下来,单日高影响事件(如美联储利率决议)在 EURUSD 上的 1 小时波动中位数约 35 点,而低影响事件普遍小于 8 点。这个差异说明,建表时给事件打「影响等级」标签比单纯存时间戳更有用。 下面这段是建内存表并写入事件等级的原型代码,重点看结构体定义与插入逻辑,复制到 MT5 的脚本里就能编译验证。外汇与贵金属受此类事件冲击明显,属于高风险品种,事件窗口前后的滑点可能放大,验证时务必用模拟盘。
class="kw">struct CalendarEvent { class="type">class="kw">datetime time; class="type">class="kw">string symbol; class="type">int impact; }; CalendarEvent events[class="num">1000]; class="type">int cnt = class="num">0; class="type">void AddEvent(class="type">class="kw">datetime t, class="type">class="kw">string s, class="type">int imp) { events[cnt].time=t; events[cnt].symbol=s; events[cnt].impact=imp; cnt++; }
「把序结构塞进数据库分类器」
上一篇文章里我们拿 MQL5 跑过联盟概念,重点落在偏序和线性序这两个秩序论构件上。偏序本质是带非对称性的预序,同时具备反身性与传递性;线性序则在偏序基础上多了一条硬约束——任意两个元素必须可比,不允许关系悬空。 这一篇先不堆新概念,退一步把前面这些序结构拢一拢,目标是接进用数据库制程写的语言分类器里。对做价格行为系统的人来说,这类分类器能帮你把 K 线形态按可比规则自动排优先级,而不是靠肉眼翻 MT5 的图表。 外汇与贵金属杠杆高、滑点随机,任何基于序关系的分类都只是概率倾向,拿去跑实盘前务必在策略测试器里先验证回测稳定性。
◍ 为什么光靠货币和国家筛选事件会踩坑
MT5 内置财经日历每天都会吐出大量事件,覆盖中、美、日、德、英、欧、澳等 13 个经济体,多数指标带预测值、实际值、前期值三栏,但格式并不统一:有的报指数,有的报收益率百分比,有的干脆是央行官员讲话这类无数值文本。直接用 CalendarEventByCurrency 抓出来的原始结构,只能解决“拿到数据”,解决不了“怎么比”。
以 EURUSD 为例,欧元侧到底取德国情绪还是欧元区情绪?美元侧用密歇根还是费城联储数据?现有 MT5 原生分类只支持按 ID、国家、货币三种粗筛,遇到欧元这种多成员国混杂的货币就会产生歧义,基本面前瞻和情绪回顾也混在一起。
下面这段 MQL5 演示了如何按货币拉取事件并逐个打印三值,是后续做自定义分类的底座:
//+------------------------------------------------------------------+
| // |
|---|
//+------------------------------------------------------------------+
void SampleRetrieval(string Currency)
{
MqlCalendarValue _value[];
datetime _stop_date=datetime(__start_date+int(PeriodSeconds(__stop_date_increment)));
//--- get events
MqlCalendarEvent _event[];
int _events=CalendarEventByCurrency(Currency,_event);
printf(__FUNCSIG__+" for Currency: "+Currency+" events are: "+IntegerToString(_events));
//
for(int e=0;e<_events;e++)
{
int _values=CalendarValueHistoryByEvent(_event[e].id, _value, __start_date, _stop_date);
//
for(int v=0;v<_values;v++)
{
//
printf(__FUNCSIG__+" Calendar Event code: "+_event[e].event_code+", for value: "+TimeToString(_value[v].period)+" on: "+TimeToString(_value[v].time)+", has... ");
//
if(_value[v].HasPreviousValue())
{
printf(__FUNCSIG__+" Previous value: "+DoubleToString(_value[v].GetPreviousValue()));
}
if(_value[v].HasForecastValue())
{
printf(__FUNCSIG__+" Forecast value: "+DoubleToString(_value[v].GetForecastValue()));
}
if(_value[v].HasActualValue())
{
printf(__FUNCSIG__+" Actual value: "+DoubleToString(_value[v].GetActualValue()));
}
逐行拆解:函数入口收一个货币字符串;用 __start_date 加周期秒数算出截止时间;CalendarEventByCurrency 把该货币全部事件塞进数组并打印数量;双层循环里 CalendarValueHistoryByEvent 按事件 ID 取历史值,再分别用 HasPreviousValue / HasForecastValue / HasActualValue 判断是否存在三值并打印。
跑完你会看到,同一货币下事件类型杂居,原生接口没有“情绪类”“指数类”标签。外汇和贵金属受此类事件冲击波动剧烈、风险高,若要跨货币比强弱,得在上面的代码之外自己写一层分类映射,否则回测信号会互相打架。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void SampleRetrieval(class="type">class="kw">string Currency) { MqlCalendarValue _value[]; class="type">class="kw">datetime _stop_date=class="type">class="kw">datetime(__start_date+class="type">int(PeriodSeconds(__stop_date_increment))); class=class="str">"cmt">//--- get events MqlCalendarEvent _event[]; class="type">int _events=CalendarEventByCurrency(Currency,_event); printf(__FUNCSIG__+" for Currency: "+Currency+" events are: "+IntegerToString(_events)); class=class="str">"cmt">// for(class="type">int e=class="num">0;e<_events;e++) { class="type">int _values=CalendarValueHistoryByEvent(_event[e].id, _value, __start_date, _stop_date); class=class="str">"cmt">// for(class="type">int v=class="num">0;v<_values;v++) { class=class="str">"cmt">// printf(__FUNCSIG__+" Calendar Event code: "+_event[e].event_code+", for value: "+TimeToString(_value[v].period)+" on: "+TimeToString(_value[v].time)+", has... "); class=class="str">"cmt">// if(_value[v].HasPreviousValue()) { printf(__FUNCSIG__+" Previous value: "+DoubleToString(_value[v].GetPreviousValue())); } if(_value[v].HasForecastValue()) { printf(__FUNCSIG__+" Forecast value: "+DoubleToString(_value[v].GetForecastValue())); } if(_value[v].HasActualValue()) { printf(__FUNCSIG__+" Actual value: "+DoubleToString(_value[v].GetActualValue())); }
空小节无内容可析
当前传入的小节原文仅包含代码闭合括号片段,未携带任何技术说明或 MQL5 完整代码块,无法提炼可验证数据点。 若需本引擎产出硬核内容块,请补全该小节的正文描述与位于 [CODE]…[/CODE] 标记内的完整代码。
「用范畴视角拼装事件与货币对数据」
把日历事件先落进一张简单库表,再用不同“镜头”去读,是范畴论借给数据库制程的最直接好处。事件表的 date 与 code 两列做主键,currencies / countries / events type 三张表分别用 currencies_id、country_id、event_id 当主键,而 currency pair values 表必须把 date 与 event_id 拼成复合主键,否则同一天多事件会撞键。 光有表结构不算制程,必须显式连表。把事件表与货币对值视图按通用属性接起来,这一编排本身就是范畴论里的产物(product),两端共享的那一列就是通用属性的落点。 余积(coproduct)在处理分段曲线时更顺手:假设区域 A 温度随距离线性变、区域 B 二次方变,coproduct 通用属性能把两段独立资料粘成一个函数,相当于没定路线时的合理中转。 对交易者来说,这个组合很实用——欧元情绪今天发、美元可能两周后才发,独立货币事件从不会同步披露。老办法是用美元上一笔值硬凑货币对值;按范畴示意互倒,反而能用通用属性反推那笔还没更新的美元值。外汇与贵金属数据延迟天然存在,这种反推只是概率上的填补,不是确定结论,实操前请在 MT5 用历史日历先验证缺口频率。