Discussão do artigo "Desenvolvendo um EA multimoeda (Parte 1): várias estratégias de trading trabalhando juntas"

 

Novo artigo Desenvolvendo um EA multimoeda (Parte 1): várias estratégias de trading trabalhando juntas foi publicado:

Existem várias estratégias de trading. Do ponto de vista da diversificação de riscos e do aumento da estabilidade dos resultados de trading, pode ser útil usar várias estratégias em paralelo. Mas se cada estratégia for implementada como um EA separado, gerenciar o trabalho conjunto delas em uma conta de trading se torna muito mais complicado. Para resolver esse problema, é um boa idea implementar o trabalho de diferentes estratégias de trading em um único EA.

Precisamos definir o que queremos e o que temos.

Nós temos (ou quase temos):

  • algumas estratégias de trading diferentes, funcionando em diferentes símbolos e timeframes como código de EA já pronto ou apenas um conjunto formulado de regras de operações de trading
  • depósito inicial
  • rebaixamento máximo permitido

Nós queremos:

  • trabalho conjunto de todas as estratégias escolhidas em uma conta, em vários símbolos e timeframes
  • distribuição do depósito inicial entre todos de forma igual ou de acordo com coeficientes estabelecidos
  • cálculo automático dos volumes das posições abertas para respeitar o rebaixamento máximo permitido
  • processamento correto do reinício do terminal
  • possibilidade de execução no MT5 e MT4

Usaremos uma abordagem orientada a objetos, MQL5, o testador padrão no MetaTrader 5.

A tarefa definida é bastante grande, então vamos resolvê-la por etapas.

Autor: Yuriy Bykov

 
class CStrategy : public CObject {
protected:
   ulong             m_magic;          // Magic
   string            m_symbol;         // Símbolo (instrumento de negociação)
   ENUM_TIMEFRAMES   m_timeframe;      // Período do gráfico (timeframe)
   double            m_fixedLot;       // Tamanho das posições abertas (fixo)

public:
   // Construtor
   CStrategy(ulong p_magic,
             string p_symbol,
             ENUM_TIMEFRAMES p_timeframe,
             double p_fixedLot);

   virtual int       Init() = 0; // Inicialização da estratégia - processamento do evento OnInit
   virtual void      Tick() = 0; // Método principal - tratamento do evento OnTick
};

Por que precisamos do método Init se existe um construtor?

Por alguma razão, eles limitaram imediatamente a classe TS a um símbolo e a um período de tempo.


Isso parece ser mais lógico.

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

Por que você precisa de um método Init se você tem um construtor?

Por algum motivo, eles imediatamente limitaram a classe TS a um símbolo e a um período de tempo.

Gostei da abordagem do autor. Há uma passagem desse tipo no artigo:

Os métodos Init() e Tick() são declarados puramente virtuais (depois que o cabeçalho do método é = 0). Isso significa que não escreveremos a implementação desses métodos na classe CStrategy. Com base nessa classe, criaremos classes descendentes, nas quais os métodos Init() e Tick() devem necessariamente estar e conter a implementação de regras comerciais específicas.

Então, a classe será abstrata, até onde eu entendo.....

 
Denis Kirichenko #:

Então, a classe será abstrata, até onde eu entendo.....

Será. Ela é usada no descendente por algum motivo. Se Deinit não for feito (há um destrutor), é lógico não fazer Init (há um construtor).

//+------------------------------------------------------------------+
//| Construtor|
//+------------------------------------------------------------------+
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) :
   // Lista de inicialização
   CStrategy(p_magic, p_symbol, p_timeframe, p_fixedLot), // Chamar o construtor da classe base
   signalPeriod_(p_signalPeriod),
   signalDeviation_(p_signalDeviation),
   signaAddlDeviation_(p_signaAddlDeviation),
   openDistance_(p_openDistance),
   stopLevel_(p_stopLevel),
   takeLevel_(p_takeLevel),
   ordersExpiration_(p_ordersExpiration),
   maxCountOfOrders_(p_maxCountOfOrders)
{}

//+------------------------------------------------------------------+
//| Função de inicialização do especialista
//+------------------------------------------------------------------+
int CSimpleVolumeStrategy::Init() {
// Carregar o indicador para obter volumes de ticks
   iVolumesHandle = iVolumes(m_symbol, m_timeframe, VOLUME_TICK);

// Defina o tamanho da matriz receptora de volumes de ticks e o endereçamento necessário
   ArrayResize(volumes, signalPeriod_);
   ArraySetAsSeries(volumes, true);

// Definir o número mágico para colocar ordens via negociação
   trade.SetExpertMagicNumber(m_magic);

   return(INIT_SUCCEEDED);
}

E o forte estreitamento artificial de possíveis TCs é uma solução estranha.


Você também pode ver claramente o incômodo com a entrada por causa da OOP. Seria bom removê-la.

 

Init() foi deixado de fora por enquanto porque é impossível retornar um resultado do construtor. Mas é possível que nunca precisemos retornar algo diferente de INIT_SUCCESS como resultado da inicialização da estratégia. Portanto, é bem possível que esse método seja removido no futuro.

A alocação de propriedades obrigatórias da estratégia na forma de símbolo e período de tempo é uma limitação deliberada. Por definição, a negociação em vários símbolos será feita por meio do trabalho de muitas instâncias de herdeiros dessa classe, mas cada instância específica trabalha com um único símbolo. Ainda não encontrei nenhuma estratégia que pudesse ser prejudicada por essa limitação. Pelo contrário, esses parâmetros foram encontrados em todas as estratégias consideradas, por isso foi decidido colocá-los na classe base de uma só vez.

Mas, no futuro, planejo considerar algumas estratégias com vários símbolos que não podem ser divididas em várias estratégias independentes com um único símbolo (se houver). Não acho que a presença de propriedades de símbolo e período de tempo na classe base dificultará muito a implementação de uma classe filha na qual serão usados vários símbolos e vários períodos de tempo.

 
Yuriy Bykov propriedades de símbolo e período de tempo na classe base o impedirá de implementar uma classe filha que usará vários símbolos e vários períodos de tempo.

Isso não interferirá - 100%. É apenas uma entidade desnecessária. A arquitetura OOP segue o princípio do geral para o particular. Você tornou o geral (classe base) "privado". Embora tudo o que seja chamado lá seja CStrategy::Tick().

 
Denis Kirichenko #:

E gostei da abordagem do autor. Há uma passagem desse tipo no artigo:

Então a classe será abstrata, até onde eu entendo.....

É, ela será usada apenas para obter classes filhas. Você não precisará criar objetos da classe base CStrategy. Mas qualquer objeto da classe filha pode ser passado para o objeto Expert Advisor a ser adicionado ao método CAdvisor::AddStrategy(CStrategy &strategy).

 
Yuriy Bykov #:

Init() foi deixado de fora por enquanto porque é impossível retornar um resultado do construtor. Mas é possível que nunca precisemos retornar algo diferente de INIT_SUCCESS como resultado da inicialização da estratégia. Portanto, é bem possível que esse método seja removido no futuro.

Caso algo dê errado no construtor (o identificador do indicador não é carregado ou a memória não é alocada, etc.), algumas pessoas mantêm essa variável comum.
static int CStrategy::InitFlag = INIT_FAILED;
 
fxsaber #:
Caso algo dê errado no construtor (o identificador do indicador não foi carregado ou a memória não foi alocada), algumas pessoas mantêm essa variável compartilhada.

Sim, eu já pensei em algo assim. Vou tentar fazer dessa forma.

 
Yuriy Bykov #:

qualquer objeto de classe secundária pode ser passado para o objeto EA a ser adicionado ao método CAdvisor::AddStrategy(CStrategy &strategy).

Parece ser um bug do compilador o fato de ele não jurar pela assinatura desse método quando chamado dessa forma.

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

Deveria ser assim.

CAdvisor::AddStrategy(CStrategy* strategy)
 
Herdado de CObject.
class CStrategy : public CObject {

Você não o usa.

class CAdvisor : public CObject {
protected:
   CStrategy         *m_strategies[];  // Conjunto de estratégias de negociação

Percebi que é uma prática bastante comum herdar do CObject.