English Русский 中文 Deutsch 日本語 Português
preview
Ejemplo de análisis de redes de causalidad (Causality Network Analysis, CNA) y modelo de autoregresión vectorial para la predicción de eventos de mercado

Ejemplo de análisis de redes de causalidad (Causality Network Analysis, CNA) y modelo de autoregresión vectorial para la predicción de eventos de mercado

MetaTrader 5Sistemas comerciales |
374 4
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introducción

El análisis de redes de causalidad (Causality Network Analysis, CNA) es un método utilizado para comprender y modelar relaciones causales complejas entre variables en un sistema. Cuando se aplica a los mercados financieros, puede ayudar a identificar cómo los distintos acontecimientos y factores del mercado se influyen mutuamente, lo que puede dar lugar a predicciones más precisas.

Descubrimiento causal: El descubrimiento causal es el proceso de inferir relaciones causales a partir de datos observacionales. En el contexto de los mercados financieros, esto significa identificar qué variables (como indicadores económicos, precios de mercado o eventos externos) tienen efectos causales sobre otros. Existen varios algoritmos para el descubrimiento causal, pero uno de los más populares es el algoritmo PC (Peter-Clark).

Este robot de negociación no implementa explícitamente un sistema denominado «Análisis de redes de causalidad para la predicción de eventos de mercado» como componente nombrado. Sin embargo, el robot incorpora elementos de análisis causal y enfoques basados en redes que son conceptualmente similares. Vamos a desglosarlo:

Análisis causal: el bot utiliza un algoritmo de descubrimiento causal, específicamente el algoritmo de inferencia causal rápida (Fast Causal Inference, FCI) (en lugar del algoritmo PC (Peter-Clark)). Esto se implementa en la función FCIAlgorithm().

Análisis de red: el bot utiliza una estructura de red para representar relaciones entre diferentes instrumentos o indicadores financieros. Esto es evidente en la estructura Node y la función SetupNetwork().

Predicción de eventos: aunque no se denomina explícitamente "predicción de eventos", el bot utiliza un modelo de autorregresión vectorial (Vector autoregression, VAR) para realizar predicciones sobre estados futuros del mercado. Esto se implementa en funciones como TrainVARModel() y PredictVARValue().


Análisis de redes de causalidad: una nueva frontera en la predicción de eventos de mercado

En el mundo del trading algorítmico, un nuevo enfoque está ganando terreno entre los analistas cuantitativos y los traders por igual: el análisis de redes de causalidad para la predicción de eventos de mercado. Este sofisticado método combina el poder de la inferencia causal, la teoría de redes y el análisis predictivo para pronosticar eventos importantes del mercado con una precisión sin precedentes.

Imagine el mercado financiero como una red enorme e interconectada. Cada hilo representa una relación entre diferentes variables del mercado: precios de acciones, indicadores económicos, eventos geopolíticos y más. El análisis tradicional a menudo se centra en las correlaciones, pero como cualquier trader experimentado sabe, la correlación no siempre implica causalidad.

Aquí es donde entra en juego el análisis de redes de causalidad. Su objetivo es descubrir las verdaderas relaciones de causa y efecto dentro de esta compleja red. Al hacerlo, proporciona a los comerciantes una comprensión más profunda de la dinámica del mercado, lo que les permite anticipar eventos que podrían ser invisibles para el análisis convencional.

En esencia, el análisis de redes de causalidad implica tres pasos clave:

1. Construyendo la red: Primero, construimos una red donde cada nodo representa una variable de mercado. Estos podrían ser cualquier cosa, desde precios de activos y volúmenes de negociación hasta indicadores económicos y puntuaciones de sentimiento.

2. Descubriendo vínculos causales: A continuación, utilizamos algoritmos avanzados para determinar las relaciones causales entre estos nodos. Aquí es donde ocurre la magia: no solo observamos qué variables se mueven juntas, sino cuáles realmente impulsan cambios en otras.

3. Predicción de eventos: Finalmente, utilizamos esta red causal para pronosticar eventos significativos del mercado. Al comprender los verdaderos impulsores de los movimientos del mercado, podemos anticipar mejor cambios importantes, caídas o repuntes.


Las ventajas para los comerciantes

Para los traders de MQL5, implementar el análisis de red de causalidad puede ser un cambio radical. He aquí por qué:

  • Gestión mejorada de riesgos: al identificar las causas fundamentales de la volatilidad del mercado, puede prepararse mejor para posibles caídas.
  • Predicciones más precisas: comprender las relaciones causales conduce a pronósticos más confiables que aquellos basados en meras correlaciones.
  • Descubrimiento de oportunidades ocultas: La red causal podría revelar conexiones que no son obvias a primera vista, dando lugar a oportunidades comerciales únicas.
  • Estrategias adaptativas: a medida que el mercado evoluciona, también lo hace la red causal, lo que permite que sus estrategias se adapten en tiempo real.


Implementación en MQL5

Si bien implementar un sistema completo de análisis de red de causalidad es complejo, MQL5 proporciona una plataforma sólida para este tipo de análisis avanzado. Podrías empezar por:

  1. Usando la función "iCustom()" para crear indicadores para cada nodo de su red.
  2. Implementar algoritmos de descubrimiento causal como el algoritmo PC o las pruebas de causalidad de Granger (en este caso utilizamos algoritmos FCI).
  3. Aprovechar las capacidades de red neuronal de MQL5 para crear modelos predictivos basados en su red causal.


¿Por qué utilizamos FCI en lugar de PC?

FCI (Fast Causal Inference) y PC (Peter-Clark) son algoritmos de descubrimiento causal utilizados en el campo de la inferencia causal. Están diseñados para inferir relaciones causales a partir de datos observacionales. He aquí por qué se podría elegir FCI en lugar de PC:

  1. Factores de confusión latentes: la principal ventaja de FCI sobre PC es su capacidad para manejar factores de confusión latentes. La FCI puede inferir la presencia de causas comunes ocultas, mientras que la PC supone suficiencia causal (sin factores de confusión latentes).
  2. Sesgo de selección: FCI también puede explicar el sesgo de selección en los datos, algo que PC no puede.
  3. Modelo más general: FCI produce un modelo gráfico más general llamado Gráfico Ancestral Parcial (Partial Ancestral Graph, PAG), que puede representar una clase más amplia de estructuras causales que los Gráficos Acíclicos Dirigidos (Directed Acyclic Graphs, DAG) producidos por PC.
  4. Solidez y completitud: FCI es sólido y completo para la clase de sistemas causalmente insuficientes, lo que significa que puede identificar correctamente todas las relaciones causales posibles dado un tamaño de muestra infinito.
  5. Robustez: debido a su capacidad para manejar factores de confusión latentes y sesgos de selección, el FCI es generalmente más robusto cuando se trabaja con datos del mundo real donde las variables ocultas son comunes.

Sin embargo, vale la pena señalar que la FCI tiene algunas desventajas en comparación con la PC:

  1. Complejidad computacional: FCI generalmente es computacionalmente más costoso que PC, especialmente para conjuntos de datos grandes.
  2. Resultado menos definitivo: los PAG producidos por FCI a menudo contienen más direcciones de borde indeterminadas que los DAG de PC, lo que refleja la incertidumbre adicional de posibles factores de confusión latentes.
  3. Interpretación: Los PAG pueden ser más difíciles de interpretar que los DAG para los no expertos.

En la práctica, la elección entre FCI y PC a menudo depende de los requisitos específicos de su tarea de inferencia causal, la naturaleza de sus datos y sus suposiciones sobre el sistema causal. Si está seguro de que no hay factores de confusión ocultos ni sesgos de selección, la PC podría ser suficiente y más eficiente. Si sospecha que hay variables latentes o sesgo de selección, FCI sería la opción más adecuada.


Modelo de autorregresión vectorial (VAR)

VAR es un algoritmo de pronóstico multivariado que se utiliza cuando dos o más series de tiempo se influyen entre sí. En este sistema comercial, es probable que se utilice para modelar las relaciones entre diferentes instrumentos financieros o indicadores económicos.

Características principales de VAR:

  1. Captura interdependencias lineales entre múltiples series de tiempo.
  2. Cada variable es una función lineal de sus rezagos pasados y de los rezagos pasados de las otras variables.
  3. Permite una dinámica rica en un sistema de series de tiempo múltiples.

En el contexto de este sistema comercial:

  • El modelo VAR se entrena utilizando la función "TrainVARModel".
  • La función "PredictVARValue" utiliza el modelo entrenado para hacer predicciones.
  • El sistema optimiza el modelo VAR seleccionando el rezago óptimo y las variables significativas utilizando la función "OptimizeVARModel".


Representación matemática:

Para un modelo VAR de dos variables con retardo 1:

y1,t = c1 + φ11y1,t-1 + φ12y2,t-1 + ε1,t

y2,t = c2 + φ21y1,t-1 + φ22y2,t-1 + ε2,t

Donde:

  • "y1,t" e "y2,t" son los valores de las dos variables en el tiempo t.
  • "c1" y "c2" son constantes.
  • "φij" son los coeficientes.
  • "ε1,t" y "ε2,t" son términos de error.

El sistema estima estos coeficientes para hacer predicciones.

Imagínese que no sólo está prediciendo el precio futuro de un solo activo, sino que simultáneamente está pronosticando múltiples variables financieras interrelacionadas. Ahí es donde brilla VAR. Es como tener una bola de cristal que no sólo te muestra un futuro, sino múltiples futuros interconectados a la vez.

En esencia, VAR es un algoritmo de pronóstico multivariado que se utiliza cuando dos o más series de tiempo se influyen entre sí. En términos más simples, es una forma de capturar las dependencias lineales entre múltiples series de tiempo.


¿Cómo hace magia VAR?

Vamos a desglosarlo:

  1. Recopilación de datos: Comienza recopilando datos históricos de todas las variables que deseas incluir en tu modelo. Estos podrían ser datos de precios de múltiples pares de divisas, materias primas o incluso indicadores económicos.
  2. Especificación del modelo: Usted decide cuántos rezagos (períodos de tiempo pasados) incluir. ¡Aquí es donde tu experiencia en trading resulta útil!
  3. Estimación: El modelo estima cómo cada variable está influenciada por sus propios valores pasados y los valores pasados de otras variables.
  4. Pronóstico: Una vez estimado, el modelo VAR puede generar pronósticos para todas las variables incluidas simultáneamente.


Implementación de VAR en MQL5

Si bien MQL5 no tiene una función VAR incorporada, puedes implementarla tú mismo. A continuación se muestra un ejemplo simplificado de cómo podría estructurar su código:

// Define your VAR model
struct VARModel {
    int lag;
    int variables;
    double[][] coefficients;
};

// Estimate VAR model
VARModel EstimateVAR(double[][] data, int lag) {
    // Implement estimation logic here
    // You might use matrix operations for efficiency
}

// Make predictions
double[] Forecast(VARModel model, double[][] history) {
    // Implement forecasting logic here
}

// In your EA or indicator
void OnTick() {
    // Collect your multivariate data
    double[][] data = CollectData();
    
    // Estimate model
    VARModel model = EstimateVAR(data, 5); // Using 5 lags
    
    // Make forecast
    double[] forecast = Forecast(model, data);
    
    // Use forecast in your trading logic
    // ...
}


La ventaja de VAR en acción

Imagínate que estás negociando con EURUSD. Un enfoque tradicional podría simplemente mirar los precios pasados del EURUSD. Pero con VAR podrías incluir:

  • Precios del EURUSD.
  • Precios del USDJPY (para capturar la fortaleza general del USD).
  • Precios del petróleo (un factor importante para muchas monedas).
  • Índice S&P 500 (para medir el sentimiento general del mercado).
  • Diferencial de tipos de interés entre EE. UU. y la UE.

Ahora su modelo captura una imagen mucho más rica del panorama forex, lo que potencialmente conduce a decisiones comerciales más informadas.


Desafíos y consideraciones

Como cualquier herramienta poderosa, VAR tiene sus desafíos:

  1. Requisitos de datos: Los modelos VAR pueden consumir muchos datos. Asegúrese de tener suficientes datos históricos para realizar estimaciones confiables.
  2. Intensidad computacional: A medida que aumentan las variables y los rezagos, aumentan los requisitos computacionales. Optimice su código para mejorar la eficiencia.
  3. Estacionariedad: VAR asume series de tiempo estacionarias. Es posible que necesites procesar previamente tus datos (por ejemplo, diferenciarlos) para cumplir con este supuesto.
  4. Interpretación: Con múltiples variables y rezagos, interpretar los resultados del VAR puede ser complejo. No olvides combinar los conocimientos estadísticos con tus conocimientos de trading.


Análisis de red en el sistema de negociación CNA

El análisis de red implementado en este Asesor Experto (EA) es un componente fundamental de su estrategia de análisis y predicción de mercado. Está diseñado para representar y analizar las relaciones complejas entre diferentes instrumentos financieros o variables del mercado.

Para resumir los puntos clave sobre el análisis de red utilizado en este EA:

  1. Estructura: La red está compuesta de nodos, cada uno de los cuales representa un instrumento financiero (normalmente un par de divisas).
  2. Objetivo: Está diseñado para modelar las relaciones e interdependencias entre diferentes instrumentos financieros en el mercado.
  3. Descubrimiento causal: El EA utiliza el algoritmo de inferencia causal rápida (FCI) para descubrir posibles relaciones causales entre estos instrumentos.
  4. Representación: Estas relaciones se representan en una matriz de adyacencia, que muestra qué instrumentos están directamente vinculados en términos de influencia causal.
  5. Análisis: El EA realiza varios análisis en esta red, incluida la identificación de estructuras v (un patrón específico en gráficos causales) y la aplicación de reglas de orientación para refinar aún más la comprensión de las relaciones causales.
  6. Integración con Predicción: La estructura de la red y las relaciones descubiertas a través de este análisis sirven como entradas para el modelo de autorregresión vectorial (VAR), que se utiliza para realizar predicciones.
  7. Naturaleza adaptativa: El análisis de red no es estático. Se puede actualizar con el tiempo, lo que permite que el EA se adapte a las condiciones cambiantes del mercado y a las relaciones entre los instrumentos.

La idea clave detrás de este enfoque es que los instrumentos financieros no se mueven de forma aislada. Al modelar y analizar la red de relaciones entre diferentes instrumentos, el EA pretende obtener una comprensión más completa de la dinámica del mercado. En teoría, esto debería conducir a predicciones más precisas y decisiones comerciales mejor informadas.

Sin embargo, es importante señalar que, si bien este enfoque es sofisticado, los mercados financieros son extremadamente complejos y están influenciados por muchos factores. La eficacia de este análisis de red dependerá de qué tan bien capture la dinámica real del mercado y de cómo se integre con otros componentes de la estrategia comercial.


Estructura de la Red

La red está representada por las siguientes estructuras:

struct Node
{
    string name;
    double value;
};

Node g_network[];
int g_node_count = 0;

Cada nodo de la red representa un instrumento financiero o una variable de mercado específica. El campo de nombre identifica el instrumento (por ejemplo, "EURUSD", "GBPUSD") y el campo de valor puede almacenar datos relevantes para ese nodo.


Configuración de la red

La red se inicializa en la función SetupNetwork():

void SetupNetwork()
{
    string symbols[] = {"EURUSD", "GBPUSD", "USDCAD", "USDCHF", "USDJPY", "AUDUSD"};
    for(int i = 0; i < ArraySize(symbols); i++)
    {
        AddNode(symbols[i]);
    }
}

void AddNode(string name)
{
    ArrayResize(g_network, g_node_count + 1);
    g_network[g_node_count].name = name;
    g_network[g_node_count].value = 0; // Initialize with default value
    g_node_count++;
}

Esta configuración crea una red donde cada nodo representa un par de divisas diferente.


Propósito de la red

La red cumple varios propósitos clave en el EA:

  1. Representación de la estructura del mercado: Modela las interconexiones entre diferentes pares de divisas, lo que permite al EA considerar cómo los movimientos en un par podrían afectar a otros.
  2. Base para el análisis causal: La estructura de la red se utiliza como base para el algoritmo de inferencia causal rápida (FCI), que intenta descubrir relaciones causales entre los nodos.
  3. Entrada para modelado predictivo: La estructura de la red y las relaciones descubiertas a través del análisis causal sirven como entradas para el modelo de autorregresión vectorial (VAR) utilizado para las predicciones.


Análisis de red en acción

El EA realiza varios tipos de análisis en esta red:

  1. Descubrimiento causal: La función FCIAlgorithm() aplica el algoritmo de inferencia causal rápida para descubrir posibles relaciones causales entre los nodos.
  2. Matriz de adyacencia: Las relaciones causales se representan en una matriz de adyacencia, donde cada entrada indica si existe un vínculo causal directo entre dos nodos.
  3. Orientación de la estructura en V: La función OrientVStructures() identifica y orienta las estructuras v en la red, que son patrones importantes en los gráficos causales.
  4. Análisis de gráficos: Se analiza la estructura final del gráfico para fundamentar las decisiones comerciales, con el supuesto de que los instrumentos vinculados causalmente pueden proporcionar información predictiva entre sí.


Implicaciones para el comercio

Este enfoque basado en redes permite al EA:

  1. Considerar dinámicas de mercado complejas que podrían no ser evidentes si se analizan los instrumentos de forma aislada.
  2. Identificar potencialmente indicadores adelantados entre los instrumentos analizados.
  3. Realice predicciones más informadas teniendo en cuenta el contexto más amplio del mercado.
  4. Adaptarse a las condiciones cambiantes del mercado a medida que se reevalúa periódicamente la estructura causal.

Aprovechando este análisis de red, el EA pretende comprender mejor la dinámica del mercado, lo que podría dar lugar a predicciones más precisas y decisiones de negociación mejor informadas.


Código de ejemplo

Este código sigue estos dos diagramas de flujo:

Diagrama de flujo

Diagrama de flujo de funciones



Explicación detallada de las funciones clave del programa de trading MQL5

Este EA comercial es bastante sofisticado y combina varios enfoques avanzados:

  1. Utiliza un modelo de autorregresión vectorial (VAR) para las predicciones.
  2. Implementa un algoritmo de descubrimiento causal (Fast Causal Inference, FCI) para comprender las relaciones entre diferentes variables del mercado.
  3. Emplea una estructura de red para representar y analizar múltiples instrumentos financieros simultáneamente.
  4. Incorpora varios filtros técnicos como volatilidad, RSI y fuerza de tendencia.
  5. Utiliza gestión de riesgos adaptativa y dimensionamiento de posiciones.
  6. Implementa una estrategia de ventana deslizante para actualizar y volver a entrenar continuamente el modelo.
  7. Incluye técnicas de validación cruzada y optimización de modelos.

La estructura del EA permite un análisis y una toma de decisiones complejos manteniendo la flexibilidad para futuras mejoras o adaptaciones a diferentes condiciones del mercado.


Explicación detallada de las funciones clave del EA de trading

1. OnInit()

int OnInit()
  {
// Step 1: Set up your network structure
   SetupNetwork();

// Step 2: Run the causal discovery algorithm (e.g., PC or FCI)
   FCIAlgorithm();

// Step 3: Train the optimized causal model
   TrainOptimizedVARModel();

   ArrayResize(g_previous_predictions, g_node_count);
   ArrayInitialize(g_previous_predictions, 0);  // Initialize with zeros

   return(INIT_SUCCEEDED);
  }

Esta es la función de inicialización que se ejecuta cuando el EA se carga o se vuelve a cargar por primera vez en un gráfico.

Responsabilidades clave:

  • Configura la estructura de la red llamando a SetupNetwork().
  • Ejecuta el algoritmo de descubrimiento causal (FCI) con FCIAlgorithm().
  • Entrena el modelo causal optimizado utilizando TrainOptimizedVARModel().
  • Inicializa la matriz de predicciones anteriores.

Esta función sienta las bases para todo el análisis y la predicción que realizará el EA.

2. OnTick()

void OnTick()
  {
   ENUM_TIMEFRAMES tf = ConvertTimeframe(InputTimeframe);

   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(Symbol(), tf, 0);

   if(currentBarTime == lastBarTime)
      return;

   lastBarTime = currentBarTime;

   Print("--- New bar on timeframe ", EnumToString(tf), " ---");

   UpdateModelSlidingWindow();

   for(int i = 0; i < g_node_count; i++)
     {
      string symbol = g_network[i].name;
      Print("Processing symbol: ", symbol);

      double prediction = PredictVARValue(i);
      Print("Prediction for ", symbol, ": ", prediction);

      int signal = GenerateSignal(symbol, prediction);
      Print("Signal for ", symbol, ": ", signal);

      // Imprimir más detalles sobre las condiciones
      Print("RSI: ", CustomRSI(symbol, ConvertTimeframe(InputTimeframe), 14, PRICE_CLOSE, 0));
      Print("Trend: ", DetermineTrend(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Trend Strong: ", IsTrendStrong(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Volatility OK: ", VolatilityFilter(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Fast MA > Slow MA: ", (iMA(symbol, PERIOD_CURRENT, 14, 0, MODE_EMA, PRICE_CLOSE) > iMA(symbol, PERIOD_CURRENT, 24, 0, MODE_EMA, PRICE_CLOSE)));

      if(signal != 0)
        {
         Print("Attempting to execute trade for ", symbol);
         ExecuteTrade(symbol, signal);
        }
      else
        {
         g_debug_no_signal_count++;
         Print("No trade signal generated for ", symbol, ". Total no-signal count: ", g_debug_no_signal_count);
        }
      ManageOpenPositions();
      ManageExistingOrders(symbol);


      Print("Current open positions for ", symbol, ": ", PositionsTotal());
      Print("Current pending orders for ", symbol, ": ", OrdersTotal());
     }

   Print("--- Bar processing complete ---");
   Print("Total no-signal count: ", g_debug_no_signal_count);
   Print("Total failed trade count: ", g_debug_failed_trade_count);
  }

Esta función se llama en cada tick del mercado.

Tareas principales:

  • Convierte el marco de tiempo de entrada.
  • Actualiza el modelo usando una ventana deslizante con UpdateModelSlidingWindow()
  • Para cada símbolo de la red:
    • Predice valores usando PredictVARValue().
    • Genera señales comerciales con GenerateSignal().
    • Ejecuta operaciones si se generan señales.
    • Gestiona puestos vacantes.
    • Gestiona pedidos existentes.

Este es el corazón operativo del EA, donde se toman las decisiones comerciales.

3. OnCalculate()

Aunque no se muestra explícitamente en el código proporcionado, esta función normalmente se usaría para calcular valores de indicadores personalizados. En este EA, algunas de estas funciones parecen estar integradas en OnTick().

4. CheckMarketConditions()

int GenerateSignal(string symbol, double prediction)
  {
   int node_index = FindNodeIndex(symbol);
   if(node_index == -1)
      return 0;

   static bool first_prediction = true;
   if(first_prediction)
     {
      first_prediction = false;
      return 0; // No generar señal en la primera predicción
     }

   double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);

// Calculate predicted change as a percentage
   double predicted_change = (prediction - current_price) / current_price * 100;

   bool volatility_ok = VolatilityFilter(symbol, ConvertTimeframe(InputTimeframe));
   double rsi = CustomRSI(symbol, ConvertTimeframe(InputTimeframe), 14, PRICE_CLOSE, 0);
   bool trend_strong = IsTrendStrong(symbol, ConvertTimeframe(InputTimeframe));
   int trend = DetermineTrend(symbol, ConvertTimeframe(InputTimeframe));

   double fastMA = iMA(symbol, PERIOD_CURRENT, 8, 0, MODE_EMA, PRICE_CLOSE);
   double slowMA = iMA(symbol, PERIOD_CURRENT, 24, 0, MODE_EMA, PRICE_CLOSE);

   Print("Debugging GenerateSignal for ", symbol);
   Print("Current price: ", current_price);
   Print("Prediction diff: ", prediction);
   Print("predicted_change: ", predicted_change);
   Print("RSI: ", rsi);
   Print("Trend: ", trend);
   Print("Trend Strong: ", trend_strong);
   Print("Volatility OK: ", volatility_ok);
   Print("Fast MA: ", fastMA, ", Slow MA: ", slowMA);

   bool buy_condition =  prediction >  0.00001 && rsi < 30 && trend_strong  && volatility_ok && fastMA > slowMA;
   bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA;

   Print("Buy condition met: ", buy_condition);
   Print("Sell condition met: ", sell_condition);

// Buy conditions
   if(buy_condition)
     {
      Print("Buy signal generated for ", symbol);
      int signal = 1;
      return signal;  // Buy signal
     }
// Sell conditions
   else
      if(sell_condition)
        {
         Print("Sell signal generated for ", symbol);
         int signal = -1;
         return signal; // Sell signal
        }
      else
        {
         Print("No signal generated for ", symbol);
         int signal = 0;
         return signal; // No signal
        }


  }

Esta función no está definida explícitamente en el código, pero su funcionalidad se distribuye en varias partes, principalmente dentro de GenerateSignal():

  • Comprueba la volatilidad con VolatilityFilter().
  • Comprueba el RSI.
  • Verifica la fuerza de la tendencia con IsTrendStrong().
  • Determina la dirección de la tendencia con DetermineTrend().
  • Compara promedios móviles rápidos y lentos.

Estas comprobaciones determinan si las condiciones del mercado son favorables para el comercio.

5. ExecuteTrade()

void ExecuteTrade(string symbol, int signal)
  {
   if(!IsMarketOpen(symbol) || !IsTradingAllowed())
     {
      Print("Market is closed or trading is not allowed for ", symbol);
      return;
     }

   double price = (signal == 1) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);

   double stopLoss, takeProfit;
   CalculateAdaptiveLevels(symbol, signal, stopLoss, takeProfit);

   double lotSize = CalculateDynamicLotSize(symbol, stopLoss);

   if(lotSize <= 0)
     {
      Print("Invalid lot size for symbol: ", symbol);
      return;
     }

   trade.SetExpertMagicNumber(123456);
   trade.SetTypeFilling(ORDER_FILLING_FOK);

   bool result = false;
   int attempts = 3;

   for(int i = 0; i < attempts; i++)
     {
      if(signal == 1)
        {
         result = trade.Buy(lotSize, symbol, price, stopLoss, takeProfit, "CNA Buy");
         Print("Attempting Buy order: Symbol=", symbol, ", Lot Size=", lotSize, ", Price=", price, ", SL=", stopLoss, ", TP=", takeProfit);
        }
      else
         if(signal == -1)
           {
            result = trade.Sell(lotSize, symbol, price, stopLoss, takeProfit, "CNA Sell");
            Print("Attempting Sell order: Symbol=", symbol, ", Lot Size=", lotSize, ", Price=", price, ", SL=", stopLoss, ", TP=", takeProfit);
           }

      if(result)
        {
         Print("Order executed successfully for ", symbol, ", Type: ", (signal == 1 ? "Buy" : "Sell"), ", Lot size: ", lotSize);
         break;
        }
      else
        {
         int lastError = GetLastError();
         Print("Attempt ", i+1, " failed for ", symbol, ". Error: ", lastError, " - ", GetErrorDescription(lastError));

         if(lastError == TRADE_RETCODE_REQUOTE || lastError == TRADE_RETCODE_PRICE_CHANGED || lastError == TRADE_RETCODE_INVALID_PRICE)
           {
            price = (signal == 1) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);
            CalculateAdaptiveLevels(symbol, signal, stopLoss, takeProfit);
           }
         else
            break;
        }
     }

   if(!result)
     {
      Print("All attempts failed for ", symbol, ". Last error: ", GetLastError(), " - ", GetErrorDescription(GetLastError()));
     }
  }

Esta función maneja la ejecución real de las operaciones.

Aspectos clave:

  • Verifica si el mercado está abierto y se permite operar.
  • Calcula niveles de Stop Loss y Take Profit adaptativos.
  • Determina el tamaño del lote en función de la gestión de riesgos.
  • Intentos de ejecutar la orden, con reintentos en caso de fallo.
  • Maneja errores y proporciona retroalimentación detallada.

6. OnDeinit()

void OnDeinit(const int reason)
  {
   if(atr_handle != INVALID_HANDLE)
      IndicatorRelease(atr_handle);
   if(rsi_handle != INVALID_HANDLE)
      IndicatorRelease(rsi_handle);
   if(momentum_handle != INVALID_HANDLE)
      IndicatorRelease(momentum_handle);
   GenerateFinalSummary();
  }

Esta función se llama cuando se elimina el EA del gráfico o cuando se cierra la terminal.

Responsabilidades principales:

  • Libera los manejadores de indicadores.
  • Genera un resumen final del rendimiento del EA llamando a GenerateFinalSummary().


Funciones importantes adicionales

UpdateModelSlidingWindow()

void UpdateModelSlidingWindow()
  {
   static int bars_since_update = 0;
   ENUM_TIMEFRAMES tf = ConvertTimeframe(InputTimeframe);

// Check if it's time to update
   if(bars_since_update >= g_update_frequency)
     {
      // Collect new data
      double training_data[];
      CollectTrainingData(training_data, g_window_size, tf);

      // Retrain model
      TrainModel(training_data);

      // Validate model
      double validation_score = ValidateModel();
      Print("Model updated. Validation score: ", validation_score);

      bars_since_update = 0;
     }
   else
     {
      bars_since_update++;
     }
  }

Actualiza el modelo VAR utilizando una ventana deslizante de datos, lo que permite que el modelo se adapte a las condiciones cambiantes del mercado.


TrainOptimizedVARModel()

void TrainOptimizedVARModel()
  {
   OptimizationResult opt_result = OptimizeVARModel();

   Print("Lag óptimo encontrado: ", opt_result.optimal_lag);
   Print("AIC: ", opt_result.aic);
   Print("Variables seleccionadas: ", ArraySize(opt_result.selected_variables));

// Usar opt_result.optimal_lag y opt_result.selected_variables para entrenar el modelo final
   TrainVARModel(opt_result.optimal_lag, opt_result.selected_variables);
  }
Optimiza y entrena el modelo VAR, seleccionando el número óptimo de retardos y variables significativas.


PredictVARValue()

double PredictVARValue(int node_index)
{
   if(node_index < 0 || node_index >= g_node_count)
   {
      Print("Error: Invalid node index: ", node_index);
      return 0;
   }

   double prediction = 0;
   int lag = g_var_params[node_index].lag;

   Print("Predicting for node: ", g_network[node_index].name, ", Lag: ", lag);

   // Retrieve the previous prediction
   double previous_prediction = g_previous_predictions[node_index];

   // Verify if there are enough coefficients
   int expected_coefficients = g_node_count * lag + 1; // +1 for the intercept
   if(ArraySize(g_var_params[node_index].coefficients) < expected_coefficients)
   {
      Print("Error: Not enough coefficients for node ", node_index, ". Expected: ", expected_coefficients, ", Actual: ", ArraySize(g_var_params[node_index].coefficients));
      return 0;
   }

   prediction = g_var_params[node_index].coefficients[0]; // Intercept
   Print("Intercept: ", prediction);

   double sum_predictions = 0;
   double sum_weights = 0;
   double current_price = iClose(g_network[node_index].name, PERIOD_CURRENT, 0);

   for(int l = 1; l <= lag; l++)
   {
      double time_weight = 1.0 - (double)(l-1) / lag; // Time-based weighting
      sum_weights += time_weight;

      double lag_prediction = 0;
      for(int j = 0; j < g_node_count; j++)
      {
         int coeff_index = (l - 1) * g_node_count + j + 1;
         if(coeff_index >= ArraySize(g_var_params[node_index].coefficients))
         {
            Print("Warning: Coefficient index out of range. Skipping. Index: ", coeff_index, ", Array size: ", ArraySize(g_var_params[node_index].coefficients));
            continue;
         }

         double coeff = g_var_params[node_index].coefficients[coeff_index];
         double raw_value = iClose(g_network[j].name, PERIOD_CURRENT, l);

         if(raw_value == 0 || !MathIsValidNumber(raw_value))
         {
            Print("Warning: Invalid raw value for ", g_network[j].name, " at lag ", l);
            continue;
         }

         // Normalize the value as a percentage change
         double normalized_value = (raw_value - current_price) / current_price;

         double partial_prediction = coeff * normalized_value;
         lag_prediction += partial_prediction;

         Print("Lag ", l, ", Node ", j, ": Coeff = ", coeff, ", Raw Value = ", raw_value,
               ", Normalized Value = ", normalized_value, ", Partial prediction: ", partial_prediction);
      }

      sum_predictions += lag_prediction * time_weight;
      Print("Lag ", l, " prediction: ", lag_prediction, ", Weighted: ", lag_prediction * time_weight);
   }

   Print("Sum of weights: ", sum_weights);

   // Calculate the final prediction
   if(sum_weights > 1e-10)
   {
      prediction = sum_predictions / sum_weights;
   }
   else
   {
      Print("Warning: sum_weights is too small (", sum_weights, "). Using raw sum of predictions.");
      prediction = sum_predictions;
   }

   // Calculate the difference between the current prediction and the previous prediction
   double prediction_change = prediction - previous_prediction;

   Print("Previous prediction: ", previous_prediction);
   Print("Current prediction: ", prediction);
   Print("Prediction change: ", prediction_change);

   // Convert the prediction to a percentage change
   double predicted_change_percent = (prediction - current_price) / current_price * 100;

   Print("Final prediction (as percentage change): ", predicted_change_percent, "%");

   // Update the current prediction for the next iteration
   g_previous_predictions[node_index] = prediction;

   // Return the difference in basis points
   return prediction_change * 10000; // Multiply by 10000 to convert to basis points
}

Realiza predicciones utilizando el modelo VAR entrenado, considerando valores históricos y coeficientes del modelo.


ManageOpenPositions() y ManageExistingOrders()

void ManageOpenPositions()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         string symbol = PositionGetString(POSITION_SYMBOL);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
         double stopLoss = PositionGetDouble(POSITION_SL);
         ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         // Implement trailing stop
         if(positionType == POSITION_TYPE_BUY && currentPrice - openPrice > 2 * CustomATR(symbol, PERIOD_CURRENT, 14, 0))
           {
            double newStopLoss = NormalizeDouble(currentPrice - CustomATR(symbol, PERIOD_CURRENT, 14, 0), SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            if(newStopLoss > stopLoss)
              {
               trade.PositionModify(ticket, newStopLoss, 0);
              }
           }
         else
            if(positionType == POSITION_TYPE_SELL && openPrice - currentPrice > 2 * CustomATR(symbol, PERIOD_CURRENT, 14, 0))
              {
               double newStopLoss = NormalizeDouble(currentPrice + CustomATR(symbol, PERIOD_CURRENT, 14, 0), SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               if(newStopLoss < stopLoss || stopLoss == 0)
                 {
                  trade.PositionModify(ticket, newStopLoss, 0);
                 }
              }
        }
     }
  }


void ManageExistingOrders(string symbol)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == symbol)
        {
         double stopLoss = PositionGetDouble(POSITION_SL);
         double takeProfit = PositionGetDouble(POSITION_TP);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
         ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         double atr = CustomATR(symbol, PERIOD_CURRENT, 14, 0);
         double newStopLoss, newTakeProfit;

         if(positionType == POSITION_TYPE_BUY)
           {
            newStopLoss = NormalizeDouble(currentPrice - 2 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            newTakeProfit = NormalizeDouble(currentPrice + 3 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            if(newStopLoss > stopLoss && currentPrice - openPrice > atr)
              {
               trade.PositionModify(ticket, newStopLoss, newTakeProfit);
              }
           }
         else
            if(positionType == POSITION_TYPE_SELL)
              {
               newStopLoss = NormalizeDouble(currentPrice + 2 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               newTakeProfit = NormalizeDouble(currentPrice - 3 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               if(newStopLoss < stopLoss && openPrice - currentPrice > atr)
                 {
                  trade.PositionModify(ticket, newStopLoss, newTakeProfit);
                 }
              }

         // Implementar cierre parcial
         double profit = PositionGetDouble(POSITION_PROFIT);
         double volume = PositionGetDouble(POSITION_VOLUME);
         if(profit > 0 && MathAbs(currentPrice - openPrice) > 2 * atr)
           {
            double closeVolume = volume * 0.5;
            if(IsValidVolume(symbol, closeVolume))
              {
               ClosePosition(ticket, closeVolume);
              }
           }
        }
     }
  }

Gestionar posiciones abiertas y órdenes existentes, implementando estrategias como Trailing Stops y cierres parciales.

Este EA combina el análisis técnico tradicional con modelos estadísticos avanzados (VAR) y técnicas de aprendizaje automático (descubrimiento causal) para tomar decisiones comerciales. La estructura modular permite una fácil modificación y mejora de componentes individuales.


Resultados

Estos son los resultados para algunos símbolos, las configuraciones y entradas seguirán siendo las mismas para todos los símbolos estudiados:

Ajustes

Entradas


EURUSD

EURUSD


GBPUSD

GBPUSD


AUDUSD

AUDUSD

USDJPY

USDJPY


USDCAD

USDCAD


USDCHF

USDCHF


¿Cómo obtener mejores resultados?

Se pueden lograr mejores resultados si en el nodo de símbolos se agregan más y también se utilizan símbolos cointegrados y correlacionados. Para saber cuáles son esos símbolos, puedes utilizar mi script de Python de este artículo: Aplicación de la teoría de juegos de Nash con filtrado HMM en el trading.

Además, se pueden lograr mejores resultados si se añade a la estrategia un aprendizaje profundo. Hay algunos ejemplos de ello en artículos como éste:Análisis de sentimientos y aprendizaje profundo para operar con EA y backtesting con Python 

Además, no se han realizado optimizaciones para los filtros, y también podría intentar agregar más filtros.

Ejemplo de cómo cambia el gráfico al agregar más símbolos a la red:

void SetupNetwork()
  {
   string symbols[] = {"EURUSD", "GBPUSD", "USDCAD", "USDCHF", "USDJPY", "AUDUSD", "XAUUSD", "SP500m", "ND100m"};
   for(int i = 0; i < ArraySize(symbols); i++)
     {
      AddNode(symbols[i]);
     }
  }


EURUSD modificado

EURUSD modificado

Sólo añadiendo más símbolos a la red, conseguimos mejorar en:

  1. Actividad comercial (más operaciones totales).
  2. Tasa de éxito en operaciones cortas.
  3. Porcentaje de operaciones rentables.
  4. Tamaño de la operación comercial más rentable.
  5. Beneficio promedio por operación ganadora.
  6. Menor porcentaje de operaciones perdedoras.
  7. Pérdida promedio menor por operación perdedora.

Estas mejoras sugieren una mejor gestión general del comercio y del control del riesgo, a pesar de que algunas métricas de rendimiento clave (como el factor de beneficio y el factor de recuperación) son más bajas.


Conclusión

Este artículo analiza un sistema de trading avanzado que integra el análisis de redes de causalidad (Causality Network Analysis, CNA) y la regresión automática vectorial (Vector Autoregression, VAR) para predecir eventos del mercado y tomar decisiones comerciales. El sistema utiliza sofisticadas técnicas estadísticas y de aprendizaje automático para modelar las relaciones entre instrumentos financieros. Si bien es prometedor, enfatiza la importancia de combinar este enfoque con una sólida gestión de riesgos y un profundo conocimiento de los mercados.

Para los traders que buscan mejores resultados, el artículo sugiere ampliar la red con más símbolos correlacionados, incorporar aprendizaje profundo y optimizar los métodos de filtrado. El aprendizaje continuo y la adaptación son cruciales para el éxito a largo plazo en el trading.

¡Felices operaciones y que sus algoritmos siempre estén un paso por delante del mercado!


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

Archivos adjuntos |
CNA_Final_v4.mq5 (179.13 KB)
Javier Santiago Gaston De Iriarte Cabrera

Si ese EA te da problemas, puedes probar con este (es una versión más antigua y los gráficos no se igualan, pero lo acabo de probar y funciona).

Por favor, avisadme cuando un EA no funcione correctamente, porque de vez en cuando formateo el ordenador y pierdo todas las demás versiones.

Javier Santiago Gaston De Iriarte Cabrera

Éste también funciona

Javier Santiago Gaston De Iriarte Cabrera

Lo siento, este debería funcionar

Javier Santiago Gaston De Iriarte Cabrera
este no da errores, y es el que usaré para empezar a trabajar.
Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (II) Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (II)
Piense en un asesor experto independiente. Anteriormente, analizamos un Asesor Experto basado en indicadores que también se asoció con un script independiente para dibujar la geometría de riesgo y recompensa. Hoy discutiremos la arquitectura de un Asesor Experto MQL5, que integra todas las características en un solo programa.
Creación de un asesor experto integrado de MQL5 y Telegram (Parte 4): Modular las funciones del código para mejorar su reutilización Creación de un asesor experto integrado de MQL5 y Telegram (Parte 4): Modular las funciones del código para mejorar su reutilización
En este artículo, refactorizamos el código existente utilizado para enviar mensajes y capturas de pantalla de MQL5 a Telegram organizándolo en funciones modulares y reutilizables. Esto agilizará el proceso, permitiendo una ejecución más eficiente y una gestión del código más sencilla en múltiples instancias.
Formulación de un Asesor Experto Multipar Dinámico (Parte 1): Correlación de divisas y correlación inversa Formulación de un Asesor Experto Multipar Dinámico (Parte 1): Correlación de divisas y correlación inversa
El asesor experto dinámico de múltiples pares aprovecha las estrategias de correlación y correlación inversa para optimizar el rendimiento comercial. Al analizar datos del mercado en tiempo real, identifica y explota la relación entre pares de divisas.
Reimaginando las estrategias clásicas (Parte VII): Análisis de los mercados Forex y la deuda soberana en el USDJPY Reimaginando las estrategias clásicas (Parte VII): Análisis de los mercados Forex y la deuda soberana en el USDJPY
En el artículo de hoy analizaremos la relación entre los tipos de cambio futuros y los bonos gubernamentales. Los bonos se encuentran entre las formas más populares de valores de renta fija y serán el foco de nuestro debate. Únase a nosotros mientras exploramos si podemos mejorar una estrategia clásica utilizando IA.