English Русский 中文 Deutsch 日本語 Português Türkçe
preview
Aproximación por fuerza bruta a la búsqueda de patrones (Parte IV): Funcionalidad mínima

Aproximación por fuerza bruta a la búsqueda de patrones (Parte IV): Funcionalidad mínima

MetaTrader 5Probador | 19 mayo 2021, 10:00
859 0
Evgeniy Ilin
Evgeniy Ilin
En este artículo, presentaremos una nueva versión de nuestro programa, que ya ha adquirido la funcionalidad mínima capaz de extraer los ajustes de trabajo que necesitamos para comerciar. Los cambios estarán enfocados principalmente en la facilidad de uso y el umbral mínimo de entrada. El objetivo principal sigue siendo involucrar a tantos usuarios como resulte posible para encontrar ajustes e investigaciones de mercado. En el presente artículo, no profundizaremos en ninguna jungla espesa de trading, pero sí que ofreceremos tanta información útil como sea posible. Intentaremos explicar de forma accesible cómo usar este método, y también hablaremos de todos sus pros y contras, así como de las perspectivas de aplicación práctica.


Cambios en la nueva versión

Al igual que en el artículo anterior, el programa ha mejorado mucho en términos de funcionalidad y, lo más importante, de usabilidad. Las versiones anteriores resultaban muy incómodas, tenían muchos errores, bugs y deficiencias. En esta versión, tenemos muchísimos cambios en este aspecto. Hemos trabajado mucho para modernizar tanto las plantillas de los asesores como el software en sí. Lista de cambios:

  1. Interfaz rediseñada
  2. Hemos añadido un polinomio adicional para la fuerza bruta (basado en la serie revisada de Fourier)
  3. Hemos ampliado el mecanismo de generación de números aleatorios
  4. Hemos ampliado el concepto del método para hacerlo más cómodo y sencillo de usar
  5. Hemos añadido mecanismos de variación de lote para las secciones ultracortas
  6. Hemos añadido un mecanismo para calcular el spread
  7. Hemos añadido un mecanismo para reducir el ruido del spread
  8. Hemos corregido multitud de errores

Hemos implementado muchas de las modificaciones planificadas. Las correciones de los algoritmos las realizaremos en el futuro, pero no serán tan globales.


Primera demostración del funcionamiento y nuevo concepto

Durante el proceso de creación de los asesores, nos damos cuenta de que no siempre nos apetece andar inventando nombres, y de que, además, tampoco tenemos ganas de hurgar todo el tiempo en los terminales para limpiar la configuración de los robots. Después de todo, es posible que ya tengamos un robot con el mismo nombre y existan ajustes de este en las profundidades del terminal, y, ciertamente, no queremos limpiarlos cada vez. La solución a este problema es el asesor-receptor de ajustes. En otras palabras, el programa genera un archivo de configuración en el formato txt habitual y el asesor experto simplemente lo lee. Este enfoque aumenta la velocidad de funcionamiento de esta solución, haciéndola más comprensible y sencilla. El esquema de la solución ahora tiene el siguiente aspecto:

Forex Awaiter Usage


Obviamente, hemos mantenido la versión del programa que genera los robots. No obstante, y especialmente para aproximar este método al usuario promedio, hemos inventado un nuevo concepto específico para los terminales MetaTrader 4 y MetaTrader 5 que hace posible utilizar esta solución de la forma más sencilla y rápida posible. A nuestro parecer, lo más cómodo de esta solución es que la configuración funciona de la misma forma tanto para MetaTrader 4 como para MetaTrader 5.

Vamos a mostrar la nueva interfaz solo en parte, ya que resulta bastante voluminosa y ocupará demasiado espacio en el artículo. Solo mostraremos la primera pestaña.

New Awaiter interface


Todos los elementos se dividen en las secciones correspondientes para facilitar su percepción; los elementos no utilizados, en cambio, se bloquearán si su uso carece de sentido.

También hemos creado un vídeo específicamente para el artículo, donde mostramos el funcionamiento del programa.



¿Por qué no trasladamos los ajustes usando nuestros viejos conocidos, los archivos set? Todo es muy simple: los archivos set no pueden contener matrices, y en este método, las matrices están presentes como configuraciones de entrada, y además, tienen una longitud flotante. En este caso, resultará más adecuado usar archivos de texto normales.


Nuevo polinomio basado en las series de Fourier modificadas

Muchas personas familiarizadas con el aprendizaje automático usan activamente la serie de Fourier para sus algoritmos y encuentran una amplia variedad de aplicaciones para el mismo. En sus inicios, la serie de Fourier se inventó para expandir las funciones en el intervalo [-π;π]. En este caso, también necesitamos saber cómo expandir la función en esta serie y por qué necesitamos esta expansión. Además, necesitamos conocer los matices del método de cambio de variable, porque es posible que necesitemos una expansión en un intervalo completamente diferente a [-π;π]. Todo esto requiere una buena formación matemática y conocimiento de los matices, así como una comprensión clara sobre si todo esto tiene algún sentido para el trading. Este es el aspecto general de la serie de Fourier:

Vista general de la serie de Fourier

En esta forma, dicho polinomio solo puede resultar útil para presentar los patrones comerciales de una manera más adecuada, y también para intentar predecir el movimiento del precio, asumiendo que el precio sea un proceso de onda. Lo más probable es que esta suposición sea válida, pero en esta forma resulta imposible de aplicar a la fuerza bruta, porque esto tendría que cambiar radicalmente todo el concepto del método. En su lugar, podríamos pensar cómo transformar el polinomio dado de manera que resulte adecuado para un método concreto. En general, pueden darse muchas variaciones, pero si queremos que la fórmula sea lo más similar posible a la serie de Fourier y al mismo tiempo no utilizar dicha fórmula para el propósito previsto, entonces, a nuestro juicio, podemos hacer esto:

Primera transformación

Nada ha cambiado aquí, excepto que la serie ha ganado mayor libertad y ahora su periodo oscila tanto en positivo como en negativo. Bueno, lo único es que hay un número finito de términos. Esto se debe a que la matriz C[] representa ​nuestros coeficientes, que combinaremos para encontrar una fórmula adecuada, y su número es limitado. No podemos escribir esta serie indefinidamente: tendremos que limitarnos a solo "m" barras. Entre otras cosas, eliminaremos el primer término para mayor simetría, de forma que los valores de la fórmula generen las señales más simétricas en los rangos "+" y "-". ¡Pero de esta manera solo podremos seleccionar una función que dependa de 1 barra! Y deberemos asegurarnos de que los valores de todas las barras estén presentes en la fórmula; además, la barra no tiene 1 parámetro, sino 6. Ya mostramos estos 6 parámetros en el artículo №2 de la presente serie. Está claro que deberemos sacrificar la precisión de procesamiento de 1 barra para poder tener en cuenta las demás. Si hablamos en términos ideales, necesitaríamos envolver esta cantidad en otra. Pero no queremos complicar el polinomio, así que, por el momento, nos limitaremos a la versión más sencilla:

Polinomio final

De hecho, la función se ha transformado de unidimensional a multidimensional, pero eso no significa que este polinomio sea capaz de describir cualquier función multidimensional en el hipercubo multidimensional seleccionado. En cualquier caso, esta fórmula nos presentará otra familia de funciones multidimensionales que podrán describir aquellos patrones que la serie de Taylor no pueda, o donde esta serie no pueda describir la función requerida con suficiente calidad. Lo único es que tendremos más posibilidades de encontrar un patrón mejor en la misma muestra de datos. 

En el código, esta función tendrá el aspecto siguiente:

if ( Method == "FOURIER" )
   {
      for ( int i=0; i<CNum; i++ )
         {
         Val+=C1[iterator]*MathSin(C1[iterator+1]*(Close[i+1]-Open[i+1])/_Point)+C1[iterator+2]*MathCos(C1[iterator+3]*(Close[i+1]-Open[i+1])/_Point);
         iterator+=4;
         }

      for ( int i=0; i<CNum; i++ )
         {
         Val+=C1[iterator]*MathSin(C1[iterator+1]*(High[i+1]-Open[i+1])/_Point)+C1[iterator+2]*MathCos(C1[iterator+3]*(High[i+1]-Open[i+1])/_Point);
         iterator+=4;
         }

      for ( int i=0; i<CNum; i++ )
         {
         Val+=C1[iterator]*MathSin(C1[iterator+1]*(Open[i+1]-Low[i+1])/_Point)+C1[iterator+2]*MathCos(C1[iterator+3]*(Open[i+1]-Low[i+1])/_Point);
         iterator+=4;
         }

      for ( int i=0; i<CNum; i++ )
         {
         Val+=C1[iterator]*MathSin(C1[iterator+1]*(High[i+1]-Close[i+1])/_Point)+C1[iterator+2]*MathCos(C1[iterator+3]*(High[i+1]-Close[i+1])/_Point);
         iterator+=4;
         }

      for ( int i=0; i<CNum; i++ )
         {
         Val+=C1[iterator]*MathSin(C1[iterator+1]*(Close[i+1]-Low[i+1])/_Point)+C1[iterator+2]*MathCos(C1[iterator+3]*(Close[i+1]-Low[i+1])/_Point);
         iterator+=4;
         }         

   return Val;
   }

Aquí no está la función completa: solo se encuentra la parte encargada de implementar el polinomio dado, pero en general esto nos bastará para comprender. A pesar de la simplicidad del diseño, incluso estas fórmulas resultan adaptables al mercado.

Hemos puesto en marcha una fuerza bruta lo más rápido posible durante el último año de la historia utilizando este método, solo para demostrar que la fórmula funciona. Peor o mejor, no lo sé, tenemos que averiguarlo. Hablando rigurosamente, todavía no hemos logrado encontrar algo que realmente funcione con esta fórmula, pero seguro que esto se debe a que realmente no hemos tenido ni el tiempo ni la potencia de cálculo necesarios. Todo el tiempo lo ha ocupado la investigación de la versión inicial. Esto es lo que hemos logrado obtener en la pareja de divisas USDJPY M15 durante el último año de historia:

FOURIER Method


Lo único que no nos gusta de esta fórmula es que resulta muy inestable respecto al mecanismo de supresión del ruido de propagación: aparentemente, estas son las peculiaridades de uso de las funciones armónicas en el marco de este método. Quizás no hayamos compuesto la fórmula correctamente al 100%, pero, por alguna razón, no nos parece que sea así. En este método, es obligatorio activar la casilla de verificación "Spread Control" en la segunda pestaña, para que el mecanismo de supresión de ruido de propagación se desactive durante la optimización y ofrezca variantes bastante buenas. Parece que todo está en orden, lo único es que esta fórmula ha resultado ser muy "suave". No obstante, como podemos ver, también es capaz de encontrar variantes bastante buenas.


Sobre la implementación del software desde dentro

Como no habíamos tocado este tema en los anteriores artículos del ciclo, decidimos descubrir un poco al lector cómo funciona todo desde adentro. La parte más interesante y sencilla es la generación de los coeficientes para la fórmula. Debemos trabajar esta parte en primer lugar, porque dejará más claro cómo se generan los coeficientes:

public void GenerateC(Tester CoreWorker)
   {
   double RX;
   TYPE_RANDOM RT;
   RX = RandomX.NextDouble();
   if (RandomType == TYPE_RANDOM.RANDOM_TYPE_R) RT = (TYPE_RANDOM)RandomX.Next(0, Enum.GetValues(typeof(TYPE_RANDOM)).Length-1);
   else RT = RandomType;

   for (int i = 0; i < CoreWorker.Variant.ANum; i++)
      {
      if (RT == TYPE_RANDOM.RANDOM_TYPE_0) 
         {
         if (i > 0) CoreWorker.Variant.Ci[i] = CoreWorker.Variant.Ci[i-1]*RandomX.NextDouble();
         else CoreWorker.Variant.Ci[0]=1.0;
         }
      if (RT == TYPE_RANDOM.RANDOM_TYPE_5)
         {
         if (RandomX.NextDouble() >= 0.5)
            {
            if (i > 0) CoreWorker.Variant.Ci[i] = CoreWorker.Variant.Ci[i - 1] * RandomX.NextDouble();
            else CoreWorker.Variant.Ci[0] = 1.0;
            }
         else
            {
            if (i > 0) CoreWorker.Variant.Ci[i] = CoreWorker.Variant.Ci[i - 1] * (-RandomX.NextDouble());
            else CoreWorker.Variant.Ci[0] = -1.0;
            }
         }
      if (RT == TYPE_RANDOM.RANDOM_TYPE_1) CoreWorker.Variant.Ci[i] = RandomX.NextDouble();
      if (RT == TYPE_RANDOM.RANDOM_TYPE_2)
         {
         if (RandomX.NextDouble() >= 0.5) CoreWorker.Variant.Ci[i] = RandomX.NextDouble();
         else CoreWorker.Variant.Ci[i] = -RandomX.NextDouble();
         }
      if (RT == TYPE_RANDOM.RANDOM_TYPE_3)
         {
         if (RandomX.NextDouble() >= RX)
            {
            if (RandomX.NextDouble() >= RX + (1.0 - RX) / 2.0) CoreWorker.Variant.Ci[i] = RandomX.NextDouble();
            else CoreWorker.Variant.Ci[i] = -RandomX.NextDouble();
            }
         else CoreWorker.Variant.Ci[i] = 0.0;
         }
      if (RT == TYPE_RANDOM.RANDOM_TYPE_4)
         {
         if (RandomX.NextDouble() >= RX) CoreWorker.Variant.Ci[i] = RandomX.NextDouble();
         else CoreWorker.Variant.Ci[i] = 0.0;
         }
      }
   }

Resulta bastante simple: existen varios tipos fijos de generación de números aleatorios, y un tipo general que implementa todo a la vez. Cada uno de los tipos de generación ha sido probado en la práctica, y resulta que el tipo de generación general "RANDOM_TYPE_R" funciona de la forma más eficiente posible. Los tipos fijos no siempre dan un buen resultado, ya que la naturaleza de las cotizaciones en diferentes instrumentos y marcos temporales resulta distinta en casi todas partes. Visualmente, en la mayoría de los casos, estas diferencias no resultan visibles, pero la máquina puede verlo todo. Aunque, eso sí, algunos tipos fijos en algunos marcos temporales son capaces de ofrecer más señales con indicadores de máxima calidad. De esta forma, percibimos que, por ejemplo, en la pareja NZDUSD H1 hay un salto brusco en la calidad de los resultados si usamos RANDOM_TYPE_4, que indica "solo ceros y números positivos", lo cual puede ser un indicio claro de la presencia de procesos ocultos de ondas que el ojo es incapaz de detectar. Querríamos investigar diferentes herramientas con mayor detalle, pero desafortunadamente, no podemos abordar esta tarea en solitario.


Nuevo mecanismo de supresión de ruido de spread y registro de spread

Como señalamos en el artículo anterior, el spread distorsiona los datos de precios de tal forma que la mayoría de los patrones encontrados se hallan principalmente dentro del spread. De hecho, el spread es el peor enemigo de cualquier estrategia por un motivo muy simple: contrariamente a nuestras expectativas, la mayoría de las estrategias no ofrecen una esperanza matemática suficiente para superar el spread. En este caso, no deberemos dejarnos engañar por un backtest o por estadísticas comerciales positivas en una cuenta real durante un mes o incluso un año, porque esta es una muestra de datos demasiado pequeña para valorar el rendimiento futuro. Por consiguiente, existe toda una clase de estrategias y sistemas de comercio automatizados llamados scalpers nocturnos. Estos robots obtienen un pequeño beneficio en un período de tiempo muy limitado. Los brókeres luchan activamente contra estos sistemas ampliando los spreads después de la medianoche. Simplemente, el margen se establece en tal nivel que la mayoría de las estrategias no resulten rentables y nuestro dinero sea enviado de forma segura al bróker.

Hay un valor que prácticamente no cambia para la mayoría de los brókeres:

  • Spread = (Ask - Bid) / _Point
  • MidPrice = ( Ask + Bid ) / 2

Dicho precio está resaltado en verde. Esto no es más que la mitad de la profundidad de mercado. Por regla general, la profundidad de mercado se alinea respecto a un precio dado, y todo esto sucede de tal forma que ambos precios estén a la misma distancia de este precio. De hecho, si usamos la definición clásica de la profundidad de mercado, este precio no tendrá ningún sentido, ya que no hay órdenes comerciales. Incluso si asumimos que todos los brókeres tienen sus propios spreads, este precio podrá ser casi el mismo para todos los brókeres. Veamos el diagrama:

Spread

La figura superior muestra una serie de precios de dos brókeres elegidos al azar. Siempre hay un precio "Ask" y un precio "Bid", que simbolizan los precios de compra o venta. Pero la línea negra es común para ambas series de precios. Este valor se calcula de forma muy simple, como hemos mostrado anteriormente. Lo más importante es que este valor casi no depende de la expansión o el estrechamiento de los spreads de un bróker en concreto, ya que el estrechamiento o la expansión de la profundidad de mercado sucede de manera prácticamente uniforme en relación con un precio dado.

La figura de abajo muestra una situación real que realmente ocurre con las cotizaciones de diferentes brókeres. El asunto es que incluso este precio promedio es diferente en diferentes flujos. Desconocemos los motivos y tampoco nos interesan, ya que es casi imposible extraer nada útil para comerciar de esta información. El autor descubrió este hecho cuando estaba involucrado en el comercio de arbitraje, porque todos estos matices son extremadamente importantes en dicho ámbito. Respecto a nuestro método, solo es importante lo siguiente:

  • MidPrice1=f(t)
  • MidPrice2=MidPrice1-D
  • MidPrice1 '(t) =  MidPrice2 '(t)

En otras palabras, el precio promedio de ambas series de precios, si se presenta como funciones temporales, tiene la misma derivada, ya que estas funciones se distinguen solo en la constante "D". Y como nuestro polinomio no usa tanto los precios en sí mismos, como su diferencia, todos estos valores serán funcionales de la derivada de estas funciones del precio medio. Y como estas derivadas son iguales para todos los brókeres, esto nos permitirá (además de abstraernos de los saltos del spread) confiar en que nuestra configuración será igual de efectiva con otro bróker. En un caso alternativo, la configuración que hemos encontrado tendrá una probabilidad extremadamente baja de superar el backtest con ticks reales y, por consiguiente, existirá una probabilidad aún menor de que nuestra configuración se aplique a otro bróker. Si nos valemos de este concepto, todos estos problemas desaparecerán: ya lo hemos probado en la práctica.

Para implementar este mecanismo, hemos tenido que introducir los cambios correspondientes en todos los elementos de la solución. En primer lugar, para implementar este enfoque, debemos registrar adicionalmente los spreads en todos los puntos importantes de la barra al escribir el archivo de cotización, por ejemplo, Open[], Close[], High[], Low[], para corregir aún más estos valores usando el mismo margen que, de hecho, nos dará el precio "Ask" gracias a que las barras se dibujan según los precios "Bid". Al mismo tiempo, los asesores para el registro de cotizaciones han pasado de asesores por barras a asesores por ticks. La función para registrar estas barras es ahora así:

void WriteBar()
   {
   FileWriteString(Handle0x,"\r\n");
   FileWriteString(Handle0x,DoubleToString(Close[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Open[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(High[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Low[1],8)+"\r\n");         
   FileWriteString(Handle0x,IntegerToString(int(Time[1]))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(PrevSpread)+"\r\n");
   FileWriteString(Handle0x,IntegerToString(CurrentSpread)+"\r\n");
   FileWriteString(Handle0x,IntegerToString(PrevHighSpread)+"\r\n");
   FileWriteString(Handle0x,IntegerToString(PrevLowSpread)+"\r\n");   
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.hour))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.min))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day_of_week))+"\r\n");         
   }      

En color verde se resaltan las 4 líneas que registran el spread en los cuatro puntos de la barra que se usan para el dibujado. En la versión anterior, estos valores no se registraban ni tampoco eran considerados en los cálculos. Escribir estos datos no supone un problema, al igual que obtenerlos. Para obtener el spread de "High" y "Low", hemos introducido una función simple que trabaja con los ticks:

void RecalcHighLowSpreads()
   {
   if ( Close[0] > LastHigh )
      {
      LastHigh=Close[0];
      HighSpread=int(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
      }
   if ( Close[0] < LastLow )
      {
      LastLow=Close[0];
      LowSpread=int(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
      }      
   }

Esta función solo determina el margen en los puntos alto y bajo de la barra mientras se forma la barra actual. Cuando aparece una nueva barra, la barra actual se considera completamente formada, y sus datos se escriben en el archivo. Por sí sola, esta función resulta insuficiente, pues trabaja en conjunto con una función por barras:

bool bNewBar()
   {
   ArraySetAsSeries(Close,false);                        
   ArraySetAsSeries(Open,false);                           
   ArraySetAsSeries(High,false);                        
   ArraySetAsSeries(Low,false);                              
   CopyOpen(_Symbol,_Period,0,2,Open);
   CopyClose(_Symbol,_Period,0,2,Close);
   CopyHigh(_Symbol,_Period,0,2,High);
   CopyLow(_Symbol,_Period,0,2,Low);
   ArraySetAsSeries(Close,true);                        
   ArraySetAsSeries(Open,true);                           
   ArraySetAsSeries(High,true);                        
   ArraySetAsSeries(Low,true);                                 
   if ( Time0 < Time[1] )
      {
      if (Time0 != 0)
         {
         Time0=Time[1];
         PrevHighSpread=HighSpread;
         PrevLowSpread=LowSpread;         
         PrevSpread=CurrentSpread;
         CurrentSpread=int(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
         HighSpread=CurrentSpread;
         LowSpread=CurrentSpread;         
         return true;
         }
      else
         {
         Time0=Time[1];
         return false;
         }
      }
   else return false;
   }

La función supone tanto un predicado como un importante elemento de lógica en el que los 4 spreads se determinan finalmente en todos los puntos importantes de las barras. Todo esto se implementa de manera similar dentro del programa. En el procesador OnTick, todo funciona de manera muy simple:

RecalcHighLowSpreads();
if ( bNewBar()) WriteBar();

En el archivo con la cotización, todo esto se verá ahora así:

Bar Structure

Dentro del programa, la matriz con los precios promedio se implementa de forma absolutamente idéntica:

OpenX[1]=Open[1]+(double(PrevSpread)/2.0)*_Point;
CloseX[1]=Close[1]+(double(Spread)/2.0)*_Point;
HighX[1]=High[1]+(double(PrevHighSpread)/2.0)*_Point;
LowX[1]=Low[1]+(double(PrevLowSpread)/2.0)*_Point;

Con respecto a la implementación de todo este enfoque en los asesores expertos, a primera vista parece posible implementar la supresión del ruido de propagación de forma idéntica, pero el problema es que para ello será necesario recopilar una cierta cantidad de ticks, y cuanto mayor sea el marco temporal, más ticks necesitaremos recolectar por barra, y cuantos más ticks tengamos, más tiempo necesitaremos para recopilarlos. Si los precios "Ask", o al menos los spreads, se mantuvieran también en las barras, sería algo fácil de hacer, pero, en este caso, resulta más fácil de calcular según los precios "Bid".

Como opción adicional, hemos añadido un mecanismo para considerar el spread a la hora de optimizar los resultados. Como han demostrado las pruebas, este mecanismo es completamente opcional, pero con la suficiente potencia informática, podría dar muy buenos resultados. El asunto es que sea posible pedir al algoritmo que las órdenes se puedan abrir y cerrar solo si el spread no supera el valor requerido. Gracias a que la versión actual de la solución también registra los spreads en los datos de la barra, podemos controlar fácilmente este valor y calcular los verdaderos indicadores de prueba menos el spread.


Variación del lote en secciones breves

En realidad, la variación de lote se puede usar no solo en secciones muy cortas, sino también en secciones largas, pero solo en una de las variedades. Solo existen 2 mecanismos para gestionar riesgos o lotes, cada cual lo que quiera.

  • Incremento de lotes
  • Disminución de lotes

El primer mecanismo debe usarse con el comercio invertido, cuando apostamos a que la señal virará pronto. La segunda variante solo funciona si sabemos que la señal es estable y durará mucho tiempo. Podemos seleccionar una amplia variedad de funciones de control. Hemos tomado las más simples, las lineales. En otras palabras, los lotes cambian en función del tiempo. Ahora mostraremos cómo funcionan estos mecanismos en la práctica.

Para ello, hemos tomado la cotización M15 para la pareja USDJPY. En este caso, hemos elegido la versión MQL4 del robot para la demostración, ya que este robot no pasa backtests en ticks reales, porque comercia con los puntos de mayor spread. Queríamos demostrar que una fuerza bruta con una calidad lo suficientemente alta en marcos temporales bajos puede ofrecer un buen periodo forward con un tamaño igual al área en la que se ha aplicado la fuerza bruta y superior. Teniendo en cuenta que las capacidades del autor ahora son extremadamente limitadas, hemos tenido que dedicar muy poco tiempo a este tema. Pero este resultado resulta suficiente para mostrar un periodo forward bastante largo y dos mecanismos para trabajar con lotes en estas secciones forward. Vamos a comenzar mostrando la variante encontrada en el área donde hemos realizado la búsqueda. Tamaño de la parcela — un año:

USDJPY M15 bruteforce piece of history

La esperanza matemática aquí es un poco superior a 12 pips, si no consideramos el spread, pero este no nos interesa particularmente, ya que nos estamos abstrayendo de él. Echemos un vistazo al factor de beneficio. La prueba para un año en el futuro se ve así:

1 year to future

Resulta interesante que, a pesar de que la búsqueda se ha hecho solo de un año, la duración del trabajo ha sido de al menos 1 año, lo cual es muy bueno. En la práctica, esto significa que si tenemos un buen hardware, podemos analizar todas las principales parejas de divisas con spreads bajos en una semana o dos, luego seleccionar las mejores variantes y explotar el patrón durante al menos otro año. Por supuesto, siempre que haya resistido el backtest con ticks reales en MetaTrader 5. Obviamente, para confirmar con rigor esta suposición, necesitaremos mucho más tiempo, así como la presencia de una gran cantidad de buenos servidores y mucho tesón, pero podemos realizar este análisis con varias personas, de ser posible, sin tener que realizar ningún esfuerzo titánico, porque todo el trabajo lo hará una máquina, mientras que nuestra tarea consistirá solo recopilar los resultados y compilar las estadísticas.

Ahora, echemos un vistazo al backtest del futuro: al principio hay una reducción muy grande, que suele ser común cuando buscamos patrones en áreas pequeñas como un año; no obstante, podemos usar esta reducción para nuestros propios fines. En el backtest de un año, dicha área está resaltada con un marco rojo. Hemos limitado el tiempo de trabajo del asesor en la configuración para que comercie exactamente 50 días hábiles (el sábado y el domingo no se incluyen) y también hemos invertido la señal para que la reducción se convierta en un beneficio. Lo hemos hecho así para cortar la parte del gráfico que comienza a desplazarse hacia el beneficio después de una reducción determinada, ya que se volverá negativa cuando vire. Como resultado, el backtest será así:

invert + fix lots 50 days to future

Preste atención al factor de beneficio. Precisamente este vamos a incrementar. Nunca sabemos realmente si habrá un viraje y hasta dónde llegará, pero generalmente se revierte una parte bastante grande del movimiento que ha ocurrido en el segmento de fuerza bruta. Si aplicamos un beneficio lineal del lote desde el mínimo al máximo, obtendremos un backtest así, además de un aumento en el factor de beneficio:

invert + increase lots 50 days to future

Ahora, mostraremos el mecanismo inverso: en el backtest de 1 año en el futuro, está resaltado en verde. En esta sección, precisamente hay un segmento ascendente bastante grande y, al final, una inversión del patrón. Vamos a corregir esta situación con la ayuda de lotes decrecientes. Hemos configurado los ajustes del robot para que el comercio llegue directamente hasta el borde de dicha área. Primero, realizaremos las pruebas con un lote fijo: así tendremos algo para comparar el factor de beneficio resultante obtenido después de aplicar nuestro segundo mecanismo:

Green box fix lot

A continuación, activamos el mecanismo que implementa la disminución de lotes a lo largo del tiempo, y también obtenemos un aumento en el factor de beneficio, solo que el gráfico se vuelve mucho más agradable y suave; además, nos hemos deshecho de la reducción al final:

GreenBox + lot decrease

Hemos visto estas técnicas en los robots de muchos vendedores. Si los aplicamos en el momento y el lugar adecuados, podremos aumentar la rentabilidad y reducir las pérdidas. Obviamente, en teoría todo resulta hermoso, pero en la práctica, la cosa se complica. No obstante, estos mecanismos ya están presentes en los asesores que genera nuestro programa, y ​​se pueden activar y desactivar dependiendo de la situación.


Variantes de trabajo con la historia global

Creo que mucha gente estaría interesada ​en ver con sus propios ojos y probar algunas variantes para las configuraciones de trabajo capaces de superar backtests globales en la historia real de ticks. Hemos encontrado algunas de estas. Como nuestra potencia de cálculo es limitada y solo realizamos fuerza bruta, estas búsquedas han requerido bastante tiempo. No obstante, ya hemos logrado encontrar algunas variantes, aquí están:

USDCAD H1 2010-2020

USDJPY H1 2017-2021

EURUSD H1 2010-2021

Adjuntaremos estas configuraciones al artículo para que todos puedan ponerlas a prueba de forma independiente, si así lo desean. Y, por supuesto, nadie nos impide buscar nuestra propia configuración y hacer comprobaciones con backtests en las cuentas demo. En general, partiendo de esta versión de la solución, cualquiera puede probar este método.


Matemáticas de la fuerza bruta

Debemos abarcar esta sección con el mayor detalle posible para que cualquier usuario tenga claro al máximo el funcionamiento de este método. Comenzaremos por el funcionamiento de la primera pestaña del programa y la interpretación de sus resultados.

Fuerza bruta en la primera pestaña

En realidad, todo lo que sucede en cualquier algoritmo de fuerza bruta siempre obedece a la teoría de la probabilidad, y todo porque siempre tenemos algún tipo de modelo y siempre hay existe una iteración. Entendemos la iteración como un ciclo completo de análisis de la variante de estrategia actual. Un ciclo completo puede constar de una prueba o de diez; todo depende del enfoque específico. No es tan importante cuántas pruebas y manipulaciones realiza el algoritmo del método con una variante: todo esto se puede clasificar como una iteración. La iteración puede considerarse exitosa o no exitosa, según las exigencias respecto al resultado. Como criterios de un resultado positivo pueden actuar varios indicadores cuantitativos, todo depende, nuevamente, de cuál sea el método de análisis.

En la salida del algoritmo, siempre hay una o varias variantes que se ajustan a nuestros requisitos. Solo podemos decirle al algoritmo cuántos datos de loa resultados almacenar en la memoria. Todos los demás resultados que cumplan con los requisitos, pero que no tengan suficiente espacio de almacenamiento, se descartarán sin remedio. No importa cuántos grados haya en nuestra fuerza bruta, este proceso siempre tendrá lugar, simplemente porque el mismo es inevitable. En caso alternativo, nos enfrentaremos a una situación en la que corremos el riesgo de perder tiempo procesando datos de una calidad deliberadamente baja. Así, no omitiremos ni una sola variante, pero podremos reducir la velocidad de búsqueda en un orden de magnitud. En última instancia, dependerá del enfoque que decidamos usar.

Ahora, entraremos directamente en materia. Cualquier proceso de búsqueda de resultados se reduce en última instancia a un proceso de prueba independiente según el esquema de Bernoulli, obviamente, si el algoritmo es fijo. Todo dependerá de la probabilidad de obtener una buena variante. Esta probabilidad siempre será fija para un algoritmo fijo. En nuestro caso, esta probabilidad dependerá de los siguientes valores:

  • El tamaño de la muestra
  • La variabilidad del algoritmo
  • La proximidad respecto a la base
  • La rigidez de los requisitos respecto al resultado final.

En este sentido, la cantidad y calidad de los resultados obtenidos aumentará con el número de iteraciones, en riguroso acuerdo con la fórmula de Bernoulli. ¡Pero vale la pena recordar que este es un proceso puramente probabilístico! Es decir, resulta imposible predecir con un 100% de probabilidad qué conjunto de resultados obtendremos, solo podemos calcular la probabilidad de hallar el resultado deseado:

  • Pk - probabilidad de que la iteración produzca una varinte funcional con los requisitos dados (dependiendo de los requisitos, esta probabilidad aumentará considerablemente)
  • C(n,m) - número de combinaciones de "n" a "m"
  • Pa=Suma(m0...m...n)[C(n,m)*Pow(Pk ,m)*Pow(1-Pk ,n-m)] - probabilidad de que después de "n" iteraciones obtengamos al menos "m0" variantes primarias que satisfagan nuestros requisitos
  • m0 — número mínimo de prototipos satisfactorios
  • Pa — probabilidad de que, como resultado del desarrollo, obtengamos al menos "m0" o más de "n" iteraciones dentro del software.
  • n — número máximo disponible de ciclos de búsqueda para prototipos en funcionamiento (cuánto tiempo estamos preparados para esperar los resultados)

También podemos expresar el número de ciclos en términos temporales, si tomamos la velocidad de la fuerza bruta del contador en la primera pestaña y el tiempo que estamos preparados para dedicar al procesamiento de los datos actuales:

  • Sh - velocidad de iteraciones por hora
  • T - tiempo en horas que estamos listos para esperar
  • n = Sh*T

De la misma forma, podemos calcular la probabilidad de encontrar variantes sujetas a requisitos de calidad. En la parte superior se encontraban las fórmulas para encontrar fácilmente las variantes que entran en el filtro "Deviation", este el requisito para la linealidad del resultado. Si este filtro no está activado, cada iteración resultará exitosa y siempre se encontrará alguna variante, sin importar las métricas halladas. Las variantes se ordenarán según la puntuación de la calidad. Sujeto a los requisitos de calidad, el valor "Ps" será la función de la magnitud de la calidad tomada según el módulo. Cuanto mayor sea la calidad necesaria, menor será el valor de esta función:

  • Ps - probabilidad de obtener un resultado con ciertos requisitos de calidad adicionales
  • q - calidad requerida que deseamos obtener
  • qMax - calidad máxima disponible
  • Ps = Ps(|q|) = K * Px(|q|) , q <= qMax
  • K = Pk - este coeficiente tiene en cuenta la probabilidad de obtener cualquier variante arbitraria (y ya entre ellas se seleccionan las variantes según la calidad)
  • Ps ' (|q|) < 0
  • Lim (q-->qMax) [  Ps(|q|) ] = 0

La primera derivada de esta función es negativa, simbolizando que, a medida que aumentan los requisitos, la probabilidad de que estos se cumplan tiende a cero. Cuando "q" tiende al valor máximo disponible, el valor de esta función tenderá a "0", ya que se trata de una probabilidad. Si "q" es superior al valor máximo, esta función no tendrá sentido, ya que una calidad superior es inalcanzable para el algoritmo elegido. Esta función es consecuencia de la función de densidad de la probabilidad de la variable aleatoria "q". A continuación, representamos Ps(q) y la densidad de probabilidad de la variable aleatoria P(q), así como varias magnitudes adicionales importantes para la compresnión general:

Variety

Basándonos en estas ilustraciones, podemos escribir:

  • Integral(q0,qMax) [P(q)] = Integral(-qMax,-q0) [P(q)] =  K*Px(|q|) = Ps(|q|)  - probabilidad de que con la iteración actual obtengamos una variante con |q| de q0 a qMax.
  • Integral(q1,q2) [P(q)] - probabilidad de que, como resultado de la iteración, obtengamos un valor de calidad en el rango de q1 a q2 (solo para tener un ejemplo sobre cómo interpretar las lecturas de la función de distribución de una variable aleatoria)

Podemos ver que cuanta más calidad necesitemos, más tiempo deberemos invertir y menos variantes de este tipo encontraremos, dependiendo de nuestros deseos. Además, cualquier método tiene un límite superior en cuanto a la magnitud de la calidad; esta depende tanto de los datos que analizamos como de la perfección de nuestro método.

Optimización en la segunda pestaña

El proceso de optimización en la segunda pestaña es ligeramente distinto del proceso de búsqueda principal, pero en general, se toma como base la misma iteración y la probabilidad de obtener una variante que cumpla con nuestros requisitos. Aquí hay muchos más filtros y, como consecuencia, la probabilidad de obtener buenos resultados también es menor. Pero, como las variantes ya procesadas se mejoran en la segunda pestaña, cuanto mejores y más hermosas sean las variantes de la primera, mayor será la probabilidad de conseguir mejores indicadores en la segunda pestaña. El proceso de mejora de una variante específica ya no puede describirse con tanta simpleza mediante la ecuación de Bernoulli; no obstante, la fórmula final será algo similar. Nos interesará la probabilidad de obtener al menos una variante que se encuentre dentro de nuestros filtros. Vamos a describirlo todo:

  • Py = Suma(1...m...n)[ Suma(0... i ... C(n,m)-1) {  Producto(0 .. j .. i-1 )[Pk[j]) * Producto(i .. j .. m) [1 - Pk[j]] } ] - probabilidad de obtener al menos una variante satisfactoria que cumpla con los requisitos de los filtros
  • Pk[i] - probabilidad de obtener una variante que cumpla con los requisitos de los filtros de la segunda pestaña
  • n - dividimos el intervalo de optimización (valor Interval Points en la segunda pestaña)

La optimización sucede exactamente de la misma forma que en los optimizadores de los terminales MetaTrader 4 y MetaTrader 5, con la única diferencia de que solo se optimiza un parámetro, a saber, nuestra señal de compra o venta. El salto de optimización se calcula automáticamente según el número de partes en que dividamos el intervalo de optimización (Interval Points). El valor superior del número que estamos optimizando se calcula durante el proceso de búsqueda en la primera pestaña. Una vez haya tenido lugar el proceso en la primera pestaña, conoceremos el rango de fluctuaciones en los valores del número optimizado, y solo necesitaremos establecer en la segunda pestaña la precisión de la cuadrícula para dividir este intervalo. Como resultado de la optimización, esta variante sigue ocupando 1 espacio de variantes en la segunda pestaña; simplemente se actualizará a medida que obtenga una mejor calidad. 

De la misma manera que en la primera pestaña, la probabilidad de obtener alguna variante con los requisitos de calidad también tendrá alguna función de distribución similar a la que fue mayor. En consecuencia, esto significa que podemos aplicar las mismas fórmulas a este proceso, con una ligera diferencia:

  • Integral(q0,qMax) [P(q)] = Integral(-qMax,-q0) [P(q)] =  K*Px(|q|) = Pz(|q|)  - probabilidad de que con la iteración actual obtengamos una variante con |q| de q0 a qMax.
  • K = Py

La única diferencia aquí es el coeficiente "K", que es igual a la nueva probabilidad que obtuvimos un poco más arriba. La probabilidad de no cumplir con la calidad requerida de una variante es insignificante, pero, después de todo, tenemos muchas de esas variantes en la primera pestaña, y cuantas más haya, mejor para nosotros. De ahí, incluso a un nivel intuitivo, podemos deducir que cuantas más variantes primarias haya, más y mejores variantes aparecerán en la segunda pestaña. Todo esto se considera de forma análoga. Desafortunadamente, no podemos aplicar aquí la fórmula de Bernoulli, pero la construcción anterior, que la reemplazó, sí que es aplicable. Aquí ya interpretamos la optimización de una variante como una iteración separada. El número de iteraciones, por consiguiente, será exactamente igual a este número. Necesitamos al menos una variante que cumpla con nuestros requisitos, por lo que la fórmula anterior resulta ideal para nosotros. Lo único es que, por supuesto, necesitaremos reemplazar el valor Pk por Pz, que estará determinado por la familia de funciones Pz[j](|q|), pues para cada variante de optimización existe una función propia semejante gracias a que las variantes son diferentes.  

  • Pb = Suma(1...m...n)[ Suma(0... i ... C(n,m)-1) {  Producto(0.. j .. i-1 )[Pz[j]) * Producto(i.. j .. m) [1 - Pz[j]] } ]
  • n - número de variantes encontradas en la primera pestaña

La conclusión es que cuanto más apliquemos la fuerza bruta, más calidad obtendremos, obviamente, sin olvidarnos de que todos los parámetros influyen tanto en las probabilidades como en el resultado. Debemos establecer la configuración con prudencia y no desperdiciar recursos informáticos. Las máquinas modernas son muy potentes, pero no debemos olvidar que la configuración competente y el conocimiento de los detalles incrementarán la eficacia de nuestras máquinas de forma sustancial, en algunos casos, decenas de veces.


Acerca de la vinculación a la historia y el reentrenamiento

La llamada "vinculación a la historia" o "reentrenamiento" supone un problema considerable de un gran número de sistemas de comercio automático. De hecho, aunque los conceptos son diferentes, en realidad indican lo mismo. Podemos crear un sistema comercial que muestre un rendimiento increíble, hasta de un 1000 por ciento mensual, pero, en la realidad, tales sistemas nunca funcionarán. Algunos usuarios ganarán dinero y estarán increíblemente felices, mientras que otros maldecirán el sistema comercial llenos de ira. Este hecho no es sorprendente. El comportamiento de las personas, especialmente de los compradores, se presta al análisis estadístico y encaja muy bien en la teoría de la probabilidad.

Vayamos al grano. Cuantos más parámetros de entrada tenga un sistema comercial y mayor sea la variabilidad de la lógica del asesor, mejor se vinculará dicho asesor a la historia. El caso es que tenemos un proceso muy sencillo para transformar una cotización en otro formato de datos. Siempre hay funciones de conversión hacia delante y hacia atrás que pueden ofrecer un proceso de conversión de datos hacia delante y hacia atrás. Podemos comparar esto con el cifrado y el descifrado. Por ejemplo, el archivo WinRar es un ejemplo típico de cifrado si establecemos una contraseña. También tiene compresión, pero la omitiremos. En el contexto de nuestra tarea, el algoritmo de cifrado sería una combinación del proceso de optimización y la presencia de la lógica comercial. Un número suficiente de backtests en el optimizador y una cierta lógica flexible son capaces de crear un milagro: el Grial de pruebas. La propia lógica comercial será el mismo decodificador que decodificará los precios futuros basándose en las lecturas del pasado.

Desafortunadamente, todos los asesores expertos son, hasta cierto punto, el Grial de las pruebas: la única diferencia está en el grado en que estos su vinculan a la historia. Pero, como existe una parte de la lógica que se vincula, también existirá una parte que debería conservar parte de la funcionalidad en el futuro. En la realidad, un algoritmo así es algo extremadamente difícil de obtener. La dificultad radica en que no sabemos cuáles son las posibilidades máximas de "predicción justa" de un algoritmo en particular y, como resultado, no podemos determinar los límites del reentrenamiento. Para abstraernos todo lo posible de este proceso negativo, deberemos conseguir el algoritmo con mayores probabilidades de predecir el movimiento de la próxima vela, y cuanto mayor sea el grado de compresión de los datos de precios, más fiabilidad ofrecerá este algoritmo. Por ejemplo, tomemos alguna función como sin(w*t). Sabemos que esta función se corresponde con un número infinito de puntos [X[i],Y[i]]; se trata de una matriz de datos de longitud infinita que es comprimida en un registro corto de función sinusoidal. En este caso, tendremos una compresión de datos perfecta. En la realidad, dicha compresión resulta imposible y siempre tenemos algún tipo de coeficiente de compresión de datos. Cuanto mayor sea este coeficiente, mayor será la calidad con la que se define la fórmula de mercado.

En nuestro método, la cantidad de datos variables es fija, pero, al igual que sucede en cualquier otro método, la vinculación a la historia resulta posible. La única forma de luchar con la vinculación a la historia es aumentar el coficiente de compresión de datos. Esto se logra solo aumentando el tamaño de la sección de la historia que analizamos. También hay una segunda forma: reduciendo el número de barras analizadas en la fórmula (Bars To Equation). En realidad, lo mejor es recurrir al primer método, porque reduciendo el número de barras en la fórmula, reduciremos conscientemente el límite superior de "qMax", y lo que querríamos es aumentarlo. Resumiendo, es mejor usar una muestra de entrenamiento grande, por lo que no ahorrar "Bars To Equation". Además, debemos recordar que un aumento excesivo en este valor reducirá la velocidad de nuestra fuerza bruta, creando un riesgo inevitable de que aumente el indicador de vinculación a la historia.


Recomendaciones de uso

Durante la puesta a prueba de la solución, hemos identificado algunos matices importantes a la hora de configurar el programa principal Awaiter.exe. Vamos a intentar enumerar los matices más importantes a continuación:

  1. Una vez hayamos establecido los ajustes en todas las pestañas de la manera deseada, deberemos guardarlos (botón Save Settings)
  2. En la segunda pestaña, podemos activar el Spread Control
  3. Cuando el asesor HistoryWriter genere las cotizaciones, usaremos la mayor muestra posible (al menos 10 años de historia)
  4. En la primera pestaña, podemos guardar más variantes, 1000 serán suficientes ( Variants Memory )
  5. En la pestaña de optimización, no debemos establecer demasiados Interval Points (20-100 serán suficientes)
  6. Si queremos obtener configuraciones más o menos normales, con la posibilidad de superar el backtest con ticks reales, no deberíamos pedirle al optimizador un gran número de órdenes en las variantes (Min Orders)
  7. Debemos controlar la velocidad de búsqueda de las variantes (si hemos estado buscando durante mucho tiempo y las variantes no se han encontrado en absoluto, deberíamos pensar en cambiar la configuración)
  8. Para obtener resultados más estables, deberemos establecer Deviation en el intervalo "0.1 - 0.2"; mejor 0.1
  9. Cuando utilicemos la ecuación "FOURIER" en la pestaña de optimización , utilizaremos la casilla de verificación "Spread Control" (la fórmula es muy delicada y extremadamente sensible al ruido del spread)


Conclusión

Como conclusión, diremos que, por supuesto, no debemos ver esta solución como el Grial. En realidad, es solo una herramienta. Resulta bastante difícil implementar una solución que sea lo suficientemente efectiva y al mismo tiempo adecuada para cualquier usuario, librando al mismo tiempo a este de cualquier tarea de programación y optimización en los terminales MetaTrader 4 y MetaTrader 5. No obstante, creemos que, aun así, la tarea mínima está completa, y la solución está lista para su uso general. Obviamente, desconocemos si gustará a alguien, pero en general eso no es tan importante.

Habrá personas que encontrarán útil este método. Por supuesto, aún hay mucho margen de mejora, y también imperfecciones y errores, pero en general ya tenemos una herramienta de trabajo tanto para investigar el mercado como para comerciar. La buena noticia es que ahora todo depende más de la potencia informática que de las mejoras. Aunque a nivel de preparación todo está todavía un poco crudo, esperamos que en el futuro todo esto se corrija.

También tenemos ideas sobre el proyecto que esperamos ir implementando poco a poco en el tiempo libre. Una de estas ideas es la construcción de un polinomio lógico basado en los indicadores de oscilador y los indicadores de precios más populares, como las Bandas de Bollinger o la Media Móvil. Pero debemos pensar este concepto cuidadosamente antes de lanzarnos a ello. No quisiéramos rebajarnos al nivel de "los indicadores se han cruzado, vamos a realizar una transacción", pero aún podemos aplicar correctamente las señales de los indicadores: tenemos algunas ideas. También esperamos haber podido aportar algo nuevo e información general útil para los lectores, algo valioso, si no en términos prácticos, al menos en teoría.


Enlaces a los artículos anteriores de la serie

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

Archivos adjuntos |
Awaiter_Project.zip (5961.38 KB)
Redes neuronales: así de sencillo (Parte 13): Normalización por lotes (Batch Normalization) Redes neuronales: así de sencillo (Parte 13): Normalización por lotes (Batch Normalization)
En el artículo anterior, comenzamos a analizar varios métodos para mejorar la calidad del aprendizaje de la red neuronal. En este artículo, proponemos al lector continuar con este tema y analizar la normalización por lotes de los datos, un enfoque muy interesante.
Otras clases en la biblioteca DoEasy (Parte 66): Clases de Colección de Señales MQL5.com Otras clases en la biblioteca DoEasy (Parte 66): Clases de Colección de Señales MQL5.com
En este artículo, crearemos una clase de colección de señales del Servicio de señales de MQL5.com con funciones para gestionar las señales suscritas, y también modificaremos la clase del objeto de instantánea de la profundidad de mercado para mostrar el volumen total de la profundidad de mercado de compra y venta.
Plantilla para proyectar el MVC y posibilidades de uso Plantilla para proyectar el MVC y posibilidades de uso
En el artículo, analizaremos una plantilla de MVC bastante extendida. Asimismo, estudiaremos sus posibilidades y las ventajas y desventajas de su uso en los programas MQL. Su esencia consiste en "dividir" el código existente en tres componentes separados: Modelo (Model), Vista (View) y Controlador (Controller).
Redes neuronales: así de sencillo (Parte 12): Dropout Redes neuronales: así de sencillo (Parte 12): Dropout
A la hora de proseguir el estudio de las redes neuronales, probablemente merezca la pena prestar un poco de atención a los métodos capaces de aumentar su convergencia durante el entrenamiento. Existen varios de estos métodos. En este artículo, proponemos al lector analizar uno de ellos: el Dropout (dilución).