English Русский Español Deutsch 日本語
preview
Uma Formulação Genérica de Otimização (GOF) para Implementar Max Personalizado com Restrições

Uma Formulação Genérica de Otimização (GOF) para Implementar Max Personalizado com Restrições

MetaTrader 5Exemplos | 2 setembro 2024, 16:07
40 0
better.trader every.day
better.trader every.day

Introdução — Noções Básicas de Otimização

Problemas de otimização têm duas fases: 1) formulação do problema e 2) solução do problema. Na primeira fase, há três componentes principais: as variáveis de entrada, as funções objetivo e as funções de restrição. Na segunda fase, a solução do problema é realizada numericamente com um algoritmo de otimização.

Variáveis (notação x_1, x_2, x_3, …, x_n) As variáveis são os "botões" que podemos ajustar para maximizar a função objetivo. Existem variáveis de diferentes tipos: inteiro, real e Booleano. Em consultores especializados, podemos usar variáveis como: o período da média móvel, a relação TP/SL na entrada, o SL em pips, etc.

Funções Objetivo (notação f_i(x)): Se houver mais de uma função objetivo, o problema é chamado de problema de otimização multiobjetivo. O algoritmo de otimização do MetaTrader 5 espera apenas uma função objetivo, portanto, para considerar mais de um objetivo, precisamos combiná-los em um só. A maneira mais simples de combinar funções objetivo é criando uma soma ponderada delas. Alguns desenvolvedores propuseram usar a operação de multiplicação para combiná-las, e isso pode funcionar em algumas situações, mas preferimos a soma como uma abordagem melhor. Neste artigo, usamos uma soma "direcionada e ponderada" de objetivos, como será explicado abaixo. Como exemplos de funções objetivo, temos: saldo, meta de lucro, taxa de vitória, retorno anual, lucro líquido, etc.

Funções de Restrição (notação g_i(x)): Estas são funções que geram um valor que o usuário deseja limitar. O limite pode ser um limite superior, como em g_i(x) ≤ U_i, onde g_i(x) é a i_ésima restrição, e U_i é o limite superior. Da mesma forma, uma restrição de limite inferior é como g_i(x) ≥ L_i, onde L_i é o limite inferior.

O algoritmo de otimização do MetaTrader 5 leva em consideração apenas as restrições impostas às variáveis de entrada (também conhecidas como restrições laterais), por exemplo, 3 <= x_1 <= 4, mas nenhum outro tipo de restrição pode ser usado. Portanto, precisamos incluir a presença de qualquer restrição adicional g_i(x) na função objetivo final e única, F(x), como será mostrado abaixo. Exemplos de funções de restrição g_i(x) são: limitar o número de perdas consecutivas, ou o índice de Sharpe, ou a taxa de vitória, etc.


Algoritmos de Otimização

Em termos gerais, existem dois tipos principais de algoritmos de otimização. O primeiro tipo é o mais clássico, baseado no cálculo de gradientes de todas as funções envolvidas no problema de otimização (isso remonta aos tempos de Isaac Newton). O segundo tipo é mais recente (desde a década de 1970) e não utiliza informações de gradiente. Entre esses dois tipos, podem haver algoritmos que combinam as duas abordagens mencionadas, mas não precisamos abordá-los aqui. O algoritmo do MetaTrader 5 chamado “Algoritmo Genético Rápido” — na aba Configurações do terminal MetaTrader 5 — pertence ao segundo tipo. Isso nos permite ignorar a necessidade de calcular gradientes para funções objetivo e de restrição. Além disso, graças à natureza sem gradiente do algoritmo do MetaTrader 5, conseguimos incluir funções de restrição que não seriam apropriadas para algoritmos baseados em gradiente. Mais sobre isso será discutido abaixo.

Um ponto importante é que o algoritmo do MetaTrader 5 chamado “Algoritmo Completo Lento” não é realmente um algoritmo de otimização, mas sim uma avaliação exaustiva de todas as combinações possíveis de valores para todas as variáveis de entrada dentro das restrições laterais.


A Função Objetivo F(x) Construída a partir de Múltiplos Objetivos f_i(x)

Nesta seção, abordaremos a combinação de múltiplas funções objetivo em uma única função objetivo F.

Como mencionado na Introdução, usamos a soma para combinar múltiplos objetivos. O leitor é convidado a alterar o código-fonte para usar uma combinação multiplicativa, se assim desejar. A fórmula de combinação por soma é:

Formulação da Função Objetivo

onde 

  • x é o vetor de n variáveis de entrada
  • f_i(x) é a i_ésima função objetivo
  • W_i é o i_ésimo peso
  • T_i é o i_ésimo alvo desejado para a i_ésima função objetivo

O alvo serve como um valor de normalização (divisor) para que um determinado objetivo seja comparável a outros objetivos na soma. Em outras palavras, a soma ponderada é para funções objetivo "normalizadas". O peso serve como tal, como um peso que multiplica o objetivo normalizado (f_i(x)/T_i) de modo que o usuário possa enfatizar qual objetivo normalizado é mais importante que o outro. Os pesos W_i são convexificados dividindo a soma de W_i*f_i(x)/T_i pela soma dos W_i.

Seleção de W_i: A convexificação dos pesos permite ao usuário focar em seu valor relativo, em vez de seu valor absoluto. Por exemplo, suponha que haja três objetivos: retorno anual, fator de recuperação e fator de lucro. Esses pesos: W_1=10, W_2=5, W_3=1, têm o mesmo efeito que W_1=100, W_2=50, W_3=10, ou seja, o objetivo normalizado 1 (retorno anual/T_1) é considerado duas vezes mais importante que o objetivo normalizado 2 (fator de recuperação/T_2), e dez vezes mais importante que o objetivo normalizado 3 (fator de lucro/T_3). Não há valores certos ou errados, desde que sejam positivos e expressem a importância relativa com base no critério do usuário.


Seleção de T_i: A seleção dos alvos T_i deve ser feita com cuidado após algumas simulações, antes de fazer a otimização completa. A razão para isso é estimar as faixas de cada função objetivo e definir valores de T_i que produzirão funções normalizadas (f_i(x)/T_i) de magnitude comparável. Por exemplo, suponha que sua conta tenha um saldo inicial de 10.000; seu algoritmo EA faz com que o saldo final seja em torno de 20.000 antes de ser otimizado; o fator de lucro é 0,9. Se você configurar seu problema de otimização com dois objetivos: f_1 = saldo, f_2 = fator de lucro, então um bom valor para os alvos seria: T_1 = 30k, T_2 = 2, o que fará com que ambas as funções normalizadas fiquem na mesma ordem de magnitude (valores comparáveis). Uma vez que você execute uma otimização completa, pode descobrir que o EA gera um saldo final muito alto e um fator de lucro semelhante. Nesse ponto, você pode ajustar os valores T_i se as funções normalizadas estiverem em ordens de magnitude diferentes. Os valores-alvo também devem ser números reais positivos. Use seu julgamento. Mais sobre este tópico será discutido quando abordarmos a saída do código GOF.


Adicionando Restrições 

A formulação da função objetivo como a soma ponderada dos objetivos individuais é bastante padrão. Agora, introduzimos uma maneira simples de incluir funções de restrição no problema de otimização de tal forma que o algoritmo Fast-Genetic-based do MetaTrader 5 ainda funcione. Vamos usar uma versão modificada do método de Lagrange, que subtrai uma penalidade do único objetivo de otimização. Para esse propósito, definiremos novas funções de penalidade que medem a quantidade de violação para cada restrição:

penalidade

Como você pode verificar, P_i(x) é positiva quando g_i(x) é violada e zero caso contrário. Se U_i ou L_i forem zero, substituímos por 1 para evitar divisão por zero.

No método do multiplicador de Lagrange, há um multiplicador por restrição, e seus valores são a solução de um sistema de equações. Na nossa versão modificada, assumimos que todos os multiplicadores são iguais e têm um valor constante k_p (veja a fórmula abaixo). Essa simplificação funciona aqui porque o algoritmo de otimização não precisa calcular gradientes de nenhuma função no problema de otimização.


A função objetivo final F(x) é


função objetivo

Nota: o símbolo “:=” significa atribuição, não definição matemática. 

O multiplicador k_p desempenha o papel dos multiplicadores de Lagrange e é usado para forçar projetos inviáveis (aqueles que violam pelo menos uma restrição) a ter um objetivo muito baixo. Essa redução do valor do objetivo fará com que o algoritmo genético no MetaTrader 5 classifique esse projeto muito baixo, tornando-o improvável de ser usado para reprodução na próxima geração de projetos.

O multiplicador k_o não faz parte do método do multiplicador de Lagrange. Ele é usado para aumentar a função objetivo para projetos viáveis, expandindo o lado positivo do eixo Y nos gráficos de otimização no terminal MetaTrader 5. 

O usuário pode alterar os valores de K_o e K_p na seção de Miscellaneous (Diversos) de entrada. Recomendamos que os valores de k_o e K_p sejam potências de 10 (por exemplo, 10, 100, 1000, etc.).


Captura de tela da aba Inputs do terminal MetaTrader 5

Existem três seções de entrada no GOF: funções objetivo, funções de restrição e diversos.

Abaixo está a seção de Funções Objetivo. Podem ser incluídos até 5 objetivos na formulação do problema, de um total de 18 funções possíveis que serão mostradas posteriormente. Adicionar mais de 5 objetivos pode ser feito seguindo o código, mas qualquer coisa além de 3 objetivos torna a seleção de pesos e alvos mais difícil. Adicionar mais de 18 funções possíveis pode ser feito seguindo o código.

seção de objetivos

Abaixo está a seção de função de restrição rígida. Podem ser adicionadas até 10 restrições à formulação de otimização, de um total de 14 funções de restrição implementadas no GOF. Adicionar mais de 10 restrições pode ser feito seguindo o código. Adicionar novas restrições à lista de 14 também pode ser feito seguindo o código.

seção de restrições

Abaixo está a Seção de Parâmetros de Otimização Diversos. Mais sobre essa seção será discutido nos próximos parágrafos.

diversos


Usando GenericOptimizationFormulation.mqh no seu EA

Se você é uma pessoa que tem pouca paciência para ler todo o artigo, primeiro fornecemos os passos para usar este código no seu EA para que você possa experimentá-lo primeiro e, em seguida, ler o restante do artigo com mais percepção. Abaixo estão os comentários iniciais no arquivo GenericOptimizationFormulation.mqh:

/*

Para usar este arquivo no seu EA, você deve fazer o seguinte:

Edite o arquivo .mq5 do EA

Insira as duas linhas seguintes após as variáveis de entrada e antes de OnInit()

     ulong gof_magic = um_valor_ulong;

     #include <YOUR_INCLUDE_LOCATION\GenericOptimizationFormulation.mqh>

Salve e compile seu arquivo EA
Se você receber erros de compilador, certifique-se de que:

substituiu um_valor_ulong pela variável que contém o número mágico ou pelo valor numérico mágico

substituiu YOUR_INCLUDE_LOCATION pelo nome da pasta onde seus arquivos de inclusão estão

*/

Esperamos que a inserção acima seja autoexplicativa. Mostraremos um exemplo usando o EA Moving Average do MetaTrader 5 mais tarde. Agora, vamos continuar com a explicação do código-fonte.


O Código-Fonte: GenericOptimizationFormulation.mqh

Agora que apresentamos as fórmulas para a função objetivo e restrições, discutimos a implementação em mql5 com trechos do código.

 Solicitações aos leitores:

  • Diz-se que um software com mais de sete linhas de código tem uma probabilidade não nula de ter um bug. O GOF tem mais de mil linhas, então a probabilidade é definitivamente não nula. Incentivamos o leitor a fornecer feedback nos comentários para melhorar o código.
  • Se você encontrar uma maneira melhor de escrever o código, estamos ansiosos para ver suas melhores linhas de código para melhorar o GOF.
  • Adicione e melhore o código com suas próprias funções objetivo e restrições. Por favor, compartilhe-as nos comentários também.
Bibliotecas Incluídas

Primeiro, precisamos incluir algumas bibliotecas para fazer algumas estatísticas e álgebra:

#include <Math\Stat\Lognormal.mqh>
#include <Math\Stat\Uniform.mqh>
#include <Math\Alglib\alglib.mqh>

Funções Objetivo para escolher:

enum gof_FunctionDefs {        // functions to build objective
   MAX_NONE=0,                 // 0] None
   MAX_AnnRetPct,              // 1] Annual Return %
   MAX_Balance,                // 2] Balance
   MAX_NetProfit,              // 3] Net Profit
   MAX_SharpeRatio,            // 4] Sharpe Ratio
   MAX_ExpPayOff,              // 5] Expected Payoff
   MAX_RecovFact,              // 6] Recovery Factor
   MAX_ProfFact,               // 7] Profit Factor
   MAX_LRcrr3,                 // 8] LRcrr^3
   MAX_NbrTradesPerWeek,       // 9] #Trades/week
   MAX_WinRatePct,             // 10] Win Rate %
   MAX_Rew2RiskRatio,          // 11] Reward/Risk(RRR=AvgWin/AvgLoss)
   MAX_OneOverLRstd,           // 12] 1/(LR std%)
   MAX_OneHoverWorstTradePct,  // 13] 100/(1+|WorstLoss/Init.Dep*100|)
   MAX_LR,                     // 14] LRslope*LRcorr/LRstd
   MAX_OneHoverEqtyMaxDDpct,   // 15] 100/(1+EqtyMaxDD%))
   MAX_StratEfficiency,        // 16] Seff=Profit/(TotalTrades*AvgLot)
   MAX_KellyCrit,              // 17] Kelly Criterion
   MAX_OneOverRoRApct          // 18] 1/Max(0.01,RoRA %)
};


Existem 18 objetivos possíveis para escolher um máximo de 5 para formular o problema de otimização. O usuário pode adicionar mais funções ao código-fonte seguindo o mesmo padrão de implementação. Os nomes das funções objetivo são destinados a serem autoexplicativos, exceto por algumas mencionadas abaixo:

  • LRcrr^3: o coeficiente de correlação de regressão linear elevado à potência de 3.
  • 1/(LR std%): LR std é o desvio padrão da regressão linear. O inverso mede quão próxima a linha de patrimônio está de uma linha reta.
  • 100/(1+|WorstLoss/Init.Dep*100|): a pior perda dividida pelo depósito inicial é uma medida de desempenho ruim. O inverso disso é uma medida de bom desempenho.
  • LRslope*LRcorr/LRstd: este é um objetivo multiplicativo de três funções da regressão linear: a inclinação, o coeficiente de correlação e o desvio padrão.
  • Seff=Profit/(TotalTrades*AvgLot): é uma medida de eficiência da estratégia. Preferimos estratégias com alto lucro, pequeno número de negociações com tamanho de lote pequeno.
  • 1/Max(0.01,RoRA %): RoRA é o risco de ruína da sua conta. Isso é calculado usando uma simulação de Monte Carlo que discutiremos mais tarde.


Restrições Rígidas para escolher:

enum gof_HardConstrains {
   hc_NONE=0,                     // 0] None
   hc_MaxAccountLoss_pct,         // 1] Account Loss % InitDep
   hc_maxAllowed_DDpct,           // 2] Equity DrawDown %
   hc_maxAllowednbrConLossTrades, // 3] Consecutive losing trades
   hc_minAllowedWin_pct,          // 4] Win Rate %
   hc_minAllowedNbrTradesPerWeek, // 5] # trades/week
   hc_minAllowedRecovFactor,      // 6] Recov Factor
   hc_minAllowedRRRFactor,        // 7] Reward/Risk ratio
   hc_minAllowedAnnualReturn_pct, // 8] Annual Return in %
   hc_minAllowedProfFactor,       // 9] Profit Factor
   hc_minAllowedSharpeFactor,     // 10] Sharpe Factor
   hc_minAllowedExpPayOff,        // 11] Expected PayOff
   hc_minAllowedMarginLevel,      // 12] Smallest Margin Level
   hc_maxAllowedTradeLoss,        // 13] Max Loss trade
   hc_maxAllowedRoRApct           // 14] Risk of Ruin(%)
};
enum gof_HardConstType {
   hc_GT=0, // >=     Greater or equal to
   hc_LT    // <=     Less or equal to
};
Existem 14 restrições possíveis para escolher um máximo de 10 para formular o problema de otimização. O usuário pode adicionar mais restrições ao código-fonte seguindo o mesmo padrão de implementação. Os nomes das funções de restrição são destinados a serem autoexplicativos. Existem dois tipos de restrições, hc_GT para restrições com limite inferior e ht_LT para restrições com limite superior. Mais sobre isso quando mostrarmos como usá-las.



Opções de Risco de Ruína

enum gof_RoRaCapital {
   roraCustomPct=0,  // Custom % of Ini.Dep.
   roraCustomAmount, // Custom Capital amount
   roraIniDep        // Initial deposit
};

Ao calcular o risco de ruína da sua conta, há três maneiras de definir o dinheiro da "conta". A primeira opção é como uma porcentagem do depósito inicial. A segunda opção é um valor de capital fixo dado na moeda da sua conta. A terceira é um caso especial da primeira opção, se a porcentagem for 100%. O risco de ruína é explicado mais adiante com o código.

Decimais da função objetivo

Como mencionado anteriormente, podemos optar por exibir informações adicionais da simulação usando os dois decimais na coluna de resultados. Aqui estão as opções:

enum gof_objFuncDecimals {
   fr_winRate=0, // WinRate %
   fr_MCRoRA,    // MonteCarlo Sim Risk of Ruin Account %
   fr_LRcorr,    // LR correlation
   fr_ConLoss,   // Max # Consecutive losing Trades
   fr_NONE       // None
};

fr_winRate é a taxa de vitória da simulação em porcentagem. Por exemplo, se a taxa de vitória for 34%, o resultado do objetivo será 0,39. Se a taxa de vitória for 100%, será exibido 0,99.

fr_MCRoRA é o risco de ruína da conta em porcentagem. Por exemplo, se o risco de ruína da conta for 11%, o resultado do objetivo será 0,11.

fr_LRcorr é o coeficiente de correlação da regressão linear. Por exemplo, se o coeficiente for 0,88, o resultado do objetivo será 0,88.

fr_ConLoss é o maior número de negociações consecutivas perdedoras. Por exemplo, se o número for 7, o resultado do objetivo será 0,07. Se o número for superior a 99, será exibido 0,99.

fr_NONE é usado quando você não deseja ver nenhuma informação nos decimais.


Funções Objetivo

A próxima seção no código é a seleção de funções objetivo individuais (máximo de 5). Abaixo está um trecho de apenas a primeira função, juntamente com seu alvo e peso.

input group   "- Build Custom Objective to Maximize:"
sinput gof_FunctionDefs gof_Func1   = MAX_AnnRetPct;          // Select Objective Function to Maximize 1:
sinput double gof_Target1           = 200;                    // Target 1
sinput double gof_Weight1           = 1;                      // Weight 1


Funções de Restrição

input group   "- Hard Constraints:"
sinput bool   gof_IncludeHardConstraints     = true;//if false, all constraints are ignored

sinput gof_HardConstrains gof_HC_1=hc_minAllowedAnnualReturn_pct; // Select Constraint Function 1:
sinput gof_HardConstType gof_HCType_1=hc_GT; // Type 1
sinput double gof_HCBound_1=50; // Bound Value 1

Há uma opção para desativar todas as restrições rígidas definindo gof_IncludeHardConstraints=false. Em seguida, há a seleção da primeira restrição, seu tipo e seu valor limite. Todas as dez restrições usam o mesmo formato.

Parâmetros de Otimização Diversos

input group   "------ Misc Optimization Params -----"
sinput gof_objFuncDecimals gof_fr                  = fr_winRate;     // Choose Result-column's decimals
sinput gof_RoRaCapital  gof_roraMaxCap             = roraCustomPct;  // Choose capital method for Risk of Ruin
sinput double           gof_RoraCustomValue        = 10;             // Custom Value for Risk of Ruin (if needed)
sinput bool             gof_drawSummary            = false;          // Draw summary on chart
sinput bool             gof_printSummary           = true;           // Print summary on journal
sinput bool             gof_discardLargestProfit   = false;          // Subtract Largest Profit from Netprofit
sinput bool             gof_discardLargestLoss     = false;          // Add Largest Loss to Net profit
sinput double           gof_PenaltyMultiplier      = 100;            // Multiplier for Penalties (k_p)
sinput double           gof_ObjMultiplier          = 100;            // Multiplier for Objectives (k_o)

Na seção acima, o usuário escolherá:

  • gof_fr: A quantidade a ser exibida como decimais na coluna de Resultados. 
  • gof_roraMaxCap: O método para calcular o capital RoRA.
  • gof_RoraCustomValue: O valor do capital ou % do depósito inicial para RoRA. Isso depende da sua seleção na linha anterior. Isso depende da sua seleção na linha anterior.
  • gof_drawSummary: Você pode escolher desenhar o resumo do relatório GOF no gráfico.
  • gof_printSummary: Você pode escolher imprimir o resumo do relatório GOF na Aba de Jornal.
  • gof_discardLargestProfit: Você pode subtrair o maior lucro do lucro líquido para desencorajar estratégias que favorecem um único grande ganho.
  • gof_discardLargestLoss: Você pode adicionar a maior perda ao lucro líquido para desencorajar estratégias que têm uma grande perda. 
  • gof_PenaltyMultiplier: o multiplicador "K_p" mostrado anteriormente na definição da função objetivo.
  • gof_ObjMultiplier: o multiplicador "K_o" mostrado anteriormente na definição da função objetivo.

Os valores padrão na seção de diversos devem funcionar bem.

As próximas linhas do código são para definir variáveis e buscar valores da função MetaTrader 5 TesterStatistics(). Depois disso, vem a seção principal do GOF:

//------------ GOF ----------------------
// Printing and displaying results from the simulation
   GOFsummaryReport();

// calculate the single objective function
   double SingleObjective = calcObjFunc();

// calculate the total penalty from constraint violations
   if(gof_IncludeHardConstraints) gof_constraintTotalPenalty=calcContraintTotalPenalty(gof_displayContraintFlag);

// Compute customMaxCriterion
// gof_PenaltyMultiplier pushes infeasible designs to have low objective values
// gof_PenaltyMultiplier expand the positive side of the Y axis
   double customMaxCriterion=gof_constraintTotalPenalty>0?
                             SingleObjective-gof_PenaltyMultiplier*gof_constraintTotalPenalty:
                             gof_ObjMultiplier*SingleObjective;

// add additional simulation result as two decimal digits in the result column
   customMaxCriterion=AddDecimalsToCustomMax(customMaxCriterion);

// Printing and displaying more results from GOF
   FinishGOFsummaryReport(customMaxCriterion);

   return (NormalizeDouble(customMaxCriterion,2));

O código acima mostra: 

  • GOFsummaryReport() para preparar o relatório resumido do GOF que vai na aba Jornal e no gráfico.
  • calcObjFunc() para calcular a função objetivo única combinada.
  • calcContraintTotalPenalty() para calcular a penalidade total devido a violações de restrições.
  • customMaxCriterion é então calculado como mostrado na Introdução, como a soma da função objetivo única menos a penalidade total das violações de restrições.
  • AddDecimalsToCustomMax() é usado para adicionar as informações nos decimais de customMaxCriterion.
  • FinishGOFsummaryReport() é para finalizar e imprimir o Relatório Resumido do GOF.


O restante do código é uma implementação direta das fórmulas apresentadas na introdução. A única parte que vale a pena discutir é o cálculo do risco de ruína.

Risco de Ruína usando Simulações de Monte Carlo

O risco de ruína poderia ser calculado com uma fórmula simples, mas escolhemos usar uma simulação de Monte Carlo em vez disso, porque a fórmula simples não estava dando resultados sensíveis. Para a abordagem de Monte Carlo, precisamos da média de ganhos e perdas, os desvios padrão desses ganhos e perdas, a taxa de vitória e o número de negociações na simulação. Além disso, precisamos fornecer o capital que define a ruína da conta. 

double MonteCarlo_RiskOfRuinAccount(double WinRatePct, double AvgWin, double AvgLoss, double limitLoss_money, int nTrades) {
// 10000 Montecarlo simulations, each with at least 100 trades.
// Ideally, if we had lots of trades in the history, we could use a Markov Chain transfer probability matrix
//  we are limiting the statistics to mean & stdev, without knowledge of a transfer probability information

   double posDealsMean,posDealsStd,negDealsMean,negDealsStd;
   CalcDealStatistics(gof_dealsEquity, posDealsMean,posDealsStd,negDealsMean,negDealsStd);

// seeding the random number generator
   MathSrand((int)TimeLocal()+1);

// ignore posDealsMean and negDealsMean. Use AvgWin and AvgLoss instead
   AvgLoss=MathAbs(AvgLoss);
   WinRatePct=MathMin(100,MathMax(0,WinRatePct));

// case when win rate is 100%:
   if((int)(WinRatePct*nTrades/100)>=nTrades) {
      WinRatePct=99;          // just to be a bit conservative if winrate=100%
      AvgLoss=AvgWin/2;       // a guessengineering value
      negDealsStd=posDealsStd;// a guessengineering value
   }

// Use log-normal distribution function. Mean and Std are estimated as:
   double win_lnMean =log(AvgWin*AvgWin/sqrt(AvgWin*AvgWin+posDealsStd*posDealsStd));
   double loss_lnMean=log(AvgLoss*AvgLoss/sqrt(AvgLoss*AvgLoss+negDealsStd*negDealsStd));
   double win_lnstd  =sqrt(log(1+(posDealsStd*posDealsStd)/(AvgWin*AvgWin)));
   double loss_lnstd =sqrt(log(1+(negDealsStd*negDealsStd)/(AvgLoss*AvgLoss)));

   double rand_Win[],rand_Loss[];
   double r[];

// limit amount of money that defines Ruin
   limitLoss_money=MathAbs(limitLoss_money);
   bool success;
   int ruinCount=0; // counter of ruins
   int successfulMCcounter=0;
   int nTradesPerSim=MathMax(100,nTrades);// at least 100 trades per sim
   int nMCsims=10000; // MC sims, each one with nTradesPerSim

   for(int iMC=0; iMC<nMCsims; iMC++) {
      success=MathRandomUniform(0,1,nTradesPerSim,r);

      // generate nTradesPerSim wins and losses for each simulation
      // use LogNormal distribution
      success&=MathQuantileLognormal(r,win_lnMean,win_lnstd,true,false,rand_Win);
      success&=MathQuantileLognormal(r,loss_lnMean,loss_lnstd,true,false,rand_Loss);
      if(!success)continue;
      successfulMCcounter++;
      //simulate nTradesPerSim
      double eqty=0; // start each simulation with zero equity
      for(int i=0; i<nTradesPerSim; i++) {

         // draw a random number in [0,1]
         double randNumber=(double)MathRand()/32767.;

         // select a win or a loss depending on the win rate and the random number
         // and add to the equity
         eqty+=randNumber*100 < WinRatePct?
               rand_Win[i]:
               -rand_Loss[i];

         // check if equity is below the limit (ruin)
         // count the number of times there is a ruin
         if(eqty<= -limitLoss_money) {
            ruinCount++;
            break;
         }
      }
   }
// compute risk of ruin as percentage
   double RiskOfRuinPct=(double)(ruinCount)/successfulMCcounter*100.;
   return(RiskOfRuinPct);
}

Inserimos muitas linhas de comentários no código acima para facilitar o entendimento. A função CalcDealStatistics(), também incluída no código-fonte, é onde são calculados os desvios padrão dos ganhos e perdas. A principal suposição no cálculo do Risco de Ruína é que o histórico de negociações segue uma distribuição Log-Normal para garantir que as amostras da distribuição sejam valores positivos e negativos para ganhos e perdas, respectivamente.

Idealmente, se tivéssemos muitas negociações no histórico, poderíamos usar uma matriz de probabilidade de transição de Cadeia de Markov em vez de assumir a "log-normalidade" do histórico de negociações. Como os históricos de negociações têm cerca de algumas centenas de negociações (no máximo), não há informações suficientes para construir a matriz de probabilidade de transição de Cadeia de Markov com boa precisão. 

Interpretando o Risco de Ruína de Monte Carlo

Um resultado de simulação de Monte Carlo precisa ser interpretado como uma probabilidade do risco de perder o capital, não como um valor preditivo. Por exemplo, se o valor de retorno da simulação de Monte Carlo for 1%, isso significa que há 1% de chance (probabilidade) de que sua estratégia de negociação elimine todo o capital em risco. Isso não significa que você perderá 1% do capital em risco.


Adicionando Decimais ao Máximo Personalizado

Isso é algo complicado de fazer. Se o leitor encontrar uma maneira melhor, por favor, compartilhe. Uma vez que os valores decimais são calculados (denominados dec no código), o objetivo (obj) é modificado da seguinte forma:

  obj=obj>0?
       MathFloor(obj*gof_ObjPositiveScalefactor)+dec/100.:
       -(MathFloor(-obj*gof_ObjNegativeScalefactor)+dec/100.);

Como você pode ver, se obj é um valor positivo, ele é multiplicado por um grande número (gof_ObjPositiveScalefactor=1e6), truncado, e então o valor "dec" é dividido por 100 e adicionado como decimal. Quando o valor obj é negativo (implicando que há muitas restrições violadas), o obj é multiplicado por um número diferente (gof_ObjPositiveScalefactor = 1e3) para comprimir o eixo vertical para valores negativos.



Implementando GOF no MetaTrader 5 Moving Average EA

Aqui está um exemplo para mostrar como implementar o GOF no MetaTrader 5 Moving Averages.mq5 expert advisor:

//+------------------------------------------------------------------+
//|                                              Moving Averages.mq5 |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade\Trade.mqh>

input double MaximumRisk        = 0.02;    // Maximum Risk in percentage
input double DecreaseFactor     = 3;       // Descrease factor
input int    MovingPeriod       = 12;      // Moving Average period
input int    MovingShift        = 6;       // Moving Average shift
//---
int    ExtHandle=0;
bool   ExtHedging=false;
CTrade ExtTrade;

#define MA_MAGIC 1234501

// changes needed to use the GenericOptimizationFormulation (GOF):
ulong gof_magic= MA_MAGIC;
#include <GenericOptimizationFormulation.mqh>
// end of changes

É isso! Apenas duas linhas precisam ser adicionadas ao arquivo EA.

Exemplos de Otimização usando GOF

Exemplo 1: Um objetivo, sem restrições (como se estivéssemos usando o MetaTrader 5 Max Balance na aba de Configurações


configurações de saldo máximo com GOF


A aba de entrada está abaixo. Como há apenas um objetivo, o alvo e o peso não são relevantes. Observe como todas as restrições estão desativadas com a primeira variável booleana na seção de Restrições Rígidas:


maxbalinput


As seções diversas são:

maxbalmisc


Resultados são mostrados abaixo. Veja como a coluna "Resultado" não é o saldo da conta, mas o objetivo modificado com a taxa de acertos em decimais.

Podemos ver que a melhor combinação de variáveis (9,9,3) produz um lucro de 939,81 e uma taxa de acertos de 41%.

maxbalresults


Simulamos a combinação ideal (primeira linha) na tabela. O Relatório Resumido GOF impresso na aba Jornal é:


maxbalreport

Vamos revisar o relatório resumido GOF em mais detalhes no próximo exemplo.


Exemplo 2: Três objetivos e cinco restrições

A aba de configurações é a mesma de antes. As variáveis de entrada e intervalos também são as mesmas. A aba de entrada para as variáveis GOF é mostrada abaixo.

Objetivos:

  • Retorno anual com meta de 50% e peso de 100
  • O fator de recuperação com meta de 10 e peso de 10
  • A porcentagem da taxa de vitória com meta de 100% e peso 10 

Restrições:

  • Perda da conta como % do depósito inicial para ser menor ou igual a 10%
  • Drawdown de capital % para ser menor ou igual a 10%
  • Número de negociações perdedoras consecutivas para ser menor ou igual a 5
  • Taxa de vitória para ser maior ou igual a 35%
  • Risco de ruína da conta para ser menor ou igual a 0,5%


ex2Inputs

O capital para calcular o Risco de Ruína foi definido como 10% do Depósito Inicial, ou 1000 unidades da moeda da conta, conforme mostrado na seção Diversos acima.

O melhor design acabou sendo (10,6,6) como você pode ver na primeira linha da tabela de otimização.

ex2results

Observe que o otimizador encontrou uma solução com lucro menor em comparação com o primeiro exemplo (805,64 vs 839,81), mas este novo design satisfaz todas as cinco restrições e maximiza três objetivos combinados.


O Relatório Resumido do GOF 

Ao simular a primeira linha na tabela de otimização acima, obtemos o relatório resumido do GOF abaixo:

ex2report

Há três seções no Resumo do GOF. A primeira seção contém muitas quantidades da aba BackTest. Existem algumas quantidades adicionais que não estão presentes na aba BackTest: lucro anualizado, duração do teste em anos, razão recompensa/risco (RRR), volume médio e maior, desvios padrão de ganho e perda e nível de margem mínimo atingido durante a simulação.

A segunda seção é para as Funções Objetivo. Aqui há quatro valores para cada objetivo: o valor do objetivo, o alvo, o peso e a porcentagem de contribuição. A porcentagem de contribuição é a contribuição dessa função objetivo para o objetivo único total. Neste exemplo, o retorno anual contribuiu com 95,1%, o fator de recuperação contribuiu com 1,4% e a taxa de vitória contribuiu com 3,5% para um total de 100% do objetivo único total. Alvos e Pesos afetam essas contribuições. 

A terceira seção é para Restrições. Uma mensagem de "passou" ou "falhou" é impressa para cada restrição, e uma comparação entre o valor real da restrição e o limite de entrada também é mostrada.

Para fins de comparação, executamos o primeiro design do exemplo #1 (9, 9, 3) através da mesma formulação de otimização do exemplo 2. Abaixo está o resumo desta simulação. Observe como há uma violação de uma restrição. O número de perdas consecutivas é 6, que é maior que o valor limite de 5 dado na formulação da otimização. Portanto, mesmo que o design MaxBalance (9,9,3) tenha melhor lucro do que o design MultiObjective/Constrained (10,6,6), é o design (10,6,6) que satisfaz todas as restrições. 

ex2-bal-compare


Recomendações ao usar GenericOptimizationFormulation.mqh

A maior liberdade para escolher múltiplos objetivos e múltiplas restrições deve ser exercida com cuidado. Aqui estão algumas recomendações gerais:

  1. Use no máximo 3 objetivos. O código permite até 5 objetivos, mas a seleção de alvos e pesos, que afetam o resultado final, torna-se mais difícil à medida que o número de objetivos é superior a 3.
  2. Use restrições baseadas em suas preferências e não as defina muito apertadas ao selecionar limites superiores (U_i) e inferiores (L_i). Se seus limites forem muito apertados, você não obterá nenhuma combinação viável de variáveis de entrada.
  3. Se você não souber qual valor limite dar para uma determinada restrição, poderá mover a restrição para a seção de objetivos e ver como ela se comporta (magnitude, sinal, etc.) inspecionando o relatório resumido do GOF.
  4. Ajuste k_o e k_p se quiser gráficos melhores ou se achar que o otimizador não está produzindo os resultados esperados.
  5. Lembre-se, o design ideal (a linha superior na tabela de Otimização) não é necessariamente o design mais lucrativo, mas o design com a função objetivo mais alta com base na sua seleção de objetivos individuais e restrições.
  6. Recomendamos que, após a otimização, você classifique os designs por outras colunas, como lucro, fator de recuperação, drawdown, expectativa de retorno, fator de lucro, etc. A linha superior em cada classificação pode ser uma candidata que você pode considerar para simular para revisar o relatório resumido do GOF.
  7. Uma boa seleção de objetivos e restrições foi mostrada no exemplo #2. Use-os como ponto inicial para sua experimentação.


Coisas que Deram Errado

Você pode configurar uma formulação de otimização que não esteja fornecendo o resultado esperado. Aqui estão alguns motivos pelos quais isso pode estar acontecendo:

  1. As restrições estão muito apertadas e o algoritmo de otimização genética do MetaTrader 5 levará muitas gerações para chegar a uma função objetivo positiva e, em alguns casos, pode não chegar lá. Solução: relaxe suas restrições.
  2. As restrições estão em conflito umas com as outras. Solução: verifique se as restrições são logicamente consistentes.
  3. Os gráficos na otimização têm um viés para os valores negativos no eixo y (ou seja, o lado negativo ocupa mais espaço que o lado positivo). Solução: aumente K_o ou diminua K_p, ou ambos.
  4. Alguns designs que você gosta não aparecem no topo da tabela de otimização. Lembre-se de que alvos e pesos afetam o objetivo de otimização e, além disso, uma única violação de restrição pode enviar o objetivo para baixo na tabela. Solução: reformule seu problema de otimização ajustando alvos, pesos e restrições.

   

Conclusão

Esperamos que esta Formulação de Otimização Genérica seja útil para você. Agora você tem a liberdade extra de escolher múltiplos objetivos e múltiplas restrições para configurar o problema de otimização que você desejar.

Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/14365

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.
Redes neurais de maneira fácil (Parte 83): Transformador espaciotemporal de atenção contínua (Conformer) Redes neurais de maneira fácil (Parte 83): Transformador espaciotemporal de atenção contínua (Conformer)
O algoritmo Conformer, apresentado aqui, foi desenvolvido para prever o tempo, que, em termos de variabilidade e imprevisibilidade, pode ser comparado aos mercados financeiros. O Conformer é um método complexo que combina as vantagens dos modelos de atenção e das equações diferenciais ordinárias.
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.
Desenvolvendo um cliente MQTT para MetaTrader 5: uma abordagem TDD — Final Desenvolvendo um cliente MQTT para MetaTrader 5: uma abordagem TDD — Final
Este artigo é a última parte de uma série que descreve nossas etapas de desenvolvimento de um cliente MQL5 nativo para o protocolo MQTT 5.0. Embora a biblioteca ainda não esteja pronta para produção, nesta parte, usaremos nosso cliente para atualizar um símbolo personalizado com ticks (ou taxas) obtidos de outro corretor. Por favor, veja o final deste artigo para mais informações sobre o status atual da biblioteca, o que falta para que ela esteja totalmente em conformidade com o protocolo MQTT 5.0, um possível roadmap, e como acompanhar e contribuir para seu desenvolvimento.