文章 "开发多币种 EA 交易(第 1 部分):多种交易策略的协作"

 

新文章 开发多币种 EA 交易(第 1 部分):多种交易策略的协作已发布:

交易策略是多种多样的,因此,或许可以采用几种策略并行运作,以分散风险,提高交易结果的稳定性。但是,如果每个策略都作为单独的 EA 交易来实现,那么在一个交易账户上管理它们的工作就会变得更加困难。为了解决这个问题,在一个 EA 中实现不同交易策略的操作是合理的。

我们需要决定我们想要什么,以及我们拥有什么。

我们有(或几乎有):

  • 以现成 EA 代码的形式,或仅仅是一套用于执行交易操作的规则,在不同交易品种和时间框架上运行的一些不同的交易策略
  • 起始存款
  • 最大允许回撤

我们想要:

  • 在多个交易品种和时间框架上,在一个账户中协作使用所有选定的策略
  • 在每个策略之间平均分配或按照规定的比例分配起始存款
  • 自动计算已开立头寸的数量,以符合最大允许回撤量的要求
  • 正确处理终端重启
  • 能够在 MetaTrader 5 和 4 中运行

我们将使用面向对象的方法、MQL5 和 MetaTrader 5 中的标准测试器。

手头的任务相当繁重,因此我们将逐步解决。

作者:Yuriy Bykov

 
class CStrategy : public CObject {
protected:
   ulong             m_magic;          // 魔术
   string            m_symbol;         // 符号(交易工具)
   ENUM_TIMEFRAMES   m_timeframe;      // 图表周期(时间框架)
   double            m_fixedLot;       // 未结头寸的大小(固定)

public:
   // 构造函数
   CStrategy(ulong p_magic,
             string p_symbol,
             ENUM_TIMEFRAMES p_timeframe,
             double p_fixedLot);

   virtual int       Init() = 0; // 策略初始化 - OnInit 事件处理
   virtual void      Tick() = 0; // 主要方法 - OnTick 事件处理
};

如果有构造函数,为什么还需要 Init 方法?

出于某种原因,他们立即将 TS 类限制为一个符号和一个时间框架。


这似乎更符合逻辑。

class SYSTEM
{
public:
  virtual void OnTick() {}
};
 
fxsaber #:

如果有构造函数,为什么还需要 Init 方法?

出于某种原因,他们立即将 TS 类限制为一个符号和一个时间框架。

我喜欢作者的方法。文章中有这样一段话

Init()和Tick()方法被声明为纯虚拟方法(方法头 = 0 后)。这意味着我们不会在 CStrategy 类中编写这些方法的实现。我们将在该类的基础上创建子类,其中必须包含 Init() 和 Tick() 方法,并包含特定交易规则的实现。

据我所知,该类将是抽象 的,.....。

 
Denis Kirichenko #:

据我所知,该类将是抽象 的.....。

会是的。由于某种原因,它被用于子类中。如果不使用 Deinit(有一个析构函数),那么不使用 Init(有一个构造函数)也是合乎逻辑的。

//+------------------------------------------------------------------+
//| 构造函数|
//+------------------------------------------------------------------+
CSimpleVolumeStrategy::CSimpleVolumeStrategy(
   ulong            p_magic,
   string           p_symbol,
   ENUM_TIMEFRAMES  p_timeframe,
   double           p_fixedLot,
   int              p_signalPeriod,
   double           p_signalDeviation,
   double           p_signaAddlDeviation,
   int              p_openDistance,
   double           p_stopLevel,
   double           p_takeLevel,
   int              p_ordersExpiration,
   int              p_maxCountOfOrders) :
   // 初始化列表
   CStrategy(p_magic, p_symbol, p_timeframe, p_fixedLot), // 调用基类构造函数
   signalPeriod_(p_signalPeriod),
   signalDeviation_(p_signalDeviation),
   signaAddlDeviation_(p_signaAddlDeviation),
   openDistance_(p_openDistance),
   stopLevel_(p_stopLevel),
   takeLevel_(p_takeLevel),
   ordersExpiration_(p_ordersExpiration),
   maxCountOfOrders_(p_maxCountOfOrders)
{}

//+------------------------------------------------------------------+
//| 专家的初始化函数
//+------------------------------------------------------------------+
int CSimpleVolumeStrategy::Init() {
// 加载指标以获取刻度线量
   iVolumesHandle = iVolumes(m_symbol, m_timeframe, VOLUME_TICK);

// 设置刻度卷数组接收器的大小和所需的寻址方式
   ArrayResize(volumes, signalPeriod_);
   ArraySetAsSeries(volumes, true);

// 设置通过交易下单的神奇号码
   trade.SetExpertMagicNumber(m_magic);

   return(INIT_SUCCEEDED);
}

而人为地缩小可能的 TC 范围是一个奇怪的解决方案。


你还可以清楚地看到,由于 OOP 的存在,输入变得繁琐。把它去掉会很好。

 

之所以暂时不使用 Init(),是因为构造函数不可能返回结果。但我们有可能永远都不需要在策略初始化时返回 INIT_SUCCESS 以外的结果。因此,这个方法很有可能在将来被移除。

以符号和时间框架的形式分配强制策略属性是一种有意的限制。在设计上,多个符号的交易将通过该类继承者的多个实例来完成,但每个特定实例只与单个符号一起工作。我还没有发现任何策略会受到这种限制的阻碍。相反,在考虑的每种策略中都发现了这些参数,这就是为什么决定将它们一次性放入基类的原因。

不过,我打算在将来考虑一些多符号策略,因为这些策略无法分成几个独立的单符号策略(如果有的话)。我不认为基类中的符号和时间框架属性 会对使用多个符号和多个时间框架的子类的实现造成很大阻碍。

 
Yuriy Bykov 属性 会妨碍您实现使用多个符号和多个时间框架的子类。

100% 不会造成干扰。它只是一个不必要的实体。OOP 架构遵循从一般到特殊的原则。您将一般类(基类)设置为 "私有"。尽管那里调用的只是 CStrategy::Tick()。

 
Denis Kirichenko #:

我喜欢作者的做法。文章中有这样一段话

那么这个类将是抽象 的,据我所知.....。

是的,它只用于获取子类。您不需要创建基类 CStrategy 的对象。但任何子类对象都可以通过 CAdvisor::AddStrategy(CStrategy &strategy) 方法添加到 Expert Advisor 对象中。

 
Yuriy Bykov #:

之所以暂时不使用 Init(),是因为构造函数不可能返回结果。但我们有可能永远都不需要在策略初始化时返回 INIT_SUCCESS 以外的结果。因此,这个方法很有可能在将来被删除。

为了防止在构造函数中出现错误(指标句柄未充电或内存未分配等),有些人会保留这样一个普通变量。
static int CStrategy::InitFlag = INIT_FAILED;
 
fxsaber #:
为了防止在构造函数中出错(指示器句柄未充电或内存未分配),有些人会保留这样一个共享变量。

是的,我已经想到了。我会尝试这样做的。

 
Yuriy Bykov #:

任何子类对象都可以传递给 EA 对象,以便添加到 CAdvisor::AddStrategy(CStrategy &strategy) 方法中。

这似乎是编译器的一个错误,当这样调用时,它不会对这个方法签名发誓。

   expert.AddStrategy(new CSimpleVolumeStrategy(
                         magic_ + 1, "EURGBP", PERIOD_H1,
                         NormalizeDouble(0.34 * depoPart_, 2),
                         130, 0.9, 1.4, 231, 3750, 50, 600, 3)

应该是这样的

CAdvisor::AddStrategy(CStrategy* strategy)
 
CObject 继承。
class CStrategy : public CObject {

你不用它。

class CAdvisor : public CObject {
protected:
   CStrategy         *m_strategies[];  // 交易策略阵列

我注意到从 CObject 继承是很常见的做法。