另一个 MQL5 OOP 类·综合运用
(3/3)·当你已懂基础语法却卡在系统整合,这篇用完整案例把类、范式与回测串成一条线
不少交易者写得出单个 MQL5 类,却总在把策略逻辑、对象职责和回测验证拼到一起时乱了阵脚。本文是系列收尾篇,直接用一个随机交易 EA 的完整生命周期,演示 OOP 在实盘编程里的综合用法。
◍ 进化类与图形容器的存取接口
这段代码片段展示了一个简易 EA 框架里两个基础类的存取设计:CEvolution 负责维护状态与操作集合,CGraphic 负责承载某交易品种的周期与 K 线数组。 CEvolution 用 GetStatus() 直接返回 m_status 枚举,GetOperations() 返回 CArrayObj 指针,外部可遍历已加入的 CObject 派生操作。SetOperation() 内部调用 m_operations.Add(operation),意味着每次挂新操作都往同一动态数组追加,没有去重逻辑。 CGraphic 的成员变量暴露了三个关键信息:m_period 存 ENUM_TIMEFRAMES,m_pair 存品种名,m_symbol 是 CSymbolInfo 指针,m_bars 是 CArrayObj 指针用于装 K 线。它的公开接口只列了构造/析构、GetPair、GetSymbol、GetBars、SetPair,说明图形对象本身不负责写盘,只做内存数据桥接。 在 MT5 里跑这套结构时,注意 m_operations.Add() 若传入栈对象地址会随作用域销毁而悬空;用 new 派生自 CObject 的实例并交给数组托管才稳。外汇与贵金属杠杆高,这类框架仅作逻辑原型,实盘前须自行补风控。
{
class="kw">return m_status;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| GetOperations |
class=class="str">"cmt">//+------------------------------------------------------------------+
CArrayObj *CEvolution::GetOperations(class="type">void)
{
class="kw">return m_operations;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetStatus |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEvolution::SetStatus(ENUM_STATUS_EA status)
{
m_status=status;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| SetOperation |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CEvolution::SetOperation(CObject *operation)
{
m_operations.Add(operation);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ExpertSimpleRandom.mq5 |
class=class="str">"cmt">//| Copyright © class="num">2013, Jordi Bassagaсas |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade\SymbolInfo.mqh>
class="macro">#include <Arrays\ArrayObj.mqh>
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CGrapic Class |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CGraphic
{
class="kw">protected:
ENUM_TIMEFRAMES m_period; class=class="str">"cmt">// Graphic&class="macro">#x27;s timeframe
class="type">class="kw">string m_pair; class=class="str">"cmt">// Graphic&class="macro">#x27;s pair
CSymbolInfo* m_symbol; class=class="str">"cmt">// CSymbolInfo object
CArrayObj* m_bars; class=class="str">"cmt">// Array of bars
class="kw">public:
class=class="str">"cmt">//--- Constructor and destructor methods
CGraphic(class="type">class="kw">string pair);
~CGraphic(class="type">void);
class=class="str">"cmt">//--- Getter methods
class="type">class="kw">string GetPair(class="type">void);
CSymbolInfo *GetSymbol(class="type">void);
CArrayObj *GetBars(class="type">void);
class=class="str">"cmt">//--- Setter methods
class="type">void SetPair(class="type">class="kw">string pair);「图形类成员与存取接口的实现骨架」
在自定义图形分析类里,构造函数只接一个货币对字符串,默认周期锁在 M1。这意味着你 new 一个对象时若不带参数重载,它只会去抓一分钟裸 K,做更高周期的价格行为还原得自己改 m_period。 下面这段是类里暴露给外部的存取通道:拿货币对、拿品种信息指针、拿 K 线对象数组,以及对应的 setter。注意 GetSymbol 返回的是 CSymbolInfo*,未初始化时就是空指针,直接解引用会崩 EA。 void SetSymbol(CSymbolInfo *symbol); void SetBar(CObject *bar); }; //+------------------------------------------------------------------+
| // | Constuctor |
|---|
//+------------------------------------------------------------------+ CGraphic::CGraphic(string pair) { m_period=PERIOD_M1; m_pair=pair; } //+------------------------------------------------------------------+
| // | Destructor |
|---|
//+------------------------------------------------------------------+ CGraphic::~CGraphic(void) { } //+------------------------------------------------------------------+
| // | GetPair |
|---|
//+------------------------------------------------------------------+ string CGraphic::GetPair(void) { return m_pair; } //+------------------------------------------------------------------+
| // | GetSymbol |
|---|
//+------------------------------------------------------------------+ CSymbolInfo *CGraphic::GetSymbol(void) { return m_symbol; } //+------------------------------------------------------------------+
| // | GetBars |
|---|
//+------------------------------------------------------------------+ CArrayObj *CGraphic::GetBars(void) { return m_bars; } //+------------------------------------------------------------------+
| // | SetPair |
|---|
//+------------------------------------------------------------------+ void CGraphic::SetPair(string pair) { m_pair=pair; } //+------------------------------------------------------------------+
| // | SetSymbol |
|---|
//+------------------------------------------------------------------+ void CGraphic::SetSymbol(CSymbolInfo *symbol) { m_symbol=symbol; } //+------------------------------------------------------------------+
| // | SetBar |
|---|
//+------------------------------------------------------------------+ 逐行拆一下关键几行:CGraphic::CGraphic(string pair) 是构造器,把传入的 pair 存进 m_pair,同时 m_period 写死 PERIOD_M1。string CGraphic::GetPair(void) 单纯回传 m_pair,无副作用。CSymbolInfo *CGraphic::GetSymbol(void) 回传 m_symbol 指针,调用前必须确认 SetSymbol 已跑过。void CGraphic::SetSymbol(CSymbolInfo *symbol) 把外部品种对象地址赋给成员,后续点差、tick 值计算都靠它。 外汇与贵金属杠杆高、滑点跳空频繁,在 MT5 里跑这套类之前,先手动给 SetSymbol 传一个已 Refresh 的 CSymbolInfo,否则 GetSymbol 拿到的空指针会让回测在首根 K 线就终止。
class="type">void SetSymbol(CSymbolInfo *symbol); class="type">void SetBar(CObject *bar); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constuctor | class=class="str">"cmt">//+------------------------------------------------------------------+ CGraphic::CGraphic(class="type">class="kw">string pair) { m_period=PERIOD_M1; m_pair=pair; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CGraphic::~CGraphic(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetPair | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CGraphic::GetPair(class="type">void) { class="kw">return m_pair; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetSymbol | class=class="str">"cmt">//+------------------------------------------------------------------+ CSymbolInfo *CGraphic::GetSymbol(class="type">void) { class="kw">return m_symbol; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| GetBars | class=class="str">"cmt">//+------------------------------------------------------------------+ CArrayObj *CGraphic::GetBars(class="type">void) { class="kw">return m_bars; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetPair | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGraphic::SetPair(class="type">class="kw">string pair) { m_pair=pair; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetSymbol | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGraphic::SetSymbol(CSymbolInfo *symbol) { m_symbol=symbol; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SetBar | class=class="str">"cmt">//+------------------------------------------------------------------+
给图形对象追加单根K线
在 MQL5 的 CGraphic 封装里,向图表叠加自定义 K 线并不复杂,核心就是往内部容器塞对象。下面这段逻辑把外部传入的 CObject 指针直接挂到 m_bars 动态数组尾部。 m_bars 通常是 CArrayObj 类型,Add() 只做指针接管、不复制实体,所以 bar 的生命周期得由调用方保证,否则图形刷新时容易踩野指针。 实际调试时可在 MT5 策略测试器里下断点,观察 m_bars.Total() 是否随每次 SetBar 调用 +1,验证追加是否生效。
class="type">void CGraphic::SetBar(CObject *bar) { m_bars.Add(bar); } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 随机系统的止损获利只在回测里现形
把 ExpertSimpleRandom.mq5 跑进 MT5 策略测试器,结论很直接:这套纯随机进场的逻辑,只对某些特定的止损、获利间距勉强有效。而且赢家参数不跨品种通用——每个货币对在同一时段的波动性格不同,EURUSD 的甜点位是 400/600,换 pair 可能完全失效。 所以真金白银上之前,先拿历史数据把 StopLoss、TakeProfit 扫一遍。下面这组是 2012 年 1 月 EURUSD 的采样输入:止损 400 点、获利 600 点、手数 0.01、生命周期按月。同一配置连跑三次,结果分布会随随机种子漂移,但能反推出该间距下系统不亏的区间。 外汇和贵金属都是高杠杆高风险品种,这类随机系统实盘存活概率极低,回测赢家参数只是帮你确认「随机也能碰巧活」的边界,不是交易依据。
「把工具请下神坛」
把自动交易系统当成有大脑、会演变、看图表的生命体,只是帮你把“震荡市空头近似随机”的直觉落成代码。2012年1月跑过多轮回测,多数情况下系统能赢,但效率受限于策略本身的简单性,别指望它扛住所有行情。
社区里有人指出 CSimpleRandom.mqh 漏了 CPositionInfo 对象创建,直接编译跑会触发无效指针访问;作者在 ExpertSimpleRandom.mq5 第23行补了 CSimpleRandom *SR=new CSimpleRandom(...),并在后续重传了文件。你拉代码时认准这一行,否则 MT5 会报指针错。
外汇和贵金属是高杠杆品种,这类随机倾向策略只在特定震荡结构里概率占优,实盘前务必用策略测试器自跑一遍,把 StopLoss、TakeProfit、LotSize 按你的品种波动调一调。
class="num">82 строчка m_positionInfo=new CPositionInfo(); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|ExpertSimpleRandom.mq5 class=class="str">"cmt">//|版权所有 © class="num">2013, Jordi Bassagaças || class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "版权所有 © class="num">2013, laplacianlab" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#include <Trade\Trade.mqh> class="macro">#include <Trade\SymbolInfo.mqh> class="macro">#include <Trade\PositionInfo.mqh> class="macro">#include <Indicators\Indicators.mqh> class="macro">#include <Mine\Enums.mqh> class="macro">#include <..\Experts\SimpleRandom\CSimpleRandom.mqh> class="kw">input class="type">int StopLoss; class="kw">input class="type">int TakeProfit; class="kw">input class="type">class="kw">double LotSize; class="kw">input ENUM_LIFE_EA TimeLife; class="type">MqlTick tick; CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 初始化函数| class=class="str">"cmt">//+------------------------------------------------------------------+