智能交易系统策略改进
使用虚方法,CSnap 对象访问 CStrategy 对象。 但是 CStrategy 对象中必须有其他方法。 策略对象必须能够做出决策。 因此,如果检测到相应的信号,它应提供入场建议,然后执行入场。 我们赫兹量化软件需要一个方法,强制 CSnap 对象调用其 CreateSnap() 方法。 我们向 CStrategy 类添加一些方法:
virtual string Name() const = 0; virtual void CreateSnap() = 0; virtual bool MayAndEnter() = 0; virtual bool MayAndContinue() = 0; virtual void MayAndClose() = 0;
这是一个非常条件性的清单,可以针对特定的 EA 进行更改或扩展。 这些方法的作用:
Name() — 返回策略名称。
CreateSnap() — 调用 CSnap 对象的同名方法。
MayAndEnter() — 检查是否有入场信号,如有信号则入场。
MayAndContinue() — 检查是否有入场信号,如有信号则再次入场。
MayAndClose() — 检查是否有离场信号,如有信号则所有持仓平仓。
当然,这张清单还远远不够完整。 它缺少一个非常重要的方法,即策略初始化方法。 我们赫兹量化的目的是实现指向 CStrategy 对象中的所有模块,因此 OnTick() 和其他响应程序仅需访问 CStrategy 对象,而不必知晓其他模块的存在。 所以,需要将模块指针添加到 CStrategy 对象。 我们不能简单地提供相应的开放字段,并在 OnInit() 响应函数中对其进行初始化。 稍后将对此进行解释。 取而代之,我们添加一个初始化方法: virtual bool Initialize(CInitializeStruct* pInit) = 0; 利用初始化对象 CInitializeStruct,它包含所需的指针。 该对象在 CStrategy.mqh 中的描述如下:class CInitializeStruct {}; 它是一个空类,旨在继承,类似于 CStrategy。 我们赫兹量化已完成了准备工作,并可以进入真正的智能交易系统。 实际用例
![](https://gbres.dfcfw.com/Files/iimage/20240524/A2035A47796A83652B1AB634CBD3EB42_w520h520.jpg)
添加图片注释,不超过 140 字(可选)
我们赫兹量化根据一个非常简单的逻辑创建一个演示版的智能交易系统:如果前一根烛条看跌,则以固定的止盈和止损点数开立空头持仓。 在前一笔持仓未平之前,不应开新仓。 所有模块我们几乎都已准备就绪。 我们研究一下从 CStrategy 派生新类: class CRealStrat1 : public CStrategy { public: static string m_name; CRealStrat1(){}; ~CRealStrat1(){}; virtual string Name() const {return m_name;} virtual bool Initialize(CInitializeStruct* pInit) { m_pparam = ((CInit1* )pInit).m_pparam; m_psnap = ((CInit1* )pInit).m_psnap; m_ptrade = ((CInit1* )pInit).m_ptrade; m_psnap.SetStrategy(GetPointer(this)); return true; }//Initialize(EA_InitializeStruct* pInit) virtual void CreateSnap() { m_tb = 0; m_psnap.CreateSnap(); } virtual bool MayAndEnter(); virtual bool MayAndContinue() {return false;} virtual void MayAndClose() {} virtual bool Stop() {return false;} virtual void OnBuyFind (ulong ticket, double price, double lot) {} virtual void OnBuySFind (ulong ticket, double price, double lot) {} virtual void OnBuyLFind (ulong ticket, double price, double lot) {} virtual void OnSellFind (ulong ticket, double price, double lot) {tb = ticket;} virtual void OnSellSFind(ulong ticket, double price, double lot) {} virtual void OnSellLFind(ulong ticket, double price, double lot) {} private: CParam* m_pparam; CSnap* m_psnap; CTradeMod* m_ptrade; ulong m_tb; }; static string CRealStrat1::m_name = "Real Strategy 1"; bool CRealStrat1::MayAndEnter() { if (tb != 0) return false; double o = iOpen(NULL,PERIOD_CURRENT,1); double c = iClose(NULL,PERIOD_CURRENT,1); if (c < o) { m_ptrade.Sell(); return true; } return false; } EA 代码很简单,故无需赘言。 我们只考虑其中一部分。 CRealStrat1 类的 CreateSnap() 方法重置保存已有空头持仓票据的字段,并调用 CSnap 模块的 CreateSnap() 方法。 CSnap 模块检查开仓。 如果发现由此 EA 开立的空头持仓,则模块调用 CStrategy 类的 OnSellFind(...) 方法,该指针包含在 CSnap 模块中。 结果则是,CRealStrat1 类的 OnSellFind(...) 方法被调用。 它会再次更改 m_tb 字段值。 MayAndEnter() 方法看到一笔已开持仓,则不会再开新仓。 在我们的 EA 中未使用 CStrategy 基类的其他方法,因此其实现为空。 另一个有趣的问题涉及 Initialize(...) 方法。 此方法在 CRealStrat1 类中添加了指向其他模块的指针,这些也许要单独决策。 CStrategy 类不知道 CRealStrat1 类可能需要哪些模块,因此它使用一个空的 CInitializeStruct 类。 我们将在包含 CRealStrat1 类的文件中添加 CInit1 类(尽管这不是必需的)。 CInit1 继承自 CInitializeStruct: class CInit1: public CInitializeStruct { public: bool Initialize(CParam* pparam, CSnap* psnap, CTradeMod* ptrade) { if (CheckPointer(pparam) == POINTER_INVALID
CheckPointer(psnap) == POINTER_INVALID) return false; m_pparam = pparam; m_psnap = psnap; m_ptrade = ptrade; return true; } CParam* m_pparam; CSnap* m_psnap; CTradeMod* m_ptrade; }; 可在 OnInit 响应程序中创建和初始化类对象,并可将其传递给 CRealStrat1 类对象中的相应方法。 因此,我们已拥有一个由独立对象组成的复杂结构。 但我们可以通过 OnTick() 响应程序中的简单接口操作该结构。 OnInit() 和 OnTick() 响应程序
![](https://gbres.dfcfw.com/Files/iimage/20240524/EAA1FD3A622E98743A4CAB27C8D147D3_w720h338.jpg)
添加图片注释,不超过 140 字(可选)
此为 OnInit() 响应函数,及其可能的全局对象清单: CParam par; CSnap snap; CTradeMod trade; CStrategy* pS1; int OnInit() { ... pS1 = new CRealStrat1(); CInit1 ci; if (!ci.Initialize(GetPointer(par), GetPointer(snap), GetPointer(trade)) ) return (INIT_FAILED); pS1.Initialize(GetPointer(ci)); return (INIT_SUCCEEDED); } 在 OnInit() 响应程序中仅创建一个对象:CRealStrat1 类实例。 它用 CInit1 类对象初始化。 然后在 OnDeinit() 响应程序中销毁该对象:void OnDeinit(const int reason) { if (CheckPointer(pS1) != POINTER_INVALID) delete pS1; } 产生的 OnTick() 响应程序非常简单: void OnTick() { if (IsNewCandle() ){ pS1.CreateSnap(); pS1.MayAndEnter(); } } 在新烛条开盘时检查现有持仓,然后检查是否有入场信号。 如果有信号且 EA 尚未执行入场,则开一笔新仓。 该响应程序非常简单,因此可在其中轻易添加一些其他“全局”代码。 例如,您可以指令 EA 不要在图表上启动后立即开始交易,而是等待用户单击按钮来确认。 此处未叙述其他一些 EA 函数,但您可在随附的 zip 存档中找到它们。 因此,我们已设计完成一款由独立模块搭建的智能交易系统。 但这还不是全部。 我们看看这种编程方法提供的其他有趣机会。 下一步是什么? 进一步可实现的首件事情涉及模块的动态替换。 一个简单的时间表可被替换为更高级的时间表。 在这种情况下,我们需要将日程表替换为一个对象,而不是向现有的日程表添加属性和方法,那样会令调试和维护变得复杂。 我们可以为单独的模块提供“测试”和“发布”版本,创建一个管理器来控制模块。 但还有一个更有趣的可能性。 依照我们的编程方法,我们可以实现动态 EA 策略替换,在我们的案例中,该策略替换为 CRealStrat1 类。 产生的 EA 将有两个内核,它们执行两种策略,即趋势交易和横盘交易策略。 甚或,可以在亚洲时段增加第三种交易策略。 这意味着我们可以在一个 EA 中实现多个智能交易系统,并动态切换它们。 如何做到这一点:
开发一个包含决策逻辑的类,该类派生自 CStrategy 类(如 CRealStrat1)
开发一个类来初始化新策略,该类派生自 CInitializeStruct(CInit1)
将结果文件连接到项目。
在输入参数中添加一个新变量,其作用是在启动时设置激活策略。
实现策略切换器,即交易面板。
本文作者可以追加内容哦 !