English Deutsch 日本語
preview
Formulación Genérica de Optimización (GOF, Generic Optimization Formulation) utilizando el `Criterio máximos del usuario` (Custom Max) con múltiples restricciones en el Probador de Estrategias

Formulación Genérica de Optimización (GOF, Generic Optimization Formulation) utilizando el `Criterio máximos del usuario` (Custom Max) con múltiples restricciones en el Probador de Estrategias

MetaTrader 5Ejemplos | 19 agosto 2024, 12:17
35 0
better.trader every.day
better.trader every.day

Introducción - Fundamentos de la optimización

Los problemas de optimización tienen dos fases: 1) formulación del problema y 2) solución del problema. En la primera fase hay tres componentes principales: las variables de entrada, las funciones objetivo y las funciones de restricción. En la segunda fase, la solución del problema se lleva a cabo numéricamente con un algoritmo de optimización.

Variables (notación x_1 , x_2 , x_3 , ..., x_n): Las variables son los «mandos» que podemos cambiar para maximizar la función objetivo. Existen variables de distintos tipos: enteras, reales y booleanas. En los asesores expertos podríamos utilizar variables como: el periodo de la media móvil, el ratio TP/SL a la entrada, el SL en pips, etc.<

Funciones objetivo (notación f_i (x)): Si hay más de una función objetivo, el problema se denomina problema de optimización multiobjetivo. MetaTrader 5 algoritmo de optimización espera sólo una función objetivo, por lo tanto, con el fin de considerar más de un objetivo, tenemos que combinarlos en uno.< La forma más sencilla de combinar funciones objetivo es creando una suma ponderada de ellas. Algunos desarrolladores han propuesto utilizar la operación de multiplicación para combinarlos, y puede funcionar en algunas situaciones, pero nosotros preferimos la suma como mejor enfoque. En este artículo utilizamos una suma «dirigida y ponderada» de objetivos, como se explicará más adelante. Como ejemplos de funciones objetivo tenemos: balance, objetivo de beneficio, tasa de ganancias, rentabilidad anual, beneficio neto, etc.

Funciones de restricción (notación g_i (x)): Son funciones que generan un valor que el usuario desea acotar. El límite podría ser un límite superior, como en g_i(x) ≤ U_i , donde g_i(x) es la i_ésima restricción, y U_i es el límite superior. Del mismo modo, una restricción de límite inferior es como g_i(x) ≥ L_i , donde L_i es el límite inferior.

El algoritmo de optimización de MetaTrader 5 sólo toma las restricciones impuestas a las variables de entrada (también conocidas como restricciones laterales), por ejemplo, 3 <= x_1 <= 4, pero no se puede utilizar ningún otro tipo de restricción. Por lo tanto, necesitamos incluir la presencia de cualquier restricción adicional g_i(x) en la función objetivo final y única, F(x), como se mostrará a continuación. Ejemplos de funciones de restricción g_i(x) son: limitar el número de pérdidas consecutivas, o el ratio de Sharpe, o la tasa de ganancias, etc.


Algoritmos de optimización

En términos generales, existen dos tipos principales de algoritmos de optimización. El primer tipo es el más clásico, basado en el cálculo de los gradientes de todas las funciones que intervienen en el problema de optimización (se remonta a los tiempos de Isaac Newton). El segundo tipo es más reciente (desde la década de 1970) y no utiliza información de gradiente en absoluto. Entre medias, puede haber algoritmos que combinen los dos enfoques mencionados, pero no hace falta que los abordemos aquí. El algoritmo de optimización de MetaTrader 5 llamado «Rápida (algoritmo genético)», en la pestaña Configuración del terminal MetaTrader 5, pertenece al segundo tipo. Esto nos permite omitir la necesidad de calcular gradientes para las funciones objetivo y de restricción. Es más, gracias a la naturaleza sin gradiente del algoritmo de MetaTrader 5, pudimos tener en cuenta funciones de restricción que no habrían sido apropiadas con algoritmos basados en gradiente. Más adelante hablaremos de ello.

Un punto importante es que el algoritmo de optimización de MetaTrader 5 llamado «Lenta (repaso completo del parámetros)» no es en realidad un algoritmo de optimización, sino una fuerza bruta, la evaluación exhaustiva de todas las posibles combinaciones de valores para todas las variables de entrada dentro de las restricciones laterales.


La función objetivo F(x) construida a partir de múltiples objetivos f_i(x)

En esta sección repasaremos la combinación de múltiples funciones objetivo en una única función objetivo F.

Como se menciona en la Introducción, utilizamos la suma para combinar varios objetivos. Si lo desea, el lector puede modificar el código fuente para utilizar una combinación multiplicativa. La fórmula de combinación sumatoria es:

Formulación de la función objetivo

Donde 

  • x es el vector de n variables de entrada
  • f_i(x) es la i-ésima función objetivo
  • W_i es el peso i
  • T_i es la i-ésima meta deseada para la i-ésima función objetivo

El objetivo sirve como valor de normalización (divisor) para que un objetivo determinado sea comparable a otros objetivos de la suma. En otras palabras, la suma ponderada es para funciones objetivo «normalizadas». El peso sirve como tal, como un peso que multiplica el objetivo normalizado ( f_i(x)/T_i ) de tal forma que el usuario es capaz de enfatizar qué objetivo normalizado es más importante que el otro. Los pesos W_i se convexizan dividiendo la suma de W_i*f_i(x)/T_i por la suma de los W_i.

Selección de W_i: La convexificación de los pesos permite al usuario centrarse en su valor relativo, en lugar de en su valor absoluto. Por ejemplo, supongamos que hay tres objetivos: rentabilidad anual, factor de recuperación y factor de beneficio. Estas ponderaciones: W_1=10, W_2=5, W_3=1, tienen el mismo efecto que W_1=100, W_2=50, W_3=10, es decir, el objetivo normalizado 1 (rentabilidad anual/T_1) se considera dos veces más importante que el objetivo normalizado 2 (factor de recuperación/T_2), y diez veces más importante que el objetivo normalizado 3 (factor de beneficio/T_3). No hay valores correctos o incorrectos siempre que sean positivos y expresen la importancia relativa en función del criterio del usuario.


Selección de T_i: La selección de objetivos T_i debe hacerse cuidadosamente después de unas cuantas simulaciones, antes de realizar la optimización completa. La razón para hacerlo es estimar los rangos de cada función objetivo y establecer valores de T_i que hagan que las funciones normalizadas (f_i(x)/T_i) tengan una magnitud comparable. Por ejemplo, suponga que su cuenta tiene un saldo inicial de 10.000; el algoritmo de su EA hace que el saldo final sea de unos 20.000 antes de ser optimizado; el factor de beneficio es 0,9. Si establece su problema de optimización con dos objetivos: f_1 = equilibrio, f_2 = factor de beneficio, entonces un buen valor para los objetivos son: T_1 = 30k, T_2 = 2, lo que hará que ambas funciones normalizadas sean del mismo orden de magnitud (valores comparables). Una vez que ejecute una optimización completa puede encontrar que el EA da un saldo final muy grande, y un factor de ganancia similar. En este punto, podría ajustar los valores de T_i si la función normalizada son de diferentes órdenes de magnitud. Los valores objetivo también deben ser números reales positivos. Usa tu criterio. Hablaremos más de este tema cuando tratemos la salida del código GOF.


Añadir restricciones 

La formulación de la función objetivo como la suma ponderada de los objetivos individuales es bastante estándar. Ahora presentamos una forma sencilla de incluir funciones de restricción en el problema de optimización de tal manera que el algoritmo basado en la genética rápida de MetaTrader 5 siga funcionando. Vamos a utilizar una versión modificada del método de Lagrange que resta una penalización al objetivo único de optimización. Para ello definiremos nuevas funciones de penalización que midan la cantidad de violación para cada restricción:<

Sanción

Como se puede comprobar, P_i(x) es positivo cuando se infringe g_i(x), y cero en caso contrario. Si U_i o L_i son cero, los sustituimos por 1 para evitar la división por cero.

En el método del multiplicador de Lagrange, hay un multiplicador por restricción y sus valores son la solución de un sistema de ecuaciones. En nuestra versión modificada, suponemos que todos los multiplicadores son iguales y tienen un valor constante k_p (véase la fórmula a continuación). Esta simplificación funciona aquí porque el algoritmo de optimización no necesita calcular gradientes de ninguna función en el problema de optimización.


La función objetivo única final F(x) es


objfun

Nota: el símbolo «:=» significa asignación, no definición matemática. 

El multiplicador k_p desempeña el papel de los multiplicadores de Lagrange, y se utiliza para forzar a los diseños no factibles (aquellos que violan al menos una restricción) a tener un objetivo muy bajo. Esta disminución del valor objetivo hará que el algoritmo genético en MetaTrader 5 para clasificar ese diseño muy bajo y es poco probable que se utiliza para la reproducción en la próxima generación de diseños.

El multiplicador k_o no forma parte del método del multiplicador de Lagrange. Se utiliza para aumentar la función objetivo para diseños factibles para ampliar el lado positivo del eje Y en los gráficos de optimización en el terminal MetaTrader 5. 

El usuario puede cambiar los valores K_o y K_p en la sección de entrada Varios. Recomendamos que los valores de k_o y K_p sean potencias de 10 (por ejemplo, 10, 100, 1000, etc.).


Captura de pantalla de la pestaña Entradas del terminal MetaTrader 5

En el GOF hay tres secciones de entrada: funciones objetivo, funciones de restricción y varios.

A continuación se muestra la sección Objective Functions. Hay hasta 5 objetivos que pueden incluirse en la formulación del problema, de las 18 funciones posibles que se mostrarán más adelante. Se pueden añadir más de 5 objetivos siguiendo el código, pero todo lo que supere los 3 objetivos dificulta la selección de pesos y objetivo. Se pueden añadir más de 18 funciones posibles siguiendo el código.<

obj section

A continuación se muestra la sección Constraint function. Hay hasta 10 restricciones que pueden añadirse a la formulación de optimización, de las 14 funciones de restricción implementadas en GOF. Se pueden añadir más de 10 restricciones siguiendo el código. También se pueden añadir nuevas restricciones a la lista de 14 siguiendo el código.

constraint section

A continuación se muestra la Miscellaneous Optimization Parameter. En los próximos apartados se hablará más de este apartado.

misc


Uso de GenericOptimizationFormulation.mqh en su EA

Si usted es una persona que tiene poca paciencia para leer todo el artículo, primero proporcionamos los pasos para utilizar este código en su EA para que pueda jugar con él primero, y luego leer el resto del artículo con más información. A continuación se muestran los comentarios iniciales en el archivo GenericOptimizationFormulation.mqh:

/*

In order to use this file in your EA you must do the following:

- Edit the EA .mq5 file

- Insert the following two lines after the input variables and before OnInit()

     ulong gof_magic= an_ulong_value;

     #include <YOUR_INCLUDE_LOCATION\GenericOptimizationFormulation.mqh>

- Save and compile your EA file

If you get compiler's errors, make sure you did:

- replace an_ulong_value by the variable containing the magic number, or by the magic numeric value

- replace YOUR_INCLUDE_LOCATION by the folder name where your include files are

*/

Espero que el texto anterior se explique por sí mismo. Más adelante mostraremos un ejemplo utilizando el EA de Media Móvil de MetaTrader 5. Ahora continuemos con la explicación del código fuente.


El código fuente: GenericOptimizationFormulation.mqh

Ahora que hemos presentado las fórmulas para la función objetivo y las restricciones, discutiremos la implementación de MQL5 con fragmentos del código.

 Petición a los lectores:

  • Se dice que un software con más de siete líneas de código tiene una probabilidad distinta de cero de tener un fallo. GOF tiene más de mil líneas, por lo que las probabilidades son definitivamente distintas de cero. Animamos al lector a que aporte su opinión en los comentarios para introducir mejoras en el código.
  • Si encuentras una forma mejor de escribir el código, estamos deseando ver tus mejores líneas de código para mejorar GOF.
  • Añada y mejore el código con sus propias funciones objetivo y restricciones. Por favor, compártalas también en los comentarios.
Librerías incluidas

En primer lugar, necesitamos incluir algunas librerías para hacer algo de estadística y álgebra:

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

Objective Functions para elegir:

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 %)
};


Hay 18 objetivos posibles para elegir un máximo de 5 para formular el problema de optimización. El usuario puede añadir más funciones al código fuente siguiendo el mismo patrón de implementación. Los nombres de las funciones objetivo son autoexplicativos, salvo algunos de los que se mencionan a continuación:

  • LRcrr^3: el coeficiente de correlación de regresión lineal a la potencia de 3.
  • 1/(LR std%): LR std es la desviación típica de la regresión lineal. La inversa mide lo ajustada que está la línea de equidad a una línea recta.
  • 100/(1+|WorstLoss/Init.Dep*100|): la peor pérdida dividida por el depósito inicial es una medida del mal rendimiento. La inversa es una medida de buen rendimiento.
  • LRslope*LRcorr/LRstd: se trata de un objetivo multiplicativo de tres funciones de la regresión lineal: la pendiente, el coeficiente de correlación y la desviación típica.
  • Seff=Beneficio/(TotalTrades*AvgLot): es una medida de la eficiencia de la estrategia. Preferimos estrategias con alto beneficio, pequeño número de operaciones con tamaño de lote pequeño.<
  • 1/Max(0.01,RoRA %): RoRA es el Riesgo de Ruina de tu Cuenta (Risk of Ruin your Account). Esto se calcula mediante una simulación de Montecarlo de la que hablaremos más adelante.


Hard Constraints para elegir:

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
};
Para formular el problema de optimización hay 14 restricciones posibles, de las que hay que elegir un máximo de 10. El usuario puede añadir más restricciones al código fuente siguiendo el mismo patrón de implementación. Los nombres de las funciones de restricción deben explicarse por sí mismos. Existen dos tipos de restricciones: hc_GT para las restricciones con un límite inferior y ht_LT para las restricciones con un límite superior. Más información al respecto cuando enseñemos a utilizarlos.



Opciones de riesgos de ruina (RoRa, Risk of Ruin your Account)

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

A la hora de calcular el riesgo de ruina de su cuenta, hay tres formas de definir el dinero de la «cuenta». La primera opción es como porcentaje del depósito inicial. La segunda opción es un capital fijo en la divisa de su cuenta. La tercera es el caso especial de la primera opción si el porcentaje es del 100%. El riesgo de ruina se explica más adelante con el código.

Función objetivo decimales

Como ya se ha mencionado, podemos optar por mostrar información adicional de la simulación utilizando los dos decimales de la columna de resultados. Aquí están las opciones:

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 es el porcentaje de victorias de la simulación en porcentaje. Por ejemplo, si el porcentaje de ganancias es del 34%, el objetivo de resultado será 0,39. Si el porcentaje de ganancias es del 100%, se mostrará 0,99.

fr_MCRoRA es el riesgo de ruina de la cuenta en porcentaje. Por ejemplo, si el riesgo de arruinar la cuenta es del 11%, el objetivo de resultado será 0,11.

fr_LRcorr es el coeficiente de correlación de regresión lineal. Por ejemplo, si el coeficiente es 0,88, el resultado objetivo será 0,88.

fr_ConLoss es el mayor número de operaciones perdedoras continuas. Por ejemplo, si el número es 7, el resultado objetivo será 0,07. Si el número es superior a 99, mostrará 0,99.

fr_NONE se utiliza cuando no se desea ver ninguna información en los decimales.


Funciones objetivo

La siguiente sección del código es la selección de funciones objetivo individuales (máximo 5). A continuación se muestra un fragmento de sólo la primera función, junto con su objetivo y 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


Funciones de restricción

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

Existe una opción para desactivar todas las restricciones duras estableciendo gof_IncludeHardConstraints=false. A continuación se selecciona la primera restricción, su tipo y su valor límite. Las diez restricciones utilizan el mismo formato.

Otros parámetros de optimización

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)

En la sección anterior, el usuario elegirá:

  • gof_fr: La cantidad a mostrar como decimales en la columna Resultado. 
  • gof_roraMaxCap: El método para calcular el capital RoRA.
  • gof_RoraCustomValue: El valor del capital o % del depósito inicial para RoRA. Esto depende de su selección en la línea anterior.
  • gof_drawSummary: Puede elegir dibujar el resumen del informe GOF en el gráfico.
  • gof_printSummary: Puede optar por imprimir el resumen del informe GOF en la pestaña Diario.
  • gof_discardLargestProfit: Puede restar la mayor ganancia de la ganancia neta para desincentivar las estrategias que favorecen una única gran ganancia.
  • gof_discardLargestLoss: Puede añadir la mayor pérdida al beneficio neto para desincentivar las estrategias que tienen una gran pérdida. 
  • gof_PenaltyMultiplier: El multiplicador «K_p» indicado anteriormente en la definición de la función objetivo.
  • gof_ObjMultiplier: El multiplicador «K_o» indicado anteriormente en la definición de la función objetivo.

Los valores por defecto de la sección miscelánea deberían funcionar bien.

Las siguientes líneas en el código son para definir variables, y para obtener valores de la función MetaTrader 5 TesterStatistics(). Después viene la sección principal del 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));

El código anterior muestra: 

  • GOFsummaryReport() para preparar el informe de resumen GOF que va en la pestaña Diario y en el gráfico.
  • calcObjFunc() para calcular la función objetivo única combinada.
  • calcContraintTotalPenalty() para calcular la penalización total debida a las violaciones de las restricciones.
  • customMaxCriterion se calcula entonces como se muestra en la Introducción como la suma del objetivo único menos la penalización total de las violaciones de las restricciones.
  • AddDecimalsToCustomMax() se utiliza para añadir la información en los decimales de customMaxCriterion.
  • FinishGOFsummaryReport() sirve para finalizar e imprimir el Informe de Resumen GOF.


El resto del código es una implementación directa de las fórmulas dadas en la introducción. La única parte que merece la pena discutir es el cálculo del riesgo de ruina.

Riesgo de ruina mediante simulaciones de Monte Carlo

El riesgo de ruina podría calcularse con una fórmula simple, pero optamos por utilizar en su lugar una simulación Monte Carlo porque la fórmula simple no arrojaba resultados sensatos. Para el enfoque de Monte Carlo necesitamos la media de ganancias y pérdidas, las desviaciones típicas de dichas ganancias y pérdidas, la tasa de ganancias y el número de operaciones de la simulación. Además, debemos aportar el capital que define la ruina de la cuenta. 

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);
}

Hemos insertado muchas líneas de comentario en el código anterior para facilitar su comprensión. La función CalcDealStatistics(), también incluida en el código fuente, es donde se calculan las desviaciones estándar de las victorias y las derrotas. El principal supuesto en el cálculo del Riesgo de Ruina es que el historial de operaciones sigue una distribución Log-Normal para asegurarse de que las muestras de la distribución son valores positivos y negativos para las victorias y las derrotas, respectivamente.

Idealmente, si tuviéramos muchas operaciones en el historial, podríamos utilizar una matriz de probabilidad de transferencia de la cadena de Markov en lugar de asumir la «log-normalidad» del historial de operaciones. Como los historiales de operaciones tienen unos cientos de operaciones (como mucho), no hay suficiente información para construir la matriz de probabilidad de transición de la cadena de Markov con buena precisión. 

Interpretar el riesgo de ruina en Monte Carlo

El resultado de una simulación Monte Carlo debe interpretarse como una probabilidad del riesgo de perder el capital, no como un valor predictivo. Por ejemplo, si el valor de retorno de la simulación de Monte Carlo es del 1%, significa que existe una posibilidad (probabilidad) del 1% de que su estrategia de negociación acabe con su capital total en riesgo. No significa que vaya a perder el 1% del capital en riesgo.


Añadir decimales a `Custom Max`

Esto es complicado. Si el lector encuentra una forma mejor, por favor, compártala. Una vez calculados los valores decimales (denominados dec en el código), el objetivo (obj) se modifica de la siguiente manera:

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

Como puede ver, si obj es un valor positivo se multiplica por un número grande (gof_ObjPositiveScalefactor=1e6), se trunca, y luego el valor «dec» se divide por 100, y se suma como decimal. Cuando el valor del objeto es negativo (lo que implica que hay muchas restricciones violadas) el objeto se multiplica por un número diferente (gof_ObjPositiveScalefactor =1e3) para comprimir el eje vertical para valores negativos.



Implementación de GOF en el EA de media móvil de MetaTrader 5

Aquí hay un ejemplo de cómo implementar GOF en el asesor experto de MetaTrader 5 `Moving Averages.mq5`:

//+------------------------------------------------------------------+
//|                                              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

¡Eso es! Sólo hay que añadir dos líneas al archivo EA.

Ejemplos de optimización con GOF

Ejemplo 1: Un objetivo, sin restricciones (como si estuviéramos usando MetaTrader 5 Max Balance en la pestaña Settings):


Ajustes de equilibrio máximo con GOF


La pestaña de entrada está más abajo. Dado que sólo hay un objetivo, el objetivo y el peso no son relevantes en absoluto. Observe cómo todas las restricciones se desactivan con la primera variable booleana en la sección Restricciones:


maxbalinput


Las secciones misceláneas son:

maxbalmisc


Los resultados se muestran a continuación. Vea cómo la columna «Resultado» no es el saldo de la cuenta, sino el objetivo modificado con el porcentaje de ganancias como decimales.

Podemos ver que la mejor combinación de variables (9,9,3) produce un beneficio de 939,81 y una tasa de ganancias del 41%.

maxbalresults


Simulamos la combinación óptima (primera línea) en la tabla. El Informe Resumen GOF impreso en la pestaña Diario es:


maxbalreport

Revisaremos el informe resumido del GOF con más detalle en el siguiente ejemplo.


Ejemplo 2: Tres objetivos y cinco restricciones

La pestaña de configuración es la misma que antes. Las variables de entrada y los rangos también son los mismos. A continuación se muestra la pestaña de entrada de variables GOF.

Objetivos:

  • Rentabilidad anual con un objetivo del 50% y una ponderación del 100
  • El factor de recuperación con un objetivo de 10 y un peso de 10
  • El porcentaje de victorias con un objetivo del 100% y un peso del 10 

Restricciones:

  • Pérdida de cuenta en % del depósito inicial inferior o igual al 10%
  • Porcentaje de reducción de capital inferior o igual al 10%
  • Número de operaciones perdedoras consecutivas inferior o igual a 5
  • El porcentaje de victorias debe ser igual o superior al 35%
  • La cuenta de riesgo de ruina debe ser inferior o igual al 0,5%


ex2Inputs

El capital para calcular el Riesgo de Ruina se fijó en el 10% del Depósito Inicial, o 1000 unidades de la divisa de la cuenta, como se indica en la sección Varios anterior.

El mejor diseño resultó ser (10,6,6), como puede verse en la primera línea de la tabla de optimización.

ex2results

Obsérvese que el optimizador encontró una solución con un beneficio inferior al del primer ejemplo (805,64 frente a 839,81), pero este nuevo diseño satisface las cinco restricciones y maximiza tres objetivos combinados.


Informe de síntesis del GOF 

Simulando la primera línea de la tabla de optimización anterior, obtenemos el siguiente informe resumido GOF:

ex2report

El Resumen del GOF consta de tres secciones. La primera sección tiene muchas cantidades de la pestaña Backtest. Hay algunas cantidades adicionales que no están presentes en la pestaña Backtest: beneficio anualizado, duración de la prueba en años, relación Recompensa/Riesgo (RRR), volumen medio y mayor, desviaciones estándar de ganancias y pérdidas, y nivel de margen mínimo alcanzado durante la simulación.

La segunda sección está dedicada a las funciones objetivo. Aquí hay cuatro valores para cada objetivo: el valor del objetivo, la meta, el peso y el porcentaje de contribución. El porcentaje de contribución es la contribución de esa función objetivo al objetivo único total. En este ejemplo, la rentabilidad anual contribuyó en un 95,1%, el factor de recuperación contribuyó en un 1,4% y la tasa de ganancia contribuyó en un 3,5% para un total del 100% del objetivo único total. El objetivo y los pesos afectan a estas contribuciones. 

La tercera sección está dedicada a las Restricciones. Para cada restricción se imprime un mensaje «pass» o «Fail», y también se muestra una comparación entre el valor real de la restricción y la entrada vinculada.

A efectos de comparación, hemos ejecutado el primer diseño del ejemplo nº 1 (9, 9, 3) a través de la misma formulación de optimización del ejemplo 2. A continuación figura el resumen de esta simulación. Obsérvese que se viola una restricción. El número de pérdidas consecutivas es 6, que es mayor que el valor límite de 5 dado en la formulación de optimización. Por lo tanto, aunque el diseño MaxBalance (9,9,3) tiene mejor beneficio que el diseño MultiObjetivo/Con restricciones (10,6,6), es el diseño (10,6,6) el que satisface todas las restricciones. 

ex2-bal-compare


Recomendaciones para el uso de GenericOptimizationFormulation.mqh

La mayor libertad para elegir múltiples objetivos y múltiples restricciones debe ejercerse con cuidado. He aquí algunas recomendaciones generales:

  1. No utilice más de 3 objetivos. El código permite hasta 5 objetivos, pero la selección de metas y ponderaciones, que afectan al resultado final, se hace más difícil a medida que el número de objetivos es superior a 3.
  2. Utilice restricciones basadas en sus preferencias y no las ajuste demasiado al seleccionar los límites superior (U_i) e inferior (L_i). Si sus límites son demasiado estrechos, no obtendrá ninguna combinación factible de variables de entrada.
  3. Si no sabe qué valor límite dar para una restricción determinada, puede mover la restricción a la sección de objetivos y ver cómo se comporta (magnitud, signo, etc.) inspeccionando el informe Resumen GOF.
  4. Ajuste k_o y k_p si desea obtener mejores gráficos o si el optimizador no produce los resultados esperados.
  5. Recuerde que el diseño óptimo (la línea superior de la tabla de optimización) no es necesariamente el diseño más rentable, sino el diseño con la función objetivo más alta basada en su selección de objetivos y restricciones individuales.
  6. Le recomendamos que, una vez realizada la optimización, ordene los diseños por otras columnas, como beneficio, factor de recuperación, reducción, pago esperado, factor de beneficio, etc. La línea superior de cada clasificación podría ser un candidato que usted podría considerar simular para revisar el informe resumido del GOF.
  7. En el ejemplo nº 2 se muestra una buena selección de objetivos y limitaciones. Utilícelos como punto de partida para su experimentación.


Cosas que salen mal

Es posible que establezca una formulación de optimización que no esté proporcionando el resultado que esperaba. He aquí algunas razones por las que esto puede estar ocurriendo:

  1. Las restricciones son demasiado estrictas y el algoritmo de optimización genética MetaTrader 5 tomará muchas generaciones para llegar a una función objetivo positiva, y en algunos casos, puede no llegar allí. Solución: relaje sus restricciones.
  2. Las limitaciones entran en conflicto entre sí. Solución: compruebe que las restricciones son lógicamente coherentes.
  3. Los gráficos de la optimización tienen un sesgo hacia los valores negativos en el eje Y (es decir, el lado negativo ocupa más espacio que el positivo). Solución: aumentar K_o o disminuir K_p, o ambos.
  4. Algunos diseños que te gustan no aparecen cerca de los primeros en la tabla de optimización. Tenga en cuenta que los objetivos y las ponderaciones afectan al objetivo de optimización y, además, una sola violación de una restricción puede hacer que el objetivo descienda en la tabla. Solución: reformule su problema de optimización ajustando los objetivos, las ponderaciones y las restricciones.

   

Conclusión

Esperamos que esta formulación de optimización genérica le resulte útil. Ahora tiene la libertad adicional de elegir múltiples objetivos y múltiples restricciones para configurar el problema de optimización que desee.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/14365

Clase básica de algoritmos de población como base para una optimización eficaz Clase básica de algoritmos de población como base para una optimización eficaz
El presente material supone un intento único de investigación para combinar una variedad de algoritmos de población en una sola clase y simplificar la aplicación de técnicas de optimización. Este enfoque no solo descubre oportunidades para el desarrollo de nuevos algoritmos, incluidas variantes híbridas, sino que también crea un banco de pruebas básico y versátil. Este banco se convertirá así en una herramienta clave para seleccionar el algoritmo óptimo según un problema específico.
Redes neuronales: así de sencillo (Parte 78): Detector de objetos basado en el Transformer (DFFT) Redes neuronales: así de sencillo (Parte 78): Detector de objetos basado en el Transformer (DFFT)
En este artículo, le propongo abordar la creación de una estrategia comercial desde una perspectiva diferente. Hoy no pronosticaremos los movimientos futuros de los precios, sino que trataremos de construir un sistema comercial basado en el análisis de datos históricos.
Superar los retos de integración de ONNX Superar los retos de integración de ONNX
ONNX es una gran herramienta para la integración de código complejo de IA entre diferentes plataformas, es una gran herramienta que viene con algunos desafíos que uno debe abordar para obtener el máximo provecho de ella, En este artículo se discuten los problemas comunes que podría enfrentar y cómo mitigarlos.
Desarrollando un cliente MQTT para MetaTrader 5: un enfoque TDD - Final Desarrollando un cliente MQTT para MetaTrader 5: un enfoque TDD - Final
Este artículo es la última parte de una serie que describe nuestros pasos de desarrollo de un cliente MQL5 nativo para el protocolo MQTT 5.0. Aunque la biblioteca aún no está lista para la producción, en esta parte utilizaremos nuestro cliente para actualizar un símbolo personalizado con ticks (o precios) procedentes de otro broker. Por favor, consulte la parte inferior de este artículo para obtener más información sobre el estado actual de la biblioteca, lo que falta para que sea totalmente compatible con el protocolo MQTT 5.0, una posible hoja de ruta, y cómo seguir y contribuir a su desarrollo.