English Русский 中文 Deutsch 日本語 Português Türkçe
preview
Aproximación por fuerza bruta a la búsqueda de patrones

Aproximación por fuerza bruta a la búsqueda de patrones

MetaTrader 5Probador | 29 enero 2021, 17:42
1 118 0
Evgeniy Ilin
Evgeniy Ilin

Introducción

En este artículo buscaremos patrones en el mercado, crearemos asesores expertos usando estos como base y verificaremos cuánto tiempo dichos patrones siguen funcionando y, en general, si se mantienen. Consideramos que este artículo resultará muy útil a aquellos que crean sistemas comerciales autoadaptables. Querríamos hablar un poco sobre la fuerza bruta, y también sobre lo que incluye para nosotros este concepto en el contexto del comercio de divisas. En general, podemos considerar aproximación por fuerza bruta (iteración completa) el intento de determinar alguna secuencia de números para un código o cualquier otro propósito que finalmente permitirá llegar al resultado deseado con la máxima probabilidad o con la máxima probabilidad disponible utilizando esta secuencia. Por ejemplo, conseguir criptomonedas o piratear la contraseña de una cuenta o wifi. Son muchas sus aplicaciones. En el caso de fórex, nuestra secuencia debería dar el máximo beneficio, y trabajando el mayor tiempo posible. La secuencia puede ser de cualquier tipo y longitud, lo principal es que tenga un efecto positivo. En general, el tipo de esta secuencia depende en última instancia de nuestro algoritmo.


¿Por qué hemos decidido desarrollar este tema en particular y qué tiene de especial?

Como norma general, el autor trata de expresar sus pensamientos de forma que tengan un uso lo más práctico posible para otros tráders. ¿Por qué? Buena pregunta. En primer lugar, para compartir la propia experiencia de forma desinteresada. Resulta muy agradable que alguien pueda aplicar esas ideas y lograr algún resultado interesante y rentable, sin importar lo que el autor obtenga de ello. Al mismo tiempo, queda claro que tal vez estas no sean ideas en absoluto propias y tal vez estemos reinventando la rueda, o tal vez queramos creer como Agente Mulder, no importa. En nuestra opinión, es necesario compartir experiencias y colaborar de la forma más productiva posible. Quizás este sea el secreto del éxito. Podríamos sentarnos entre 4 paredes toda la vida y construir castillos en el aire, pero no sacaríamos ningún resultado de ello. El tema de los patrones y, en general, cómo encontrarlos y por qué encontrarlos, es de fundamental importancia para entender la física del mercado. Lo que tratamos de decir es muy importante, y su carrera como tráder puede depender incluso de una comprensión elemental de estas cosas, aunque, obviamente, la propia expresión "carrera como tráder" suena bastante divertida. Esperamos poder ofrecer algún beneficio a las personas que todavía andan enredadas con estos problemas.


Sobre la aproximación por fuerza bruta y sus diferencias con las redes neuronales

En general, una red neuronal también constituye una especie de fuerza bruta, solo que sus algoritmos son muy diferentes a los de la simple fuerza bruta. Ahora no vamos a analizar las arquitecturas separadas de las redes neuronales y los tipos de sus elementos constituyentes, como el perceptrón, pero sí que trataremos de abarcar el tema de manera global. En general, si nos vinculamos a una determinada arquitectura, ya estaremos limitando de antemano las capacidades de nuestro algoritmo. Y una arquitectura fija supone en sí misma una limitación irreparable. En nuestro caso, una red neuronal es una especie de arquitectura de una posible estrategia. Como resultado, la configuración de una red neuronal siempre se corresponderá con un determinado archivo con un mapa de red. De hecho, siempre constituyen indicaciones del montaje de una determinada unidad. Es como una impresora 3D. Establecemos los parámetros de la pieza, y ella la montará por usted. En pocas palabras, una red neuronal es un código general que carece de sentido sin un mapa. Es similar a coger cualquier lenguaje de programación con todas sus capacidades, y simplemente crear un proyecto vacío. Como resultado, el idioma estará ahí, pero la plantilla se encontrará vacía y, por consiguiente, no hará nada. Lo mismo sucede aquí. Lo único es que, a diferencia de la fuerza bruta, una red neuronal puede ofrecer una variabilidad casi ilimitada a nuestras estrategias, cualquier número de criterios y una mayor eficiencia. El único inconveniente de este enfoque es que si escribimos este código genérico de manera descuidada, su efectividad podría incluso terminar siendo inferior a la fuerza bruta. La posible complejidad del sistema aumenta, lo que significa que también aumenta la glotonería del programa. Como resultado, convertiremos nuestra estrategia en un mapa de red, que es su equivalente. En el enfoque de fuerza bruta, hacemos lo mismo, solo que se convierte en una secuencia simple de algunos números. Esta secuencia resulta mucho más simple que un mapa de red y es más fácil de calcular, pero también tiene límites en cuanto a eficiencia A continuación, vamos a representar esquemáticamente lo dicho.


En otras palabras, en el caso de la aproximación por fuerza bruta, seleccionamos una secuencia de números que, al interactuar con nuestro código, dará resultados diferentes. Pero como el algoritmo es fijo, toda su flexibilidad se contiene en la matriz de nuestros números. Su longitud es fija y la estructura es muy simple. En el caso de las redes neuronales, buscamos el mapa de red que ofrezca el mejor resultado. Y en ese caso, siempre buscamos alguna secuencia de bytes, a saber, algunos datos que finalmente se convierten en el algoritmo resultante. La única diferencia reside en sus capacidades y complejidad. 


El algoritmo de optimización y fuerza bruta

Para nuestro algoritmo, utilizaremos la expansión multidimensional de Taylor. Hemos elegido esta aproximación por los siguientes motivos. Queríamos ofrecer un algoritmo lo más variable posible dentro de su sencillez, y que además no estuiera vinculado a ninguna fórmula en particular, ya que al final cualquier función se puede expandir en una serie de Taylor o una serie de Fourier. Pero la serie de Fourier no nos parece muy adecuada para este problema y no estamos familiarizados con el equivalente multidimensional. Así que nos hemos decantado por la primera variante, que es mucho más fácil de implementar de forma programática. Una serie de Taylor unidimensional se ve así:

Y = Cs[0]+Cs[1]*(x-x0)^1 + Cs[2]*(x-x0) ^2 + ... + Cs[i]*(x-x0)^n

donde los coeficientes frente a las potencias desempeñan el papel de derivadas del orden de 0 hasta n. Podemos convertir esto a una forma más simple abriendo todos los corchetes:

Y = C[0]+C[1]*x^1 + C[2]*x^2 + ... + C[i]*x^n + ...= Suma(0,+infinito)(C[i]x^i)

En este caso, solo tenemos una variable. Esta serie puede imitar cualquier función continua y diferenciable en la vecindad de cualquier punto x0 seleccionado. Cuantos más términos contenga esta fórmula, con mayor precisión describirá nuestra función. Si su número es igual a infinito, entonces este será el equivalente absoluto de nuestra función. No vamos a mostrar aquí cómo expandir ninguna función en una serie de Taylor en la vecindad de cualquier punto. El lector encontrará esta información en cualquier libro de matemáticas. No obstante, la variante unidimensional no resulta suficiente, porque queremos utilizar los datos de varias barras, aumentando la variabilidad de la fórmula general. Por consiguiente, deberemos usar la variante multidimensional:

Y = Suma(0,+infinito)( C[i]*Variante del producto(x1^p1*x2^p2...*xN^pN) 

Resulta bastante difícil escribir esta fórmula de otra manera. Su lógica es la misma que la de la versión unidimensional. Debemos ofrecer todas las posibles derivadas parciales. Si restringimos la potencia más alta de los términos, podemos calcular de forma combinatoria el número total de estos términos utilizando combinaciones y sumas, pero no creemos que merezca la pena presentar estos cálculos aquí; en nuestro caso son inútiles. En nuestro algoritmo, solo utilizaremos la limitación de la potencia más alta, porque no queremos que nuestra computadora sufra un colapso. 

Pero eso resulta aún insuficiente para que nuestra función de fuerza bruta parezca adecuada. Sería mejor quitar el primer término C[0], ya que resulta mejor que la función ofrezca la máxima simetría respecto a los valores negativos o positivos que le vamos a suministrar. Además, será mejor asegurarse de que si la función ofrece un número positivo, lo interpretemos como una señal de compra, y si la función es negativa, como una señal de venta. Un aumento en el límite inferior del módulo de esta señal, en teoría, debería conllevar un aumento en la expectativa y el factor de beneficio, pero también conduciría inevitablemente a una disminución en el número de señales. Cuanto más cerca esté la función de nuestros requisitos, mejor. Las variables que transmitiremos serán (Close[i]-Open[i]) los valores de una vela en particular.

Todo lo que nos queda por hacer es generar aleatoriamente variantes de estos coeficientes y comprobar cómo se comporta una u otra variante en el simulador. Pero, por razones obvias, nadie en su sano juicio pondrá a prueba estos coeficientes manualmente; por consiguiente, o bien aplicamos un asesor astuto que sea capaz de producir tales variantes y simultáneamente admitir miles de estas, o bien algún software de terceros que implemente parcialmente la funcionalidad del simulador de estrategias. De inicio, hubo que escribir un asesor experto en MQL4 y probarlo primero en él: lo adjuntaremos al artículo junto con las instrucciones, y cualquiera podrá usarlo, tal vez incluso modificarlo. Pero, en lugar de este, vamos a usar otra aplicación que desarrollamos en C#. Desafortunadamente, no podemos ofrecer esta aplicación de forma gratuita, por motivos obvios. Sus capacidades trascienden el área de investigación. Sin embargo, sí que hablaremos de sus capacidades y su uso en la práctica, para que todo el que sepa programar (así creemos) pueda crear un análogo de esta aplicación; o quizá alguien incluso la escriba mejor, si tiene un motivo para ello. Más tarde, ofreceremos capturas de pantalla, en la parte dedicada al análisis de los resultados de su trabajo.

Ahora, hablaremos un poco de sus principales características. La búsqueda de las matrices de los coeficientes se realiza en 2 pasos. En la primera etapa, simplemente buscamos en la cotización cargada las matrices que den la expectativa máxima o el factor de beneficio máximo en la siguiente vela, realizando pasadas como en el simulador de estrategias. Es decir, en esencia, solo tenemos que encontrar una fórmula que prediga con la máxima precisión la dirección de la siguiente barra. Algunos de los mejores resultados se guardan como variantes de matrices en la memoria y en el disco. Podrá poner a prueba solo una parte de la cotización cargada, especificando su valor como un tanto por ciento relativo al archivo de cotizaciones cargado. Esto es necesario para eliminar los resultados aleatorios que no suponen regularidades en la segunda etapa. En la segunda etapa, simulamos las órdenes de mercado y construimos una curva de balance; todo esto se realiza de manera completa para toda el área cargada. Al mismo tiempo, realizamos un aumento suave en la magnitud de la señal, además de la búsqueda de opciones de mejor calidad. También existen muchos filtros que son necesarios para lograr gráficos más suaves y uniformes. Cuanto más suave sea el gráfico, mejor fórmula habremos encontrado. Una vez completada la segunda etapa de la búsqueda, aún quedará una cierta cantidad de las mejores opciones, que todavía se podrán notar visualmente al clicar los botones de la lista. Una vez seleccionada la opción necesaria, podremos generar un robot para MetaTrader 4 y MetaTrader 5 en la tercera pestaña. La generación se realiza según una plantilla precompilada, en la que ciertos lugares son simplemente rellenados con números.


Construyendo una plantilla simple para esta tarea

La plantilla se hizo originalmente en MQL4, y después para MQL5. Al igual que en el artículo anterior, el código está adaptado al máximo para ambas plataformas. Lo hemos hecho así a propósito, para dedicar menos tiempo a la adaptación. Para usar matrices predefinidas como en MQL4, necesitaremos añadir algo de código al asesor experto, concretamente, el fragmento que describimos en el último artículo. No vamos a repetirnos. Seguiremos adelante asumiendo que el lector sabe cómo se hace todo esto. Hablando con franqueza, no tiene nada de complicado, y cualquier desarrollador podrá implementar esto fácilmente si fuera necesario, incluso mejor que el autor. Vamos a comenzar describiendo las variables y matrices que se completarán automáticamente al generarse el robot, y comentando para qué sirven.

double C1[] = { %%%CVALUES%%% };//array of coefficients
int CNum=%%%CNUMVALUE%%%;//number of candlesticks in the formula
int DeepBruteX=%%%DEEPVALUE%%%;//formula depth
int DatetimeStart=%%%DATETIMESTART%%%;//start point in time
input bool bInvert=%%%INVERT%%%;//inverted trading
input double DaysToTrade=%%%DAYS%%%;//number of days into the future to trade

Aquí, C1 es solo la matriz de coeficientes ante las potencias que hemos elegido. CNum es el número de últimas velas en el gráfico de precios que usaremos para calcular el valor de nuestro polinomio. Después va la profundidad de la fórmula, o, dicho de forma más simple, la potencia máxima de nuestro polinomio multidimensional. Solemos utilizar 1, porque, a diferencia de la serie de Taylor unidimensional, la multidimensional experimenta un increíble aumento de complejidad en el cálculo al aumentar la potencia, debido a que el número total de coeficientes con el aumento de potencia se descontrola como una avalancha. Necesitamos el punto temporal inicial para limitar el tiempo de funcionamiento de nuestro asesor y, para limitarlo, necesitaremos conocer desde qué punto temporal contar el propio tiempo. Bueno, también necesitaremos la inversión, primero, para asegurar que nuestro polinomio funcione en la dirección correcta. Y es que, si todos los signos frente a los coeficientes de potencia se invierten, en esencia, el polinomio en sí no cambiará, sino que simplemente todos los números que genera cambiarán de signo. El más importante es la relación entre los coeficientes. Si un valor negativo del polinomio indica una venta y un valor positivo una compra, la inversión sería false. De lo contrario, true. Por consiguiente, le decimos al algoritmo "usa los valores polinomiales, pero solo del signo invertido". Bueno, el segundo punto sería: mejor hacer de esta variable un valor de entrada, puesto que resulta posible que necesitemos la capacidad de invertir el comercio, y también el número de días que le permitiremos comerciar en el futuro.

Si necesitamos calcular el tamaño de una matriz con coeficientes, podemos realizar la tarea de la forma siguiente:

int NumCAll=0;//size of the array of coefficients
void DeepN(int Nums,int deepC=1)//intermediate fractal
   {
   for ( int i=0; i<Nums; i++ )
      {
      if (deepC > 1)
         {       
         DeepN(Nums,deepC-1);
         } 
      else 
         {
         NumCAll++;
         }
      }   
   }

void CalcDeepN(int Nums,int deepC=1)//to launch calculations
   {
   NumCAll=0;
   for ( int i=0; i<deepC; i++ )
      {
      DeepN(Nums,i+1);
      }   
   }

Aquí, una función llamada fractal intermedio cuenta el número de términos con el mismo grado total de todos los multiplicadores. Esto se realiza así para mayor sencillez, porque no resulta tan importante para nosotros en qué orden sumemos nuestros términos, sino solo que esto se implemente en forma de código de la manera más simple posible. La segunda función llama simplemente a la primera en un ciclo tantas veces como tipo de términos haya en total. Por ejemplo, si la expansión de nuestra serie multidimensional se limita a (supongamos) 4, entonces llamaremos a la primera función con todos los números naturales del 1 al 4.

La función que se encargará ya de calcular el valor del polinomio en sí será casi idéntica, solo que en nuestro caso la matriz se generará ella misma, y no resultará necesario establecer su tamaño. Este es el aspecto que tendrá:

double ValW;//the number where everything is multiplied (and then added to ValStart)
uint NumC;//the current number for the coefficient
double ValStart;//the number where to add everything
void Deep(double &Ci0[],int Nums,int deepC=1,double Val0=1.0)//calculate the sum of one degree
   {
   for ( int i=0; i<Nums; i++ )
      {
      if (deepC > 1)
         {
         ValW=(Close[i+1]-Open[i+1])*Val0;      
         Deep(Ci0,Nums,deepC-1,ValW);
         } 
      else 
         {
         ValStart+=Ci0[NumC]*(Close[i+1]-Open[i+1])*Val0/Point;
         NumC++;
         }
      }   
   }
   
void CalcDeep(double &Ci0[],int Nums,int deepC=1)//calculate the entire polynomial
   {
   NumC=0;
   ValStart=0.0;
   for ( int i=0; i<deepC; i++ )
      {
      Deep(Ci0,Nums,i+1);
      }   
   }

Todo lo que calculemos se añadirá a ValStart, es decir, el resultado se añadirá a una variable global. También necesitaremos una variable global ValW más, necesaria para multiplicar el producto existente por algún valor. En nuestro caso, será el movimiento en puntos de la barra correspondiente. El movimiento puede darse hacia arriba o hacia abajo, lo cual es considerado por la señal. Podemos ver que estas funciones tienen una estructura muy interesante. Se llaman a sí mismas de forma interna; además, el número y la estructura de estas llamadas son siempre diferentes. Obtenemos una especie de árbol de llamadas. Es un placer trabajar con estas funciones, porque son muy variables. En nuestro caso, implementaremos la serie multidimensional de Taylor de una forma sencilla y elegante.

También podemos implementar una función adicional si solo utilizamos la versión unidimensional del polinomio. En este caso, la serie al completo se simplifica mucho, convirtiéndose en la suma de los coeficientes multiplicada por el movimiento de una de las barras a la primera potencia. Su número se torna idéntico al número de barras usadas. Esto puede simplificar ligeramente los cálculos. Si nuestra potencia es uno, utilizaremos una versión simplificada, y si no, un método más universal para cualquier poder.

double Val;
double PolinomTrade()//optimized polynomial
   {
   Val=0;
   if ( DeepBruteX <= 1 )
      {
      for ( int i=0; i<ArraySize(C1); i++ )
         {
         Val+=C1[i]*(Close[i+1]-Open[i+1])/Point;
         }
      return Val;   
      }
   else
      {
      CalcDeep(C1,CNum,DeepBruteX);
      return ValStart;
      }      
   }

En el caso de la variante simple, añadiremos el resultado a otra variable Val.

Ahora, vamos a escribir el método principal que llamaremos cuando aparezca una nueva barra:

void Trade()
   {
   double Value;
   Value=PolinomTrade();
   
   if ( Value > ValueCloseE)
      {
      if ( !bInvert )
         {
         CloseBuyF();
         }      
      else 
         {
         CloseSellF();
         }
      }
      
   if ( Value < -ValueCloseE)
      {
      if ( !bInvert )
         {
         CloseSellF();
         }      
      else 
         {
         CloseBuyF();
         }      
      }   
   
   if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToTrade && Value > ValueOpenE && Value <= ValueOpenEMax )
      {
      if ( !bInvert ) SellF();
      else BuyF();
      }
      
   if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToTrade && Value < -ValueOpenE && Value >= -ValueOpenEMax )
      {
      if ( !bInvert ) BuyF();
      else SellF();
      }

   }

Como puede ver, la función resulta de lo más sencilla, y solo nos queda implementar las funciones de apertura y cierre de posiciones que más nos convengan.

Podrá detectar la aparición de una barra como esta:

void CalcTimer()
   {
   if ( Time[1] > PrevTimeAlpha )
       {
       if ( PrevTimeAlpha > 0 )
          {
          Trade();
          }
       PrevTimeAlpha=Time[1];
       }
   }

A nuestro juicio, el código es de lo más simple y comprensible para cualquiera.

Los coeficientes que genera nuestro código se crean según los 4 modelos que hemos explicado anteriormente. Para facilitar la lectura, dichos coeficientes se encuentran en el intervalo [-1,1], dado que las proporciones de los valores también son importantes para nosotros. La función que genera estos números a partir del prototipo de nuestro programa MQL5 tiene el aspecto que sigue:

   void GenerateC()
      {
      double RX;
      if ( DeepBrute > 1 ) CalcDeepN(CandlesE,DeepBrute);
      else NumCAll=CandlesE;
      for ( int j=0; j<VariantsE; j++ )
         {
         ArrayResize(Variants[j].Ci,NumCAll,0);
         Variants[j].CNum=CandlesE;
         Variants[j].ANum=NumCAll;
         Variants[j].DeepBruteX=DeepBrute;
         RX=MathRand()/32767.0;
         for ( int i=0; i<Variants[j].ANum; i++ )
            {
            if ( RE == RANDOM_TYPE_1 ) Variants[j].Ci[i]=double(MathRand())/32767.0;
            if ( RE == RANDOM_TYPE_2 )
               {
               if ( MathRand()/32767.0 >= 0.5  )
                  {
                  Variants[j].Ci[i]=double(MathRand())/32767.0;                  
                  }
               else
                  {
                  Variants[j].Ci[i]=double(-MathRand())/32767.0;                  
                  }
               }
            if ( RE == RANDOM_TYPE_3 )
               {
               if ( MathRand()/32767.0 >= RX  )
                  {
                  if ( MathRand()/32767.0 >= RX+(1.0-RX)/2.0  )
                     {
                     Variants[j].Ci[i]=double(MathRand())/32767.0;
                     ///Print(Variants[j].Ci[i]);
                     }
                  else
                     {
                     Variants[j].Ci[i]=double(-MathRand())/32767.0;                  
                     }        
                  }
               else
                  {
                  Variants[j].Ci[i]=0.0;                  
                  }
               }
            if ( RE == RANDOM_TYPE_4 )
               {
               if ( MathRand()/32767.0 >= RX  )
                  {
                  Variants[j].Ci[i]=double(MathRand())/32767.0;
                  }
               else
                  {
                  Variants[j].Ci[i]=0.0;                  
                  }
               }                              
            }
         }
      }

Adjuntamos al artículo un prototipo de fuerza bruta en MQL4 y MQL5. No hemos mostrado nuestras implementaciones de las funciones comerciales, ya que no resulta necesario, ni tampoco interesante. Solo queríamos mostrar cómo se implementa nuestro enfoque dentro del marco de la plantilla. Si alguien se interesa en cómo se está implementando el resto, podrá verlo en los asesores adjuntos al artículo. Todos los asesores y materiales se adjuntarán al artículo. En general, en nuestra plantilla hay muchas funciones sobrantes, incluyendo funciones innecesarias: en algún lugar se pueden optimizar, mientras que hay otros donde podrá eliminar las variables innecesarias. Personalmente, este punto no nos preocupa. Lo que interfiera, será eliminado. Lo más importante es que funcione, eso basta. Los programas están en constante desarrollo, y no siempre hay tiempo suficiente para dedicarse a todo. Tampoco vemos motivos para guardar todos los procedimientos y variables en clases, a no ser por amor al orden y la legibilidad. La plantilla es muy sencilla, por lo que no tiene sentido pasarse de listo. Los archivos con las cotizaciones que utilizará nuestro programa, serán generados por un asesor experto especial, que recorriendo simplemente la historia, escribirá los datos de la barra en un archivo de texto con una estructura que sea fácil de leer para nuestro programa. No vamos a mostrar su código, para que el asesor sea lo más fácil posible.


Utilizando nuestro programa para encontrar y analizar patrones

Como áreas de mercado analizadas, hemos seleccionado 3 segmentos de un mes de duración que van uno tras otro. Pareja EURUSD, periodo M5.

  • Primer segmento  2020.01.13 - 2020. 02.16
  • Segundo segmento  2020.02.13 - 2020.03.15
  • Tercer segmento  2020.03.13 - 2020.04.18

Hemos elegido los segmentos de forma que el último día de los mismos sea siempre el viernes. Como todos sabemos, el viernes es el último día comercial de la semana. Si tomamos los segmentos de esta manera, tendremos 2 días completos para investigar los patrones, hasta que la bolsa comience a operar nuevamente. Probablemente esto sea un pequeño truco. Resulta obvio que, en nuestro caso, esto no tendrá ningún efecto, ya que seguiremos verificando todo en el simulador. Hemos decidido destacar 12 variantes en relación a los patrones encontrados. De estas, 6 variantes serán para polinomios con una potencia máxima de 1. Y otras 6 variantes para polinomios con una potencia máxima de 2. A nuestro entender, será suficiente.

Aquí está la primera pestaña de nuestro programa:

Tiene la capacidad de cambiar el tipo de generación de nuestros números, por ejemplo, solo positivo, positivo y negativo, positivo y ceros; positivo; negativo y ceros. El segundo ComboBox configura los criterios de búsqueda. Hay 2 variantes: esperanza matemática en puntos y nuestro análogo de la fórmula del factor de beneficio. Solo que en nuestra fórmula, este valor oscila entre -1 y +1. Y no puede haber casos en los que el factor de beneficio no se pueda calcular debido a la división por cero.

P_Factor=(Profit-Loss)/(Profit+Loss).

Bueno, después va la potencia máxima del polinomio y el número de núcleos de procesador a usar para el cálculo. En los cuadros de texto establecemos, respectivamente, el número de barras para nuestro polinomio, o lo que es lo mismo, el propio número de dimensiones; después también hemos inventado el coeficiente de asimetría de transacciones, que es muy similar a la fórmula anterior

D_Asymmetry=|(BuyTrades-SellTrades)|/(BuyTrades+SellTrades).

Solo que sus valores se encuentran en el rango de 0 a 1. Necesitamos este filtro para poder exigir que una variante contenga un número similar de señales de compra y venta, para evitar así las situaciones en las que una tendencia global se está produciendo y todas las transacciones están en la misma dirección. A continuación, viene el número de mejores opciones entre todas las encontradas, que guardamos en la memoria, y el porcentaje de la cotización cargada que usamos para la fuerza bruta. Este pedazo se calcula desde la última barra según la hora de apertura, y el trozo que quede detrás se usará para optimización. No vamos a explicar el resto de indicadores y la hoja con opciones; ahí mismo se dice qué es qué.

La segunda pestaña tiene el siguiente aspecto:


La pestaña se corresponde con la primera variante para el primer segmento de estos robots que les mostraremos. Podrá comparar el gráfico en nuestro programa con el gráfico del simulador. Todo esto lo verá más abajo. En amarillo se dibujan las órdenes de la parte de la cotización a la que se aplica la fuerza bruta, y ya puede imaginarse que está dibujado en rojo. No ofreceremos capturas de pantalla de todas las variantes, para no sobrecargar el artículo con imágenes innecesarias; estas se adjuntarán al final del artículo.

Ahora vamos a describir el contenido de esta pestaña. Interval Points son la división del rango de valores de nuestro polinomio. Cuando aplicamos la fuerza bruta en la primera pestaña, además de los parámetros principales de la variante, también se calcula el valor máximo de este polinomio de acuerdo con el módulo, para que podamos conocer la ventana de valores de nuestro polinomio y podamos tratar de detectar señales más fuertes, dividiendo esta ventana en partes iguales y aumentando gradualmente el valor. De esto se encarga la segunda pestaña. También contiene un tipo de búsqueda como en la primera pestaña y el número de mejores variantes guardadas en la memoria de optimización. Luego tenemos los filtros que nos permiten filtrar las variantes innecesarias que no se ajustan a nuestra definición de estándar. Line Control incluye una ejecución adicional para cada variante, en la que la desviación relativa de la línea del gráfico se calcula partiendo de la línea recta que une el inicio y el final del gráfico. 

Deviation = Max(|Profit[i]-LineProfit[i]|)/EndProfit.

Aquí, Profit[i] es el valor de la curva de equilibrio en la i-ésima orden, LineProfit[i] es el mismo valor, solo que el de nuestra línea recta, y EndProfit es el valor de beneficio al final del gráfico.

Todas las magnitudes se miden en puntos. Use Percent Control se refiere al porcentaje de la parte roja del gráfico, pero en este caso no hemos usado este filtro. También existe un filtro para el número mínimo de ódenes.

Aquí está la pestaña de generación de bots:


Todo funciona según el siguiente principio: en la pestaña de optimización, elegimos la opción que nos gustaba, cambiamos a esta y generamos un asesor.

Vamos a comprobar los robots que hemos generado en el simulador de estrategias MetaTrader 4. Lo hemos elegido porque allí podemos establecer un margen de 1, nivelando así prácticamente su influencia sobre la visualización del gráfico. La mayoría de los robots encontrados de esta manera tendrán una esperanza matemática ligeramente superior al spread promedio en una pareja, y esto no nos permitirá analizar visualmente los patrones con un esperanza matemática pequeña. Hemos aplicado la fuerza bruta y optimizado cada variante durante aproximadamente 2 horas y, naturalmente, esto no es suficiente para encontrar un patrón con una calidad muy alta. Para conseguir un resultado normal, necesitaríamos uno o dos días. Pero en este caso, lo que hay será suficiente, pues el propósito de este artículo no es encontrar un patrón extraordinario, sino analizar cómo se comportarán estos patrones en el futuro. Por cierto, si el lector decide poner a prueba nuestros robots en el simulador, deberá asegurarse de prestar atención a la variable DaysToTrade. Por defecto, está configurada en 3 días. Así que mejor no sorprenderse de que no haya casi transacciones tras el segmento de fuerza bruta.

En primer lugar, analizaremos los robots que se han generado usando como base un polinomio de primer grado.

Primer segmento.  2020.01.13 - 2020. 02.16

Bot 1:

                                                              Segmento de fuerza bruta

                                                              10 días en el futuro 

El primer gráfico muestra la variante de la captura de pantalla de la segunda pestaña del programa. El segundo gráfico, a su vez, es una prueba del mismo robot en el futuro durante 10 días. A nuestro parecer, 10 días son suficientes para ver todo lo que necesitamos. Aquí podemos ver que el patrón continúa durante algún tiempo, y luego vira abruptamente y va en la dirección opuesta. A primera vista, todo está bien: el patrón funciona lo suficiente como para obtener beneficios en dos o tres días. Veamos el segundo robot del mismo segmento del mercado:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Aquí todo no va tan bien. ¿Por qué? Parece que el patrón debería funcionar durante al menos uno o dos días, pero desde los primeros segundos comienza a menguar, aunque el viraje es bastante suave. Estos probablemente no sean los resultados que se esperaban 🙂. Pero todo esto resulta sencillo de explicar. Al final nostraremos cómo.

Pasamos al segundo segmento de prueba.  2020.02.13 - 2020.03.15

Vayamos al tercer robot:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 


La primera imagen de las tres, que tiene buen aspecto. El gráfico es muy plano y muy similar a una línea recta. Lo cual parece indicar que el patrón es muy estable y que es más probable que continúe moviéndose durante algún tiempo, como precisamente ha sucedido. Si observamos con atención, podremos ver que incluso en el futuro este movimiento se parece a una línea; en otras palabras, muchos parámetros de este patrón han continuado funcionando.

Echemos un vistazo al cuarto robot:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Aquí hay un movimiento ascendente considerable y, a primera vista, todo parece bastante suave y estable, salvo por el fuerte aumento al inicio del gráfico. Pero eso es precisamente lo que debe notarse, porque dicha asimetría debería indicarnos de inmediato que esto posiblemente sea solo una coincidencia o un resultado accidental. Y en el futuro, vemos inmediatamente desde los primeros segundos la inversión del patrón completo en la dirección opuesta, lo que, en principio, no resulta sorprendente.

Pasamos al tercer segmento de prueba.  2020.03.13 - 2020.04.18

Quinto robot:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Aquí tenemos casi la misma imagen. La misma asimetría aparente, algunas ondas al principio, atenuación al final. Parece un estándar, pero, en nuestra opinión, esto resulta discutible. Resultaría peligroso comerciar usando la continuación como base. Bueno, y también eran de esperar una inversión inmediata del gráfico en el futuro y una inversión de toda la fórmula. No vamos a mostrar el sexto robot, su gráfico se encontrará en el archivo. Es casi idéntico a este gráfico.

Ahora, vamos a pasar a los robots basados ​​en el polinomio de segundo grado.

Primer segmento.  2020.01.13 - 2020. 02.16

Veamos el séptimo robot:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Una característica manifiesta de estos robots es, como podemos suponer, un mejor funcionamiento del robot en el segmento de fuerza bruta. Esto parece mostrar una calidad inferior en el segmento que precede al de fuerza bruta. Es decir, en el segmento donde utilizamos la fuerza bruta siempre se observa una media onda brusca y positiva, mientras que en el segmento restante existe cierta confusión y, en el mejor de los casos, un ligero movimiento ascendente. No obstante, estos son solo indicadores de una fórmula específica; como podremos ver más adelante, todo se comporta de manera parecida. Un poco de caos al principio, y luego un movimiento en la dirección opuesta.

Veamos la octava variante:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Aquí las cosas se ponen mucho peor: no hay un patrón general, pero, al igual que en la versión anterior, existe un movimiento ascendente en la sección de fuerza bruta. No hay ninguna regularidad global aquí, por lo que, como era de esperar, el gráfico desciende de inmediato.

Pasamos al segundo segmento de prueba.  2020.02.13 - 2020.03.15

Noveno robot:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

En nuestra opinión, se ve el comienzo de una ola en el primer gráfico y su final en el futuro. No hay un patrón global aquí; no obstante, el viraje es lo suficientemente suave como para tener tiempo incluso para lograr algunos beneficios: poca cosa, pero ganancias al fin y al cabo.

Echemos un vistazo al robot número 10:

                                                              Segmento de fuerza bruta

                                                             10 días en el futuro 

Aquí la cosa mejora, y ya se parece más a un patrón. El patrón prosigue durante uno o dos días como máximo. Siendo honestos, no conviene arriesgarse con un gráfico así. La desviación de la línea recta es demasiado pronunciada. 

Pasamos al tercer segmento de prueba.  2020.03.13 - 2020.04.18

Undécimo robot:

                                                               Segmento de fuerza bruta


                                                              10 días en el futuro  

La imagen no es muy bonita, pero es posible detectar cierto parecido con la línea recta. En el futuro, el patrón prosigue, pero quizá esto sea más fruto de la suerte que un resultado en sí: hay demasiado ruido aleatorio. O tal vez incluso estos no sean ruidos, sino algunas olas más pequeñas; no está claro. 

Duodécimo robot:

                                                                Segmento de fuerza bruta

                                                               10 días en el futuro  

Es un gráfico bastante feo, pero podemos ver el extremo agudo de una onda y otra enorme que la sigue. En el futuro, esta enorme ola se desarrolla lentamente, y en algún momento se invierte finalmente. Parece que la inversión de tendencia se ha producido con mayor fluidez en robots con un polinomio superior al segundo grado. Menos sorpresas, como se suele decir. Es de suponer que si dedicamos más tiempo y probamos la tercera potencia, será mejor. En teoría, debería ser así, ya que la regularidad debería ser mejor descrita por el polinomio, que tiene la mayor potencia total de los multiplicadores en términos.


Conclusiones y verdades matemáticas de nuestra investigación

Ahora podemos resumir todos los resultados de las pruebas de nuestros asesores expertos. A primera vista, resulta difícil de hacer, ya que en el segmento de fuerza bruta y optimización todo parece ir en ascenso, mientras que en los segmentos donde se utiliza la fuerza bruta como base todo es un desastre. Pero, en realidad, no es así:

  • En cada una de las simulaciones en el futuro, siempre existe un punto donde el gráfico se despliega y la fórmula se invierte.
  • Aunque puede suceder tanto de forma paulatina como instantánea, la inversión siempre ocurre.
  • Básicamente, la gran mayoría de los gráficos dejarán de funcionar en el futuro.
  • En ocasiones, el patrón prosigue durante un tiempo al principio.
  • Por norma general, para todas las simulaciones futuras, resulta obvio que el patrón funciona en la dirección opuesta.
  • Si la curva de balance se desvía de la línea recta, las posibilidades de continuación son mucho menores.
  • En la mejor de las variantes encontradas, el patrón funciona durante unos días.

Ahora trataremos de explicar todos estos hechos. Vamos a comenzar con el primer y más importante hecho, que abordamos hace mucho tiempo, por cierto, cuando estos programas ni siquiera estaban en nuestra mente. Se trata de una verdad matemática muy simple. A continuación, dibujaremos un gráfico de balance para una estrategia arbitraria. La línea negra muestra un patrón pequeño, y la línea violeta, uno grande. El gráfico muestra su comportamiento aproximado si comerciáramos con ellos a lo largo de la historia, y no solo en el segmento donde se usa la fuerza bruta:

No hemos insertado aquí cotizaciones a propósito, ya que en nuestro caso la base analizada puede ser el gráfico de balance. No debería importarnos cómo se dibuja esta cotización allí y qué hay en ella. Por el momento, no veremos más en ella que nuestra estrategia.

Imagínese si probáramos todos nuestros robots desde el comienzo de la historia y en el futuro; sin embargo, aunque aún no tenemos las cotizaciones del futuro, podemos suponer que las tendremos con un 100% de precisión. ¿Tonterías, dice? No, no es una tontería. Primero, imaginemos que estamos probando los robots en toda la historia de cotizaciones disponible. ¿Que veremos? Algún tipo de confusión que se mueve hacia arriba y hacia abajo, que sube de nuevo, y baja después otra vez. ¿Y qué deberíamos ver en este lío, se preguntará? La respuesta es ondas. No importa qué forma tengan, qué longitud de onda y amplitud. Y ciertamente no es una sinusoide, pero eso no nos importa. Solo nos importa que este proceso sea periódico. Si también asumimos que la cotización es infinita, entonces podríamos decir, basándonos en la investigación matemática del último artículo del autor, que, aplicando una estrategia aleatoria con una fórmula aleatoria, su expectativa a lo largo de la historia tenderá a cero cuando la cantidad de datos históricos tiende hasta el infinito. ¿Qué obtenemos de ella? Y lo que podemos decir ahora es que cualquier curva de balance con un número infinito de transacciones cruzará la línea de balance inicial un número infinito de veces. E incluso si, por algún motivo, el balance sube o baja inmediatamente y se mantiene todo el tiempo, tendremos derecho a desplazar esa línea ligeramente hacia arriba o hacia abajo y todavía encontrar ese punto de equilibrio cerca del cual oscila el balance.

No consideraremos aquellas fórmulas que conlleven beneficios o pérdidas pronunciados a lo largo de la historia; aunque estas variantes también se pueden incluir en esta categoría, resulta simplemente que en este caso caemos en la media onda positiva o negativa de una enorme ola, más grande que toda nuestra historia. Dentro de nuestras suposiciones, encontramos que los patrones hallados no son más que las partes encontradas en medias ondas positivas. Cuanto más grande sea la parte encontrada, más probable será que el punto de equilibrio esté muy por debajo. Partiendo de esto, como ahora existe una media onda positiva, pronto debería aparecer una negativa. En el lenguaje de las matemáticas, la probabilidad de movimiento en la dirección negativa es mayor cuanto mayor es la media onda que captamos. Y viceversa, si tomamos una media onda negativa, cuanto más grande sea esa media onda, más probable será que comience una media onda positiva. Puede resultar más sencillo: si tenemos una estrategia con una esperanza matemática cero a lo largo de la historia, toda esta historia constará de segmentos con expectativas negativas y positivas, que siguirán unos a otros alternándose constantemente. Disponemos incluso de un asesor que implementa este principio y funciona para cualquier pareja de divisas basada en la historia completa de cotizaciones. Por consiguiente, nuestros argumentos están respaldados no solo por la investigación que hemos realizado aquí, sino también por los asesores expertos. En general, este principio no solo se puede escalar, sino también disponer en capas infinitas como una muñeca matryoshka, aumentando así la eficiencia del sistema. Claro que podemos jugar con la continuación de la tendencia, pero le recomendamos que haga esto solo si el patrón es muy agradable y uniforme, y luego, no más del 5-10% del patrón que se encuentre en el futuro. Las apuestas son muy altas. Además, jugará contra las matemáticas, lo cual es una estupidez. Si fuera posible valorar incluso el tiempo de funcionamiento restante aproximado de esta norma, podríamos hacer. Pero en nuestro caso, esto es imposible, ya que la naturaleza del patrón no está clara. E incluso si la naturaleza del patrón fuera clara, tal análisis resulta extremadamente difícil.


¿Cómo determinar el nivel respecto al cual ocurren las fluctuaciones?

Prosiguiendo con el tema de las fluctuaciones, trataremos de responder al lector, ¿cómo puede definir este maldito nivel respecto al cual necesita determinar las ondas y su movimiento en relación con el propio nivel? La respuesta es escandalosamente simple: de ninguna forma. Pero esto no significa en absoluto que este nivel no exista y que este conocimiento no pueda ayudarnos a realizar correctamente una transacción comercial. Es importante entender que este nivel no es fijo, y que está solo en nuestra cabeza. Resulta aún más importante comprender lo siguiente: cuando el tamaño de una media onda tiende al infinito, la relación entre el tamaño de dicha media onda y la distancia de este nivel desde el balance inicial tiende al infinito. Es decir, cuanto más fuerte sea la media onda que hemos hallado, menos podremos pensar en dónde se encuentra este nivel, ya que de todos modos, con un aumento en el número de transacciones, este nivel tenderá al punto cero. Todo lo que necesitamos es localizar las medias ondas más intensas. Otro dato que corrobora esto es que cuanto mayor y más perfecta sea la media onda, menos probable será que en el resto de la prueba virtual haya una onda con una amplitud comparable a la nuestra. Intentaremos representar visualmente lo que hemos mencionado en la figura:

En sí mismo, este nivel no garantiza que el patrón vaya a virar hacia allá al 100% y que se mueva con viveza, pero, lo que es más importante, el mero hecho de que haya un patrón que sea parte de la media onda nos indica que lo más probable es que haya más de una onda, e incluso es posible que estén presentes durante toda la historia, si probamos el asesor a lo largo de la misma. En este caso, hay una posibilidad bastante considerable de que exista un gran movimiento de retroceso. Eso es lo que el lector necesita captar. Incluso cuando hemos probado varios asesores que no han funcionado a escala global, han existido muchas áreas locales donde esto funcionó con una inversión. Había ondas claramente pronunciadas y el gráfico no parecía confuso, estaba claramente estructurado.


Para completar la imagen

Intentaremos decir aproximadamente cómo podemos comerciar (en nuestra opinión) con estas ondas de manera más eficaz. En primer lugar, explicaremos algunas cosas:


La primera opción consiste comerciar usando como base una inversión del patrón; la segunda es continuar. Si consideramos la primera opción, lo ideal sería que siempre alcanzáramos un cierto nivel y detuviéramos el ciclo de negociación allí, y luego esperásemos al siguiente. En el caso de usar un martingale parcial, habría una mejora, pero si sabemos que el gráfico pronto se invertirá. De lo contrario, la expectativa seguirá siendo "0". Puede jugar basándose en la continuación de la tendencia solo cuando el patrón se acerca al ideal, y este es el tiempo más corto posible. En nuestra opinión, en la segunda opción, podemos usar el martingale inverso. En general, para ser honestos, todas las estrategias que hemos probado indican un hecho matemático indiscutible: cuando se comercia con un lote fijo, si no sabemos cómo se comportará el precio en el futuro (y casi nunca podemos saberlo), entonces tendremos "0".

Pero todavía hay situaciones en las que accidentalmente captamos un patrón estándar global que permanece operativo durante mucho tiempo, aunque en nuestra opinión, sería mejor no esperar a que se den tales situaciones. Es mejor elegir un esquema comercial y ceñirse a él. No podemos hacer afirmaciones de forma unívoca. No hemos tenido tiempo de hacer pruebas ni siquiera en cuentas demo, ya que lleva de 2 a 3 meses. Un mes tiene cuatro semanas, y cada fin de semana tenemos que utilizar la fuerza bruta 2 días :). Desafortunadamente, aún no hemos tenido la oportunidad de hacer pruebas en una computadora que funcione las 24 horas del día. El auto solo dispone de un netbook que está ocupado con otros robots. Quizás en el futuro, cuando aparezca una máquina más potente, la probaremos en una cuenta demo y crearemos una señal por separado.


Conclusión

En el presente artículo, hemos sacado conclusiones simples, pero muy importantes, sobre los patrones y su física aplicados al mercado. A saber: el mercado no es caótico, existen muchos patrones ocultos dentro de él en diferentes periodos de los gráficos, solo que, al superponerse, crean la ilusión de un caos. Los patrones son un proceso periódico que se puede repetir e invertir. Una vez que se repiten los patrones, estas ondas pueden acusar una limitación en su amplitud que podemos usar para nuestras estrategias. Hemos tratado de hacer este artículo lo más claro posible, ofreciendo un presupuesto mínimo de matemáticas para transmitir mejor su idea principal. Esperamos que esta información ayude al lector a desarrollar sus propios sistemas comerciales. Si alguien puede ver más en estos resultados, le agradeceremos que intervenga activamente en los comentarios. Desafortunadamente, no hemos podido usar la fuerza bruta en periodos temporales mayores, debido a que los procesos derivados requieren mucho tiempo, incluso usando nuestro software. Si al lector le interesa un análisis más profundo, estaremos listo para continuar con este tema, pero en el marco de otro artículo. Este artículo es más bien introductorio e ilustrativo.


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

Archivos adjuntos |
results.zip (2075.33 KB)
Trabajando con las series temporales en la biblioteca DoEasy (Parte 58): Series temporales de los datos de búferes de indicadores Trabajando con las series temporales en la biblioteca DoEasy (Parte 58): Series temporales de los datos de búferes de indicadores
En conclusión del tema de trabajo con series temporales, vamos a organizar el almacenamiento, la búsqueda y la ordenación de los datos que se guardan en los búferes de indicadores. En el futuro, eso nos permitirá realizar el análisis a base de los valores de los indicadores que se crean a base de la biblioteca en nuestros programas. El concepto general de todas las clases de colección de la biblioteca permite encontrar fácilmente los datos necesarios en la colección correspondiente, y por tanto, lo mismo también será posible en la clase que vamos a crear hoy.
Trabajando con las series temporales en la biblioteca DoEasy (Parte 57): Objeto de datos del búfer de indicador Trabajando con las series temporales en la biblioteca DoEasy (Parte 57): Objeto de datos del búfer de indicador
En este artículo, vamos a desarrollar el objeto que incluirá todos los datos de un búfer de un indicador. Estos objetos serán necesarios para almacenar los datos de serie de los búferes de indicadores, a través de los cuales será posible ordenar y comparar los datos de los búferes de cualquier indicador, así como otros datos parecidos.
Trabajando con los precios en la biblioteca DoEasy (Parte 59): Objeto para almacenar los datos de un tick Trabajando con los precios en la biblioteca DoEasy (Parte 59): Objeto para almacenar los datos de un tick
A partir de este artículo, procedemos a la creación de la funcionalidad de la biblioteca para trabajar con los datos de precios. Hoy, crearemos una clase del objeto que va a almacenar todos los datos de los precios que llegan con un tick.
El comercio en fórex y sus matemáticas básicas El comercio en fórex y sus matemáticas básicas
El artículo pretende describir las principales características del comercio de divisas de la forma más rápida y simple posible, para compartir verdades sencillas con los lectores principiantes. También intentaremos responder a las preguntas más interesantes en el entorno comercial, así como escribir un indicador simple.