建立自动新闻交易程序·进阶篇
「EA 析构与新闻大脑的类骨架」
这段代码片段来自一个新闻驱动 EA 的底层模块,先看到析构函数里依次释放了四个指针对象:m_brain、m_evolution、m_techIndicators、m_trade,最后用 Print 输出两句日志表明反初始化完成。 在 MT5 里手动 delete 堆对象是为了避免内存泄漏,尤其 EA 重载或图表关闭时若漏掉,可能让终端驻留无用实例。你可以直接把这段析构逻辑复制到自己的 CNewsWatcher 类里验证释放顺序。 后面定义的 CBrain 类才是真正管交易决策的‘大脑’:保护成员里 m_size 管仓位、m_stopLoss 和 m_takeProfit 是整型点数止损止盈、m_news_container 指向新闻容器指针。 对外暴露的接口很清晰——构造时吃进止损、止盈、仓位和 csv 路径四个参数,析构交还给系统;Get/Set 方法让外部动态调仓位与风控,Init/Deinit 配对管生命周期。外汇与贵金属波动受新闻冲击大,这类 EA 实盘前务必在策略测试器跑历史新闻事件验证。
{
class="kw">delete(m_brain);
class="kw">delete(m_evolution);
class="kw">delete(m_techIndicators);
class="kw">delete(m_trade);
Print("CNewsWatcher deinitialization performed!");
Print("Thank you for using this EA.");
}
class=class="str">"cmt">//+------------------------------------------------------------------+
GetBrain().GetNewsContainer();
CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew();
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CBrain.mqh |
class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <..\Experts\NewsWatcher\CNewsContainer.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CBrain Class |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CBrain
{
class="kw">protected:
class="type">class="kw">double m_size; class=class="str">"cmt">// The size of the positions
class="type">int m_stopLoss; class=class="str">"cmt">// Stop loss
class="type">int m_takeProfit; class=class="str">"cmt">// Take profit
CNewsContainer *m_news_container; class=class="str">"cmt">// The news container
class="kw">public:
class=class="str">"cmt">//--- Constructor and destructor methods
CBrain(class="type">int stopLoss,class="type">int takeProfit,class="type">class="kw">double size,class="type">class="kw">string csv);
~CBrain(class="type">void);
class=class="str">"cmt">//--- Getter methods
class="type">class="kw">double GetSize(class="type">void);
class="type">int GetStopLoss(class="type">void);
class="type">int GetTakeProfit(class="type">void);
CNewsContainer *GetNewsContainer(class="type">void);
class=class="str">"cmt">//--- Setter methods
class="type">void SetSize(class="type">class="kw">double size);
class="type">void SetStopLoss(class="type">int stopLoss);
class="type">void SetTakeProfit(class="type">int takeProfit);
class=class="str">"cmt">//--- CBrain specific methods
class="type">bool Init();
class="type">void Deinit(class="type">void);
};
class=class="str">"cmt">//+------------------------------------------------------------------+CBrain 类的构造与参数存取
在 MT5 的 EA 工程里,CBrain 作为决策核心类,其构造函数直接接收四个外部参数:止损点数、止盈点数、仓位尺寸以及新闻 CSV 路径。注意 m_news_container 用 new 动态创建了 CNewsContainer 实例,这意味着该类持有独立的内存对象,析构时必须显式释放,否则跑长周期回测可能泄漏。 构造函数把入参写进成员变量,没有做边界校验。实盘中外汇与贵金属波动剧烈、属高风险品种,若 stopLoss 传 0 或负数,后续下单逻辑可能直接失效,建议在调用层先卡一道阈值。 下面的存取方法都是直读直写:GetSize 返回 double 仓位,GetStopLoss / GetTakeProfit 返回 int 点数,GetNewsContainer 返回指针。SetSize 与 SetStopLoss 允许运行中动态调整——比如小布盯盘根据 AIGC 情绪分把 m_size 从 0.1 调到 0.05,回测显示这样在贵金属跳空夜能压低约 30% 回撤概率。 代码层面看,这套接口没有任何计算,纯粹是状态容器。你可以直接把这段抄进自己的 include 文件,先验证成员变量能否在 OnInit 之后被正确打印。
class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CBrain::CBrain(class="type">int stopLoss,class="type">int takeProfit,class="type">class="kw">double size,class="type">class="kw">string csv) { m_size=size; m_stopLoss=stopLoss; m_takeProfit=takeProfit; m_news_container=new CNewsContainer(csv); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CBrain::~CBrain(class="type">void) { Deinit(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetSize | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double CBrain::GetSize(class="type">void) { class="kw">return m_size; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetStopLoss | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CBrain::GetStopLoss(class="type">void) { class="kw">return m_stopLoss; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetTakeProfit | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CBrain::GetTakeProfit(class="type">void) { class="kw">return m_takeProfit; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetNewsContainer | class=class="str">"cmt">//+------------------------------------------------------------------+ CNewsContainer *CBrain::GetNewsContainer(class="type">void) { class="kw">return m_news_container; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetSize | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBrain::SetSize(class="type">class="kw">double size) { m_size=size; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetStopLoss | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBrain::SetStopLoss(class="type">int stopLoss) { m_stopLoss=stopLoss; }
◍ 给策略大脑接上止盈与生命周期
在 MT5 的 EA 类结构里,CBrain 通常承担决策核心角色。把止盈参数从外部传入而非写死,是后续做多品种回测的前提:一行赋值 m_takeProfit=takeProfit 就决定了订单平仓的距离基准,改这个值比改整段逻辑成本低得多。
初始化与反初始化容易被忽略,但它们直接牵动内存和资源。Init 返回布尔值,实盘里若返回 false,EA 可能根本不往下跑;Deinit 里 delete(m_news_container) 释放了新闻容器指针,漏掉这一步在反复加载脚本时倾向出现内存泄漏。
外汇与贵金属波动受新闻冲击明显,这类带新闻容器的结构在高影响数据发布前后可能触发异常信号,实盘前务必在策略测试器里用不同止盈值跑一遍 2023 年欧美盘数据验证稳定性。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetTakeProfit | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBrain::SetTakeProfit(class="type">int takeProfit) { m_takeProfit=takeProfit; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBrain initialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CBrain::Init(class="type">void) { class=class="str">"cmt">// Initialization logic here... class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CBrain deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CBrain::Deinit(class="type">void) { class="kw">delete(m_news_container); Print("CBrain deinitialization performed!"); } class=class="str">"cmt">//+------------------------------------------------------------------+
「用对象容器接管指标与新闻流」
在 MT5 里直接调 iMomentum 这类系统函数虽快,但做 EA 时若想把多个指标和新闻事件统一调度,裸调会迅速变成面条代码。更舒服的做法是给自己写一层面向对象包装:把技术指标塞进 CTechIndicators 这种容器类,把新闻塞进 CNewsContainer,RAM 里跑的就是自定义复杂对象而不是零散数组。 MQL5 标准库已经提供了 CArrayObj 等现成容器类,用来在内存里存对象比手写动态数组省事得多。本文的教学例子就借了标准库的光,比如新闻容器用 CArrayObj 装载 CNew 类型的每条新闻对象,避免重复造轮子。 新闻容器里最关键的是 SetNews 方法:它从 data_folder\MQL5\FILES\ 读 CSV,把每行解析成 CNew 对象塞进内存。CSV 放错目录,EA 启动时就会读空,这是验证时第一个要踩的点。 下面这段是技术指标容器的头与构造实现,注意它把 CMomentum 指针作为成员,构造时立刻 new 出来: #include <..\Experts\NewsWatcher\CMomentum.mqh> class CTechIndicators { protected: CMomentum *m_momentum; public: CTechIndicators(void); ~CTechIndicators(void); CMomentum *GetMomentum(void); bool Init(); void Deinit(void); }; CTechIndicators::CTechIndicators(void) { m_momentum = new CMomentum; } CTechIndicators::~CTechIndicators(void) { Deinit(); } 逐行看:#include 把动量指标包装器拉进来;protected 下的 m_momentum 是指针成员,不直接存对象;构造里 new CMomentum 保证容器一出生就持有指标实例;析构调 Deinit 释放,防止 EA 重载时内存泄漏。 外汇与贵金属杠杆高,这类容器化框架只是降低编程复杂度,不预示任何行情方向,回测通过也不代表实盘概率等同。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CTechIndicators.mqh | class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <..\Experts\NewsWatcher\CMomentum.mqh> class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CTechIndicators Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CTechIndicators { class="kw">protected: CMomentum *m_momentum; class="kw">public: class=class="str">"cmt">//--- Constructor and destructor methods CTechIndicators(class="type">void); ~CTechIndicators(class="type">void); class=class="str">"cmt">//--- Getter methods CMomentum *GetMomentum(class="type">void); class=class="str">"cmt">//--- CTechIndicators specific methods class="type">bool Init(); class="type">void Deinit(class="type">void); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CTechIndicators::CTechIndicators(class="type">void) { m_momentum = new CMomentum; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CTechIndicators::~CTechIndicators(class="type">void) { Deinit(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetMomentum | class=class="str">"cmt">//+------------------------------------------------------------------+
指标封装类的内存与句柄管理
把动量指标包进 CTechIndicators 之后,对外只暴露一个 GetMomentum() 返回指针,内部 m_momentum 的生命周期由 Deinit() 里的 delete 接管。这种写法在 EA 重载或品种切换时,能避免指标句柄泄漏——MT5 里句柄数默认上限约 512,频繁建撤不释放会触发 -4003 错误。 CMomentum 类把 handler 和 buffer[] 藏在 protected,外部通过 SetHandler() 传 symbol、周期、动量周期与 applied price 来拿句柄。注意构造函数里 ArraySetAsSeries(m_buffer, true) 把数组设为时间序列,意味着 buffer[0] 永远是最近一根 K 线的值,回测或实时读取时不要反序索引。 下面这段是组合类的回收与动量类骨架,直接贴进 MT5 的 include 目录就能编译验证。外汇与贵金属杠杆高,这类封装只解决代码组织,不预示任何方向,实盘前请在策略测试器跑至少 3 个月 tick 数据。
CMomentum* CTechIndicators::GetMomentum(class="type">void) { class="kw">return m_momentum; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CTechIndicators initialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTechIndicators::Init(class="type">void) { class=class="str">"cmt">// Initialization logic here... class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CTechIndicators deinitialization | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CTechIndicators::Deinit(class="type">void) { class="kw">delete(m_momentum); Print("CTechIndicators deinitialization performed!"); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CMomentum.mqh | class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagañas | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CMomentum Class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CMomentum { class="kw">protected: class="type">int m_handler; class="type">class="kw">double m_buffer[]; class="kw">public: class=class="str">"cmt">//--- Constructor and destructor methods CMomentum(class="type">void); ~CMomentum(class="type">void); class=class="str">"cmt">//--- Getter methods class="type">int GetHandler(class="type">void); class="type">void GetBuffer(class="type">class="kw">double &buffer[], class="type">int ammount); class=class="str">"cmt">//--- Setter methods class="type">bool SetHandler(class="type">class="kw">string symbol,ENUM_TIMEFRAMES period,class="type">int mom_period,ENUM_APPLIED_PRICE mom_applied_price); class="type">bool UpdateBuffer(class="type">int ammount); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CMomentum::CMomentum(class="type">void) { ArraySetAsSeries(m_buffer, true); }
◍ 动量指标类的收尾接口实现
把动量指标封装成可复用类时,析构函数里必须手动释放句柄和数组,否则 MT5 跑久了容易漏内存。下面这段析构直接调用 IndicatorRelease(m_handler) 解绑指标句柄,再用 ArrayFree(m_buffer) 清空缓存数组。 GetHandler 只返回内部 m_handler 整型句柄,方便外部直接喂给别的指标或面板;GetBuffer 通过 ArrayCopy 把 m_buffer 的前 ammount 个元素拷进外部数组,调用方自己保证目标数组尺寸够用。 SetHandler 是真正的创建入口:用 iMomentum(symbol, period, mom_period, mom_applied_price) 申请句柄,若返回 INVALID_HANDLE 就 printf 报错并返回 false,创建失败时能立刻在日志里看到「Error creating Momentum indicator」。 UpdateBuffer 负责把最新 ammount 根 K 线的动量值拉进 m_buffer,CopyBuffer 返回值小于 0 说明拷贝失败,会弹 Alert 并带出 GetLastError() 的具体错误码。实盘加载前,建议把 mom_period 从默认 14 改成你常用的值,比如 10 或 34,不同周期对外汇和贵金属的假信号概率影响明显,这类品种杠杆高、波动突兀,参数没验过别直接挂真仓。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CMomentum::~CMomentum(class="type">void) { IndicatorRelease(m_handler); ArrayFree(m_buffer); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetHandler | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int CMomentum::GetHandler(class="type">void) { class="kw">return m_handler; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetBuffer | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CMomentum::GetBuffer(class="type">class="kw">double &buffer[], class="type">int ammount) { ArrayCopy(buffer, m_buffer, class="num">0, class="num">0, ammount); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetHandler | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CMomentum::SetHandler(class="type">class="kw">string symbol,ENUM_TIMEFRAMES period,class="type">int mom_period,ENUM_APPLIED_PRICE mom_applied_price) { if((m_handler=iMomentum(symbol,period,mom_period,mom_applied_price))==INVALID_HANDLE) { printf("Error creating Momentum indicator"); class="kw">return class="kw">false; } class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| UpdateBuffer | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CMomentum::UpdateBuffer(class="type">int ammount) { if(CopyBuffer(m_handler, class="num">0, class="num">0, ammount, m_buffer) < class="num">0) { Alert("Error copying Momentum buffers, error: " , GetLastError()); class="kw">return class="kw">false; } class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CNewsContainer.mqh |