English Русский
preview
Desenvolvendo um EA multimoeda (Parte 5): tamanho de posição variável

Desenvolvendo um EA multimoeda (Parte 5): tamanho de posição variável

MetaTrader 5Testador | 7 agosto 2024, 16:24
27 0
Yuriy Bykov
Yuriy Bykov

Introdução

Na parte anterior, adicionamos a capacidade de restaurar o estado do EA após um reinício. Não importa a causa, quer seja reinicialização do terminal, mudança de timeframe no gráfico com o EA, lançamento de uma versão mais recente do EA; em todos os casos, a restauração do estado permitia que o EA não começasse do zero e não perdesse posições já abertas, mas continuasse a processá-las.

No entanto, o tamanho das posições abertas para cada instância da estratégia permanecia o mesmo durante todo o período de teste. Seu tamanho era definido na inicialização do EA. Se, como resultado do trabalho do EA, o saldo da conta de negociação aumentasse, isso permitiria usar um tamanho de posição maior sem aumentar o risco. Tal possibilidade é desejável, então vamos implementar o uso de tamanhos de posição variáveis.


Conceitos

Primeiramente, precisamos definir os termos que usaremos, com base no objetivo geral: alcançar uma cooperação ideal entre várias instâncias de estratégias de negociação.

Tamanho Fixo da Estratégia (Fixed Lot) — um tamanho usado para calcular o tamanho de todas as posições abertas na estratégia de negociação. No caso mais simples, todas as posições abertas podem ter um tamanho igual a esse valor. Assim, o tamanho fixo define o tamanho da primeira posição na série, e as subsequentes são calculadas com base nele e no número de posições já abertas, tudo isso mediante artifícios para aumentar ou diminuir os tamanhos da segunda e das posições subsequentes na série. Vale ressaltar que isso é considerado uma prática não ótima. Mas ainda estamos longe de um consenso sobre essa questão, se é que ele é possível.

Saldo Normalizado da Estratégia (Fitted Balance) — o tamanho do saldo inicial, no qual, para o tamanho fixo da estratégia escolhida, a perda máxima durante o período de teste atinge, mas não ultrapassa, 10% do saldo inicial. Por que exatamente 10%? Esse número continua a ser uma perda não muito grande, psicologicamente admissível, e mostrou-se mais adequada para cálculos rápidos de cabeça. No geral, qualquer valor poderia ser escolhido - 1%, 5%, 50%. Isso é apenas um parâmetro de normalização.

Estratégia de Negociação Normalizada — uma estratégia de negociação para a qual foi escolhido um tamanho fixo da estratégia e um saldo normalizado da estratégia. Portanto, ao executar tal estratégia no período de teste escolhido, devemos obter um valor máximo de perda de aproximadamente 10% do saldo normalizado da estratégia.

Ao ter uma estratégia de negociação, podemos transformá-la em uma estratégia de negociação normalizada realizando as seguintes ações:

  • Escolhemos um tamanho fixo da estratégia, por exemplo, 0.01.
  • Escolhemos o período de teste (data inicial e final).
  • Executamos a estratégia no período de teste escolhido com um saldo inicial elevado e observamos o valor do máximo rebaixamento absoluto do saldo.
  • Encontramos o valor do saldo normalizado da estratégia, multiplicando o valor do máximo rebaixamento absoluto do saldo por 10.

Consideremos um exemplo. Suponhamos que, para o tamanho fixo da estratégia de 0.01, obtivemos um valor de rebaixamento absoluto máximo de $440. Se queremos que esse valor corresponda exatamente a 10% do saldo inicial, podemos dividir $440 por 0.10 ou, o que é o mesmo, multiplicar por 10:

$440 / 0.10 = $440 * 10 = $4400.

Esses dois valores (0.01 e 4400) estabelecemos nos parâmetros de criação de instância da estratégia de negociação, obtendo assim uma estratégia de negociação normalizada.

Agora podemos calcular o tamanho das posições abertas para qualquer valor de saldo, mantendo a máxima perda relativa de 10%. Para isso, basta alterar o tamanho das posições abertas proporcionalmente à relação entre o saldo total atual (Total Balance) e o saldo normalizado (Fitted Balance).

CurrentLot = FixedLot * (TotalBalance / FittedBalance)

Por exemplo, para os valores usados acima de 0.01 e 4400, com um saldo de $10000, o tamanho das posições abertas deve ser calculado com base no valor básico:

CurrentLot = 0.01 * (10,000 / 4400) = 0.0227

Abrir exatamente esse tamanho não será possível, teremos que arredondar para 0.02, portanto, a perda nesse caso pode ser um pouco menor que 10%. Se arredondarmos para cima (para 0.03), a perda pode ser um pouco maior que 10%. Mas com o aumento do saldo, os erros de arredondamento diminuirão.

Se introduzimos o conceito de tamanho fixo das posições para a estratégia, qualquer variante de gerenciamento dos tamanhos das posições da estratégia pode ser atribuída à própria estratégia. Portanto, ao nível do EA, que integra várias instâncias de estratégias de negociação, basta implementar apenas três possíveis variantes de estratégia de gerenciamento de capital:

  • Tamanho fixo ou ausência de estratégia de gerenciamento de capital. Aplica-se o tamanho fixo especificado na estratégia independentemente do saldo da conta de negociação. Essa estratégia será aplicada ao testar uma instância individual da estratégia para determinar o saldo normalizado.

  • Tamanho constante para um saldo fixo especificado. No início, é calculado o tamanho proporcional ao saldo fixo para a estratégia, com base no saldo normalizado e no tamanho fixo da estratégia. Esse tamanho é usado durante todo o período de teste. Essa estratégia será aplicada para verificar a uniformidade (linearidade) do crescimento da curva de saldo durante todo o período de teste, mantendo a máxima perda absoluta declarada.

  • Tamanho variável para o saldo atual. A cada operação de abertura de posições, o tamanho é determinado proporcionalmente ao saldo atual da conta, com base no saldo normalizado da estratégia e no tamanho fixo da estratégia. Essa estratégia será aplicada à operação real, garantindo o valor esperado de máxima perda relativa.

Daremos exemplos de uso dessas três variantes. Tomemos um EA com uma instância da estratégia, configuremos um grande saldo inicial de $100000 e iniciemos o teste no período de 2018-2022 com um tamanho fixo de posições abertas de 0.01 lotes. Obteremos os seguintes resultados:

Fig. 1. Resultados com tamanho fixo e saldo de $100000


Como podemos ver, neste período de teste, houve uma perda absoluta máxima de cerca de $153, o que corresponde a aproximadamente 0,15% do saldo da conta. Mais precisamente, é correto avaliar a perda relativa em relação ao saldo inicial da conta. Mas, como a diferença entre o saldo inicial e final é pequena (cerca de 1% do saldo inicial), a perda de 0,15% será aproximadamente a mesma $150 em qualquer momento do período de teste.

Calculemos qual saldo inicial podemos definir para que a perda absoluta máxima seja 10% do saldo inicial:

FittedBalance = MaxDrawdown / 10% = 153 / 0.10 = 153 * 10 = USD 1530

Verifiquemos nossos cálculos:

Fig. 2. Resultados com tamanho fixo e saldo de $1530


Vemos que a perda absoluta foi a mesma, $153, mas a relativa foi de apenas 7,2%, não 10%. Isso é normal por indicar que a maior perda ocorreu quando o saldo da conta já havia crescido um pouco do valor inicial, e a quantia de $153 representava menos de 10% do saldo atual.

Agora vamos verificar a segunda variante — tamanho constante para um saldo fixo especificado. Novamente, definimos um grande saldo inicial de $100000, mas permitimos usar apenas um décimo, ou seja, $10000. Esse será o valor do Current Balance, que será constante durante todo o período de teste. Nessas condições, descobrimos que o tamanho das posições abertas deve ser:

CurrentBalance = TotalBalance * 0.1 = 10,000

CurrentLot = FixedLot *  (CurrentBalance / FittedBalance) = 0.01 * (10,000 / 1530) = 0.0653

Durante a operação, esse valor será arredondado ao valor mais próximo compatível com o passo de mudança de lote. Obtivemos os seguintes resultados:

Fig. 3. Resultados com tamanho constante para saldo fixo de $10000 dos disponíveis $100000


Como podemos ver, a perda absoluta foi de $1016, o que com precisão suficiente representa 10% dos $10000 alocados para essa estratégia. No entanto, em relação ao saldo total, isso representou apenas 1%.

E finalmente, vamos ver a terceira variante — tamanho variável para o saldo atual. Agora definimos o saldo inicial igual a $10000 e permitimos usá-lo totalmente. Aqui está o que obtemos:


Fig. 4. Resultados com tamanho variável para o saldo atual

Aqui vemos que o valor da perda absoluta máxima já excede 10% do saldo inicial, mas a perda relativa continua nos 10% permitidos. Obtivemos uma estratégia de negociação normalizada, com um Fitted Balance = 1530, e agora podemos calcular facilmente os tamanhos das posições abertas para garantir a perda estipulada de 10%.


Cálculo dos tamanhos das posições

Após analisar essas variantes de gerenciamento de capital, podemos fazer as seguintes observações:

  • Se estivermos falando de uma única instância da estratégia, será útil a variante com lote variável? Parece que não. Nos basta usar apenas a primeira variante. A segunda e a terceira podem ser usadas algumas vezes para demonstrar a funcionalidade, mas depois não serão necessárias.

  • Se estivermos trabalhando com um EA que combina várias instâncias de estratégias de negociação, será útil a negociação com lote fixo? Parece que não. Nesse caso, na fase de teste, a segunda variante pode ser útil, mas a principal usada será a terceira.

Isso nos permite chegar a uma conclusão: as ordens virtuais sempre têm tamanho fixo, calculado a partir do parâmetro tamanho fixo da estratégia. Nas estratégias consideradas neste ciclo de artigos, é suficiente usar como tamanho fixo o lote mínimo, que para a maioria dos instrumentos é 0.01.

Com base no esquema de operação, resulta que o objeto receptor ou os receptores simbólicos devem converter o tamanho para o tamanho real das posições abertas. Para isso, eles devem obter da estratégia, ou mais precisamente da ordem virtual que consultará a estratégia, o valor do saldo normalizado, que garante uma perda de 10% desse saldo.

Mas e se quisermos garantir uma perda menor ou maior? Para isso, basta ajustar de alguma forma o tamanho das posições abertas proporcionalmente ao quanto queremos alterar a perda máxima esperada em comparação com o valor de 10%.

Uma das maneiras é introduzir explicitamente um multiplicador de peso, que mostra qual parte do saldo atual da conta pode ser usada pelo EA.

Saldo Alocado (Current Balance) — parte do saldo total da conta alocada para negociação por este EA.

Multiplicador de Saldo (Depo Part) — relação entre o saldo alocado da estratégia e o saldo total da conta.

DepoPart =  CurrentBalance / TotalBalance

Então, o tamanho inicial das posições pode ser calculado assim:

CurrentLot = FixedLot * (CurrentBalance / FittedBalance)

CurrentLot =  FixedLot * (DepoPart * TotalBalance / FittedBalance)

Aqui podemos fazer uma observação importante que será muito útil para a implementação. Se após a criação da instância da estratégia recalcularmos seu saldo normalizado, então na fórmula para o cálculo do tamanho das posições, será usado o saldo total em vez do saldo atual da estratégia:

FittedBalance = FittedBalance / DepoPart

CurrentLot =  FixedLot * (TotalBalance / FittedBalance)

Esse recalculo do saldo normalizado será feito uma vez durante a inicialização do EA, após o qual o multiplicador Depo Part já não será necessário. 


Combinação de várias estratégias

As considerações anteriores foram para usar uma instância da estratégia no EA. Vamos agora pensar no que será necessário fazer se quisermos combinar várias estratégias normalizadas em um único EA, permitindo uma perda máxima de 10% (ou outra previamente definida) durante todo o período de teste. Inicialmente, estimamos uma perda de 10%

Se nos concentrarmos no pior cenário possível ao combinar estratégias, isso significa que todas as instâncias das estratégias atingem simultaneamente sua perda máxima de 10%. Nesse caso, teremos que reduzir o tamanho das posições de cada estratégia proporcionalmente ao número de instâncias. Por exemplo, se combinarmos três instâncias da estratégia, teremos que reduzir o tamanho das posições em três vezes. 

Isso pode ser feito reduzindo o saldo alocado para a estratégia proporcionalmente ao número de vezes ou aumentando o saldo normalizado das estratégias proporcionalmente ao número de vezes. Usaremos a segunda opção. 

Se designarmos o número de estratégias no grupo como StrategiesCount, as fórmulas para recalcular o saldo normalizado terão a seguinte forma:

FittedBalance = StrategiesCount * FittedBalance

No entanto, a probabilidade de ocorrer esse pior cenário diminui significativamente com o aumento do número de instâncias de estratégias, especialmente se elas forem escolhidas para serem o mais diferentes possível entre si. Nesse caso, as perdas ocorrerão em momentos diferentes, não simultaneamente. Isso pode ser observado durante o teste. Então, podemos introduzir outro multiplicador de escala (Scale), que por padrão é igual a um, mas que pode ser ajustado para aumentar o tamanho das posições, reduzindo o saldo normalizado das estratégias:

FittedBalance = StrategiesCount * FittedBalance

FittedBalance = FittedBalance / Scale

Ao ajustar o multiplicador Scale, podemos garantir que o grupo de estratégias tenha a perda estipulada durante todo o período de teste. Dessa forma, obteremos um grupo normalizado de estratégias.

Grupo Normalizado de Estratégias — um grupo de estratégias de negociação normalizadas, para o qual foi escolhido um multiplicador de escala que garante que, ao operar em conjunto, o grupo terá uma perda máxima não superior a 10%.

Então, se criarmos vários grupos normalizados de estratégias, podemos combiná-los novamente em um novo grupo normalizado seguindo o mesmo princípio usado para combinar estratégias normalizadas. Ou seja, selecionar um multiplicador para o grupo de grupos de modo que a perda máxima, ao operar simultaneamente todas as estratégias de todos os grupos, não exceda 10%. Esse processo de combinação pode continuar para qualquer número de níveis. Ao mesmo tempo, o saldo normalizado original de cada estratégia será simplesmente multiplicado pelo número de estratégias ou grupos no grupo em cada nível de combinação e dividido pelos multiplicadores de escala de cada nível:

FittedBalance = StrategiesCount1 * FittedBalance

FittedBalance = StrategiesCount2 * FittedBalance

...

FittedBalance = FittedBalance / Scale1

FittedBalance = FittedBalance / Scale2

...

Então, a fórmula final para recalcular o saldo normalizado de cada estratégia será:

FittedBalance = (StrategiesCount1 *  StrategiesCount1  * ... ) * FittedBalance / (Scale1 * Scale2 * ... )

E, no final, para possivelmente converter o grupo normalizado de estratégias, presente no nível mais alto da combinação, em um grupo com outra perda estipulada diferente de 10%, aplicaremos o último multiplicador de escala Depo Part na fórmula de cálculo dos tamanhos das posições:

CurrentLot =  FixedLot * (DepoPart * TotalBalance / FittedBalance)

Para a implementação do código, precisamos de duas novas classes. A primeira classe CVirtualStrategyGroup será responsável pelo recalculo dos saldos normalizados das estratégias ao combiná-las em grupos. A segunda classe CMoney será responsável pelo cálculo dos volumes reais abertos com base no tamanho fixo da estratégia, saldo normalizado da estratégia e saldo alocado para a estratégia. 


Classe do grupo de estratégias de negociação

Esta classe será usada para criar objetos que representam um grupo de estratégias ou um grupo de grupos de estratégias. Em ambos os casos, ao criar o grupo, será aplicado um multiplicador de escala através da chamada de um único método Scale().

//+------------------------------------------------------------------+
//| Class of trading strategies group(s)                             |
//+------------------------------------------------------------------+
class CVirtualStrategyGroup {
protected:
   void              Scale(double p_scale); // Scale normalized balance 
public:
   CVirtualStrategyGroup(CVirtualStrategy *&p_strategies[],
                         double p_scale = 1);   // Constructor for a group of strategies
   CVirtualStrategyGroup(CVirtualStrategyGroup *&p_groups[],
                         double p_scale = 1);   // Constructor for a group of strategy groups

   CVirtualStrategy      *m_strategies[];       // Array of strategies
   CVirtualStrategyGroup *m_groups[];           // Array of strategy groups
};

Os construtores aceitarão como parâmetros o multiplicador de escala e um array de ponteiros para estratégias ou um array de ponteiros para grupos de estratégias. O array obtido será copiado para a propriedade correspondente do objeto criado, e cada elemento do array terá o método Scale() aplicado. Para os objetos de estratégias, precisaremos adicionar esse método na classe da estratégia.

//+------------------------------------------------------------------+
//| Constructor for strategy groups                                  |
//+------------------------------------------------------------------+
CVirtualStrategyGroup::CVirtualStrategyGroup(
   CVirtualStrategy *&p_strategies[],
   double p_scale
) {
   ArrayCopy(m_strategies, p_strategies);
   Scale(p_scale / ArraySize(m_strategies));
}

//+------------------------------------------------------------------+
//| Constructor for a group of strategy groups                       |
//+------------------------------------------------------------------+
CVirtualStrategyGroup::CVirtualStrategyGroup(
   CVirtualStrategyGroup *&p_groups[],
   double p_scale
) {
   ArrayCopy(m_groups, p_groups);
   Scale(p_scale / ArraySize(m_groups));
}

//+------------------------------------------------------------------+
//| Scale normalized balance                                         |
//+------------------------------------------------------------------+
void CVirtualStrategyGroup::Scale(double p_scale) {
   FOREACH(m_groups,     m_groups[i].Scale(p_scale));
   FOREACH(m_strategies, m_strategies[i].Scale(p_scale));
}

Salvaremos esse código no arquivo VirtualStrategyGroup.mqh na pasta atual.

Faremos as adições necessárias na classe de estratégia virtual. Precisaremos adicionar duas novas propriedades de classe para armazenar o saldo normalizado da estratégia e o tamanho fixo da estratégia. Como elas devem ser necessariamente configuradas, será necessário um construtor, que antes não era necessário. O método público FittedBalance() simplesmente retornará o valor do saldo normalizado da estratégia, e o método Scale() realizará a escalagem com o multiplicador especificado.

//+------------------------------------------------------------------+
//| Class of a trading strategy with virtual positions               |
//+------------------------------------------------------------------+
class CVirtualStrategy : public CStrategy {
protected:
   ...
   double            m_fittedBalance;  // Strategy normalized balance
   double            m_fixedLot;       // Strategy fixed size
   ...
public:
   CVirtualStrategy(double p_fittedBalance = 0, double p_fixedLot = 0.01); // Constructor
   ...
   double            FittedBalance() {    // Strategy normalized balance
      return m_fittedBalance;
   }

   void              Scale(double p_scale) { // Scale normalized balance
      m_fittedBalance /= p_scale;
   }
};

Salvaremos esse código no arquivo VirtualStrategy.mqh na pasta atual.

Também será necessário fazer pequenas alterações na classe CSimpleVolumesStrategy. Precisamos adicionar ao construtor um parâmetro adicional para o saldo normalizado da estratégia e remover o parâmetro para definir os tamanhos das posições virtuais. Agora ele será sempre o mesmo e igual ao lote mínimo de 0.01.

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSimpleVolumesStrategy::CSimpleVolumesStrategy(
   string           p_symbol,
   ENUM_TIMEFRAMES  p_timeframe,
   int              p_signalPeriod,
   double           p_signalDeviation,
   double           p_signaAddlDeviation,
   int              p_openDistance,
   double           p_stopLevel,
   double           p_takeLevel,
   int              p_ordersExpiration,
   int              p_maxCountOfOrders,
   double           p_fittedBalance = 0) :
// Initialization list
   CVirtualStrategy(p_fittedBalance, 0.01),
   m_symbol(p_symbol),
   m_timeframe(p_timeframe),
   m_signalPeriod(p_signalPeriod),
   m_signalDeviation(p_signalDeviation),
   m_signaAddlDeviation(p_signaAddlDeviation),
   m_openDistance(p_openDistance),
   m_stopLevel(p_stopLevel),
   m_takeLevel(p_takeLevel),
   m_ordersExpiration(p_ordersExpiration),
   m_maxCountOfOrders(p_maxCountOfOrders) {
   ...
}

Salvaremos as alterações no arquivo SimpleVolumesStrategy.mqh na pasta atual.

Para permitir adicionar grupos de estratégias ao objeto do EA, ou seja, instâncias da nossa nova classe CVirtualStrategyGroup, adicionaremos ao classe do EA um método sobrecarregado Add(), que realizará essa função:

//+------------------------------------------------------------------+
//| Class of the EA handling virtual positions (orders)              |
//+------------------------------------------------------------------+
class CVirtualAdvisor : public CAdvisor {
   ...
public:
   ...
   virtual void      Add(CVirtualStrategyGroup &p_group);  // Method for adding a group of strategies
   ...
};

//+------------------------------------------------------------------+
//| Method for adding a group of strategies                          |
//+------------------------------------------------------------------+
void CVirtualAdvisor::Add(CVirtualStrategyGroup &p_group) {
   FOREACH(p_group.m_groups, {
      CVirtualAdvisor::Add(p_group.m_groups[i]);
      delete p_group.m_groups[i];
   });
   FOREACH(p_group.m_strategies, CAdvisor::Add(p_group.m_strategies[i]));
}

Como os grupos de estratégias não serão mais necessários após serem adicionados ao EA, no método, iremos removê-los imediatamente da memória dinâmica. Salvaremos as alterações no arquivo VirtualAdvisor.mqh na pasta atual.


Classe de gerenciamento de capital

Esta classe será responsável por determinar o tamanho real das posições virtuais de acordo com as três possíveis variantes de estratégia de gerenciamento de capital.

O objeto desta classe deve ser único. Portanto, podemos usar o padrão de projeto Singleton, ou, como foi implementado, a classe pode conter apenas campos e métodos estáticos, acessíveis por qualquer objeto.

O método principal dessa classe é o método para determinar o tamanho real da posição virtual (ordem) Volume(). E outros dois métodos permitem definir os valores de dois parâmetros que determinam qual parte do saldo da conta de negociação participa na negociação.

//+------------------------------------------------------------------+
//| Basic money management class                                     |
//+------------------------------------------------------------------+
class CMoney {
   static double     s_depoPart;       // Used part of the total balance
   static double     s_fixedBalance;   // Total balance used
public:
   CMoney() = delete;                  // Disable the constructor
   static double     Volume(CVirtualOrder *p_order); // Determine the real size of the virtual position

   static void       DepoPart(double p_depoPart) {
      s_depoPart = p_depoPart;
   }
   static void       FixedBalance(double p_fixedBalance) {
      s_fixedBalance = p_fixedBalance;
   }
};

double CMoney::s_depoPart = 1.0;
double CMoney::s_fixedBalance = 0;

//+------------------------------------------------------------------+
//| Determine the real size of the virtual position                  |
//+------------------------------------------------------------------+
double CMoney::Volume(CVirtualOrder *p_order) {
   // Request the normalized strategy balance for the virtual position  
   double fittedBalance = p_order.FittedBalance();
   
   // If it is 0, then the real volume is equal to the virtual one
   if(fittedBalance == 0.0) {
      return p_order.Volume();
   }
   
   // Otherwise, find the value of the total balance for trading
   double totalBalance = s_fixedBalance > 0 ? s_fixedBalance : AccountInfoDouble(ACCOUNT_BALANCE);
   
   // Return the calculated real volume based on the virtual one
   return p_order.Volume() * totalBalance * s_depoPart / fittedBalance ;
}
//+------------------------------------------------------------------+

Salvaremos esse código no arquivo Money.mqh na pasta atual.


EAs para teste

Para testar, faremos alterações nos arquivos dos EAs. No arquivo SimpleVolumesExpertSingle.mq5, basta remover o parâmetro de tamanho da posição da lista de parâmetros do construtor da estratégia na função de inicialização do EA:

int OnInit() {
// Create an EA handling virtual positions
   expert = new CVirtualAdvisor(magic_, "SimpleVolumesSingle");

   expert.Add(new CSimpleVolumesStrategy(
                 symbol_, timeframe_,
                 fixedLot_,
                 signalPeriod_, signalDeviation_, signaAddlDeviation_,
                 openDistance_, stopLevel_, takeLevel_, ordersExpiration_,
                 maxCountOfOrders_)
             );       // Add one strategy instance

   return(INIT_SUCCEEDED);
}

Não usaremos este EA para encontrar novas boas combinações de parâmetros para instâncias individuais de estratégias, pois usaremos as combinações encontradas anteriormente. Mas, se necessário, o EA estará pronto para realizar a otimização.

No arquivo SimpleVolumesExpert.mq5, faremos adições mais significativas. Essas adições são principalmente para demonstrar as capacidades das classes adicionadas, por isso não devem ser consideradas como código final.

Primeiro, criaremos uma enumeração para denotar diferentes maneiras de combinar instâncias de estratégias de negociação em grupos:

enum ENUM_VA_GROUP {
   VAG_EURGBP,          // Only EURGBP (3 items)
   VAG_EURUSD,          // Only EURUSD (3 items)
   VAG_GBPUSD,          // Only GBPUSD (3 items)
   VAG_EURGBPUSD_9,     // EUR-GBP-USD (9 items)
   VAG_EURGBPUSD_3_3_3  // EUR-GBP-USD (3+3+3 items)
};

Os primeiros três valores corresponderão ao uso de três instâncias de estratégias de negociação para um dos símbolos (EURGBP, EURUSD ou GBPUSD). O quarto valor corresponderá ao uso de um grupo de todas as nove instâncias de estratégias. E o quinto valor corresponderá ao uso de um grupo de três grupos normalizados, cada um contendo três instâncias de estratégias de negociação para um determinado símbolo.

Expandiremos um pouco a lista de parâmetros de entrada:

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "::: Strategy groups"
input ENUM_VA_GROUP group_ = VAG_EURGBP;  // - Strategy group

input group "::: Money management"
input double expectedDrawdown_ = 10;      // - Maximum risk (%)
input double fixedBalance_ = 0;           // - Used deposit (0 - use all) in the account currency
input double scale_ = 1.0;                // - Group scaling multiplier

input group "::: Other parameters"
input ulong  magic_        = 27183;       // - Magic

Na função de inicialização do EA, definiremos os parâmetros de gerenciamento de capital levando em consideração a normalização da perda máxima permitida de 10%, criaremos nove instâncias de estratégias, agruparemos conforme a agrupação escolhida e adicionaremos ao EA:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Set parameters in the money management class
   CMoney::DepoPart(expectedDrawdown_ / 10.0);
   CMoney::FixedBalance(fixedBalance_);

   // Create an EA handling virtual positions
   expert = new CVirtualAdvisor(magic_, "SimpleVolumes_" + EnumToString(group_));

   // Create and fill the array of all strategy instances
   CVirtualStrategy *strategies[] = {
      new CSimpleVolumesStrategy("EURGBP", PERIOD_H1,  13, 0.3, 1.0, 0, 10500,  465,  1000, 3, 1600),
      new CSimpleVolumesStrategy("EURGBP", PERIOD_H1,  17, 1.7, 0.5, 0, 16500,  220,  1000, 3,  900),
      new CSimpleVolumesStrategy("EURGBP", PERIOD_H1,  51, 0.5, 1.1, 0, 19500,  370, 22000, 3, 1600),

      new CSimpleVolumesStrategy("EURUSD", PERIOD_H1,  24, 0.1, 0.3, 0,  7500, 2400, 24000, 3, 2300),
      new CSimpleVolumesStrategy("EURUSD", PERIOD_H1,  18, 0.2, 0.4, 0, 19500, 1480,  6000, 3, 2000),
      new CSimpleVolumesStrategy("EURUSD", PERIOD_H1, 128, 0.7, 0.3, 0,  3000,  170, 42000, 3, 2200),

      new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1,  80, 1.1, 0.2, 0,  6000, 1190,  1000, 3, 2500),
      new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1, 128, 2.0, 0.9, 0,  2000, 1170,  1000, 3,  900),
      new CSimpleVolumesStrategy("GBPUSD", PERIOD_H1,  13, 1.5, 0.8, 0,  2500, 1375,  1000, 3, 1400),
   };

   // Create arrays of pointers to strategies, one symbol at a time, from the available strategies
   CVirtualStrategy *strategiesEG[] = {strategies[0], strategies[1], strategies[2]};
   CVirtualStrategy *strategiesEU[] = {strategies[3], strategies[4], strategies[5]};
   CVirtualStrategy *strategiesGU[] = {strategies[6], strategies[7], strategies[8]};

   // Create and add selected groups of strategies to the EA
   switch(group_) {
   case VAG_EURGBP: {
      expert.Add(CVirtualStrategyGroup(strategiesEG, scale_));
      FOREACH(strategiesEU, delete strategiesEU[i]);
      FOREACH(strategiesGU, delete strategiesGU[i]);
      break;
   }
   case VAG_EURUSD: {
      expert.Add(CVirtualStrategyGroup(strategiesEU, scale_));
      FOREACH(strategiesEG, delete strategiesEG[i]);
      FOREACH(strategiesGU, delete strategiesGU[i]);
      break;
   }
   case VAG_GBPUSD: {
      expert.Add(CVirtualStrategyGroup(strategiesGU, scale_));
      FOREACH(strategiesEU, delete strategiesEU[i]);
      FOREACH(strategiesEG, delete strategiesEG[i]);
      break;
   }
   case VAG_EURGBPUSD_9: {
      expert.Add(CVirtualStrategyGroup(strategies, scale_));
      break;
   }
   case VAG_EURGBPUSD_3_3_3: {
      // Create a group of three strategy groups
      CVirtualStrategyGroup *groups[] = {
         new CVirtualStrategyGroup(strategiesEG, 1.25),
         new CVirtualStrategyGroup(strategiesEU, 2.24),
         new CVirtualStrategyGroup(strategiesGU, 2.64)
      };

      expert.Add(CVirtualStrategyGroup(groups, scale_));
      break;
   }
   default:
      return(INIT_FAILED);
   }

// Load the previous state if available
   expert.Load();

   return(INIT_SUCCEEDED);
}

Salvaremos as alterações no arquivo SimpleVolumesExpert.mq5 na pasta atual.


Teste

Vamos testar o primeiro grupo — três instâncias da estratégia operando no símbolo EURGBP. Obtivemos os seguintes resultados:


Fig. 5. Resultados para EURGBP com três estratégias, Scale=1


Como podemos ver, ao combinar, a perda relativa máxima foi de 8% em vez de 10% para cada instância individual da estratégia normalizada. Isso significa que podemos aumentar um pouco o tamanho das posições. Para atingir uma perda de 10%, definiremos o valor da escala em 10% / 8% = 1.25.


Fig. 6. Resultados para EURGBP com três estratégias, Scale=1.25


Agora a perda é de aproximadamente 10%. Faremos uma operação semelhante para ajustar o multiplicador de escala para o segundo e terceiro grupo. Obtivemos os seguintes resultados:

Fig. 7. Resultados para EURUSD com três estratégias, Scale=2.24


Fig. 8. Resultados para GBPUSD com três estratégias, Scale=2.64


Os valores de multiplicadores de escala ajustados serão usados no código para criar um grupo normalizado de três grupos normalizados de estratégias:

// Create a group of three strategy groups
CVirtualStrategyGroup *groups[] = {
     new CVirtualStrategyGroup(strategiesEG, 1.25),
     new CVirtualStrategyGroup(strategiesEU, 2.24),
     new CVirtualStrategyGroup(strategiesGU, 2.64)
 };

Agora ajustaremos o multiplicador de escala para o quarto grupo. Se combinarmos todas as 9 instâncias em um grupo, obteremos os seguintes resultados:


Fig. 9. Resultados para EURGBP, EURUSD, GBPUSD (total de 9 estratégias), Scale=1


Isso nos permite aumentar o multiplicador de escala para 3.3 e ainda manter a perda relativa em 10%:


Fig. 10. Resultados para EURGBP, EURUSD, GBPUSD (total de 9 estratégias), Scale=3.3


E finalmente, o mais interessante. Combinamos as mesmas 9 estratégias normalizadas de outra forma: primeiro normalizamos os grupos de três estratégias para os símbolos individuais, e depois combinamos os três grupos normalizados resultantes. Obtemos o seguinte:


Fig. 11. Resultados para EURGBP, EURUSD, GBPUSD (3 + 3 + 3 estratégias), Scale=1


O saldo final é maior do que para a quarta combinação com a mesma Scale=1, mas a perda também é maior: 4.57% em vez de 3%. Ajustamos a quinta combinação para uma perda de 10% e comparar o resultado:


Fig. 12. Resultados para EURGBP, EURUSD, GBPUSD (3 + 3 + 3 estratégias), Scale=2.18


Agora está claro que a quinta variante de combinação de estratégias oferece resultados muito melhores enquanto mantém a perda relativa máxima em 10%. Durante o período de teste selecionado, o lucro mais que dobrou em comparação com a quarta variante de combinação.

Finalmente, verificamos a linearidade do crescimento do saldo para a quinta variante de combinação. Isso permitirá avaliar se há períodos internos em que o EA funciona significativamente pior do que em outros períodos internos do período de teste completo. Para isso, definiremos o valor do parâmetro FixedBalance= 10000, para que o EA sempre use apenas esse valor de saldo da conta para calcular os tamanhos das posições.



Fig. 13. Resultados para EURGBP, EURUSD, GBPUSD (3 + 3 + 3 estratégias), FixedBalance = 10000, Scale=2.18


No gráfico de teste, marcamos com retângulos verdes os períodos internos em que o crescimento do saldo foi próximo de zero. A duração desses períodos varia de um mês a seis meses. Bom, significa que ainda há espaço para melhorias. A maneira mais simples de lidar com esses períodos é continuar diversificando: usar mais instâncias de estratégias de negociação que operam em diferentes símbolos e timeframes.

Também notamos que a perda máxima em termos absolutos foi de $995, aproximadamente 10% do saldo usado para negociação de $10000. Isso confirma que a implementação do gerenciamento de capital está funcionando corretamente.


Considerações finais

Agora podemos usar o EA desenvolvido para operar em contas de negociação com diferentes valores iniciais de saldo e controlar como o saldo será distribuído entre as diferentes instâncias de estratégias de negociação. Algumas instâncias receberão mais, abrindo posições maiores, enquanto outras receberão menos, abrindo posições menores. Mas, no geral, podemos ajustar os parâmetros mediante testes para garantir que a perda máxima permitida seja respeitada.

Vale destacar que a conformidade com a perda máxima só pode ser verificada nos testes. Não podemos garantir que ela será mantida quando o EA for executado em um período não utilizado para otimização. A perda pode variar tanto para mais quanto (estranhamente) para menos. Deste modo, cada um deve decidir por conta própria o quanto confia e como utilizar os resultados obtidos nos testes.

Continuaremos trabalhando no desenvolvimento deste projeto. Obrigado pela atenção!


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/14336

Caminhe em novos trilhos: Personalize indicadores no MQL5 Caminhe em novos trilhos: Personalize indicadores no MQL5
Vou agora listar todas as possibilidades novas e recursos do novo terminal e linguagem. Elas são várias, e algumas novidades valem a discussão em um artigo separado. Além disso, não há códigos aqui escritos com programação orientada ao objeto, é um tópico muito importante para ser simplesmente mencionado em um contexto como vantagens adicionais para os desenvolvedores. Neste artigo vamos considerar os indicadores, sua estrutura, desenho, tipos e seus detalhes de programação em comparação com o MQL4. Espero que este artigo seja útil tanto para desenvolvedores iniciantes quanto para experientes, talvez alguns deles encontrem algo novo.
Rede neural na prática: Esboçando um neurônio Rede neural na prática: Esboçando um neurônio
Neste artigo, faremos a confecção de um neurônio básico. Apesar de ele ser algo simples, e muitos acharem que o código é totalmente bobo e sem nenhum propósito. Quero que você, meu caro leitor, e entusiasta pelo tema de redes neurais. Brinque e se divirta estudando este simples esboço de neurônio. Não precisa ficar com receio de mexer no código a fim de entender o mesmo.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Do básico ao intermediário: Operadores Do básico ao intermediário: Operadores
Neste artigo vamos ver um pouco sobre os operadores básicos. Apesar de ser um assunto bem fácil de ser compreendido. Existem alguns pequenos detalhes, que fazem muita diferença na hora de colocar expressões matemáticas em formato de código. Sem entender de maneira adequada tais detalhes. Muitos programadores com quase nenhuma experiência, acabam desistindo de tentar criar suas próprias soluções.