English Русский 中文 Deutsch 日本語 Português
preview
Características del Wizard MQL5 que debe conocer (Parte 22): Redes generativas adversativas (RGAs) condicionales

Características del Wizard MQL5 que debe conocer (Parte 22): Redes generativas adversativas (RGAs) condicionales

MetaTrader 5Sistemas comerciales |
328 0
Stephen Njuki
Stephen Njuki

Introducción

Las redes generativas adversarias condicionales son un tipo de RGAs que permiten la personalización del tipo de datos de entrada en su red generativa. Como se puede ver en el enlace compartido y al leer sobre el tema, las GAN son un par de redes neuronales: un generador y un discriminador. Ambos se entrenan o se entrenan entre sí, y el generador mejora en la generación de una salida objetivo mientras que el discriminador se entrena en la identificación de datos (también conocido como los datos falsos) del generador.

La aplicación de esto es típicamente en el análisis de imágenes, donde se utiliza una red generadora para generar imágenes y la red discriminadora identifica si la imagen que se le suministra como entrada fue creada por la red generadora o es real. El entrenamiento mutuo se realiza alimentando las imágenes del generador discriminador alternadas con imágenes reales y, como en cualquier red, la retropropagación ajustaría apropiadamente los pesos del discriminador. Por otro lado, el generador, en configuraciones no condicionales o típicas, recibe datos de entrada aleatorios y se supone que debe generar imágenes lo más realistas posible, independientemente de esto.

En una configuración RGA condicional, hacemos una ligera modificación: alimentamos la red generativa con un cierto tipo de datos como entrada y no datos aleatorios. Esto es aplicable o útil en situaciones donde el tipo de datos que alimentamos al discriminador están emparejados o en 2 partes y el objetivo de la red discriminadora es determinar si los datos emparejados de entrada son válidos o inventados.

La mayoría de las aplicaciones RGA y RGA condicionales parecen estar en el reconocimiento o procesamiento de imágenes, pero en este artículo exploramos cómo se puede construir un modelo muy simple para el pronóstico de series de tiempo financieras en torno a ellas. Como se menciona en el título, adoptaremos un RGA condicional en lugar de un RGA. Para ilustrar la diferencia entre ambos, podemos considerar los dos diagramas siguientes:

Fuente



Fuente

Ambas imágenes, cuyos enlaces de origen se comparten arriba, apuntan a configuraciones donde la salida de una red generadora se alimenta a una red discriminadora para pruebas o verificación. Las RGAs son adversarias en el sentido de que el generador está entrenado para mejorar en engañar al discriminador, mientras que el discriminador está entrenado para mejorar en identificar la salida del generador a partir de datos de red reales o que no son del generador. Sin embargo, la principal diferencia entre estas dos configuraciones es que con las RGAs la red generadora toma datos de entrada aleatorios y los utiliza para generar datos que el discriminador no puede distinguir de los datos reales. Para nuestros propósitos en la previsión de series de tiempo financieras, esto tendrá una aplicabilidad y un uso limitados.

Sin embargo, con las RGAs lo que se denomina ruido en el diagrama son esencialmente datos independientes o datos para los que el generador está intentando generar una etiqueta (en nuestro caso de adaptación). No estamos ingresando etiquetas a la red del generador como se muestra en el diagrama anterior, sin embargo, la red discriminadora recibe un emparejamiento de datos de ruido (o datos independientes) y su etiqueta respectiva, y luego intenta determinar si este emparejamiento proviene de datos reales o si la etiqueta asignada a los datos independientes provenía de un generador.

¿Cuáles son los beneficios de las RGAs condicionales para la previsión de series temporales financieras? Bueno, la prueba está en el pudín, como se suele decir, por lo que realizaremos algunas pruebas al final de este artículo, como es habitual. Sin embargo, en el reconocimiento de imágenes, las RGA tienen ciertamente cierto peso, aunque no les vaya tan bien como a las redes neuronales convolucionadas o los transformadores de visión debido a su gasto en computación. Sin embargo, se dice que son mejores en la síntesis y el aumento de imágenes.


Configuración del entorno

Para construir nuestro modelo RGA condicional, utilizaremos la red de perceptrones multicapa introducida en este artículo como clase base. Esta clase base representa todas las 'herramientas y bibliotecas' que necesitaríamos para poner en funcionamiento nuestro RGA condicional, ya que tanto la red generadora como la red discriminadora simplemente serán tratadas como instancias de un perceptrón multicapa. Esta clase base tiene simplemente 2 funciones principales: el método de propagación hacia adelante 'Forward()' y la función de propagación hacia atrás 'Backward()'. Por supuesto, existe el constructor de clase que toma las configuraciones de red y algunos métodos de mantenimiento que permiten guardar los pesos entrenados como un archivo, además de algunas otras funciones que establecen objetivos de entrenamiento y leen los resultados de avance.

A pesar de utilizar nuestra clase base de perceptrón multicapa típica para este RGA condicional, necesitamos realizar algunos cambios específicos de GAN en la forma en que la red generativa realiza su retropropagación o aprende. El generador de pérdidas se puede calcular a partir de la siguiente fórmula:

−log(D(G(zy)y))

Donde:

  • 'D()' es la función de salida del discriminador.
  • 'G()' es la función de salida del generador.
  • 'z' son los datos independientes.
  • 'y' es el dato dependiente o de etiqueta o pronóstico.

Entonces, este valor del generador de pérdida, que normalmente estaría en forma de vector dependiendo del tamaño de salida, actuaría como una ponderación del valor de error de cada paso hacia adelante al iniciar la propagación hacia atrás. Realizamos estos cambios en nuestra clase de red de la siguiente manera:

//+------------------------------------------------------------------+
//| Backward pass through the neural network to update weights       |
//| and biases using gradient descent                                |
//+------------------------------------------------------------------+
void Cgan::Backward(vector<double> &DiscriminatorOutput, double LearningRate = 0.05)
{  if(target.Size() != output.Size())
   {  printf(__FUNCSIG__ + " Target & output size should match. ");
      return;
   }
   if(ArraySize(weights) != hidden_layers + 1)
   {  printf(__FUNCSIG__ + " weights matrix array size should be: " + IntegerToString(hidden_layers + 1));
      return;
   }

        ...

// Update output layer weights and biases
   vector _output_error = -1.0*MathLog(DiscriminatorOutput)*(target - output);//solo modification for GAN
   Back(_output_error, LearningRate);
}

Nuestra función 'Backward()' se sobrecarga ya que una variante normalmente toma la salida del discriminador como entrada y ambas funciones sobrecargadas luego llaman a una función 'Back()' que esencialmente tiene la mayor parte del código que teníamos en la antigua función de retropropagación y se introduce aquí para reducir la duplicidad. Sin embargo, lo que hace esta ponderación es garantizar que, al entrenar al generador, no solo estemos mejorando en predecir cuál será el próximo cambio de precio de cierre que debemos pronosticar, sino que también estemos "mejorando" en engañar al discriminador haciéndole creer que los datos del generador son reales. Mientras tanto, el discriminador se está entrenando "en la dirección opuesta" al intentar ser bueno en distinguir los datos del generador de los datos reales.

Por el contrario, si tuviéramos que implementar esta configuración en una aplicación de terceras partes, definir una red similar con flujo de sensores en Python requeriría añadir cada capa con un comando independiente, o una línea de código. Esta opción de Python, por supuesto, proporciona más personalizaciones que nuestra clase básica no ofrece, pero como herramienta de creación de prototipos para dar una vuelta a los RGA condicional en el entorno MQL5 no debería ser la opción preferida. Por no mencionar que el uso de Python y de cualquiera de sus bibliotecas de redes neuronales requiere disponer de «adaptadores» como ONNX o una implementación personalizada equivalente que permita exportar los resultados del entrenamiento de nuevo a MQL5. Éstas tienen ciertamente sus ventajas cuando el modelo está diseñado para ser entrenado una vez en desarrollo y luego desplegado o para ser entrenado periódicamente, pero cuando está fuera de línea (no desplegado).

En escenarios en los que el entrenamiento de una red neuronal tendría que ser en vivo o realizarse durante el despliegue, entonces los numerosos «adaptadores» hacia y desde python pueden llegar a ser poco manejables, aunque sigue siendo posible.


Diseño de la clase de señal personalizada

Las clases de señales, como hemos visto a lo largo de la serie, cuentan con funciones estándar para la inicialización, la validación y la evaluación de las condiciones del mercado. Además, se le puede añadir un número ilimitado de funciones para personalizar la señal propia, ya sea con un indicador personalizado o con una combinación de indicadores típicos ya disponibles en la biblioteca MQL5. Puesto que estamos construyendo un RGA condicional que se basa en perceptrones multicapa, comenzaremos con un número adicional de funciones similares a las que adoptamos en este artículo anterior que también utilizaba nuestra clase base perceptrón.

Serán las funciones 'GetOutput()', 'Setoutput()' y 'Norm()'. Su papel aquí será muy similar al que teníamos en ese artículo anterior, en el sentido de que la función 'get' será la función de anclaje encargada de determinar las condiciones del mercado, mientras que la función set estará disponible, como antes, para escribir los pesos de la red después de cada pasada de entrenamiento, mientras que la función norm desempeña el papel crucial de normalizar nuestros datos de entrada antes de alimentarlos.

Hay 3 nuevas funciones adicionales que introducimos para la clase de señal personalizada RGA condiconales y que tienen que ver con la separación del procesamiento de la red generadora de la red discriminadora.

La arquitectura de la red generativa se elige arbitrariamente con 7 capas, de las cuales hay una capa de entrada, 5 capas ocultas y una capa de salida. La determinación adecuada de esto se puede hacer con una búsqueda de arquitectura neuronal que vimos en este artículo mencionado anteriormente, pero para nuestros propósitos aquí estos supuestos serán suficientes en la demostración de un RGA condicional. Estos ajustes de red se definen en una matriz que utilizamos para inicializar una instancia de una clase de red, a la que denominamos «GEN».

Nuestra red generativa tendrá como entradas los cambios anteriores en el precio de cierre y como salida un único cambio previsto también en el precio de cierre. Esto no difiere mucho de la implementación que tenemos cuando examinamos la búsqueda de arquitectura neuronal en el artículo ya referenciado. La previsión de salida será el cambio en el precio de cierre que sigue a los 4 cambios que sirven como entradas.

Así pues, el emparejamiento de estos 4 cambios previos con el valor previsto es lo que constituirá los datos de entrada a la red discriminadora, que veremos más adelante. La clase base de red que utilizamos realiza su activación por softplus que es fija. Dado que se proporciona el código fuente completo, los lectores pueden adaptarlo fácilmente a lo que convenga a su configuración. Los únicos parámetros ajustables que tomará nuestra clase de señal serán, por tanto, la tasa de aprendizaje, el número de épocas de entrenamiento y el tamaño del conjunto de datos de entrenamiento. Se les asignan los nombres 'm_learning_rate', 'm_epochs' y 'm_train_set' respectivamente. Dentro de la función get output, así es como cargamos los datos de entrada de la red, la alimentamos y la entrenamos en cada nueva barra:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSignalCGAN::GetOutput(double &GenOut, bool &DisOut)
{  GenOut = 0.0;
   DisOut = false;
   for(int i = m_epochs; i >= 0; i--)
   {  for(int ii = m_train_set; ii >= 0; ii--)
      {  vector _in, _out;
         vector _in_new, _out_new, _in_old, _out_old;
         _in_new.CopyRates(m_symbol.Name(), m_period, 8, ii + 1, __GEN_INPUTS);
         _in_old.CopyRates(m_symbol.Name(), m_period, 8, ii + 1 + 1, __GEN_INPUTS);
         _in = Norm(_in_new, _in_old);
         GEN.Set(_in);
         GEN.Forward();
         if(ii > 0)// train
         {  _out_new.CopyRates(m_symbol.Name(), m_period, 8, ii, __GEN_OUTPUTS);
            _out_old.CopyRates(m_symbol.Name(), m_period, 8, ii + 1, __GEN_OUTPUTS);
            _out = Norm(_out_new, _out_old);
            
                ...

         }
         else if(ii == 0 && i == 0)
         { 
                ...
         }
      }
   }
}

Nuestra RGA es condicional porque las entradas a la red generadora no son aleatorias y las de la red discriminadora son dobles, capturando la entrada al generador y su salida. Por lo tanto, la función de la red discriminadora es determinar si sus datos de entrada se obtienen de una secuencia de series en tiempo real de 5 cambios de precio de cierre consecutivos, o si es un emparejamiento de la entrada de datos de la red generadora con su salida. En otras palabras, determina si sus datos de entrada son “reales” o “falsos”, respectivamente.

Esto implica que la salida de la red discriminadora es muy simple: booleana. O bien los datos de entrada proceden en su totalidad de los mercados (verdadero) o fueron parcialmente creados por el generador (falso). Representamos esto con 1 y 0 respectivamente, y de las ejecuciones de prueba posteriores al entrenamiento, el valor devuelto es un número de punto flotante entre 0.0 y 1.0. Así pues, para entrenar nuestra red discriminadora la alimentaremos alternativamente con cambios de precios de cierre reales como 5 puntos de datos (siendo 5 cambios consecutivos) y con otros 5 cambios de precios de cierre de los cuales sólo 4 son reales y el quinto es la previsión de la red generadora. El entrenamiento de los datos reales se realiza en parte mediante la función «R» cuyo código figura a continuación:

//+------------------------------------------------------------------+
//| Process Real Data in Discriminator                               |
//+------------------------------------------------------------------+
void CSignalCGAN::R(vector &IN, vector &OUT)
{  vector _out_r, _out_real, _in_real;
   _out_r.Copy(OUT);
   _in_real.Copy(IN);
   Sum(_in_real, _out_r);
   DIS.Set(_in_real);
   DIS.Forward();
   _out_real.Resize(__DIS_OUTPUTS);
   _out_real.Fill(1.0);
   DIS.Get(_out_real);
   DIS.Backward(m_learning_rate);
}

Y para el entrenamiento de los datos falsos se utiliza la función «F», cuyo código también se facilita aquí:

//+------------------------------------------------------------------+
//| Process Fake Data in Discriminator                               |
//+------------------------------------------------------------------+
void CSignalCGAN::F(vector &IN, vector &OUT)
{  vector _out_f, _out_fake, _in_fake;
   _out_f.Copy(OUT);
   _in_fake.Copy(IN);
   Sum(_in_fake, _out_f);
   DIS.Set(_in_fake);
   DIS.Forward();
   _out_fake.Resize(__DIS_OUTPUTS);
   _out_fake.Fill(0.0);
   DIS.Get(_out_fake);
   DIS.Backward(m_learning_rate);
}

Estas dos funciones se llaman dentro de la función de salida 'get' como se muestra a continuación:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSignalCGAN::GetOutput(double &GenOut, bool &DisOut)
{  GenOut = 0.0;
   DisOut = false;
   for(int i = m_epochs; i >= 0; i--)
   {  for(int ii = m_train_set; ii >= 0; ii--)
      {  
                ...

         if(ii > 0)// train
         {  _out_new.CopyRates(m_symbol.Name(), m_period, 8, ii, __GEN_OUTPUTS);
            _out_old.CopyRates(m_symbol.Name(), m_period, 8, ii + 1, __GEN_OUTPUTS);
            _out = Norm(_out_new, _out_old);
            //
            int _dis_sort = MathRand()%2;
            if(_dis_sort == 0)
            {  F(_in, GEN.output);
               GEN.Get(_out);
               GEN.Backward(DIS.output, m_learning_rate);
               R(_in, _out);
            }
            else if(_dis_sort == 1)
            {  R(_in, _out);
               GEN.Get(_out);
               GEN.Backward(DIS.output, m_learning_rate);
               F(_in, GEN.output);
            }
         }
         else if(ii == 0 && i == 0)
         {  GenOut = GEN.output[0];
            DisOut = (((DIS.output[0] >= 0.5 && GenOut >= 0.5)||(DIS.output[0] < 0.5 && GenOut < 0.5)) ? true : false);
         }
      }
   }
}

Utilizamos la función 'Sum' para emparejar 4 cambios de precio de cierre con el siguiente cambio de precio de cierre en caso de que nos interese obtener datos reales, o con la previsión del generador si nos interesa obtener datos «falsos». Así pues, tras un entrenamiento posterior, el generador, como cabría esperar de cualquier perceptrón, se vuelve mejor a la hora de hacer previsiones que luego podemos utilizar para evaluar las condiciones del mercado. Pero, ¿qué hacemos entonces con los esfuerzos de formación del discriminador?

Bien, en primer lugar, como se ha mencionado anteriormente, el entrenamiento ayuda a afinar también los pesos de la red generatriz, ya que utilizamos el peso de la red generatriz para ajustar el valor de pérdida utilizado en la retropropagación de la red generatriz. En segundo lugar, después de que la red se haya entrenado y esté en despliegue, el discriminador puede seguir utilizándose para verificar las previsiones del generador. Si no es capaz de decir que están junto al generador, entonces sirve como confirmación de que nuestra red de generadores ha hecho bien su trabajo.


Integración de RGAs condicionales con la clase de señales de MQL5

Para que esto funcione dentro de una clase de señal necesitaríamos codificar las funciones de condición larga y corta para llamar a la función 'GetOutput()' que devuelve 2 cosas. El cambio estimado en el precio de cierre que es capturado por la variable doble «GenOut» y la variable booleana «DisOut» que mide si este cambio en la previsión de cierre fue capaz o no de engañar a la red discriminadora. El lector es libre de probar configuraciones en las que sólo se utilice la salida del generador para determinar las condiciones del mercado, como suele ocurrir en la generación de imágenes, que es el uso más común de las RGAs condionales. Sin embargo, el hecho de que la red discriminadora compruebe estas previsiones actúa como un paso seguro adicional en la evaluación de las condiciones, y por eso se incluye aquí.

Los valores de entrada de la red están todos normalizados para estar en el rango de -1.0 a +1.0, y del mismo modo, en su mayor parte, esperaríamos que las salidas estuvieran en un rango similar. Esto significa que nuestro generador nos está dando un cambio porcentual previsto en el precio de cierre. Como son porcentajes, podemos multiplicarlos por 100 para obtener un valor que no supere 100. El signo de este valor, ya sea positivo o negativo, indicaría si debemos posicionarnos en largo o en corto respectivamente. Entonces, para procesar las condiciones y obtener una salida entera en el rango 0 – 100 como se espera de las funciones de condición larga y corta, tendríamos nuestras funciones de condición larga como se indica a continuación:

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalCGAN::LongCondition(void)
{  int result = 0;
   double _gen_out = 0.0;
   bool _dis_out = false;
   GetOutput(_gen_out, _dis_out);
   _gen_out *= 100.0;
   if(_dis_out && _gen_out > 50.0)
   {  result = int(_gen_out);
   }
   //printf(__FUNCSIG__ + " generator output is: %.5f, which is backed by discriminator as: %s", _gen_out, string(_dis_out));
   return(result);
}

La condición corta es muy similar, con la excepción, por supuesto, de que el porcentaje previsto debe ser negativo para que al resultado se le asigne un valor distinto de cero y que este valor es la cantidad absoluta del porcentaje previsto después de la multiplicación por 100.


Pruebas y validación

Si realizamos ejecuciones de prueba con un Asesor Experto ensamblado a través del asistente MQL5 (las pautas para esto sonaquíyaquí) obtenemos los siguientes resultados en una de las ejecuciones:

r2

c2

Al procesar las señales para obtener estas ejecuciones, aleatorizamos el orden en el que se entrena la red discriminadora, es decir, a veces entrenamos primero con datos reales y otras veces entrenamos primero con datos falsos. Esto, a partir de las pruebas, hizo que la red discriminadora no se inclinara siempre hacia un solo lado, como estar solo en largo o solo en corto, porque tener un orden de prueba estricto sesga la red discriminadora. Y como se mencionó anteriormente, el uso típico de RGAs no requiere verificación de red discriminadora, es simplemente algo que hemos decidido adoptar aquí en un esfuerzo por ser más diligentes.

Debido a esta adición de verificación, nuestros resultados no son fácilmente repetibles en cada ejecución de prueba, especialmente porque nuestra arquitectura de red es muy pequeña dado que hemos utilizado solo 5 capas ocultas y cada una con un tamaño de solo 5. Si se quieren obtener resultados más consistentes con esta verificación de la red discriminadora, se deben entrenar redes con 5 a 25 capas ocultas, donde el tamaño de cada una probablemente no sea menor a 100. El tamaño de la capa más que el número de capas tiende a ser un factor clave para generar resultados de red más confiables.

Sin embargo, si descartamos esta verificación de red discriminadora, nuestra red debería producir resultados de pruebas menos volátiles, aunque con algunos problemas en el rendimiento. Un compromiso podría ser agregar un parámetro de entrada adicional que permita al usuario elegir si la verificación de la red discriminadora está activada o no.


Conclusión

En resumen, hemos visto cómo las redes generativas adversativas (RGAs) condicionales se pueden desarrollar en una clase de señal personalizada que se puede ensamblar en un asesor experto gracias al asistente MQL5 (Wizard MQL5). Las RGAs condicionales son una modificación de una RGA ya que utiliza datos no aleatorios al entrenar la red del generador y los datos de entrada para los datos del discriminador, en nuestro caso, fue un emparejamiento de estos datos de entrada de la red del generador con los datos de salida del generador como ya se demostró. Las redes neuronales en entrenamiento aprenden pesos, por lo que es una buena práctica tener y utilizar disposiciones para registrar estos pesos de una red cada vez que finaliza un proceso de entrenamiento. No hemos considerado ni explorado estos beneficios para las pruebas realizadas para este artículo.

Además, no hemos explorado las posibles ventajas y desventajas de emplear distintos regímenes de entrenamiento de la red. Por ejemplo, en este artículo entrenamos la red en cada nueva barra, lo que se supone que permite una mayor flexibilidad y adaptabilidad de la red y del sistema de negociación a las condiciones potencialmente cambiantes del mercado; sin embargo, un argumento en contra y probablemente creíble podría ser que al entrenar siempre una red en cada nueva barra, se está entrenando innecesariamente en el ruido; en contrasta un régimen en el que digamos que el entrenamiento se realizaría una vez cada 6 meses, de tal manera que sólo los aspectos cruciales a «largo plazo» de los mercados se utilizarían como puntos de datos de entrenamiento, podría ofrecer resultados más sostenibles.

Además, la siempre importante cuestión previa de la búsqueda de la arquitectura neuronal se ha «escatimado» porque no era nuestro tema principal; sin embargo, como cualquiera que esté familiarizado con las redes sabrá, se trata de un aspecto muy sensible al rendimiento de las redes neuronales que requiere cierta diligencia antes de que cualquier red se entrene y, finalmente, se despliegue. Así pues, estas 3 facetas clave no se han abordado adecuadamente a pesar de su importancia, por lo que se insta al lector a que las utilice como punto de partida para desarrollar y perfeccionar esta clase de RGAs condicionales antes de que pueda considerarse digna de comercio. Como siempre, esto no es un consejo de inversión y se espera una diligencia independiente sobre todas y cada una de las ideas compartidas en este artículo por parte del lector antes de su uso posterior. Buena caza.

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

Archivos adjuntos |
cgan.mq5 (6.54 KB)
SignalWZ_22_1.mqh (11.53 KB)
Cgan.mqh (12.51 KB)
Integración de modelos ocultos de Márkov en MetaTrader 5 Integración de modelos ocultos de Márkov en MetaTrader 5
En este artículo demostramos cómo los modelos ocultos de Márkov entrenados con Python pueden integrarse en las aplicaciones de MetaTrader 5. Los modelos ocultos de Márkov son una potente herramienta estadística utilizada para modelar datos de series temporales, en los que el sistema modelado se caracteriza por estados no observables (ocultos). Una premisa fundamental de los modelos ocultos de Márkov es que la probabilidad de estar en un estado determinado en un momento concreto depende del estado del proceso en el intervalo de tiempo anterior.
Características del Wizard MQL5 que debe conocer (Parte 21): Pruebas con datos del calendario económico Características del Wizard MQL5 que debe conocer (Parte 21): Pruebas con datos del calendario económico
De manera predeterminada, los datos del calendario económico no están disponibles para realizar pruebas con asesores expertos dentro del Probador de estrategias. Analizamos cómo las bases de datos podrían ayudar a solucionar esta limitación. Entonces, en este artículo exploramos cómo se pueden usar las bases de datos SQLite para archivar noticias del Calendario Económico, de modo que los Asesores Expertos ensamblados mediante un asistente puedan usarlas para generar señales comerciales.
Guía paso a paso para operar con la estrategia de ruptura de estructura (BoS, Break of Structure) Guía paso a paso para operar con la estrategia de ruptura de estructura (BoS, Break of Structure)
Una guía completa para desarrollar un algoritmo de trading automatizado basado en la estrategia de ruptura de estructura (BoS, Break of Structure). Información detallada sobre todos los aspectos de la creación de un asesor en MQL5 y su prueba en MetaTrader 5, desde el análisis de soportes y resistencias de precios, hasta la gestión de riesgos.
Obtenga una ventaja sobre cualquier mercado (Parte II): Predicción de indicadores técnicos Obtenga una ventaja sobre cualquier mercado (Parte II): Predicción de indicadores técnicos
¿Sabía que podemos obtener más precisión pronosticando ciertos indicadores técnicos que prediciendo el precio subyacente de un símbolo negociado? Únase a nosotros para explorar cómo aprovechar esta información para mejorar las estrategias de negociación.