Русский Español
preview
Gerenciamento de riscos (Parte 5): Integração do sistema de gerenciamento de riscos ao EA

Gerenciamento de riscos (Parte 5): Integração do sistema de gerenciamento de riscos ao EA

MetaTrader 5Exemplos |
101 0
Niquel Mendoza
Niquel Mendoza


Introdução

Bem-vindo à parte final de nossa série de artigos sobre gerenciamento de riscos. Neste artigo, analisaremos as diferenças na aplicação do sistema de gerenciamento de riscos: há alguma diferença entre usá-lo e não usá-lo, quais são as vantagens e desvantagens de uma abordagem dinâmica de gerenciamento de riscos e em quais situações seu uso é mais apropriado.

Responderemos a essas perguntas criando um EA simples que utiliza o indicador Order Blocks desenvolvido nos artigos anteriores sobre gerenciamento de riscos.

Começaremos pela otimização desse indicador, o que permitirá realizar backtests com mais rapidez e eficiência, além de simplificar a otimização do EA.

Também detalharemos o processo passo a passo de criação do EA, a definição de seus parâmetros e a integração dos principais eventos, como "OnTradeTransaction", que deve ser executado apenas dentro da função correspondente.

Por fim, responderemos às questões levantadas neste artigo realizando quatro testes do EA com diferentes combinações de parâmetros. Desse modo, analisaremos a diferença entre operar com e sem limitações de prejuízo e lucro, além de avaliar em quais casos o gerenciamento dinâmico de risco pode ser aplicado de forma mais eficaz.


Aprimoramentos no indicador Order Blocks

Começaremos este artigo com a otimização do indicador Order Blocks. Antes, seu desempenho deixava a desejar. Após análise, percebi que havia possibilidades de melhorar a eficiência, tanto por meio da otimização do processamento dos dados das velas quanto pela remoção de laços desnecessários.

1. Otimização do laço de busca de blocos de ordens

No primeiro laço do indicador, reduziremos o número de iterações, pois não será necessário percorrer todas as velas a cada atualização. Na inicialização do indicador, o parâmetro "prev_calculated" da função "OnCalculate" é igual a 0, esse parâmetro indica a quantidade de velas já calculadas. Como na primeira execução nenhuma vela foi calculada, o valor "prev_calculated" é igual a 0. Assim, no primeiro cálculo, o laço percorrerá as velas do número "Rango_universal_busqueda" até a vela 6; e quando "prev_calculated" for maior que 0, apenas a vela 6 será verificada.

int inicio = prev_calculated == 0 ? Rango_universal_busqueda : 6;

    for(int i = inicio  ; i  > 5  ; i--)
     {
      //----

     }

2. Busca de mitigações nos blocos de ordens

Para determinar se um Order Block foi "mitigado", isto é, se o preço atingiu ou ultrapassou o nível do bloco, aprimoramos duas funções: uma para blocos de alta e outra para blocos de baixa, e ambas foram simplificadas usando apenas os valores high e low das velas.

Mitigação em um Order Block de alta.

datetime  mitigados_alcsitas(double price, const double &lowArray[], const  datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);

  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(price > lowArray[i])
     {
      return Time[i]; //si encuentra que si hubo retorna el tiempo de la vela donde hubo la mitigacion
      Print("el orderblock tuvo mitigaciones", TimeToString(end));
     }
   }

  return 0; //En caso no se haya encontrado  niguna mitigacion retorna 0
 }

Mitigação em um Order Block de baixa.

//+------------------------------------------------------------------+
datetime  mitigado_bajista(double price, const  double &highArray[], const datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(highArray[i] > price)
     {
      return Time[i]; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

Nessas duas funções, os valores low e high das velas são usados diretamente para reduzir a quantidade de parâmetros e diminuir ligeiramente o tempo de execução do laço.

Além disso, essas alterações foram implementadas nas funções "esOb_mitigado_array_...", que anteriormente chamavam iOpen, iClose e outras funções semelhantes. Isso não era necessário, pois, por exemplo, se o fechamento de uma vela for menor que o nível do Order Block, isso indica que o valor low também está abaixo. Assim, podemos simplificar a verificação de mitigação baseando-nos exclusivamente no low ou no high, dependendo do tipo de bloco.

Order Block de alta.

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_alcista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double low = iLow(_Symbol, PERIOD_CURRENT, i);
    if(newblock.price2 >= low)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

Order Block de baixa.

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_bajista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double high = iHigh(_Symbol, PERIOD_CURRENT, i);
    if(high >= newblock.price2)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2;
     }
   }
  return 0; // no se mitigo hasta el momento
 }

3. Funções template para adicionar elementos ao array

Adicionamos várias funções template que serão mais eficientes ao inserir um elemento em um array. Antes, criávamos funções específicas que aceitavam apenas um parâmetro determinado, pois, por exemplo, se quiséssemos adicionar uma estrutura do tipo Order_Blocks, era necessário escrever uma função própria para isso.

As funções template permitem executar a mesma operação usando diferentes tipos. No nosso caso, criamos uma função para adicionar um elemento ao array.

template <typename S>
bool AddArray(S &Array[], const S &Value)
 {
  for(int i = 0 ; i < ArraySize(Array) ; i++)
   {
    if(Array[i].name == Value.name)
      return false;
   }
  ArrayResize(Array, Array.Size() + 1);
  Array[Array.Size() - 1] = Value;
  return true;
 }

template <typename X>
void AddArrayNoVerification(X &array[], const X &value)
 {
  ArrayResize(array, array.Size() + 1);
  array[array.Size() - 1] = value;
 }

4. Função template para remover um elemento pelo nome

Agora criaremos uma função template para remover um elemento com base em seu nome. Isso significa que o array com o qual trabalharemos deve ser uma estrutura ou classe contendo um membro público "name". Essa função servirá para remover um elemento com base no parâmetro targetName, o que é útil para eliminar itens desnecessários do array de blocos de ordens mitigados.

template<typename T>
bool DeleteArrayBiName(T &array[], const string targetName)
 {
  int size = ArraySize(array);
  int index = -1;

// Buscar el índice y desplazar elementos en un solo bucle
  for(int i = 0; i < size; i++)
   {
    if(array[i].name == targetName)
     {
      index = i;
     }
    if(index != -1 && i < size - 1)
     {
      array[i] = array[i + 1]; // Desplaza los elementos
     }
   }

  if(index == -1)
    return false;

  if(size > 1)
    ArrayResize(array, size - 1);
  else
    ArrayFree(array); // Si el array tenía solo un elemento, se libera completamente

  return true;
 }

5. Nova funcionalidade para "remover" blocos de ordens

Nas versões anteriores, quando um Order Block era considerado mitigado, seu nome era adicionado a um array auxiliar, mas o bloco permanecia no array principal. Com essa melhoria, o array auxiliar armazena apenas o nome do Order Block mitigado, enquanto o bloco em si é totalmente removido do array principal.  

Order Blocks de baixa.

    static bool buscar_obb = true;
    static datetime time_b = 0;
    string curr_elimiandor_obb[];

    for(int i = 0; i < ArraySize(ob_bajistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_bajista(ob_bajistas[i], ob_bajistas[i].time1);

      if(ob_bajistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_bajistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_bajistas[i].name, 0, ob_bajistas[i].time1, ob_bajistas[i].price1,
                          time[0], ob_bajistas[i].price2, Color_Order_Block_Bajista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          sellOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_bajistas[i].time1)] = ob_bajistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_bajistas[i].name, 1, time[0], ob_bajistas[i].price2);
       }
      else
       {
        Alert("El order block bajista esta siendo mitigado: ", TimeToString(ob_bajistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_obb, ob_bajistas[i].name);
        AddArrayNoVerification(curr_elimiandor_obb, ob_bajistas[i].name); 

        if(buscar_obb == true)
         {
          time_b = iTime(_Symbol, _Period, 0);
          buscar_obb = false;
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_obb) ; i++)
     {
      DeleteArrayBiName(ob_bajistas, curr_elimiandor_obb[i]);
     }

Order Blocks de alta.

    static bool buscar_oba = true;
    static datetime time_a = 0;
    string curr_elimiandor_oba[];

    for(int i = 0; i < ArraySize(ob_alcistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_alcista(ob_alcistas[i], ob_alcistas[i].time1);

      if(ob_alcistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_alcistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_alcistas[i].name, 0, ob_alcistas[i].time1, ob_alcistas[i].price1,
                          time[0], ob_alcistas[i].price2, Color_Order_Block_Alcista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          buyOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_alcistas[i].time1)] = ob_alcistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_alcistas[i].name, 1, time[0], ob_alcistas[i].price2); //por el contrario si si existe el objeto lo unico que haremos es actulizarlo al tiempo actual usando el punto de anclaje 1
       }
      else
       {
        Alert("El order block alcista esta siendo mitigado: ", TimeToString(ob_alcistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_oba, ob_alcistas[i].name);
        AddArrayNoVerification(curr_elimiandor_oba, ob_alcistas[i].name);

        if(buscar_oba == true)
         {
          buscar_oba = false;
          time_a = iTime(_Symbol, _Period, 1);
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_oba) ; i++)
     {
      DeleteArrayBiName(ob_alcistas, curr_elimiandor_oba[i]);
     }

6. Função para remover objetos

Ao remover objetos do gráfico, é necessário fazer algumas alterações. Antes, era criado um array no qual eram armazenados os nomes dos Order Blocks mitigados para evitar que fossem processados novamente. Agora foi implementada uma nova função que remove esses Order Blocks do array principal. Por esse motivo, os nomes dos Order Blocks mitigados precisam ser armazenados em arrays auxiliares, para que posteriormente possam ser removidos do gráfico.

A seguir, apresentamos a função Delete_Objects, modificada de modo que ela também percorra os arrays auxiliares e remova seu conteúdo:

void Eliminar_Objetos()
 {
  ObjectsDeleteAll(0, "ENTRY", -1, -1);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_oba) ; i++) ObjectDelete(0,pricetwo_eliminados_oba[i]);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_obb) ; i++) ObjectDelete(0,pricetwo_eliminados_obb[i]); 
  for(int i = 0; i < ArraySize(ob_alcistas) ; i++) ObjectDelete(0,ob_alcistas[i].name);
  for(int i = 0; i < ArraySize(ob_bajistas) ; i++) ObjectDelete(0,ob_bajistas[i].name);
  
  ArrayFree(pricetwo_eliminados_oba);
  ArrayFree(pricetwo_eliminados_obb);
 }

7. Uso de arrays predefinidos

A lógica foi otimizada com o uso de arrays predefinidos que o MQL fornece na função OnCalculate. Agora utilizaremos esses arrays para o cálculo dos Order Blocks. Os principais dados usados das velas são "open", "close", "high", "low" e "tick_volume".

Essa abordagem facilita o gerenciamento e a interpretação das informações ao trabalhar com Order Blocks.

    ArraySetAsSeries(open, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(atr, true);


Criação do EA e seus parâmetros

Comecemos criando o EA:

1. Criamos o robô pelo template:

Part 1

2. Indicamos o nome e o autor:

Part 2

3. Selecionamos apenas o evento OnTradeTransaction:

Part 3

4. Finalizamos:

Part 4

Em seguida, criaremos os parâmetros necessários para que o EA funcione corretamente. Começaremos pelos parâmetros gerais do robô, como o número mágico e as configurações padrão, que já eram usadas no indicador Order Blocks.

1. Parâmetros gerais

Antes de configurar os parâmetros gerais, definiremos uma enumeração contendo os tipos de TP e SL aceitos pelo indicador, que serão os mesmos usados no próprio indicador.

enum ENUM_TP_SL_STYLE
 {
  ATR = 0,
  POINT = 1
 };

A seguir, apresentamos brevemente o significado de cada parâmetro adicionado:

  • Magic número mágico que identifica as operações do robô, permitindo distingui-las das demais.

  • timeframe_order_block define o timeframe no qual será realizada a detecção dos Order Blocks.

  • Rango_universal_busqueda quantidade de velas anteriores que o robô analisará em busca de possíveis Order Blocks.

  • Witdth_order_block define a espessura das linhas (bordas) dos retângulos que representam os Order Blocks.

  • Back_order_block eFill_order_block parâmetros gráficos para desenhar o fundo e o preenchimento do retângulo que representa cada Order Block.

  • Color_Order_Block_Bassist e Color_Order_Block_Bullish cores dos Order Blocks de baixa e de alta, respectivamente.

  • tp_sl_style estilo de cálculo do Take Profit e Stop Loss (em ATR ou em pontos).

  • Atr_Multiplier_1 eAtr_Multiplier_2 multiplicadores usados caso o estilo ATR esteja ativo.

  • TP_POINT eSL_POINT valores de Take Profit e Stop Loss quando se utiliza o estilo POINT.

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

2. Parâmetros de gerenciamento de riscos

Dando continuidade à descrição dos parâmetros do EA, definiremos aqueles que serão usados no gerenciamento de riscos. Os parâmetros gerais são os seguintes:

  • Lote_Type determina se será usado lote dinâmico, baseado no gerenciamento de risco por operação, ou lote fixo.

  • lote tamanho do lote utilizado quando Lote_Type está definido como Fixed.

  • risk_mode permite escolher se a conta é pessoal ou de uma PropFirm, como propfirm_ftmo.

  • get_mode determina a forma de cálculo do tamanho do lote: ele se baseia no valor do Stop Loss e ajusta o lote conforme o risco por operação.

  • prop_firm_balance quando se utiliza uma conta FTMO (ou outra PropFirm com regras semelhantes), define o saldo inicial da conta. Como explicado em artigos anteriores, esse parâmetro é usado para calcular o prejuízo máximo por operação e o prejuízo máximo diário.

input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

3. Parâmetros para o prejuízo máximo (diário, semanal e total)

Agora definimos os parâmetros para controlar o prejuízo máximo (ML), o prejuízo máximo semanal (MWL) e o prejuízo máximo diário (MDL). Cada um desses parâmetros se baseia em três variáveis, que determinam como o limite de perda será calculado e aplicado:

  • percentage_or_money_...*input representa o valor em percentual ou em dinheiro, dependendo do modo escolhido. Se for definido como 0, o limite de prejuízo não será utilizado.

  • mode_calculation*... indica se o parâmetro será avaliado em percentual ou em dinheiro.

  • applied_percentages_... define sobre qual base o percentual será aplicado, por exemplo, saldo, lucro da conta etc.

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

4. GMLPO: risco máximo por operação

Prosseguindo com a configuração dos parâmetros do EA, ajustaremos o prejuízo máximo por operação. Essa seção é mais complexa, pois não se limita a três parâmetros, mas inclui mais variáveis, já que, como mencionado no início, aqui é acrescentado o risco dinâmico.

4.1 Parâmetros gerais do GMLPO

Começaremos definindo cinco parâmetros principais para o risco dinâmico. Eles incluem três parâmetros já utilizados para o prejuízo máximo diário, semanal e total, além de dois parâmetros adicionais. Esses dois últimos permitem configurar a frequência da verificação que determina a necessidade de alterar o risco por operação, bem como o tipo de configuração do risco dinâmico que será utilizada. A seguir são mostradas as enumerações adicionais que serão utilizadas:

//--- Enumeration of the types of dynamic operational risk
enum ENUM_OF_DYNAMIC_MODES_OF_GMLPO
 {
  DYNAMIC_GMLPO_FULL_CUSTOM, //Customisable dynamic risk per operation
  DYNAMIC_GMLPO_FIXED_PARAMETERS,//Risk per operation with fixed parameters
  NO_DYNAMIC_GMLPO //No dynamic risk for risk per operation
 };
//--- Enumeration to determine when to review a decrease in the initial balance to modify the risk per operation
enum ENUM_REVISION_TYPE
 {
  REVISION_ON_CLOSE_POSITION, //Check GMLPO only when closing positions
  REVISION_ON_TICK //Check GMLPO on all ticks
 };
sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:
  • mode_gmlpo determina se o risco por operação será dinâmico. Seleciona-se um dos modos de configuração do risco dinâmico. Se o risco dinâmico não for utilizado, deve ser escolhido o tipo "NO_DYNAMIC_GMLPO".
  • revision_type_gmlpo indica quando a equity da conta será verificada para ajustar o risco por operação no fechamento da posição ou a cada tick do mercado.
  • percentage_or_money_gmlpo_input valor em percentual ou em dinheiro que será usado como base para determinar o risco por operação. Se for definido como 0, a função GMLPO não será ativada.
  • mode_calculation_gmlpo determina se "percentage_or_money_gmlpo_input" será interpretado como percentual ou como valor monetário fixo.
  • applied_percentages_gmlpo define a base, como Balance ou Equity, sobre a qual o percentual será calculado, caso esse modo esteja selecionado.

Observação: cada uma das enumerações utilizadas, já mencionadas em artigos anteriores, fornece informações mais detalhadas sobre como ocorre o cálculo interno do risco. 

4.2 Configurações do GMLPO dinâmico

Como já mencionado, existem dois modos de configuração do risco dinâmico: totalmente personalizável e baseado em parâmetros fixos. Na publicação anterior, expliquei as razões para essa decisão. Em resumo, o problema de usar strings como parâmetro é que elas não podem ser otimizadas, o que cria uma limitação na busca pelos melhores parâmetros do risco dinâmico. Para resolver isso, foi implementada uma enumeração.

4.2.1 GMLPO dinâmico personalizável

Nesse modo, o usuário define exatamente em qual percentual de redução do saldo será necessário alterar o risco, bem como qual será o novo valor de risco por operação. Esse modo oferece máxima flexibilidade, embora seja baseado em strings, o que impede a otimização.

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

  • str_percentages_to_be_reviewed contém a lista de percentuais, ordenados de forma crescente, que indicam o valor limite a partir do qual o risco por operação será alterado. Por padrão, são definidos os valores "2,5,7,9", o que significa que, quando o lucro da conta, expresso em percentual, cair abaixo de 2%, o risco será alterado para o respectivo valor, sendo aplicado o primeiro valor de str_percentages_to_apply (1).

  • str_percentages_to_apply contém os novos percentuais de risco para os quais o risco por operação será ajustado.

4.2.2 GMLPO dinâmico com parâmetros fixos

Se você preferir configurar o risco dinâmico por meio de parâmetros fixos, foi incluída uma seção específica nos parâmetros do EA. O princípio de funcionamento é semelhante ao modo personalizável, porém, nesse caso, é possível definir até quatro parâmetros. Se não for necessário utilizar todos os quatro e bastar apenas três, é suficiente inserir dois zeros nos campos que não serão usados. Por exemplo, se apenas três "modificadores" forem usados na seção “-- 4 --”, os dois parâmetros restantes devem ser definidos como 0.

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

5. Máxima lucratividade diária (MDP)

Para completar a lista de parâmetros de gerenciamento, adiciona-se o parâmetro Máxima lucratividade diária (MDP). Ele contém três parâmetros gerais utilizados para todos os limites de prejuízo/lucratividade, além de um novo parâmetro mdp_is_strict. Esse parâmetro indica se o modo de verificação da superação da lucratividade máxima por operação será utilizado.

Se mdp_is_strict for igual a false, isso significa que, independentemente de ter havido prejuízos ao longo do dia, basta ultrapassar o valor de "MDP". Por exemplo, se a meta de lucro é 10 USD e durante o dia houve um prejuízo de 4 USD e depois um ganho de, digamos, 5 USD, isso será considerado como superação da lucratividade máxima por operação. Por outro lado, o que acontece se mdp_is_strict for igual a true? Isso significa que não apenas será necessário alcançar o nível alvo de lucro, mas também recuperar todos os prejuízos diários antes que o lucro seja contabilizado como superior ao MDP. Por exemplo, se o alvo é 10 USD e houve um prejuízo de 5 USD, será necessário primeiro recuperar esses 5 USD e depois ganhar mais 10 USD.

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

6. Sessão de negociação

Para evitar negociações em períodos de baixa volatilidade ou em horários inadequados, foi adicionada a possibilidade de definir uma sessão de negociação.

Se o horário atual estiver dentro do intervalo definido, o robô executará operações; caso contrário a negociação não será realizada.

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)

Lista completa de parâmetros:

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

sinput group "--- Risk Management ---"
input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)


Declaração de variáveis globais

Nesta seção serão criadas várias variáveis globais para o gerenciamento de riscos e para a atividade operacional do EA.

1. Objetos "CTrade" e "CRiskManagement"

Para realizar operações de negociação, conectaremos nossa biblioteca "Risk_Management.mqh", que foi desenvolvida ao longo de toda a série de artigos. Além disso, será declarado um objeto do tipo CTrade.

#include  <Risk_Management.mqh>
CTrade trade;

Em seguida, é criado um instância da classe CRiskManagement e são preenchidos os parâmetros exigidos pelo construtor.

CRiskManagemet risk(mdp_is_strict, get_mode, Magic, risk_mode, prop_firm_balance);

2. Variáveis para armazenar descritores dos indicadores

Serão criadas duas variáveis para armazenar os descritores dos indicadores: uma para o indicador Order Blocks e outra para a EMA, permitindo visualizar o processo de tomada de decisão do EA.

//--- Handles
int order_block_indicator_handle;
int hanlde_ma;

3. Arrays para armazenar valores de TP e SL

Para armazenar os valores que são copiados dos buffers do indicador Order Blocks, serão criados quatro arrays:

double tp1[];
double tp2[];
double sl1[];
double sl2[];

4. Variáveis para os fechamentos anteriores (diários, semanais e do timeframe dos Order Blocks)

O robô precisa salvar o timestamp, isto é, o momento de fechamento da última vela diária, semanal ou da vela do timeframe específico no qual foi detectado um bloco de ordens:

//---
datetime TiempoBarraApertua;
datetime TiempoBarraApertua_1;
datetime prev_vela;

5. Variável lógica para ativar ou desativar a atividade operacional

Para controlar a possibilidade de o EA continuar operando após exceder determinados limites de lucro ou prejuízo, será criada uma variável do tipo bool indicando se é permitido operar (true) ou não (false):

//---
bool opera = true;

6. Variáveis para armazenar o início e o fim da sessão de negociação

Por fim, são definidas as variáveis que registrarão o início e o fim da sessão na qual serão procurados sinais de compra ou venda:

datetime start_sesion;
datetime end_sesion;


Função OnInit

Nesta seção configuraremos tudo o que é necessário para o funcionamento correto do EA. Declaramos e definimos os parâmetros do indicador Order Blocks, inicializamos o descritor desse indicador e da EMA e, finalmente, configuramos o sistema de gerenciamento de riscos.

1. Criação do array MqlParam para o indicador Order Blocks

O primeiro passo é preparar o array de parâmetros correspondente a cada uma das configurações do indicador. Com a estrutura MqlParam, esses parâmetros podem ser passados para a função IndicatorCreate() de forma organizada:

//---
  MqlParam param[17];

  param[0].type = TYPE_STRING;
  param[0].string_value = "::Indicators\\Order_Block_Indicador_New_Part_2";

  param[1].type = TYPE_STRING;
  param[1].string_value = "--- Order Block Indicator settings ---";

  param[2].type = TYPE_STRING;
  param[2].string_value = "-- Order Block --";

  param[3].type = TYPE_INT;
  param[3].integer_value = Rango_universal_busqueda;

  param[4].type = TYPE_INT;
  param[4].integer_value = Witdth_order_block;

  param[5].type = TYPE_BOOL;
  param[5].integer_value = Back_order_block;

  param[6].type = TYPE_BOOL;
  param[6].integer_value = Fill_order_block;

  param[7].type = TYPE_COLOR;
  param[7].integer_value = Color_Order_Block_Bajista;

  param[8].type = TYPE_COLOR;
  param[8].integer_value = Color_Order_Block_Alcista;

  param[9].type = TYPE_STRING;
  param[9].string_value = "-- Strategy --";

  param[10].type = TYPE_INT;
  param[10].integer_value = tp_sl_style;

  param[11].type = TYPE_STRING;
  param[11].string_value = "- ATR";

  param[12].type = TYPE_DOUBLE;
  param[12].double_value = Atr_Multiplier_1;

  param[13].type = TYPE_DOUBLE;
  param[13].double_value = Atr_Multiplier_2;

  param[14].type = TYPE_STRING;
  param[14].string_value = "- POINT";

  param[15].type = TYPE_INT;
  param[15].integer_value = TP_POINT;

  param[16].type = TYPE_INT;
  param[16].integer_value = SL_POINT;

Os índices do array param[] correspondem a cada parâmetro necessário para o funcionamento do indicador Order Blocks.

2. Criação e verificação dos descritores dos indicadores

Após preencher o array, utiliza-se a função IndicatorCreate() para obter o descritor do indicador Order Blocks. Em seguida, é criado o descritor da EMA, que servirá como referência adicional na estratégia de entradas e saídas.

//---
  order_block_indicator_handle = IndicatorCreate(_Symbol, timeframe_order_block, IND_CUSTOM, ArraySize(param), param);
  hanlde_ma = iMA(_Symbol, timeframe_order_block, 30, 0, MODE_EMA, PRICE_CLOSE);
  trade.SetExpertMagicNumber(Magic);

  if(order_block_indicator_handle == INVALID_HANDLE)
   {
    Print("The order blocks indicator is not available last error: ", _LastError);
    return INIT_FAILED;
   }

  if(hanlde_ma == INVALID_HANDLE)
   {
    Print("The ema indicator is not available latest error: ", _LastError);
    return INIT_FAILED;
   }

3. Adição dos indicadores ao gráfico

Para facilitar a depuração e o controle visual, os indicadores podem ser adicionados diretamente ao gráfico. Essa etapa é opcional, porém, ao visualizar os objetos desenhados, torna-se mais fácil confirmar se os parâmetros estão corretos e se a representação gráfica está consistente.

  ChartIndicatorAdd(0, 0, order_block_indicator_handle);
  ChartIndicatorAdd(0, 0, hanlde_ma);

4. Configuração do gerenciamento de riscos

Configuramos nosso objeto CRiskManagement, que será responsável por aplicar os limites de prejuízo e lucro, além de calcular o tamanho ideal do lote.

//---
  risk.SetPorcentages(percentage_or_money_mdl_input, percentage_or_money_mwl_input, percentage_or_money_gmlpo_input
                      , percentage_or_money_ml_input, percentage_or_money_mdp_input);
  risk.SetEnums(mode_calculation_mdl, mode_calculation_mwl, mode_calculation_gmlpo, mode_calculation_ml, mode_calculation_mdp);
  risk.SetApplieds(applied_percentages_mdl, applied_percentages_mwl, applied_percentages_gmlpo, applied_percentages_ml, applied_percentages_mdp);

Configuração do GMLPO dinâmico

Dependendo do modo de configuração selecionado, seja fixo ou totalmente personalizável, são definidos os valores correspondentes de risco por operação.

  if(mode_gmlpo == DYNAMIC_GMLPO_FIXED_PARAMETERS)
   {
    string percentages_to_activate, risks_to_be_applied;
    SetDynamicGMLPOUsingFixedParameters(inp_balance_percentage_to_activate_the_risk_1, inp_balance_percentage_to_activate_the_risk_2, inp_balance_percentage_to_activate_the_risk_3
                                        , inp_balance_percentage_to_activate_the_risk_4, inp_percentage_to_be_modified_1, inp_percentage_to_be_modified_2, inp_percentage_to_be_modified_3, inp_percentage_to_be_modified_4
                                        , percentages_to_activate, risks_to_be_applied);
    risk.SetDynamicGMLPO(percentages_to_activate, risks_to_be_applied, revision_type_gmlpo);
   }
  else
    if(mode_gmlpo == DYNAMIC_GMLPO_FULL_CUSTOM)
      risk.SetDynamicGMLPO(str_percentages_to_be_reviewed, str_percentages_to_apply, revision_type_gmlpo);

A função SetDynamicGMLPOUsingFixedParameters() converte os parâmetros fixos (inp_balance_percentage_to_activate_the_risk_X e inp_percentage_to_be_modified_X) em strings. Essa função é bastante simples, pois essencialmente cria strings somando os valores das variáveis previamente convertidas para o tipo string:

void SetDynamicGMLPOUsingFixedParameters(
  double _balance_percentage_to_activate_the_risk_1, double _balance_percentage_to_activate_the_risk_2, double _balance_percentage_to_activate_the_risk_3, double _balance_percentage_to_activate_the_risk_4,
  double _percentage_to_be_modified_1, double _percentage_to_be_modified_2, double _percentage_to_be_modified_3, double _percentage_to_be_modified_4,
  string &percentages_to_activate, string &risks_to_be_applied)
 {
  percentages_to_activate = DoubleToString(_balance_percentage_to_activate_the_risk_1) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_2) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_3)
                            + "," + DoubleToString(_balance_percentage_to_activate_the_risk_4);
  risks_to_be_applied = DoubleToString(_percentage_to_be_modified_1) + "," + DoubleToString(_percentage_to_be_modified_2) + "," + DoubleToString(_percentage_to_be_modified_3)
                        + "," + DoubleToString(_percentage_to_be_modified_4);
 }

 5. Configuração do array para TP/SL do indicador Order Blocks

Por fim, são configurados os arrays nos quais os dados do indicador Order Blocks serão armazenados de forma sequencial:

//---
  ArraySetAsSeries(tp1, true);
  ArraySetAsSeries(tp2, true);
  ArraySetAsSeries(sl1, true);
  ArraySetAsSeries(sl2, true);


Funções OnTick e OnTradeTransaction

As funções OnTick e OnTradeTransaction são fundamentais para qualquer sistema de negociação. No nosso caso, OnTick é usada para verificar a superação de limites de prejuízo, determinar o início de novas velas diárias, semanais etc., além de executar sinais com base nos dados do indicador Order Blocks.

1. Verificação do início de um novo dia e de uma nova semana, configuração da sessão

Na função OnTick, o primeiro passo é determinar se um novo dia ou uma nova semana começou. Isso é feito comparando o datetime do último bar dos períodos diário (PERIOD_D1) e semanal (PERIOD_W1).

Durante a verificação da vela diária, a variável opera é redefinida para true, indicando que a negociação está permitida. Além disso, é executada a função OnNewDay do objeto risk e é calculado o horário da sessão de negociação do dia atual.

//---
  if(TiempoBarraApertua != iTime(_Symbol, PERIOD_D1, 0))
   {
    opera = true;
    risk.OnNewDay();
    start_sesion = HoraYMinutoADatetime(hora_inicio,min_inicio);
    end_sesion = HoraYMinutoADatetime(hora_fin,min_fin);

    if(TiempoBarraApertua_1 != iTime(_Symbol, PERIOD_W1, 0))
     {
      risk.OnNewWeek();
      TiempoBarraApertua_1 = iTime(_Symbol, PERIOD_W1, 0);
     }

    TiempoBarraApertua = iTime(_Symbol, PERIOD_D1, 0);
   }

Observação: a função HoraYMinutoADatetime(int hora, int minuto) converte as horas e minutos definidos nos parâmetros de entrada em uma variável do tipo datetime.

datetime HoraYMinutoADatetime(int hora, int minuto) {
  MqlDateTime tm;
  TimeCurrent(tm);
// Asigna la hora y el minuto deseado
  tm.hour = hora;
  tm.min = minuto;
  tm.sec = 0; // Puedes ajustar los segundos si es necesario
  return StructToTime(tm);;
}

2. Verificação e lógica a cada nova vela do timeframe dos Order Blocks

O próximo passo é verificar se uma nova vela se formou no timeframe definido para o indicador Order Blocks. Após detectar uma nova vela, os buffers do indicador são copiados para obter os valores de TP e SL. Em seguida, o SL é definido para o cálculo do tamanho do lote, e uma ordem de compra ou venda é executada conforme o sinal.

 if(prev_vela != iTime(_Symbol, timeframe_order_block, 0))
   {
    CopyBuffer(order_block_indicator_handle, 2, 0, 5, tp1);
    CopyBuffer(order_block_indicator_handle, 3, 0, 5, tp2);
    CopyBuffer(order_block_indicator_handle, 4, 0, 5, sl1);
    CopyBuffer(order_block_indicator_handle, 5, 0, 5, sl2);

    if(tp1[0] > 0 && tp2[0]  > 0 && sl1[0] > 0 &&  sl2[0] > 0)
     {
      if(tp2[0] > sl2[0] && risk.GetPositionsTotal() == 0)  //compras
       {
        double ASK = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
        risk.SetStopLoss(ASK - sl1[0]);
        double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_BUY) : lote);

        if(lot > 0.0)
          trade.Buy(lot, _Symbol, ASK, sl1[0], tp1[0], "Order Block EA Buy");
       }
      else
        if(sl2[0] > tp2[0] && risk.GetPositionsTotal() == 0)  //venta
         {
          double BID = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
          risk.SetStopLoss(sl1[0] - BID);
          double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_SELL) : lote);

          if(lot > 0.0)
            trade.Sell(lot, _Symbol, BID, sl1[0], tp1[0], "Order Block EA Sell");
         }
     }

    prev_vela = iTime(_Symbol, timeframe_order_block, 0);
   }

Observação: a função risk.GetPositionsTotal() limita o número de operações abertas simultaneamente. Neste exemplo, verifica-se se não há posições abertas antes de enviar uma nova ordem.

São utilizados os valores sl1 e tp1, mas, se necessário, também é possível utilizar tp2, de forma a configurar relações diferentes, como 1:2, conforme definido nos parâmetros.

3. Verificação final do gerenciamento de riscos

Ao final de cada OnTick, é verificado se os limites de prejuízo ou lucro foram excedidos. Caso esses limites sejam ultrapassados, todas as posições abertas pelo EA são encerradas, e a variável "opera" é alterada para false.

 risk.OnTickEvent();

  if(risk.ML_IsSuperated(CLOSE_POSITION_AND_EQUITY)  == true)
   {
    if(risk_mode == propfirm_ftmo)
     {
      Print("The expert advisor lost the funding test");
      ExpertRemove();
     }
    else
     {
      risk.CloseAllPositions();
      Print("Maximum loss exceeded now");
      opera = false;
     }
   }

  if(risk.MDL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Maximum daily loss exceeded now");
    opera = false;
   }

  if(risk.MDP_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Excellent Maximum daily profit achieved");
    opera = false;
   }

Observação: podem ser adicionadas verificações adicionais para controlar outros tipos de prejuízo, como o prejuízo máximo semanal.

 if(risk.MWL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("The maximum weekly loss has been exceeded");
    opera = false;
    extra = false;
   }

4. Evento OnTradeTransaction

Para finalizar, a função OnTradeTransaction é implementada para capturar eventos de abertura, fechamento ou modificação de transações. Essa função chama o método correspondente da classe de gerenciamento de riscos para processar esses eventos.

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
 {
  risk.OnTradeTransactionEvent(trans);
 }


Função OnDeinit

A função OnDeinit é executada imediatamente antes da remoção ou desligamento do EA do gráfico. Nesse momento, o ideal é liberar todos os recursos e remover todos os indicadores ou objetos adicionados durante a execução, para deixar o gráfico limpo e evitar vazamentos de memória.

A seguir está um exemplo de como realizar essa limpeza.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
 {
//---
  ChartIndicatorDelete(0, 0, ChartIndicatorName(0, 0, GetMovingAverageIndex()));
  ChartIndicatorDelete(0, 0, "Order Block Indicator");

  if(hanlde_ma != INVALID_HANDLE)
    IndicatorRelease(hanlde_ma);

  if(order_block_indicator_handle != INVALID_HANDLE)
    IndicatorRelease(order_block_indicator_handle);

 }

Busca e remoção da média móvel (EMA) do gráfico

Para excluir um indicador do tipo Moving Average, é necessário primeiro localizar seu nome, que aparece na aba de indicadores do gráfico. Às vezes, a única forma de fazer isso é realizando uma busca iterativa pelo nome ou por parte dele.

//+------------------------------------------------------------------+
//| Extra Functions                                                  |
//+------------------------------------------------------------------+
int GetMovingAverageIndex(long chart_id = 0)
 {
  int total_indicators = ChartIndicatorsTotal(chart_id, 0);
  for(int i = 0; i < total_indicators; i++)
   {
    string indicator_name = ChartIndicatorName(chart_id, 0, i);
    if(StringFind(indicator_name, "MA") >= 0)  return i;
   }
  return -1;
 }
//+------------------------------------------------------------------+

ChartIndicatorsTotal(chart_id, 0) retorna o número total de indicadores anexados à janela principal do gráfico.

ChartIndicatorName(chart_id, 0, i) retorna o nome de cada indicador presente na janela principal.

StringFind(indicator_name, "MA") verifica se o nome contém a palavra "MA" (pode ser "EMA", "MA" etc.). Se houver correspondência, a função retorna o índice.

Assim que obtivermos o índice na lista de indicadores, podemos usar esse nome para excluir o indicador com ChartIndicatorDelete.

Liberação dos descritores dos indicadores

A chamada da função IndicatorRelease() é usada para garantir a liberação completa do indicador da memória, especialmente no caso de indicadores personalizados ou quando seus dados são manipulados por meio de descritores. Caso isso não seja feito, dados residuais podem permanecer na memória após o fechamento do EA.
  • handle_ma descritor da média móvel exponencial (EMA).
  • order_block_indicator_handle descritor do indicador Order Blocks.


Testes

Por fim, na seção final, realizaremos testes com este EA e avaliaremos as vantagens e desvantagens do uso do sistema de gerenciamento de riscos.

Nosso primeiro backtest será executado utilizando dados do último ano.

Período do teste: de 01.01.2024 a 28.03.2025.

  • Símbolo: ouro (Gold)
  • Timeframe: M5
  • Modelo: execução em cada tick, baseado em ticks reais
  • Alavancagem: 1:100

    Configurações:

    Gráfico:

                                                                                                           Backtest 1

    Observação: lembramos que este backtest não foi otimizado.

    Agora executaremos a otimização do EA, e estes são os resultados:

    • Novo timeframe: M3
    • Parâmetros utilizados: máxima lucratividade diária e máximo prejuízo diário

                                                                                                          Backtest 2       

    Nós otimizamos o horário de negociação, o prejuízo máximo, a lucratividade máxima, o risco por operação, o multiplicador do ATR e o timeframe, que idealmente é de 3 minutos.

    Observação: nos dois backtests realizados foi utilizado o ATR para calcular o Stop Loss e o Take Profit.

    Por fim, para demonstrar que o gerenciamento de riscos pode influenciar os resultados do backtest, removeremos os parâmetros de prejuízo máximo diário e de máxima lucratividade, mantendo os demais parâmetros.

    Aqui está o gráfico:

                                                                                                      Backtest 3                                                   

    Como pode ser visto, no gráfico do backtest 3 observa-se um crescimento inicial mais rápido, que posteriormente é afetado por diversos fatores e começa a cair. Já o gráfico do backtest 2, por outro lado, apresenta um crescimento suave e constante, sem picos pronunciados. Isso indica que, ao definir um limite de lucro, podemos evitar um número excessivo de operações e nos proteger de sequências de perdas e de situações imprevistas. Enquanto o limite de prejuízo máximo ajuda a limitar as perdas potenciais que podem ocorrer, por exemplo, durante uma série de operações negativas. A restrição do prejuízo máximo diário pode restaurar o equilíbrio alcançado pelo EA.

    Realizaremos o último teste usando os mesmos parâmetros do backtest 3, mas com uma diferença o risco dinâmico será ativado quando a conta começar a apresentar resultados negativos.

                                                                                                    Backtest 4  

    Podemos notar que, quando o percentual de lucro da conta fica negativo, o risco por operação diminui drasticamente, limitando as perdas potenciais. Entre os backtests 3 e 4 existem duas diferenças principais:

    • Sequência lucrativa

    Durante o backtest 3, na sequência de operações positivas, não apenas os prejuízos foram recuperados, mas também o saldo aumentou em 10%. Em uma avaliação para obtenção de financiamento, esse cenário resultaria em prejuízo, pois o saldo cairia aproximadamente para 8600. Em contraste, no backtest 4, mesmo com uma sequência lucrativa, o saldo permaneceu no nível inicial (10000) sem apresentar um aumento significativo.

    • Recuperação após perdas

    No backtest 4, a recuperação das perdas normalmente requer mais tempo e mais operações. A vantagem do risco dinâmico é que, mesmo durante uma avaliação para financiamento, a conta fica melhor protegida, e o saldo mínimo se mantém em torno de 9086.

    A conclusão que podemos tirar desses resultados é que o risco dinâmico é preferível para PropFirms, pois limita de forma significativa as perdas potenciais que o robô pode gerar. Contudo, em uma conta comum, o uso do risco dinâmico pode aumentar o tempo necessário para a recuperação, o que não representa um problema em uma PropFirm, onde o objetivo é gerenciar a conta e não obter lucros rapidamente.


    Considerações finais

    Neste artigo, implementamos o sistema de gerenciamento de riscos em um EA baseado em Order Blocks. Como se pode notar, há diferenças significativas na aplicação de limites de lucro e de prejuízo, assim como no gerenciamento de risco dinâmico. Na parte final do artigo, o funcionamento do sistema foi apresentado graficamente, demonstrando de forma clara a eficácia dos mecanismos de proteção aplicados.

    Arquivos usados/melhorados neste artigo:

    Nome do arquivo Tipo Descrição 
    Risk_Management.mqh  .mqh (arquivo incluído) Arquivo principal que contém as funções gerais e a implementação da classe CRiskManagement, responsável pelo gerenciamento de riscos do sistema. Neste arquivo são definidas, desenvolvidas e ampliadas todas as funções relacionadas ao gerenciamento de lucros e prejuízos.
    Order_Block_Indicador_New_Part_2.mq5 .mq5 Arquivo que contém o código do indicador Order Blocks.
    Order Block EA MT5.mq5  .mq5 Arquivo que contém o código do EA Order Blocks.
    Set 1% Risk.set  .set Configurações usadas no backtest 1.
    Set Order Block EA.set .set Configurações usadas no backtest 2, com limites diários de prejuízo máximo e de lucro.
    Set Dynamic Risk.set .set Configurações aplicadas para testar o risco dinâmico (backtest 3) sem limites diários de prejuízo e lucro.
    Set No Dynamic Risk.set .set Configurações aplicadas sem risco dinâmico (backtest 4), também sem limites diários de prejuízo e lucro.

    Traduzido do espanhol pela MetaQuotes Ltd.
    Artigo original: https://www.mql5.com/es/articles/17640

    Arquivos anexados |
    MQL5.zip (36.61 KB)
    Redes neurais em trading: Detecção de anomalias no domínio da frequência (Conclusão) Redes neurais em trading: Detecção de anomalias no domínio da frequência (Conclusão)
    Damos continuidade ao trabalho de implementação das abordagens do framework CATCH, que combina a transformada de Fourier e o mecanismo de patching em frequência, possibilitando a detecção precisa de anomalias de mercado. Nesta etapa, concluímos a realização da nossa própria versão das abordagens propostas e conduziremos testes com os novos modelos utilizando dados históricos reais.
    Reimaginando Estratégias Clássicas (Parte 13): Minimizando o Atraso em Cruzamentos de Médias Móveis Reimaginando Estratégias Clássicas (Parte 13): Minimizando o Atraso em Cruzamentos de Médias Móveis
    Os cruzamentos de médias móveis são amplamente conhecidos pelos traders em nossa comunidade, e ainda assim o núcleo da estratégia mudou muito pouco desde sua criação. Nesta discussão, apresentaremos um leve ajuste à estratégia original, que busca minimizar o atraso presente na estratégia de negociação. Todos os fãs da estratégia original podem considerar revisar a estratégia de acordo com os insights que discutiremos hoje. Ao usar 2 médias móveis com o mesmo período, reduzimos consideravelmente o atraso na estratégia de negociação, sem violar os princípios fundamentais da estratégia.
    Critérios de tendência. Conclusão Critérios de tendência. Conclusão
    Neste artigo, analisaremos as particularidades da aplicação prática de alguns critérios de tendência. Além disso, tentaremos desenvolver alguns novos critérios. A principal atenção será dada à eficácia desses critérios na análise de dados de mercado e no trading.
    Desenvolvimento de um sistema de monitoramento de entradas de swing (EA) Desenvolvimento de um sistema de monitoramento de entradas de swing (EA)
    À medida que o ano se aproxima do fim, traders de longo prazo costumam refletir sobre o histórico do mercado para analisar seu comportamento e tendências, visando projetar potenciais movimentos futuros. Neste artigo, exploraremos o desenvolvimento de um Expert Advisor (EA) de monitoramento de entradas de longo prazo usando MQL5. O objetivo é abordar o desafio das oportunidades de negociação de longo prazo perdidas devido ao trading manual e à ausência de sistemas automatizados de monitoramento. Usaremos um dos pares mais negociados como exemplo para estruturar e desenvolver nossa solução de forma eficaz.