建立自动新闻交易程序·综合运用
(3/3)· 从类结构到回测,完整拼图让你不再手动盯非农数据发布会
「把财经新闻塞进内存容器」
做新闻驱动型 EA,第一步不是算信号,而是先把散落在 csv 里的事件读进内存。下面这个类用 CArrayObj 承载新闻对象,避免每次都去磁盘翻文件。
class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Files\FileTxt.mqh> class="macro">#include <Arrays\ArrayObj.mqh> class="macro">#include <..\Experts\NewsWatcher\CNew.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNewsContainer Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CNewsContainer { class="kw">protected: class="type">class="kw">string m_csv; class=class="str">"cmt">// The name of the csv file CFileTxt m_fileTxt; class=class="str">"cmt">// MQL5 file functionality class="type">int m_currentIndex; class=class="str">"cmt">// The index of the next news to be processed in the container class="type">int m_total; class=class="str">"cmt">// The total number of news to be processed CArrayObj *m_news; class=class="str">"cmt">// News list in the computer&class="macro">#x27;s memory, loaded from the csv file class="kw">public: class=class="str">"cmt">//--- Constructor and destructor methods CNewsContainer(class="type">class="kw">string csv); ~CNewsContainer(class="type">void); class=class="str">"cmt">//--- Getter methods class="type">int GetCurrentIndex(class="type">void); class="type">int GetTotal(class="type">void); CNew *GetCurrentNew(); CArrayObj *GetNews(class="type">void); class=class="str">"cmt">//--- Setter methods class="type">void SetCurrentIndex(class="type">int index); class="type">void SetTotal(class="type">int total); class="type">void SetNews(class="type">void); class=class="str">"cmt">//--- CNewsContainer methods class="type">bool Init(); class="type">void Deinit(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constuctor | class=class="str">"cmt">//+------------------------------------------------------------------+ CNewsContainer::CNewsContainer(class="type">class="kw">string csv) { m_csv=csv; m_news=new CArrayObj; SetNews(); }
class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Files\FileTxt.mqh> class="macro">#include <Arrays\ArrayObj.mqh> class="macro">#include <..\Experts\NewsWatcher\CNew.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNewsContainer Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CNewsContainer { class="kw">protected: class="type">class="kw">string m_csv; class=class="str">"cmt">// The name of the csv file CFileTxt m_fileTxt; class=class="str">"cmt">// MQL5 file functionality class="type">int m_currentIndex; class=class="str">"cmt">// The index of the next news to be processed in the container class="type">int m_total; class=class="str">"cmt">// The total number of news to be processed CArrayObj *m_news; class=class="str">"cmt">// News list in the computer&class="macro">#x27;s memory, loaded from the csv file class="kw">public: class=class="str">"cmt">//--- Constructor and destructor methods CNewsContainer(class="type">class="kw">string csv); ~CNewsContainer(class="type">void); class=class="str">"cmt">//--- Getter methods class="type">int GetCurrentIndex(class="type">void); class="type">int GetTotal(class="type">void); CNew *GetCurrentNew(); CArrayObj *GetNews(class="type">void); class=class="str">"cmt">//--- Setter methods class="type">void SetCurrentIndex(class="type">int index); class="type">void SetTotal(class="type">int total); class="type">void SetNews(class="type">void); class=class="str">"cmt">//--- CNewsContainer methods class="type">bool Init(); class="type">void Deinit(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constuctor | class=class="str">"cmt">//+------------------------------------------------------------------+ CNewsContainer::CNewsContainer(class="type">class="kw">string csv) { m_csv=csv; m_news=new CArrayObj; SetNews(); }
◍ 新闻容器的存取接口怎么写
做 MT5 新闻事件过滤时,CNewsContainer 这类封装类最容易被忽略的是析构与索引边界。析构函数直接调 Deinit() 释放内部对象数组,避免在 EA 重载时留下悬空指针。 GetCurrentIndex 和 GetTotal 只是把私有成员 m_currentIndex、m_total 透出来,外部靠这两个值判断当前遍历到哪条、一共几条。GetNews 返回 CArrayObj 指针,GetCurrentNew 则用 m_news.At(m_currentIndex) 取当前指针——这里若 m_currentIndex 越界会返回 NULL,调用前必须自己兜底。 SetNews 里先把当前索引归零,再定义分号分隔符 sep 和 u_sep 准备切字符串。外汇与贵金属受新闻跳空影响大,这类容器若没正确重置索引,可能在重大数据发布时误读上一条旧闻,带来错误信号。 下面这段是接口原貌,逐行看清楚成员怎么暴露:析构走 Deinit;取数全是 return 成员;SetCurrentIndex / SetTotal 只做赋值;SetNews 开头先 SetCurrentIndex(0) 再声明 sep=";" 与 u_sep。
class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CNewsContainer::~CNewsContainer(class="type">void) { Deinit(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCurrentIndex | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CNewsContainer::GetCurrentIndex(class="type">void) { class="kw">return m_currentIndex; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetTotal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CNewsContainer::GetTotal(class="type">void) { class="kw">return m_total; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetNews | class=class="str">"cmt">//+------------------------------------------------------------------+ CArrayObj *CNewsContainer::GetNews(class="type">void) { class="kw">return m_news; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCurrentNew | class=class="str">"cmt">//+------------------------------------------------------------------+ CNew *CNewsContainer::GetCurrentNew(class="type">void) { class="kw">return m_news.At(m_currentIndex); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetCurrentIndex | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNewsContainer::SetCurrentIndex(class="type">int index) { m_currentIndex=index; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetTotal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNewsContainer::SetTotal(class="type">int total) { m_total=total; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetNews | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNewsContainer::SetNews(class="type">void) { class=class="str">"cmt">//--- let&class="macro">#x27;s first init some vars! SetCurrentIndex(class="num">0); class="type">class="kw">string sep= ";"; class="type">class="kw">ushort u_sep;
把财经CSV塞进MT5内存的对象化写法
做新闻日历类指标时,第一步往往是把外部 CSV 读进 MT5 再转成可操作对象。下面这段逻辑用 StringSplit 按分隔符切行,再把每行构造成 CNew 实例塞进 m_news 动态数组,省去手写解析的状态机。 注意 FileReadString 配合 FILE_CSV 标志会按行返回,循环条件用 FileIsEnding 判断句柄是否到尾。实测若 CSV 首行是表头(如 country,time,event),必须在读完后 m_news.Delete(0) 清掉,否则第一条新闻会是脏数据。 析构侧也不能漏:Deinit 里用 DeleteRange(0, m_total-1) 批量释放,再 delete(m_news) 交还堆内存。外汇与贵金属受新闻跳空影响大,这类日历加载失败(Invalid_Handle)时务必 Print(GetLastError()) 定位路径权限问题,避免静默空跑。 别让表头混进新闻队列 m_news.Delete(0) 这行看着不起眼,但漏掉的话后续统计 Total() 会多算一条无效记录,导致小布类播报把「country」当国名念出来。
class="type">class="kw">string substrings[]; u_sep=StringGetCharacter(sep,class="num">0); class=class="str">"cmt">//--- then open and process the CSV file class="type">int file_handle=m_fileTxt.Open(m_csv, FILE_READ|FILE_CSV); if(file_handle!=INVALID_HANDLE) { while(!FileIsEnding(file_handle)) { class="type">class="kw">string line = FileReadString(file_handle); class="type">int k = StringSplit(line,u_sep,substrings); CNew *current = new CNew(substrings[class="num">0],(class="type">class="kw">datetime)substrings[class="num">1],substrings[class="num">2]); m_news.Add(current); } FileClose(file_handle); class=class="str">"cmt">//--- and finally refine and count the news m_news.Delete(class="num">0); class=class="str">"cmt">// --- here we class="kw">delete the CSV&class="macro">#x27;s header! SetTotal(m_news.Total()); } else { Print("Failed to open the file ",m_csv); Print("Error code ",GetLastError()); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNewsContainer initialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CNewsContainer::Init(class="type">void) { class=class="str">"cmt">// Initialization logic here... class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNewsContainer deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNewsContainer::Deinit(class="type">void) { m_news.DeleteRange(class="num">0, m_total-class="num">1); class="kw">delete(m_news); Print("CNewsContainer deinitialization performed!"); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNew.mqh | class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Object.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNew Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CNew : class="kw">public CObject { class="kw">protected: class="type">class="kw">string m_country; class=class="str">"cmt">// The country&class="macro">#x27;s name
「新闻事件类的存取与构造拆解」
在 MT5 里把一条财经新闻封装成对象,最朴素的做法就是建一个 CNew 类,把国家、发布时间、名称塞进私有成员。下面这段声明里,m_time_release 用 datetime 类型存发布时刻,m_name 用 string 存事件名,外部只能通过 Getter/Setter 碰它们。 构造函数 CNew::CNew 接收三个参数并直接赋值给成员变量,没有做校验——这意味着你传错时区或空字符串,对象也不会报错,回测时可能悄悄漏掉关键事件。 析构函数只调了 Deinit(),把资源清理交给单独方法,这种写法在 EA 里方便复用初始化逻辑。GetCountry 和 GetTimeRelease 都是单行 return,开销极低,适合在 OnTick 里高频调用。 别把正态当圣经:datetime 本质是秒级整数,跨服务器时区不对齐时,新闻触发判断会偏移数小时,实盘前务必用 Print(TimeToString(m_time_release)) 在 MT5 终端核对一次。
class="type">class="kw">datetime m_time_release; class=class="str">"cmt">// The date and time of the news class="type">class="kw">string m_name; class=class="str">"cmt">// The name of the news class="kw">public: class=class="str">"cmt">//--- Constructor and destructor methods CNew(class="type">class="kw">string country,class="type">class="kw">datetime time_release,class="type">class="kw">string name); ~CNew(class="type">void); class=class="str">"cmt">//--- Getter methods class="type">class="kw">string GetCountry(class="type">void); class="type">class="kw">datetime GetTimeRelease(class="type">void); class="type">class="kw">string GetName(class="type">void); class=class="str">"cmt">//--- Setter methods class="type">void SetCountry(class="type">class="kw">string country); class="type">void SetTimeRelease(class="type">class="kw">datetime time_release); class="type">void SetName(class="type">class="kw">string name); class=class="str">"cmt">//--- CNew specific methods class="type">bool Init(); class="type">void Deinit(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constuctor | class=class="str">"cmt">//+------------------------------------------------------------------+ CNew::CNew(class="type">class="kw">string country,class="type">class="kw">datetime time_release,class="type">class="kw">string name) { m_country=country; m_time_release=time_release; m_name=name; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CNew::~CNew(class="type">void) { Deinit(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetCountry | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CNew::GetCountry(class="type">void) { class="kw">return m_country; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetTimeRelease | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">datetime CNew::GetTimeRelease(class="type">void) { class="kw">return m_time_release; } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 新闻对象的存取与初始化封装
在 MT5 自建新闻解析类时,把字段访问做成显式 getter / setter 是最省心的做法。下面这段 CNew 类的成员函数,把名称、国家、发布时间三个字段和外部隔离,调用方改不了内部变量,只能走接口。 GetName 直接返回私有成员 m_name,没有参数;SetCountry 和 SetName 接收 string 入参并写回对应私有变量;SetTimeRelease 接收 datetime 类型,专门存发布时间戳。注意 datetime 是 MT5 原生时间类型,和服务器时间对齐时不用自己换算。 Init 目前只返回 true,注释里留了初始化逻辑位,实盘里你大概率要在这里拉一次经济日历接口或读文件把 m_name 等填实。Deinit 调了 Print 打一行注销日志,回测时能在专家日志里看到「CNew deinitialization performed!」确认对象销毁。 别把 Init 空壳当完成。真要跑新闻触发策略,至少得在 Init 里把 m_time_release 用 iTime 或 WebRequest 拿到的值填进去,否则后续按时间过滤财经事件会全部失效。
class=class="str">"cmt">//| GetName | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CNew::GetName(class="type">void) { class="kw">return m_name; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetCountry | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNew::SetCountry(class="type">class="kw">string country) { m_country=country; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetTimeRelease | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNew::SetTimeRelease(class="type">class="kw">datetime timeRelease) { m_time_release=timeRelease; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetName | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNew::SetName(class="type">class="kw">string name) { m_name=name; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNew initialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CNew::Init(class="type">void) { class=class="str">"cmt">//--- initialization logic here... class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNew deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CNew::Deinit(class="type">void) { class=class="str">"cmt">//--- deinitialization logic here... Print("CNew deinitialization performed!"); } class=class="str">"cmt">//+------------------------------------------------------------------+
把新闻Watcher跑进MT5回测台
这套新闻驱动 EA 的工程文件拆成两块:头文件(.mqh)放 MQL5\Include\Mine,主程序与类库放 MQL5\Experts\NewsWatcher。附带的数据文件原始是 .txt,必须手动改名为 news_watcher.csv 再丢进 data_folder\MQL5\FILES\,因为发布平台当时拦了 .csv 后缀。编译过后,就能像普通 EA 一样挂进策略测试器。 回测先用一组人为错开时间的假想新闻(动量值从 100.47 到 99.81 不等)验证可控环境下的触发逻辑,再换成 DailyFX 真实日程。初始参数固定为:周期 1 分钟、止损 400 点、止盈 600 点、手数 0.01、CsvFile 指向 news_watcher.csv。 真实数据下 EA 只进场三次。根因是现实新闻常重叠——比如 2013.07.15 12:30:00 同时撞上零售销售等多条——而程序逻辑是「有未平仓位就忽略后续新闻,先把当前序列平掉」。若同一时刻有多条重要事件,得在 CSV 里逐条单列,否则只认第一条。 下面这段是 EA 内部取动量并框定新闻前监听窗口的核心片段,timeWindow=600 表示新闻发布前 10 分钟开始盯盘: double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, 2); //--- Number of seconds before the news releases. GMT +- timeWindow is the real time from which the robot starts //--- listening to the market. For instance, if there is a news release programmed at 13:00 GMT you can set TimeWindow //--- to 900 seconds so that the EA starts listening to the market fifteen minutes before that news release. int timeWindow=600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); return; } //--- is there any open position? 外汇与贵金属受新闻跳空影响大,回测结果仅反映历史样本行为,实盘可能因点差扩大或流动性枯竭而偏离,属高风险验证。
Country;Time;Event USD;class="num">2013.06.class="num">03 class="num">17:class="num">19:class="num">00;A. Momentum equals class="num">100.47 USD;class="num">2013.06.class="num">13 class="num">17:class="num">09:class="num">00;B. Momentum equals class="num">100.40 USD;class="num">2013.06.class="num">21 class="num">18:class="num">52:class="num">00;C. Momentum equals class="num">100.19 USD;class="num">2013.07.class="num">01 class="num">17:class="num">32:class="num">00;D. Momentum equals class="num">100.18 USD;class="num">2013.07.class="num">08 class="num">15:class="num">17:class="num">00;E. Momentum equals class="num">100.18 USD;class="num">2013.07.class="num">16 class="num">10:class="num">00:class="num">00;F. Momentum equals class="num">99.81 USD;class="num">2013.07.class="num">24 class="num">09:class="num">30:class="num">00;G. Momentum equals class="num">100.25 Country;Time;Event USD;class="num">2013.07.class="num">15 class="num">12:class="num">00:class="num">00;USD Fed&class="macro">#x27;s Tarullo Speaks on Banking Regulation in Washington USD;class="num">2013.07.class="num">15 class="num">12:class="num">30:class="num">00;USD Advance Retail Sales(JUN) and others USD;class="num">2013.07.class="num">15 class="num">14:class="num">00:class="num">00;USD USD Business Inventories(MAY) USD;class="num">2013.07.class="num">15 class="num">21:class="num">00:class="num">00;USD EIA Gasoline and Diesel Fuel Update USD;class="num">2013.07.class="num">16 class="num">12:class="num">30:class="num">00;USD Several Consumer Price Indexes USD;class="num">2013.07.class="num">16 class="num">13:class="num">00:class="num">00;USD USD Net Long-term TIC Flows(MAY) & USD Total Net TIC Flows(MAY) USD;class="num">2013.07.class="num">16 class="num">13:class="num">15:class="num">00;USD Industrial Production(JUN) and others USD;class="num">2013.07.class="num">16 class="num">14:class="num">00:class="num">00;USD NAHB Housing Market Index(JUL) USD;class="num">2013.07.class="num">16 class="num">18:class="num">15:class="num">00;USD Fed&class="macro">#x27;s George Speaks on Economic Conditions and Agriculture USD;class="num">2013.07.class="num">22 class="num">12:class="num">30:class="num">00;USD Chicago Fed Nat Activity Index(JUN) USD;class="num">2013.07.class="num">22 class="num">14:class="num">00:class="num">00;USD Existing Home Sales(MoM) (JUN) & Existing Home Sales(JUN) USD;class="num">2013.07.class="num">22 class="num">21:class="num">00:class="num">00;USD EIA Gasoline and Diesel Fuel Update USD;class="num">2013.07.class="num">23 class="num">13:class="num">00:class="num">00;USD House Price Index(MoM) (MAY) USD;class="num">2013.07.class="num">23 class="num">14:class="num">00:class="num">00;USD Richmond Fed Manufacturing Index(JUL) USD;class="num">2013.07.class="num">24 class="num">11:class="num">00:class="num">00;USD MBA Mortgage Applications(JUL class="num">19) USD;class="num">2013.07.class="num">24 class="num">12:class="num">58:class="num">00;USD Markit US PMI Preliminary(JUL) USD;class="num">2013.07.class="num">24 class="num">14:class="num">00:class="num">00;USD USD New Home Sales(MoM) (JUN) & USD New Home Sales(JUN) USD;class="num">2013.07.class="num">24 class="num">14:class="num">30:class="num">00;USD USD DOE U.S. Crude Oil Inventories(JUL class="num">19) and others class="type">class="kw">double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, class="num">2); class=class="str">"cmt">//--- Number of seconds before the news releases. GMT +- timeWindow is the real time from which the robot starts class=class="str">"cmt">//--- listening to the market. For instance, if there is a news release programmed at class="num">13:class="num">00 GMT you can set TimeWindow class=class="str">"cmt">//--- to class="num">900 seconds so that the EA starts listening to the market fifteen minutes before that news release. class="type">int timeWindow=class="num">600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); class="type">int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+class="num">1); class="kw">return; } class=class="str">"cmt">//--- is there any open position?
「新闻窗口内的动量触发逻辑」
这段控制流只做一件事:当前品种没有持仓时,才允许在新闻发布前后一段时间窗内根据动量缓冲值去挂方向。若已持有仓位,则直接置为 DO_NOTHING,把后续交给数学期望值自行发挥作用,不再追单。 时间窗由 timeWindow 与 currentNew.GetTimeRelease() 共同决定,用 TimeGMT() 做边界判断:早于发布前 timeWindow 秒或晚于发布后 timeWindow 秒都不进场。 动量阈值写死在代码里——momentumBuffer[0] 大于 100.10 倾向 SELL,小于 99.90 倾向 BUY,中间地带不动作。这两个数不是拍脑袋,是相对 100 基准的 ±0.10 偏离带,开 MT5 把 momentumBuffer 打印出来就能看到实时值。 外汇与贵金属在新闻秒级波动里滑点可能吞掉阈值利润,实盘前先用策略测试器跑一遍该段逻辑确认触发频次。
if(!m_positionInfo.Select(_Symbol)) { class=class="str">"cmt">//--- if there is no open position, we try to open one class="type">bool timeHasCome = TimeGMT() >= currentNew.GetTimeRelease() - timeWindow && TimeGMT() <= currentNew.GetTimeRelease() + timeWindow; if(timeHasCome && momentumBuffer[class="num">0] > class="num">100.10) { GetEvolution().SetStatus(SELL); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+class="num">1); } else if(timeHasCome && momentumBuffer[class="num">0] < class="num">99.90) { GetEvolution().SetStatus(BUY); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+class="num">1); } } class=class="str">"cmt">//--- if there is an open position, we let it work the mathematical expectation else { GetEvolution().SetStatus(DO_NOTHING); }
◍ 画得少,看得清
这套新闻监听 EA 把面向对象容器和文件系统的活都拆给了 cnewscontainer.mqh 与 cnewswatcher.mqh,主程序 expertnewswatcher.mq5 只有 1.3 KB,真正下单逻辑反而最轻。附件里 11 个文件总体积不到 40 KB,却把新闻等级解析、CSV 落地、Watcher 轮询全分了层。 有用户在 XAUUSD M1 上跑出过错误代码 5002:终端找不到 news_watcher.csv,路径落在 Terminal\<hash>\MQL5\Files\ 下。开 MT5 前先确认该文件已手动丢进 Files 目录,否则初始化阶段就直接 deinit。 外汇和贵金属新闻行情瞬时跳空概率高,用挂单或秒级监听都只是降低滑点,不消除风险。类写清楚了,剩下是你自己决定监听哪些等级、过滤哪些币种。