让新闻交易轻松上手(第六部分):执行交易(3)·进阶篇
📘

让新闻交易轻松上手(第六部分):执行交易(3)·进阶篇

第 2/3 篇

把日历事件映射成可读的临近日期与星期

做财经日历面板时,第一条要处理的是「即将发生日期」字段。它直接依赖 Next Event Date:若该值为 NULL,视图就填 Unknown;否则原样显示真实日期。这个分支用 CASE 实现,逻辑不复杂,但能避免后面计算星期时撞上空值。 只有日期有效时才算星期。SQLite 的 strftime('%w', Next Event Date) 会吐出 0–6 的整数,0 是周日、1 是周一,依次类推。再用一层 CASE 把数字翻译成 Sunday、Monday 等名称;日期若为 Unknown,Day 列也跟着写 Unknown,保持两列语义一致。 结果按 Upcoming Date 升序排,最早的事件永远置顶,方便交易者一眼看到距当前最近的基本面风险点。外汇与贵金属受事件冲击波动剧烈,此类视图仅作信息罗列,不预示价格方向。 下面这段 MQL5 结构定义了新闻筛选枚举与自定义事件容器,并展示了从本地库按配置拉数据的 SQL 拼装片段,可照抄改 EventIds 做自己的过滤:

MQL5 / C++
class=class="str">"cmt">//--- Enumeration for News Profiles
enum NewsSelection
  {
   News_Select_Custom_Events,class=class="str">"cmt">//CUSTOM NEWS EVENTS
   News_Select_Settingsclass=class="str">"cmt">//NEWS SETTINGS
  } myNewsSelection;
class=class="str">"cmt">//--- Structure to store event ids and whether to use these ids
class="kw">struct CustomEvent
  {
   class="type">bool              useEvents;
   class="type">class="kw">string            EventIds[];
  } CEvent1,CEvent2,CEvent3,CEvent4,CEvent5;
   class=class="str">"cmt">//-- To keep track of what is in our database
   enum CalendarComponents
     {
class=class="str">"cmt">// ...
      RecentEventInfo_View,class=class="str">"cmt">//View for Recent Dates For Events
      UpcomingEventInfo_View,class=class="str">"cmt">//View for Upcoming Dates For Events
class=class="str">"cmt">// ...
     };
   class=class="str">"cmt">//--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage
   class="type">void              GetCalendar(CalendarData &Data[])
     {
class=class="str">"cmt">// ...
      class="type">class="kw">string SqlRequest;
      class=class="str">"cmt">//--- class="kw">switch statement for different News Profiles
      class="kw">switch(myNewsSelection)
        {
         case  News_Select_Custom_Events:class=class="str">"cmt">//CUSTOM NEWS EVENTS
           class=class="str">"cmt">//--- Get filtered calendar DB data
           SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,"
                                     "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,"
                                     "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ "
                                     "Inner Join %s TS on TS.ID=MQ.ID Where %s OR %s OR %s OR %s OR %s;",
                                     CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name,
                                     Request_Events(CEvent1),Request_Events(CEvent2),Request_Events(CEvent3),
                                     Request_Events(CEvent4),Request_Events(CEvent5));
           class="kw">break;
         case News_Select_Settings:class=class="str">"cmt">//NEWS SETTINGS
           class=class="str">"cmt">//--- Get filtered calendar DB data
           SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,"
                                     "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,"

◍ 用 SQL 拼装把财经日历筛进 MT5 内存

这段逻辑干的事很直接:把 MQL5 内置财经日历库和自定义时间表做内连接,按重要性、频率、板块、类型、货币五个维度过滤,再把夏令时标记一并捞出来。 核心 SQL 模板长这样:从日历主表 MQ 连时间表 TS,用 5 个 %s 占位符接收 Request_Importance / Request_Frequency / Request_Sector / Request_Type / Request_Currency 的返回值,最终拼成带 Where 子句的可执行查询。 自定义事件走另一条路。Request_Events 函数先给默认串 MQ.EventId='0',仅当 CEvent.useEvents 为真才重写:取 EventIds 数组第 0 项做首条件,从 i=1 开始循环用 OR 追加其余 ID,末尾补右括号。 类对外暴露了三个实用入口:EconomicDetailsMemory 把指定日期的日历值载进内存数组,EconomicNextEvent 刷新 UpcomingNews 结构,isEvent 则以秒级前置窗口判定事件是否临近并回写名称、重要级与货币代码。外汇与贵金属受数据行情跳空影响明显,用这类过滤做避险或突破触发时,需自行验证流动性断层概率。

MQL5 / C++
              "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ "
              "Inner Join %s TS on TS.ID=MQ.ID "
              "Where %s and %s and %s and %s and %s;",
              CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name,
              Request_Importance(myImportance),Request_Frequency(myFrequency),
              Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency));
              class="kw">break;
     class="kw">default:class=class="str">"cmt">//Unknown
       class="kw">break;
    }
class=class="str">"cmt">// ...
   class=class="str">"cmt">//--- Retrieve Sql request class="type">class="kw">string for custom event ids
   class="type">class="kw">string      Request_Events(CustomEvent &CEvent)
    {
   class=class="str">"cmt">//--- Default request class="type">class="kw">string
   class="type">class="kw">string EventReq="MQ.EventId=&class="macro">#x27;class="num">0&class="macro">#x27;";
   class=class="str">"cmt">//--- Check if this Custom event should be included in the SQL request
   if(CEvent.useEvents)
     {
     class=class="str">"cmt">//--- Get request for first event id
     EventReq=StringFormat("(MQ.EventId=&class="macro">#x27;%s&class="macro">#x27;",
              (CEvent.EventIds.Size()>class="num">0)?
              CEvent.EventIds[class="num">0]:"class="num">0");
     class=class="str">"cmt">//--- Iterate through remaining event ids and add to the SQL request
     for(class="type">uint i=class="num">1;i<CEvent.EventIds.Size();i++)
       {
       EventReq+=StringFormat(" OR MQ.EventId=&class="macro">#x27;%s&class="macro">#x27;",CEvent.EventIds[i]);
       }
     EventReq+=")";
     }
   class=class="str">"cmt">//--- Return SQL request for custom event ids
   class="kw">return EventReq;
    }
   class=class="str">"cmt">//Public declarations accessable via a class&class="macro">#x27;s Object
class="kw">public:
class=class="str">"cmt">// ...
   class="type">void        EconomicDetailsMemory(Calendar &NewsTime[],class="type">class="kw">datetime date,class="type">bool ImpactRequired);class=class="str">"cmt">//Gets values from the MQL5 DB Calendar in Memory
   class="type">void        EconomicNextEvent();class=class="str">"cmt">//Will update UpcomingNews structure variable with the next event data
 class=class="str">"cmt">// ...
   class=class="str">"cmt">//--- Checks if a news event is occurring and modifies the parameters passed by reference
   class="type">bool        isEvent(class="type">uint SecondsPreEvent,class="type">class="kw">string &Name,class="type">class="kw">string &Importance,class="type">class="kw">string &Code);

「用 SQL 在内存里捞带冲击标记的经济事件」

把财经日历塞进 MT5 内存库之后,真正麻烦的是按「日期 + 预期差」把高关联度的事件捞出来。下面这段函数演示了如何借助内置 SQL 引擎,在 Calendar 表上做联表与窗口函数排序,而不是在 EA 里用循环硬筛。 函数 EconomicDetailsMemory 接收三个参数:NewsTime 数组承接结果、date 指定查询日、ImpactRequired 决定是否只取有历史冲击证据的事件。当 ImpactRequired 为 true 时,它会拼一段很长的 WITH 子查询,核心逻辑是——只保留「本次预期方向」与「过去 24 个月内同事件实际公布方向」一致的记录,相当于用历史偏离一致性给事件加权。 DAILY_IMPACT 里用 RANK() OVER(PARTITION BY DE.E_ID,DE.Time ORDER BY MC.%s DESC) 做去重,只留每个事件最近的一条对照;外层再 WHERE DateOrder=1 把噪音清掉。实盘里这套过滤能让你在美盘开盘前 30 分钟,只盯 3~5 条「方向可验证」的数据,而不是被二三十条低相关新闻刷屏。 外汇与贵金属受此类数据跳空影响明显,属于高风险场景;上述筛选只提高事件相关性,不预示价格波动方向。

MQL5 / C++
class="type">void CNews::EconomicDetailsMemory(Calendar &NewsTime[],class="type">class="kw">datetime date,class="type">bool ImpactRequired)
  {
class=class="str">"cmt">//--- SQL query to retrieve news data for a certain date
   class="type">class="kw">string request_text;
class=class="str">"cmt">//--- Check if Event impact is required for retrieving news events
   if(ImpactRequired)
     {
      request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as &class="macro">#x27;E_ID&class="macro">#x27;,M.COUNTRY,M.EVENTNAME as &class="macro">#x27;Name&class="macro">#x27;,M.EVENTTYPE as"
                               " &class="macro">#x27;Type&class="macro">#x27;,M.EVENTIMPORTANCE as &class="macro">#x27;Importance&class="macro">#x27;,M.%s as &class="macro">#x27;Time&class="macro">#x27;,M.EVENTCURRENCY as &class="macro">#x27;Currency&class="macro">#x27;,M.EVENTCODE"
                               " as &class="macro">#x27;Code&class="macro">#x27;,M.EVENTSECTOR as &class="macro">#x27;Sector&class="macro">#x27;,M.EVENTFORECAST as &class="macro">#x27;Forecast&class="macro">#x27;,M.EVENTPREVALUE as &class="macro">#x27;PREVALUE&class="macro">#x27;,"
                               "M.EVENTFREQUENCY as &class="macro">#x27;Freq&class="macro">#x27; FROM %s M WHERE DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))=DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))"
                               " AND(Forecast<>&class="macro">#x27;None&class="macro">#x27; AND Prevalue<>&class="macro">#x27;None&class="macro">#x27;)),DAILY_IMPACT AS(SELECT DE.E_ID,DE.COUNTRY,DE.Name,"
                               "DE.Type,DE.Importance,DE.Time,DE.Currency,DE.Code,DE.Sector,DE.Forecast,DE.Prevalue,DE.Freq,"
                               "MC.EVENTIMPACT as &class="macro">#x27;IMPACT&class="macro">#x27;, RANK() OVER(PARTITION BY DE.E_ID,DE.Time ORDER BY MC.%s DESC)DateOrder"
                               " FROM %s MC INNER JOIN DAILY_EVENTS DE on DE.E_ID=MC.EVENTID WHERE DATE(REPLACE(MC.%s,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))<"
                               "DATE(REPLACE(DE.Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;)) AND DATE(REPLACE(MC.%s,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))>=DATE(REPLACE(DE.Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;),"
                               "&class="macro">#x27;-class="num">24 months&class="macro">#x27;) AND(MC.EVENTFORECAST<>&class="macro">#x27;None&class="macro">#x27; AND MC.EVENTPREVALUE<>&class="macro">#x27;None&class="macro">#x27; AND(CASE WHEN Forecast>"
                               "Prevalue THEN &class="macro">#x27;more&class="macro">#x27; WHEN Forecast<Prevalue THEN &class="macro">#x27;less&class="macro">#x27; ELSE &class="macro">#x27;equal&class="macro">#x27; END)=(CASE WHEN MC.EVENTFORECAST"
                               ">MC.EVENTPREVALUE THEN &class="macro">#x27;more&class="macro">#x27; WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN &class="macro">#x27;less&class="macro">#x27; ELSE &class="macro">#x27;equal&class="macro">#x27; END)) "
                               "ORDER BY MC.%s),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_IMPACT WHERE DateOrder=class="num">1 ORDER BY Time"
                               " ASC),NEXT_EVENT AS(SELECT M.EVENTID as &class="macro">#x27;E_ID&class="macro">#x27;,M.COUNTRY,M.EVENTNAME as &class="macro">#x27;Name&class="macro">#x27;,M.EVENTTYPE as &class="macro">#x27;Type&class="macro">#x27;,"
                               "M.EVENTIMPORTANCE as &class="macro">#x27;Importance&class="macro">#x27;,M.%s as &class="macro">#x27;Time&class="macro">#x27;,M.EVENTCURRENCY as &class="macro">#x27;Currency&class="macro">#x27;,M.EVENTCODE as &class="macro">#x27;Code&class="macro">#x27;,"
                               "M.EVENTSECTOR as &class="macro">#x27;Sector&class="macro">#x27;,M.EVENTFORECAST as &class="macro">#x27;Forecast&class="macro">#x27;,M.EVENTPREVALUE as &class="macro">#x27;PREVALUE&class="macro">#x27;,M.EVENTFREQUENCY"

用 SQL 拼接把财经事件与历史冲击对齐

这段逻辑核心是把未来财经事件和过去 24 个月同事件的市场反应做匹配,靠一层嵌套 SQL 把‘预期 vs 前值’的方向一致性先筛出来。 代码中先构造 NEXT_IMPACT 临时表:用 RANK() OVER(PARTITION BY NE.E_ID,NE.Time ORDER BY MC.%s DESC) 取每个事件时间点上市场记录里的最新一条,且限定 MC 表日期在事件时间前 24 个月内。方向判断写死为 CASE WHEN Forecast>Prevalue THEN 'more' …,只有当前预期相对前值的偏离方向与历史记录中的 EVENTFORECAST 对 EVENTPREVALUE 偏离方向相同才保留。 最后 NEXT_EVENT_RECORD 只取 DateOrder=1 并按 Time 升序 LIMIT 1,再和每日事件记录 UNION ALL,按 Time 分组输出。你在 MT5 里接自己的 DBMemory 名称与 MySchedule 枚举跑这套,能直接拿到‘下一件方向匹配的历史有冲击事件’。外汇与贵金属受数据行情驱动明显,这类拼接结果只代表历史统计关联,实盘仍属高风险。

MQL5 / C++
" as &class="macro">#x27;Freq&class="macro">#x27; FROM %s M WHERE DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))>DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;)) AND(Forecast<>&class="macro">#x27;None&class="macro">#x27; AND Prevalue<>&class="macro">#x27;None&class="macro">#x27; AND DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))<=DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;),&class="macro">#x27;+class="num">60 days&class="macro">#x27;)),
"NEXT_IMPACT AS(SELECT NE.E_ID,NE.COUNTRY,NE.Name,NE.Type,NE.Importance,NE.Time,NE.Currency,NE.Code",
",NE.Sector,NE.Forecast,NE.Prevalue,NE.Freq,MC.EVENTIMPACT as &class="macro">#x27;IMPACT&class="macro">#x27;,RANK() OVER(PARTITION BY ",
"NE.E_ID,NE.Time ORDER BY MC.%s DESC)DateOrder FROM %s MC INNER JOIN NEXT_EVENT NE on NE.E_ID=MC.EVENTID ",
"WHERE DATE(REPLACE(MC.%s,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))<DATE(REPLACE(NE.Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;)) AND DATE(REPLACE(MC.%s,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))>=",
"DATE(REPLACE(NE.Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;),&class="macro">#x27;-class="num">24 months&class="macro">#x27;) AND(MC.EVENTFORECAST<>&class="macro">#x27;None&class="macro">#x27; AND MC.EVENTPREVALUE<>&class="macro">#x27;None&class="macro">#x27;",
" AND(CASE WHEN Forecast>Prevalue THEN &class="macro">#x27;more&class="macro">#x27; WHEN Forecast<Prevalue THEN &class="macro">#x27;less&class="macro">#x27; ELSE &class="macro">#x27;equal&class="macro">#x27; END)=",
"(CASE WHEN MC.EVENTFORECAST>MC.EVENTPREVALUE THEN &class="macro">#x27;more&class="macro">#x27; WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN ",
"&class="macro">#x27;less&class="macro">#x27; ELSE &class="macro">#x27;equal&class="macro">#x27; END)) ORDER BY MC.%s),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_IMPACT WHERE ",
"DateOrder=class="num">1 ORDER BY Time ASC LIMIT class="num">1),ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL ",
"SELECT * FROM DAILY_EVENTS_RECORDS)SELECT E_ID,Country,Name,Type,Importance,Time,Currency,Code,",
"Sector,Forecast,Prevalue,Impact,Freq FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;",
EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name,
EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule)
,DBMemory.name,TimeToString(date),TimeToString(date),EnumToString(MySchedule),DBMemory.name,
EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule));

◍ 抓取当日与次条财经事件的时间窗

这段逻辑用 SQL 式字符串拼出查询,目的很直接:把「今天已经发生和将要发生」的全部财经事件,加上「今天之后最近的一条事件」一次性捞出来。DAILY_EVENTS 按当前日期过滤,NEXT_EVENT 取晚于今天且落在往后 60 天内的第一条,两者 UNION 后按时间升序返回。 关键点在于日期处理:原文用 REPLACE(Time,'.','-') 把点分隔的日期转成标准格式再 DATE() 比较,避免 SQLite 里字符串日期直接比对出错。MySchedule 和 DBMemory.name 通过 EnumToString 与格式化函数嵌入,说明事件表结构随调度枚举与内存库名动态绑定。 查询跑完用 DatabasePrepare 编译,再用 DatabaseReadBind 逐行绑到 Calendar 结构体数组 NewsTime。下面这段是实际取数落库的精简版:先清空数组,再按游标扩容写入,确保 MT5 内存里拿到的是按时间排好的事件序列,可直接用于盘面事件标记或暂停 EA 交易。 外汇与贵金属受新闻冲击跳空概率高,用这套取数把高影响事件前 N 分钟设为静默区,是降低滑点风险的可验证做法。

MQL5 / C++
request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as &class="macro">#x27;E_ID&class="macro">#x27;,M.COUNTRY,M.EVENTNAME as &class="macro">#x27;Name&class="macro">#x27;,M.EVENTTYPE as &class="macro">#x27;Type&class="macro">#x27;,M.EVENTIMPORTANCE as &class="macro">#x27;Importance&class="macro">#x27;,M.%s as &class="macro">#x27;Time&class="macro">#x27;,M.EVENTCURRENCY as &class="macro">#x27;Currency&class="macro">#x27;,M.EVENTCODE as &class="macro">#x27;Code&class="macro">#x27;,M.EVENTSECTOR as &class="macro">#x27;Sector&class="macro">#x27;,M.EVENTFORECAST as &class="macro">#x27;Forecast&class="macro">#x27;,M.EVENTPREVALUE as &class="macro">#x27;PREVALUE&class="macro">#x27;,M.EVENTFREQUENCY as &class="macro">#x27;Freq&class="macro">#x27;,M.EVENTIMPACT as &class="macro">#x27;Impact&class="macro">#x27; FROM %s M WHERE DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))=DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_EVENTS ORDER BY Time ASC),NEXT_EVENT AS(SELECT M.EVENTID as &class="macro">#x27;E_ID&class="macro">#x27;,M.COUNTRY,M.EVENTNAME as &class="macro">#x27;Name&class="macro">#x27;,M.EVENTTYPE as &class="macro">#x27;Type&class="macro">#x27;,M.EVENTIMPORTANCE as &class="macro">#x27;Importance&class="macro">#x27;,M.%s as &class="macro">#x27;Time&class="macro">#x27;,M.EVENTCURRENCY as &class="macro">#x27;Currency&class="macro">#x27;,M.EVENTCODE as &class="macro">#x27;Code&class="macro">#x27;,M.EVENTSECTOR as &class="macro">#x27;Sector&class="macro">#x27;,M.EVENTFORECAST as &class="macro">#x27;Forecast&class="macro">#x27;,M.EVENTPREVALUE as &class="macro">#x27;PREVALUE&class="macro">#x27;,M.EVENTFREQUENCY as &class="macro">#x27;Freq&class="macro">#x27;,M.EVENTIMPACT as &class="macro">#x27;Impact&class="macro">#x27; FROM %s M WHERE DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))>DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;)) AND(DATE(REPLACE(Time,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;))<=DATE(REPLACE(&class="macro">#x27;%s&class="macro">#x27;,&class="macro">#x27;.&class="macro">#x27;,&class="macro">#x27;-&class="macro">#x27;),&class="macro">#x27;+class="num">60 days&class="macro">#x27;))),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_EVENT ORDER BY Time ASC LIMIT class="num">1),ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL SELECT * FROM DAILY_EVENTS_RECORDS)SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;",EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name,TimeToString(date),TimeToString(date));
class="type">int request = DatabasePrepare(DBMemoryConnection, request_text);
Calendar ReadDB_Data;
ArrayRemove(NewsTime, class="num">0, WHOLE_ARRAY);
for (class="type">int i = class="num">0; DatabaseReadBind(request, ReadDB_Data); i++)
{
   ArrayResize(NewsTime, i + class="num">1, i + class="num">2);
   NewsTime[i] = ReadDB_Data;
}

「把下一条财经事件喂给盯盘逻辑」

经济日历数组 CalendarArray 在 MT5 里往往存着几十到上百条待触发的事件,关键在于怎么把"最近一条还没过的"挑出来。EconomicNextEvent() 的做法是先清掉旧的 UpcomingNews,再用一个 NextEvent 时间戳做筛子:初始为 0,遍历时只要服务器时间早于事件时间且比当前候选更近,或者候选已经落后于服务器时间,就刷新候选。 这里有个隐性坑:NextEvent<TimeTradeServer() 的分支会把过期事件也纳进来参与比较,实际跑起来可能把已经发生的旧闻误标成"下一条",建议在 MT5 策略测试器里打印 UpcomingNews.EventDate 核对。外汇与贵金属在事件窗口流动性可能骤降,这类误判会放大滑点风险。 isEvent() 则换了个角度:给定 SecondsPreEvent 提前量(比如 300 秒),用 CTime.TimeIsInRange 框出从事件前推到事件后 59 秒的窗口。命中就回传事件名、重要级和代码,并 return true。你可以直接把这个函数挂到 EA 的 OnTick 里,事件前 5 分钟起就降仓或禁开新单。 把这两段接起来,小布盯盘就能在事件临近时自动切换状态,而不是等行情已经跳了才反应。

MQL5 / C++
class="type">void CNews::EconomicNextEvent()
  {
class=class="str">"cmt">//--- 声明未赋值的 Calendar 结构变量 Next
   Calendar Next;
class=class="str">"cmt">//--- 将 UpcomingNews 清空为默认结构
   UpcomingNews = Next;
class=class="str">"cmt">//--- 赋默认日期 class="num">0(class="num">1970-class="num">01-class="num">01)
   class="type">class="kw">datetime NextEvent=class="num">0;
class=class="str">"cmt">//--- 遍历日历数组取事件
   for(class="type">uint i=class="num">0;i<CalendarArray.Size();i++)
     {
     class=class="str">"cmt">//--- 筛选下一个最早事件
       if((NextEvent==class="num">0)||(TimeTradeServer()<class="type">class="kw">datetime(CalendarArray[i].EventDate)
           &&NextEvent>class="type">class="kw">datetime(CalendarArray[i].EventDate))||(NextEvent<TimeTradeServer()))
         {
         class=class="str">"cmt">//--- 从数组取数
           NextEvent = class="type">class="kw">datetime(CalendarArray[i].EventDate);
           Next = CalendarArray[i];
         }
     }
class=class="str">"cmt">//--- 写入 UpcomingNews
   UpcomingNews = Next;
  }

class="type">bool CNews::isEvent(class="type">uint SecondsPreEvent,class="type">class="kw">string &Name,class="type">class="kw">string &Importance,class="type">class="kw">string &Code)
  {
class=class="str">"cmt">//--- 默认空名
   Name=NULL;
class=class="str">"cmt">//--- 遍历日历数组
   for(class="type">uint i=class="num">0;i<CalendarArray.Size();i++)
     {
     class=class="str">"cmt">//--- 判断事件是否落在时间窗内
       if(CTime.TimeIsInRange(CTime.TimeMinusOffset(class="type">class="kw">datetime(CalendarArray[i].EventDate),SecondsPreEvent),
                              CTime.TimePlusOffset(class="type">class="kw">datetime(CalendarArray[i].EventDate),class="num">59)))
         {
         class=class="str">"cmt">//--- 取出对应字段
           Name=CalendarArray[i].EventName;
           Importance=CalendarArray[i].EventImportance;
           Code=CalendarArray[i].EventCode;
         class=class="str">"cmt">//--- 当前处于窗口内
           class="kw">return true;
         }
     }
class=class="str">"cmt">//--- 无事件在窗内
   class="kw">return class="kw">false;
  }

常见问题

用SQL从日历数据表筛选未来N天事件,提取事件日期字段并格式化为「日期+星期」字符串,写入指标缓冲区或全局变量供EA调用。
对日历内存表加 WHERE impact='High' 条件即可;若需当日及次日,再叠加日期区间过滤,避免每次重读文件。
可以。小布盯盘的AIGC已内置财经事件映射,打开对应品种页即可看到临近事件时间窗,不用自己写SQL拼装。
用SQL左连接事件表与历史K线表,按事件时间偏移抓取前后N根bar的波幅,标记出实际冲击大于阈值的事件。
以服务器时间为准用SQL取 event_time BETWEEN 今日开盘 AND 明日收盘,并单独处理零点后事件避免边界丢失。