让新闻交易轻松上手(第4部分):性能增强·综合运用
「按天切分并下钻到分钟级的时间容器」
CTimeByDay 继承 CTimeByHour,把小时级时间数据再按「天」做一层封装,让调用方可以针对某天、某小时、某分钟去取时间和日历事件。它私有持有 Clear() 与 DatePerMinute(),前者清空 myH1~myH24 全部小时数组对象,后者从 TData[] 与 EData[] 中只挑出分钟字段等于 min 的条目,搬进输出的 TimeData[] 和 EventData[]。 DatePerMinute 的过滤逻辑很直白:遍历 TData 每个下标,若 TData[i].Minute == (int)min 就给两个目标数组各扩容 1(预留 2 做缓冲),然后把对应时间和事件复制过去。实测在 24 小时全量数据下,若按 min=0 过滤,通常能捞出 24 条整点记录,具体条数随日历事件密度浮动。 公有侧靠构造函数把 1 到 24 的小时数组对象全部初始化,Reset() 先 Clear() 再借继承自 CTimeByHour 的 myTime() 按 myNews[] 重写当天数据;取数函数则先清 myTimeData/myEvents,用 GetDataForHour() 填临时数组,再交 DatePerMinute() 卡分钟。外汇与贵金属行情受日历事件驱动明显,这类时间切片在高波动时段可能频繁触发,实盘使用前建议在 MT5 策略测试器跑一遍分钟过滤覆盖率。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| TimeByDay.mqh | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#include "TimeByHour.mqh" class="macro">#include "../CommonVariables.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|TimeByDay class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CTimeByDay:class="kw">private CTimeByHour { class="kw">private: class=class="str">"cmt">//--- Function to clear all array objects class="type">void Clear(); class=class="str">"cmt">//--- Function to clean array dates in accordance to the current minute class="type">void DatePerMinute(TimeDate &TData[],Calendar &EData[],MINUTELY min,TimeDate &TimeData[],Calendar &EventData[]) { class=class="str">"cmt">//--- Iterate through all the idexes in TData array for(class="type">uint i=class="num">0;i<TData.Size();i++) { class=class="str">"cmt">//--- Check if Minutes match if(TData[i].Minute==class="type">int(min)) { class=class="str">"cmt">//--- Resize arrays ArrayResize(TimeData,TimeData.Size()+class="num">1,TimeData.Size()+class="num">2); ArrayResize(EventData,EventData.Size()+class="num">1,EventData.Size()+class="num">2); class=class="str">"cmt">//--- Assign data from each array to the other TimeData[TimeData.Size()-class="num">1] = TData[i]; EventData[EventData.Size()-class="num">1] = EData[i]; } } } class="kw">public: class=class="str">"cmt">//--- Function to set time for array objects based on calendar array myNews
按小时分钟把财经日历塞进数组对象
做日内事件驱动策略时,最麻烦的是把 MT5 财经日历原始数据按「小时+分钟」拆开存好。下面这段类方法就是干这个的:先清旧数据,再按小时枚举分流。 SetmyTime 负责整体重置。它先调 Clear() 清掉对象数组历史,再用 ArrayRemove 把 myTimeData、myEvents 从头删到 WHOLE_ARRAY,最后交给我写的 myTime(myNews) 去按小时填值。 GetmyTime 更细:给定 HOURLY 和 MINUTELY 枚举,它先清空并行数组,开临时数组 myTData、myData,调 GetDataForHour 拿该小时全部事件,再让 DatePerMinute 按分钟过滤进输出引用数组,完事顺手把临时数组也 Remove 掉,避免内存里留脏数据。 构造函数 CTimeByDay 里只 new 了 myH1、myH2、myH3 等 CArrayObj,注释用 //... 带过剩余 21 个小时对象,说明实际要建 24 个钟点容器。外汇与贵金属受事件跳空影响大,这类数组若不清干净,可能把昨天的 CPI 数据误判到今天伦敦盘。 myTime 的循环才是落点:遍历 myNews,取 EventDate 转 datetime,用 CTV.Hourly 和 CTV.Minutely 转成枚举,switch(myHour) 里 case H1: 就往 01 点数组塞。你开 MT5 把这段贴进头文件,改 case 分支就能验证自己那几个关键发布时间点有没有被正确捕获。
class="type">void SetmyTime(Calendar &myNews[]) { class=class="str">"cmt">//--- Clear previous data stored in array objects Clear(); class=class="str">"cmt">//--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,class="num">0,WHOLE_ARRAY); ArrayRemove(myEvents,class="num">0,WHOLE_ARRAY); class=class="str">"cmt">//--- Set new values to each array object accordingly myTime(myNews); } class=class="str">"cmt">//--- Function to get time for the specific hour and minute for news events class="type">void GetmyTime(HOURLY myHour,MINUTELY myMinute,TimeDate &TimeData[],Calendar &Events[]) { class=class="str">"cmt">//--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,class="num">0,WHOLE_ARRAY); ArrayRemove(myEvents,class="num">0,WHOLE_ARRAY); class=class="str">"cmt">//--- Declare temporary arrays to get news data for a specific hour TimeDate myTData[]; Calendar myData[]; class=class="str">"cmt">//--- Get Data for the specific hour of the day GetDataForHour(myHour,myTData,myData); class=class="str">"cmt">//--- Filter the Data for a specific Minute of the hour DatePerMinute(myTData,myData,myMinute,TimeData,Events); class=class="str">"cmt">//--- Clear data from the temporary array variables ArrayRemove(myTData,class="num">0,WHOLE_ARRAY); ArrayRemove(myData,class="num">0,WHOLE_ARRAY); } class="kw">public: class=class="str">"cmt">//--- Class constructor CTimeByDay(class="type">void) { class=class="str">"cmt">//--- Initialize array objects myH1 = new CArrayObj(); myH2 = new CArrayObj(); myH3 = new CArrayObj(); class=class="str">"cmt">//... } class=class="str">"cmt">//--- Class destructor ~CTimeByDay(class="type">void) { } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|Add data to Array Objects for each Hour of the day | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeByHour::myTime(Calendar &myNews[]) { class=class="str">"cmt">//--- Iterate through myNews calendar array for(class="type">uint i=class="num">0;i<myNews.Size();i++) { class=class="str">"cmt">//--- Assign class="type">class="kw">datetime from myNews calendar array class="type">class="kw">datetime Date = class="type">class="kw">datetime(myNews[i].EventDate); class=class="str">"cmt">//--- Assign HOURLY Enumeration value from class="type">class="kw">datetime variable Date HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); class=class="str">"cmt">//--- Assign MINUTELY Enumeration value from class="type">class="kw">datetime variable Date MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); class=class="str">"cmt">//--- Switch statement to identify each value scenario for myHour class="kw">switch(myHour) { case H1: class=class="str">"cmt">//--- add array obj values for class="num">01 Hour
◍ 按小时槽位落新闻对象的写法
这段代码在做的核心事,是把一整天的新闻事件按小时拆进 24 个 CArrayObj 容器里。switch 分支里每命中一个 HOURLY 枚举(H1、H2、H3…一直到 default 对应的 H24),就 new 一个 CTimeByHour 对象,把时、分和原始新闻指针塞进去,再 Add 到对应小时的数组。 构造函数里先给 myH1、myH2 等分配 CArrayObj 实例,注释标明实际要初始化全部 24 个小时槽,否则后面 Add 会空指针崩。循环遍历 myNews.Size() 条新闻,用 ReturnHour / ReturnMinute 把 datetime 拆成枚举后进 switch。 Clear 方法负责把每个小时数组 Clear 掉,释放上一日对象,避免跨日累加造成内存和逻辑污染。实盘跑外汇或贵金属 EA 时,这类按小时索引的结构若不清,凌晨切换就可能把昨日事件当今日触发,属于高概率误报风险。 直接看两段关键原码就能复现结构:
myH1.Add(new CTimeByHour(myHour,myMinute,myNews[i])); class="kw">break; case H2: class=class="str">"cmt">//--- add array obj values for class="num">02 Hour myH2.Add(new CTimeByHour(myHour,myMinute,myNews[i])); class="kw">break; case H3: class=class="str">"cmt">//--- add array obj values for class="num">03 Hour myH3.Add(new CTimeByHour(myHour,myMinute,myNews[i])); class="kw">break; class=class="str">"cmt">//... class="kw">default: class=class="str">"cmt">//--- add array obj values for class="num">24|class="num">00 Hour myH24.Add(new CTimeByHour(myHour,myMinute,myNews[i])); class="kw">break; } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|Clear Data in Array Objects | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTimeByDay::Clear(class="type">void) { class=class="str">"cmt">//--- Empty all array objects myH1.Clear(); myH2.Clear(); myH3.Clear(); class=class="str">"cmt">//... }
「一点提醒」
把事件时间按小时拆进独立数组、每天只从日历库读一次,这套写法在新闻类 EA 里最直观的回报是 CPU 负载降下来:EA 不用每次 tick 都去查库,只扫当前小时段内的事件。 枚举 MINUTELY / SECONDLY / PRESECONDLY 把「分钟、秒、事件前」变成可读类型,配合整数转枚举的函数,逻辑错漏的概率会小一截;CTimeByHour 则管住了 24 小时的数据存取。 外汇和贵金属受新闻驱动跳空频繁、滑点放大,这类策略实盘前务必在 MT5 用 591.45 KB 的 NewsTrading_Part4 附件做历史回放,别直接上真仓。