Descargar MetaTrader 5

Trademinator 3: el auge de las máquinas de trading

13 marzo 2014, 12:07
Roman Zamozhnyy
0
578

Prólogo

Erase una vez, en un lejano foro (MQL5), dos artículos fueron publicados: "Algoritmos genéticos, ¡es fácil!" de joo y "Dr. Tradelove..." mio. En el primer artículo el autor nos proporcionó una poderosa herramienta para optimizar todo lo que necesitáramos, incluyendo estrategias de trading, un algoritmo genético implementado mediante el lenguaje MQL5.

Usando este algoritmo, en el segundo artículo intenté desarrollar un asesor experto autooptimizado basado en él. El artículo finalizó con la formulación de la siguiente tarea: crear un asesor experto (autooptimizado, por supuesto), que pueda no solo seleccionar los mejores parámetros para un sistema de trading particular, sino también elegir la mejor estrategia de todas las estrategias desarrolladas. Vamos a ver si eso es posible y, si lo es, cómo puede hacerse.

Cuentos de los robots de trading

Primero formulamos los requisitos generales para un asesor experto autooptimizado.

Este debe poder (en base a los datos históricos):

  • seleccionar la mejor estrategia de entre las descritas
  • elegir el mejor instrumento financiero
  • elegir el mejor tamaño de depósito para el trading con corrección del apalancamiento
  • elegir los mejores parámetros de indicadores en la estrategia seleccionada

Además, en la vida real, debe poder:

  • abrir y cerrar posiciones
  • elegir el tamaño de la posición
  • decidir si es necesaria una nueva optimización

La figura siguiente muestra un diagrama esquemático del asesor experto propuesto.


En el archivo adjunto Scheme_en se encuentra un esquema detallado con parámetros.

Teniendo en cuenta que es imposible abarcar la inmensidad, introducimos las restricciones en la lógica del asesor experto. Acordamos que (IMPORTANTE):

  1. El asesor experto tomará decisiones de trading cuando se genere una nueva barra (en cualquier periodo de tiempo que elijamos).
  2. En base a p.1, pero sin limitarse a él, el asesor experto cerrará las transacciones solo sobre las señales del indicador que no utilicen Take Profit y Stop Loss y, en consecuencia, no use Trailing Stop.
  3. Condición para iniciar una nueva optimización: una reducción del saldo es mayor que el valor establecido durante la inicialización del nivel. Por favor, tenga en cuenta que esta es mi condición personal, y cada uno de vosotros puede elegir una condición específica.
  4. Una función de aptitud modela el trading sobre el historial y maximiza el saldo modelado, siempre que la reducción relativa del saldo de las transacciones simuladas esté por debajo de un cierto nivel preestablecido. Tenga en cuenta también que esta es mi función de aptitud personal y que cada uno puede elegir una condición específica.
  5. Limitamos el número de parámetros a optimizar, excepto para los tres generales (estrategia, instrumento y depósito) a cinco para los parámetros de los buffers del indicador. Esta limitación procede lógicamente del máximo número de buffers de indicador para indicadores técnicos incluidos. Si va a describir las estrategias que usan indicadores personalizados con un gran número de buffers de indicador, simplemente cambie la variable OptParamCount en el archivo main.mq5 a la cantidad deseada.

Ahora que los requisitos se han especificado y se han elegido las limitaciones, puede mirar el código que implementa todo esto.

Vamos a empezar con la función donde todo se ejecuta.

void OnTick()
{
  if(isNewBars()==true)
  {
    trig=false;
    switch(strat)
    {
      case  0: {trig=NeedCloseMA()   ; break;};                      //The number of case strings must be equal to the number of strategies
      case  1: {trig=NeedCloseSAR()  ; break;};
      case  2: {trig=NeedCloseStoch(); break;};
      default: {trig=NeedCloseMA()   ; break;};
    }
    if(trig==true)
    {
      if(GetRelDD()>maxDD)                                           //If a balance drawdown is above the max allowed value:
      {
        GA();                                                        //Call the genetic optimization function
        GetTrainResults();                                           //Get the optimized parameters
        maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);               //Now count the drawdown not from the balance maximum...
                                                                     //...but from the current balance
      }
    }
    switch(strat)
    {
      case  0: {trig=NeedOpenMA()   ; break;};                       //The number of case strings must be equal to the number of strategies
      case  1: {trig=NeedOpenSAR()  ; break;};
      case  2: {trig=NeedOpenStoch(); break;};
      default: {trig=NeedOpenMA()   ; break;};
    }
    Print(TimeToString(TimeCurrent()),";","Main:OnTick:isNewBars(true)",
          ";","strat=",strat);
  }
}

¿Qué hay aquí? Como se muestra en el diagrama, miramos a cada tick en el que haya una nueva barra. Si la hay, entonces sabiendo qué estrategia elegir ahora llamamos a su función específica para comprobar si hay una posición abierta y la cerramos si es necesario. Supongamos ahora que la mejor estrategia de avance es SAR, consecuentemente será llamada la función NeedCloseSAR:

bool NeedCloseSAR()
{
  CopyBuffer(SAR,0,0,count,SARBuffer);
  CopyOpen(s,tf,0,count,o);
  Print(TimeToString(TimeCurrent()),";","StrategySAR:NeedCloseSAR",
        ";","SAR[0]=",SARBuffer[0],";","SAR[1]=",SARBuffer[1],";","Open[0]=",o[0],";","Open[1]=",o[1]);
  if((SARBuffer[0]>o[0]&&SARBuffer[1]<o[1])||
     (SARBuffer[0]<o[0]&&SARBuffer[1]>o[1]))
  {
    if(PositionsTotal()>0)
    {
      ClosePosition();
      return(true);
    }
  }
  return(false);
}

Cualquier función de cierre de posición debe ser booleana y devolver el valor verdadero al cerrar una posición. Esto permite que el siguiente bloque de código de la función OnTick() decida si es necesaria una nueva optimización:

    if(trig==true)
    {
      if(GetRelDD()>maxDD)                                           //If the balance drawdown is above the max allowed one:
      {
        GA();                                                        //Call the genetic optimization function
        GetTrainResults();                                           //Get optimized parameters
        maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);                   //Now count the drawdown not from the balance maximum...
                                                                     //...but from the current balance
      }
    }

Tomamos la reducción del saldo actual y lo comparamos con el máximo permitido. Si ha excedido el valor máximo, ejecutamos una nueva optimización (GA()). La función GA(), a su vez, llama al corazón del asesor experto, la función de aptitud FitnessFunction(int chromos) del módulo GAModule.mqh:

void FitnessFunction(int chromos)                                    //A fitness function for the genetic optimizer:...
                                                                     //...selects a strategy, symbol, deposit share,...
                                                                     //...parameters of indicator buffers;...
                                                                     //...you can optimize whatever you need, but...
                                                                     //...watch carefully the number of genes
{
  double ff=0.0;                                                     //The fitness function
  strat=(int)MathRound(Colony[GeneCount-2][chromos]*StratCount);     //GA selects a strategy
 //For EA testing mode use the following code...
  z=(int)MathRound(Colony[GeneCount-1][chromos]*3);                  //GA selects a symbol
  switch(z)
  {
    case  0: {s="EURUSD"; break;};
    case  1: {s="GBPUSD"; break;};
    case  2: {s="USDCHF"; break;};
    case  3: {s="USDJPY"; break;};
    default: {s="EURUSD"; break;};
  }
//..for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window)
/*
  z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));//GA selects a symbol
  s=SymbolName(z,true);
*/
  optF=Colony[GeneCount][chromos];                                   //GA selects a deposit share
  switch(strat)
  {
    case  0: {ff=FFMA(   Colony[1][chromos],                         //The number of case strings must be equal to the number of strategies
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    case  1: {ff=FFSAR(  Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    case  2: {ff=FFStoch(Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
    default: {ff=FFMA(   Colony[1][chromos],
                         Colony[2][chromos],
                         Colony[3][chromos],
                         Colony[4][chromos],
                         Colony[5][chromos]); break;};
  }
  AmountStartsFF++;
  Colony[0][chromos]=ff;
  Print(TimeToString(TimeCurrent()),";","GAModule:FitnessFunction",
        ";","strat=",strat,";","s=",s,";","optF=",optF,
        ";",Colony[1][chromos],";",Colony[2][chromos],";",Colony[3][chromos],";",Colony[4][chromos],";",Colony[5][chromos]);
}

Dependiendo de la estrategia seleccionada actualmente, se llama al módulo de cálculo de la función de aptitud específico de una estrategia en particular. Por ejemplo, la función GA ha elegido un estocástico, FFStoch () será llamada y se le transferirán los parámetros de optimización de los buffers de indicador:

double FFStoch(double par1,double par2,double par3,double par4,double par5)
{
  int    b;
  bool   FFtrig=false;                                               //Is there an open position?
  string dir="";                                                     //Direction of the open position
  double OpenPrice;                                                  //Position Open price
  double t=cap;                                                      //Current balance
  double maxt=t;                                                     //Maximum balance
  double aDD=0.0;                                                    //Absolute drawdown
  double rDD=0.000001;                                               //Relative drawdown
  Stoch=iStochastic(s,tf,(int)MathRound(par1*MaxStochPeriod)+1,
                         (int)MathRound(par2*MaxStochPeriod)+1,
                         (int)MathRound(par3*MaxStochPeriod)+1,MODE_SMA,STO_CLOSECLOSE);
  StochTopLimit   =par4*100.0;
  StochBottomLimit=par5*100.0;
  dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS));
  leverage=AccountInfoInteger(ACCOUNT_LEVERAGE);
  contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE);
  b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth);
  for(from=b;from>=1;from--)                                         //Where to start copying of history
  {
    CopyBuffer(Stoch,0,from,count,StochBufferMain);
    CopyBuffer(Stoch,1,from,count,StochBufferSignal);
    if((StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1])||
       (StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1]))
    {
      if(FFtrig==true)
      {
        if(dir=="BUY")
        {
          CopyOpen(s,tf,from,count,o);
          if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0;
          if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
          if((maxt>0)&&(aDD/maxt>rDD)) rDD=aDD/maxt;
        }
        if(dir=="SELL")
        {
          CopyOpen(s,tf,from,count,o);
          if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0;
          if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
          if((maxt>0)&&(aDD/maxt>rDD)) rDD=aDD/maxt;
        }
        FFtrig=false;
      }
   }
    if(StochBufferMain[0]>StochBufferSignal[0]&&StochBufferMain[1]<StochBufferSignal[1]&&StochBufferMain[1]>StochTopLimit)
    {
      CopyOpen(s,tf,from,count,o);
      OpenPrice=o[1];
      dir="SELL";
      FFtrig=true;
    }
    if(StochBufferMain[0]<StochBufferSignal[0]&&StochBufferMain[1]>StochBufferSignal[1]&&StochBufferMain[1]<StochBottomLimit)
    {
      CopyOpen(s,tf,from,count,o);
      OpenPrice=o[1];
      dir="BUY";
      FFtrig=true;
    }
  }
  Print(TimeToString(TimeCurrent()),";","StrategyStoch:FFStoch",
        ";","K=",(int)MathRound(par1*MaxStochPeriod)+1,";","D=",(int)MathRound(par2*MaxStochPeriod)+1,
        ";","Slow=",(int)MathRound(par3*MaxStochPeriod)+1,";","TopLimit=",StochTopLimit,";","BottomLimit=",StochBottomLimit,
        ";","rDD=",rDD,";","Cap=",t);
  if(rDD<=trainDD) return(t); else return(0.0);
}

La función de aptitud del estocástico devuelve un saldo simulado a la función principal, que pasará al algoritmo genético. En algún momento, la función GA decide finalizar la optimización y usando la función GetTrainResults() devolvemos los mejores valores actuales de la estrategia (por ejemplo, las medias móviles), símbolo, el porcentaje del depósito y los parámetros de los buffers del indicador al programa básico, así como crear indicadores para trading real adicional:

void GetTrainResults()                                               //Get the best parameters
{
  strat=(int)MathRound(Chromosome[GeneCount-2]*StratCount);          //Remember the best strategy
//For EA testing mode use the following code...
  z=(int)MathRound(Chromosome[GeneCount-1]*3);                       //Remember the best symbol
  switch(z)
  {
    case  0: {s="EURUSD"; break;};
    case  1: {s="GBPUSD"; break;};
    case  2: {s="USDCHF"; break;};
    case  3: {s="USDJPY"; break;};
    default: {s="EURUSD"; break;};
  }
//...for real mode, comment the previous code and uncomment the following one (symbols are selected in the MarketWatch window)
/*
  z=(int)MathRound(Chromosome[GeneCount-1]*(SymbolsTotal(true)-1));  //Remember the best symbol
  s=SymbolName(z,true);
*/
  optF=Chromosome[GeneCount];                                        //Remember the best deposit share
  switch(strat)
  {
    case  0: {GTRMA(   Chromosome[1],                                //The number of case strings must be equal to the number of strategies
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    case  1: {GTRSAR(  Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    case  2: {GTRStoch(Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
    default: {GTRMA(   Chromosome[1],
                       Chromosome[2],
                       Chromosome[3],
                       Chromosome[4],
                       Chromosome[5]) ; break;};
  }
  Print(TimeToString(TimeCurrent()),";","GAModule:GetTrainResults",
        ";","strat=",strat,";","s=",s,";","optF=",optF,
        ";",Chromosome[1],";",Chromosome[2],";",Chromosome[3],";",Chromosome[4],";",Chromosome[5]);
}

void GTRMA(double par1,double par2,double par3,double par4,double par5)
{
  MAshort=iMA(s,tf,(int)MathRound(par1*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
  MAlong =iMA(s,tf,(int)MathRound(par2*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
  CopyBuffer(MAshort,0,from,count,ShortBuffer);
  CopyBuffer(MAlong, 0,from,count,LongBuffer );
  Print(TimeToString(TimeCurrent()),";","StrategyMA:GTRMA",
        ";","MAL=",(int)MathRound(par2*MaxMAPeriod)+1,";","MAS=",(int)MathRound(par1*MaxMAPeriod)+1);
}

Ahora todo vuelve al lugar donde todo se ejecuta (OnTick()): sabiendo qué estrategia es ahora la mejor, se comprueba si es el momento de ir al mercado:

bool NeedOpenMA()
{
  CopyBuffer(MAshort,0,0,count,ShortBuffer);
  CopyBuffer(MAlong, 0,0,count,LongBuffer );
  Print(TimeToString(TimeCurrent()),";","StrategyMA:NeedOpenMA",
        ";","LB[0]=",LongBuffer[0],";","LB[1]=",LongBuffer[1],";","SB[0]=",ShortBuffer[0],";","SB[1]=",ShortBuffer[1]);
  if(LongBuffer[0]>LongBuffer[1]&&ShortBuffer[0]>LongBuffer[0]&&ShortBuffer[1]<LongBuffer[1])
  {
    request.type=ORDER_TYPE_SELL;
    OpenPosition();
    return(false);
  }
  if(LongBuffer[0]<LongBuffer[1]&&ShortBuffer[0]<LongBuffer[0]&&ShortBuffer[1]>LongBuffer[1])
  {
    request.type=ORDER_TYPE_BUY;
    OpenPosition();
    return(false);
  }
  return(true);
}

El círculo se cierra.

Vamos a comprobar cómo funciona. Aquí tenemos un informe de 2011 sobre un periodo de tiempo de 1 hora con cuatro pares principales: EURUSD, GBPUSD, USDCHF, USDJPY:

Informe del probador de estrategia
InstaForex-Server (Build 567)
Ajustes
Experto: Principal
Símbolo: EURUSD
Periodo: H1 (2011.01.01 - 2011.12.31)
Parámetros de entrada: trainDD=0.50000000
maxDD=0.20000000
Broker: InstaForex Companies Group
Moneda: USD
Depósito inicial: 10.000,00
Apalancamiento: 1:100
Resultados
Calidad del historial: 100%
Barras: 6197 Ticks: 1321631
Beneficio total neto: -538,74 Beneficio bruto: 3.535,51 Pérdida bruta: -4.074,25
Factor de beneficio 0,87 Retribución esperada: -89,79 Nivel de margen: 85,71%
Factor de recuperación: -0,08 Ratio de Sharpe 0,07 Resultado de OnTester: 0
Reducción de saldo:
Reducción de saldo absoluta: 4.074,25 Reducción de saldo máxima: 4.074,25 (40,74%) Reducción de saldo relativa: 40,74% (4.074,25)
Reducción de valores
Reducción de saldo absoluta: 4.889,56 Reducción de saldo máxima: 6.690,90 (50,53%) Reducción de saldo relativa: 50,53% (4.074,25)
Transacciones totales: 6 Transacciones cortas (% ganadas): 6 (16,67%) Transacciones cortas (% ganadas): 0 (0,00%)
Transacciones totales: 12 Transacciones con beneficio (% del total): 1 (16,67%) Transacciones con pérdida (% del total): 5 (83,33%)
Transacción con el mayor beneficio: 3.535,51 Transacción con la mayor pérdida: -1.325,40
Transacción con beneficio promedio: 3.535,51 Transacción con pérdida promedio: -814,85
Máximo número de transacciones ganadoras consecutivas: 1 (3.535,51) Máximo número de transacciones perdedoras consecutivas: 5 (-4.074,25)
Máximo número de transacciones con beneficio consecutivas (recuento): 3.535,51 (1) Máximo número de transacciones con pérdida consecutivas (recuento): -4.074,25 (5)
Transacciones ganadoras consecutivas promedio: 1 Transacciones perdedoras consecutivas promedio: 5


 

Órdenes
Tiempo de apertura Orden Símbolo Tipo Volumen Price S / L T / P Tiempo Estado Comentario
2011.01.03 01:002USDCHFvender28.21 / 28.210,93212011.01.03 01:00relleno
2011.01.03 3:003USDCHFcomprar28.21 / 28.210,93652011.01.03 3:00relleno
2011.01.03 6:004USDCHFvender24.47 / 24.470,93522011.01.03 6:00relleno
2011.01.03 9:005USDCHFcomprar24.47 / 24.470,93722011.01.03 9:00relleno
2011.01.03 13:006USDCHFvender22.99 / 22.990,93522011.01.03 13:00relleno
2011.01.03 16:007USDCHFcomprar22.99 / 22.990,93752011.01.03 16:00relleno
2011.01.03 18:008USDJPYvender72.09 / 72.0981,572011.01.03 18:00relleno
2011.01.03 21:009USDJPYcomprar72.09 / 72.0981,662011.01.03 21:00relleno
2011.01.04 01:0010USDJPYvender64.54 / 64.5481,672011.01.04 01:00relleno
2011.01.04 2:0011USDJPYcomprar64.54 / 64.5481,782011.01.04 2:00relleno
2011.10.20 21:0012USDCHFvender56.30 / 56.300,89642011.10.20 21:00relleno
2011.10.21 12:0013USDCHFcomprar56.30 / 56.300,89082011.10.21 12:00relleno
Transacciones
Tiempo Tiempo Símbolo Tipo Dirección Volumen Price Símbolo Comisión Swap Profit Saldo Comentario
2011.01.01 0:001saldo0,000,0010.000,0010.000,00
2011.01.03 01:002USDCHFvenderdentro28,210,932120,000,000,0010.000,00
2011.01.03 3:003USDCHFcomprarfuera28,210,936530,000,00-1.325,408.674,60
2011.01.03 6:004USDCHFvenderdentro24,470,935240,000,000,008.674,60
2011.01.03 9:005USDCHFcomprarfuera24,470,937250,000,00-522,198.152,41
2011.01.03 13:006USDCHFvenderdentro22,990,935260,000,000,008.152,41
2011.01.03 16:007USDCHFcomprarfuera22,990,937570,000,00-564,027.588,39
2011.01.03 18:008USDJPYvenderdentro72,0981,5780,000,000,007.588,39
2011.01.03 21:009USDJPYcomprarfuera72,0981,6690,000,00-794,536.793,86
2011.01.04 01:0010USDJPYvenderdentro64,5481,67100,000,000,006.793,86
2011.01.04 2:0011USDJPYcomprarfuera64,5481,78110,000,00-868,115.925,75
2011.10.20 21:0012USDCHFvenderdentro56,300,8964120,000,000,005.925,75
2011.10.21 12:0013USDCHFcomprarfuera56,300,8908130,00-3,783.539,299.461,26
0,00 -3,78 -534,96 9.461,26
Copyright 2001-2011, MetaQuotes Software Corp.

Permítanme explicar la zona marcada en el gráfico (las explicaciones se han tomado del análisis del registro):

  1. Después del inicio del asesor experto, el algoritmo genético seleccionó la estrategia de avance SAR en USDCHF con un porcentaje del depósito en la transacción igual al 28%, y luego se mantuvo operativo hasta el 3 de enero, perdió más del 20% del saldo y comenzó a reoptimizarse.

  2. Luego, el asesor experto decidió operar el avance del SAR en USDJPY pero con todo el depósito (98%). Naturalmente, no pudo operar mucho tiempo, y por tanto comenzó su tercera optimización en la mañana del 4 de enero.
  3. Esta vez decidió operar golden y dead cross de medias móviles en USDCHF una vez más para todo el depósito. Y esperó a la primera dead cross hasta el 20 de octubre y la vendió al máximo y recuperó todo lo que había perdido. Después de esto hasta el final del año el asesor experto no vio condiciones favorables para entrar al mercado.

¿Continuará?

¿Puede continuar? ¿Cuál sería la próxima generación de asesores expertos? Aquellos que inventan estrategias y seleccionan la mejor entre todas. Y además, puede gestionar el dinero, comprar un hardware y una canal más potente, y demás...

Aviso sobre el riesgo:

Esta breve información no revela al completo todos los riesgos y demás aspectos significativos del trading en mercados forex de divisas sobre el margen. Debe entender la naturaleza del trading y el alcance de su exposición al riesgo. Debe considerar cuidadosamente si el trading es adecuado para usted a la luz de su experiencia, objetivos, recursos financieros y demás circunstancias relevantes.

El mercado Forex no es solo rentable, también es un mercado de alto riesgo. En términos de trading de margen, pocas fluctuaciones en los intercambios pueden tener un impacto significativo en la cuenta del operador, dando como resultado una pérdida igual al depósito inicial y cualquier fondo adicional depositado en la cuenta para mantener posiciones abiertas. No debe invertir dinero cuya pérdida no pueda afrontar. Antes de decidir sobre una transacción, por favor, asegúrese de que comprende todos los riesgos y tenga en cuenta su nivel de experiencia. Si es necesario, busque asesoramiento.

Licencias:

El módulo está desarrollado y distribuido bajo licencia BSD por Andrey Dik conocido como joo.

El asesor experto y los módulos auxiliares adjuntos a este artículo han sido desarrollados y distribuidos bajo licencia BSD por el autor Roman Rich. El texto de la licencia está disponible en el archivo Lic.txt.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/350

Archivos adjuntos |
lic.txt (1.43 KB)
sheme_en.gif (147.11 KB)
gamodule.mqh (7.24 KB)
main.mq5 (4.99 KB)
musthave.mqh (7.79 KB)
strategyma.mqh (5.13 KB)
strategysar.mqh (4.09 KB)
strategystoch.mqh (6.04 KB)
ugalib.mqh (33.36 KB)
Crear asesores expertos usando el Expert Advisor Visual Wizard Crear asesores expertos usando el Expert Advisor Visual Wizard

El Expert Advisor Visual Wizard para Meta Trader 5 proporciona un entorno gráfico muy intuitivo con un conjunto completo de bloques de trading predefinidos que le permitirá diseñar Expert Advisors en minutos. El enfoque clic, arrastrar y soltar del Expert Advisor Visual Wizard le permitirá crear representaciones visuales de las estrategias y señales de trading forex como lo haría con lápiz y papel. Estos diagramas de trading se analizan automáticamente por el generador de código de MQL5 Molanis que los transforma para que puedan usarse directamente como Expert Advisors. El entorno gráfico interactivo simplifica el proceso de diseño y elimina la necesidad de escribir código MQL5.

Predicción de series de tiempo usando el ajuste exponencial (continuación) Predicción de series de tiempo usando el ajuste exponencial (continuación)

Este artículo pretende actualizar el indicador creado anteriormente y trata brevemente sobre un método para estimar intervalos de confianza en las predicciones usando bootstrapping y cuantiles. Como resultado, obtendremos el indicador de predicción y los scripts a usar para la estimación de la precisión de la predicción.

Proteger el código MQL5 Protección con contraseña, generadores de claves, límites de tiempo, licencias remotas y técnicas de encriptación de claves de licencia de asesores expertos avanzadas Proteger el código MQL5 Protección con contraseña, generadores de claves, límites de tiempo, licencias remotas y técnicas de encriptación de claves de licencia de asesores expertos avanzadas

La mayoría de desarrolladores necesitan tener su código protegido. Este artículo presenta diferentes formas de proteger el software MQL5 mediante métodos que permiten disponer de licencias para scripts de MQL5, Expert Advisors e indicadores. Se incluye la protección mediante contraseñas, los generadores de claves, las licencias de cuentas, las pruebas de evaluación y la protección remota mediante llamadas MQL5-RPC.

Promocione sus proyectos de desarrollo usando las librerías EX5 Promocione sus proyectos de desarrollo usando las librerías EX5

Ocultar los detalles de la implementación de las clases/funciones en un archivo .ex5 le permitirá compartir sus algoritmos propios con otros desarrolladores, iniciar proyectos y promocionarlos en la Web. Y mientras el equipo de MetaQuotes no escatima esfuerzos para tener la posibilidad de heredar directamente las clases de las librerías ex5, vamos a implementarlas ahora.