English Русский 中文 Deutsch 日本語 Português
preview
Construya Asesores Expertos Auto-Optimizables con MQL5 y Python

Construya Asesores Expertos Auto-Optimizables con MQL5 y Python

MetaTrader 5Ejemplos |
504 3
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Sinopsis

Los creadores de algoritmos de negociación se enfrentan al importante reto de adaptarse a unas condiciones de mercado en constante evolución, que cambian de forma impredecible con el tiempo. A medida que cambian estas condiciones, también deben hacerlo las estrategias empleadas. Por ejemplo, una estrategia de reversión a la media podría ser óptima cuando los mercados muestran un comportamiento de rango limitado. Sin embargo, cuando los mercados comienzan a tender de forma constante en una dirección, resulta más adecuada una estrategia de seguimiento de tendencias.

A menudo, como desarrolladores, ponemos en práctica una única estrategia de negociación e intentamos aplicarla de forma universal en todas las condiciones de mercado; lamentablemente, este enfoque no puede garantizar un éxito constante. Alternativamente, también es posible codificar múltiples estrategias de negociación en un programa, permitiendo al usuario final seleccionar manualmente la estrategia más adecuada utilizando su criterio.

Por lo tanto, es evidente que necesitamos diseñar programas capaces de seleccionar y cambiar de forma autónoma entre distintas estrategias en función de las condiciones imperantes en el mercado. Para ello, necesitamos un método cuantitativo que mida la fuerza de las tendencias o los movimientos de reversión de la media en el mercado. Una vez que nuestro Asesor Experto evalúa la fuerza de cada movimiento, puede elegir potencialmente la estrategia óptima a seguir.

Este artículo demuestra cómo podemos lograr nuestro objetivo de forma inteligente utilizando una matriz de transición para modelizar el comportamiento del mercado y determinar si debemos emplear estrategias de seguimiento de tendencias o de reversión a la media. Empezaremos por desarrollar una comprensión de alto nivel de las matrices de transición. A continuación, exploramos cómo pueden utilizarse estas herramientas matemáticas para crear algoritmos de negociación inteligentes con mayor capacidad de decisión.


Introducción: ¿Quién era Andrey Markov?

El siglo XIX fue una época de descubrimientos brillantes, como la invención del teléfono por Alexander Graham Bell, la creación de la bombilla por Thomas Edison y el desarrollo de la radio por Guglielmo Marconi. Sin embargo, entre todos los avances científicos de aquella época, pocos son más significativos para nosotros como desarrolladores algorítmicos que las aportaciones del brillante matemático ruso Andrey Markov.

Markov

Fig. 1: Fotografía de un joven Andrey Markov.

Markov trabajó en muchos problemas que le exigían modelizar procesos completamente aleatorios, similares a nuestro reto de hacer frente a la imprevisibilidad de la dinámica de los mercados. Describió formalmente un marco que hoy se conoce como «cadena de Markov». Comprendámoslo intuitivamente.

Imagine que dirige una empresa de transporte público que lleva más de 70 años prestando servicios de autobús en Alemania. La empresa está considerando añadir más autobuses a la flota y usted, como gerente, debe decidir qué destinos deben recibir los autobuses adicionales y cuáles no merecen una mayor inversión.

Plantear el problema como una cadena de Markov podría simplificarle el proceso de toma de decisiones como gestor. Imaginemos que el siguiente diagrama representa la cadena de Markov de todos los viajes realizados por la empresa a lo largo de sus 70 años de historia.


Modelo de Markov

Fig. 2: Modelo de Markov ficticio de una empresa de transporte y las rutas utilizadas aleatoriamente por sus clientes.

Interpretemos la cadena de Markov anterior. Podemos observar que el 40% de los pasajeros que embarcan en Fráncfort suelen desembarcar en Múnich, mientras que el 60% restante tiende a dirigirse a Colonia. Entre los pasajeros de Colonia, el 30% suele regresar a Fráncfort, y el 70% suele dirigirse a Berlín. Este modelo destaca claramente las rutas más utilizadas por sus clientes.

Además, tenga en cuenta que hay destinos sin conexiones directas. La ausencia de conexión indica que, a lo largo de los 70 años de historia de la empresa, ningún cliente ha necesitado nunca viajar directamente entre esas dos ciudades. Por lo tanto, como gestor, puedes concluir con seguridad que añadir autobuses de Fráncfort a Berlín puede no ser tan rentable en comparación con otras rutas populares, como Fráncfort a Colonia.

Lo que se quiere ilustrar es que una matriz de transición muestra las diferentes probabilidades de pasar de un estado a otro. Según Andrey Markov, la probabilidad de cualquier estado depende únicamente de su estado actual. Nos ayuda a comprender cómo cambia un sistema y a qué estado es más probable que pase después. Antes de poder aplicar matrices de transición a los mercados financieros, debemos definir todos los estados posibles en los que puede encontrarse el mercado.



Construir nuestra estrategia: Definir los estados del mercado

Una forma eficaz de definir los estados del mercado es utilizar indicadores técnicos. En el siguiente ejemplo, hemos aplicado una media móvil a un símbolo de nuestro Terminal MetaTrader 5. Podemos definir los estados de la siguiente manera: «Siempre que una vela cierra por encima de la media móvil, el estado es ARRIBA (1 en el diagrama), y siempre que una vela cierra por debajo de la media móvil, el estado es ABAJO (2 en el diagrama).»


Definición de los estados del mercado

Fig. 3: Un diagrama esquemático que muestra el estado del mercado como 1 o 2.

Podemos construir una cadena de Markov para modelizar cómo el mercado pasa de cerrar por encima de la media móvil a cerrar por debajo. En otras palabras, una cadena de Markov que modele la relación entre la media móvil y el precio de cierre respondería a preguntas como: «Si una vela cierra por encima de la media móvil, ¿cuál es la probabilidad de que la vela siguiente también cierre por encima de la media móvil?». Si esta probabilidad es superior a 0,5, el mercado puede ser adecuado para las estrategias de seguimiento de tendencias. De lo contrario, es más probable que el mercado se preste a estrategias de reversión a la media.


Primeros pasos: Construir nuestra primera matriz de transición

Para empezar, comenzaremos importando nuestras librerías python estándar para comunicarnos con nuestro terminal MetaTrader 5 y para el análisis de datos.

#Import packages
import pandas as pd
import numpy as np
import MetaTrader5 as mt5
from   datetime import datetime
import pandas_ta as ta
import time

A continuación, tenemos que definir nuestras credenciales de acceso y especificar otras variables globales de interés, como el símbolo con el que deseamos operar y el marco temporal que queremos utilizar. 

#Account login details
login = 123456789
password = "Enter Your Password"
server = "Enter Your Server"
symbol = "EURUSD"
#What timeframe are we working on?
timeframe = mt5.TIMEFRAME_M1
#This data frame will store the most recent price update
last_close = pd.DataFrame()
#We may not always enter at the price we want, how much deviation can we tolerate?
deviation = 100
#The size of our positions
volume = 0
#How many times the minimum volume should our positions be
lot_multiple = 1

Ahora, podemos iniciar sesión.

#Login
if(mt5.initialize(login=login,password=password,server=server)):
    print("Logged in successfully")
else:
    print("Failed to login")
Conectado correctamente

A continuación, definimos nuestro volumen de negociación.

#Setup trading volume
symbols = mt5.symbols_get()
for index,symbol in enumerate(symbols):
    if symbol.name == "EURUSD":
        print(f"{symbol.name} has minimum volume: {symbol.volume_min}")
        volume = symbol.volume_min * lot_multiple
EURUSD tiene un volumen mínimo: 0.01
Ahora necesitamos especificar cuántos datos necesitamos de nuestro Terminal MetaTrader 5.
#Specify date range of data to be collected
date_start = datetime(2020,1,1)
date_end = datetime.now()

Una vez obtenidos los datos, podemos proceder a calcular nuestra matriz de transición para ver cómo evoluciona el mercado EURUSD.

#Fetch market data
market_data = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,date_start,date_end))
market_data["time"] = pd.to_datetime(market_data["time"],unit='s')
#Add simple moving average technical indicator
market_data.ta.sma(length=20,append=True)
#Delete missing rows
market_data.dropna(inplace=True)
#Inspect the data frame
market_data

Nuestro marco de datos

Fig. 4: Nuestro marco de datos en su formato actual.

Tenemos que definir cuánto espacio debe haber entre las dos velas de interés. En este ejemplo, estamos interesados en responder a la pregunta «Si la vela actual cierra por encima de la media móvil, ¿cuál es la probabilidad de que la siguiente vela también cierre por encima de la media móvil?» Si le interesan las probabilidades de transición en horizontes temporales mayores, éste es el parámetro que debe aumentar para satisfacer las necesidades específicas de su estrategia.

#Define how far ahead we are looking
look_ahead = 1

Calcular una matriz de transición es fácil:

  1. Primero, define todos los estados posibles (Nosotros definimos 2 estados simples, ARRIBA y ABAJO).
  2. Cuenta cuántas velas corresponden a cada estado.
  3. Calcula qué proporción de todas las velas en el estado ARRIBA fueron seguidas por otra vela en el mismo estado.
  4. Calcula qué proporción de todas las velas en estado ABAJO fueron seguidas por otra vela en el mismo estado.
#Count the number of times price was above the moving average
up = market_data.loc[market_data["close"] > market_data["SMA_20"]].shape[0]

#Count the number of times price was below the moving average
down = market_data.loc[market_data["close"] < market_data["SMA_20"]].shape[0]

#Count the number of times price was above the moving average and remained above it
up_and_up = (market_data.loc[( (market_data["close"] > market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) > market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / up

#Count the number of times price was below the moving average and remained below it
down_and_down = (market_data.loc[( (market_data["close"] < market_data["SMA_20"]) & (market_data["close"].shift(-look_ahead) < market_data["SMA_20"].shift(-look_ahead)) )].shape[0]) / down
A continuación, combinamos los datos en un marco de datos.
transition_matrix = pd.DataFrame({
    "UP":[up_and_up,(1-down_and_down)],
    "DOWN":[(1-up_and_up),down_and_down]
},index=['UP','DOWN'])

Veamos nuestra matriz de transición.

transition_matrix

Matriz de transición

Fig. 5: Nuestra matriz de transición.

Interpretemos juntos la matriz de transición, nuestra matriz nos está informando que si la vela actual cierra por encima de la media móvil, hay un 88% de posibilidades de que la siguiente vela también cierre por encima de la media móvil y un 12% de posibilidades de que la siguiente vela cierre por debajo de la media móvil. Esta es una buena señal de que los movimientos en este mercado en particular no se invierten tan a menudo. Por lo tanto, el mercado puede ajustarse a las estrategias de seguimiento de tendencias. 

Ahora que hemos construido nuestra matriz de transición, podemos construir el resto de nuestro algoritmo que utilizará esta matriz de transición para guiar sus decisiones sobre si debe comprar o vender un valor en particular. 

Primero definimos una función que obtendrá los datos de precios actuales de nuestro terminal y calculará los valores de nuestros indicadores técnicos.

def get_prices():
    start = datetime(2024,6,1)
    end   = datetime.now()
    data  = pd.DataFrame(mt5.copy_rates_range("EURUSD",timeframe,start,end))
    #Add simple moving average technical indicator
    data.ta.sma(length=20,append=True)
    #Delete missing rows
    data.dropna(inplace=True)
    data['time'] = pd.to_datetime(data['time'],unit='s')
    data.set_index('time',inplace=True)
    return(data.iloc[-1,:])

A continuación, definiremos una función para obtener el estado actual del mercado.

def get_state(current_data):
    #Price is above the moving average, UP state
    if(current_data["close"]  > current_data["SMA_20"]):
        return(1)
    #Price is below the moving average, DOWN state
    elif(current_data["close"] < current_data["SMA_20"]):
        return(2)

Por último, definiremos una función para seleccionar una acción dado el estado actual del mercado y la probabilidad de transición.

def get_action(current_state):
    if(current_state == 1):
        if(transition_matrix.iloc[0,0] > transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong trends, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[0,0] < transition_matrix.iloc[0,1]):
            print("The market is above the moving average and has strong mean reverting moves, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)
    elif(current_state == 2):
        if(transition_matrix.iloc[1,0] > transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong mean reverting moves, buy")
            print("Opening a BUY position")
            mt5.Buy("EURUSD",volume)
        elif(transition_matrix.iloc[1,0] < transition_matrix.iloc[1,1]):
            print("The market is below the moving average and has strong trends, sell")
            print("Opening a sell position")
            mt5.Sell("EURUSD",volume)

Ahora podemos ver nuestro algoritmo en acción.

while True:
    #Get data on the current state of our terminal and our portfolio
    positions = mt5.positions_total()
    #If we have no open positions then we can open one
    if(positions == 0):
        get_action(get_state(get_prices()))
    #If we have finished all checks then we can wait for one day before checking our positions again
    time.sleep(60)

El mercado está por debajo de la media móvil y tiene tendencias fuertes, vender.

Abrir una posición de venta.

Condiciones iniciales del mercado

Fig. 6: Operación seleccionada por nuestro algoritmo de negociación.


Nuestro comercio al día siguiente.

Fig. 7: Operación seleccionada por nuestro algoritmo de negociación al día siguiente.

Esto no es todo lo que se puede decir sobre las matrices de transición. Sin embargo, es una buena introducción al tema. Antes de concluir nuestra discusión, es importante que discutamos qué variables afectan a nuestra matriz de transición y cómo podemos manipular la matriz de transición si es necesario.


El símbolo
La primera variable que afecta a nuestra matriz de transición es obviamente el símbolo elegido, por ejemplo si dejamos todas las demás variables igual y simplemente seleccionamos un nuevo símbolo, «Boom 1000 Index», este es el aspecto que tiene ahora nuestra matriz de transición.



UP DOWN
UP 0.926 0.074
DOWN 0.043 0.957

Como puedes observar, cuando seleccionamos el EURUSD como símbolo, la probabilidad de ver 2 velas consecutivas por encima de la media móvil era del 88% pero ahora con este nuevo símbolo que hemos seleccionado, «Boom 1000 Index» la probabilidad de ver 2 velas consecutivas por encima de la media móvil ha aumentado hasta el 93%. Por lo tanto, el símbolo de elección tiene un efecto innegable en la matriz de transición.

Parámetros de los indicadores técnicos

Recordemos que utilizamos indicadores técnicos para ayudarnos a definir fácilmente los estados del mercado en relación con el indicador. Por lo tanto, cambiar el periodo de la media móvil afectaría en gran medida a la matriz de transición. Para ilustrar simplemente el punto, volveremos a nuestras condiciones iniciales de modelado del EURUSD, pero esta vez la única diferencia es que utilizaremos un periodo de 2, mientras que en nuestro ejemplo inicial utilizábamos un periodo de 20. Todas las demás variables se mantienen constantes.


UP DOWN
UP 0.456 0.544
DOWN 0.547 0.453

Obsérvese cómo las probabilidades de transición convergen ahora hacia un 50/50 de posibilidades de ir en cualquier dirección. Esto nos indica implícitamente que, a medida que aumenta el periodo de nuestra media móvil, nuestras probabilidades de transición se alejan cada vez más de las probabilidades del 50/50.

El hueco entre las velas

En nuestro debate, sólo nos preocupaba la relación entre dos velas consecutivas. Sin embargo, a medida que aumentamos la distancia entre las velas examinadas, nuestra matriz de transición también cambia. Una vez más, vamos a volver a las condiciones iniciales que utilizamos para modelar el EURUSD sin embargo, esta vez, vamos a aumentar la brecha entre las 2 velas para ser 100. Así que todas las demás variables serán las mismas, excepto la distancia entre las 2 velas.


UP DOWN
UP 0.503 0.497
DOWN 0.507 0.493


Recomendaciones 

No hay una manera absoluta «correcta» o «incorrecta» de diseñar su Cadena de Markov, sin embargo para que su aplicación sea consistente con nuestra discusión es imperativo que siga el patrón de diseño descrito a continuación cuando construya sus Cadenas de Markov:
transition_matrix = pd.DataFrame({
    "UP":["UP AND UP","UP AND DOWN"],
    "DOWN":["DOWN AND UP","DOWN AND DOWN"]
},index=['UP','DOWN'])

Nuestra matriz de transición está diseñada para mostrarnos rápidamente si debemos seguir la tendencia o jugar contra ella.

Las estrategias de seguimiento de tendencias pueden funcionar mejor cuando la diagonal principal contiene las mayores probabilidades, esto significa que el mercado tiende a recoger una tendencia, tiende a permanecer en la tendencia:

Seguir la tendencia

Fig. 8: Matriz de transición que sigue una tendencia.

Por el contrario, las estrategias de reversión a la media pueden funcionar mejor cuando la fuera de diagonal contiene las mayores probabilidades, lo que significa que el mercado tiende a volver a los niveles de equilibrio:

Reversión media

Fig. 9: Matriz de transición de reversión media.

Además, si las mayores probabilidades se encuentran en la fila inferior, significa que el mercado es bajista:


Mercado bajista

Fig. 10: Matriz de transición bajista.


Por último, si las mayores probabilidades se encuentran en la fila superior, significa que el mercado es alcista:

Mercado alcista

Fig. 11: Matriz de transición alcista.


Implementación en MQL5

Ahora procederemos a implementar la estrategia usando MQL5 para que podamos probar extensivamente la estrategia usando datos reales del mercado.

En primer lugar, cargamos las librerías que necesitamos.

//+------------------------------------------------------------------+
//|                                          Transition Matrices.mq5 |
//|                                       Gamuchirai Zororo Ndawana. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Gamuchirai Zororo Ndawana."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//|Overview                                                          |
//+------------------------------------------------------------------+
/*
This expert advisor will demonstrate how we can use transition matrices to build
self optimizing expert advisors. We will use the transition matrix to decide whether
we should employ trend following, or mean reverting trading strategies.

Gamuchirai Zororo Ndawana
Friday 19 July 2024, 10:09
Selebi Phikwe
Botswana
*/

//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+
#include <Trade/Trade.mqh>//Trade class
CTrade Trade;

A continuación, definimos los parámetros de entrada que puede editar el usuario final.

//+------------------------------------------------------------------+
//| Inputs                                                            |
//+------------------------------------------------------------------+
input int fetch = 5; //How much historical data should we fetch?
input int look_ahead = 1; //Our forecast horizon

input int ma_period = 20; //The MA Period
input int rsi_period = 20; //The RSI Period
input int wpr_period = 20; //The Williams Percent Range Period

input int  lot_multiple = 20; //How big should the lot sizes be
input double sl_width = 0.4; //Stop loss size

Continuando, hay variables globales que necesitaremos a lo largo de nuestra aplicación.

//+------------------------------------------------------------------+
//|Global variables                                                  |
//+------------------------------------------------------------------+
double minimum_volume;//Smallest lot size
double ask_price;//Ask
double bid_price;//Bid
int ma_handler,rsi_handler,wpr_handler;//The handlers for our technical indicators
vector ma_readings(fetch);//MA indicator values
vector rsi_readings(fetch);//RSI indicator values
vector wpr_readings(fetch);//WPR indicator values
vector price_readings(fetch);//The vector we will use to store our historical price values
matrix transition_matrix = matrix::Zeros(2,2);//The matrix to store our observations on price's transition behavior
bool transition_matrix_initialized = false;//This flag will instruct the application to initialize the transition matrix
double up_and_up = 0;//These variables will keep count of the price transitions
double up_and_down = 0;
double down_and_up = 0;
double down_and_down = 0;
double total_count = (double) fetch - look_ahead;//This variable will store the total number of observations used to calculate the transition matrix
double trading_volume;//This is our desired trading size
vector market_behavior = vector::Zeros(4);//Transition matrix interpretations

Tenemos que definir la función de inicialización para nuestro Asesor Experto, esta función se asegurará de que nuestro usuario pasó entradas válidas y configurar nuestros indicadores técnicos.

//+------------------------------------------------------------------+
//| Initialization Function                                          |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the technical indicator
   ma_handler = iMA(_Symbol,PERIOD_CURRENT,ma_period,0,MODE_EMA,PRICE_CLOSE);
   rsi_handler = iRSI(_Symbol,PERIOD_CURRENT,rsi_period,PRICE_CLOSE);
   wpr_handler = iWPR(_Symbol,PERIOD_CURRENT,wpr_period);
   minimum_volume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trading_volume = minimum_volume * lot_multiple;
//--- Look ahead cannot be greater than fetch
   if(look_ahead > fetch)
     {
      Comment("We cannot forecast further into the future than thr to total amount of  data fetched.\nEither fetch more data or forecast nearer to the present.");
      return(INIT_FAILED);
     }
//--- End of initialization
   return(INIT_SUCCEEDED);
  }

Nuestro programa también necesita un procedimiento a seguir cada vez que se desinicializa.

//+------------------------------------------------------------------+
//| Expert de-initialization function                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Remove technical indicators
   IndicatorRelease(rsi_handler);
   IndicatorRelease(wpr_handler);
   IndicatorRelease(ma_handler);
//--- Remove Expert Advisor
   ExpertRemove();
  }

También crearemos una función para actualizar nuestros indicadores técnicos y obtener los precios actuales del mercado.

//+------------------------------------------------------------------+
//|This function will update our technical indicator values          |
//+------------------------------------------------------------------+
void update_technical_indicators(void)
  {
//--- Update bid and ask price
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   bid_price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
//--- Update each indicator value, we only need the most recent reading
   rsi_readings.CopyIndicatorBuffer(rsi_handler,0,0,1);
   wpr_readings.CopyIndicatorBuffer(wpr_handler,0,0,1);
   ma_readings.CopyIndicatorBuffer(ma_handler,0,0,1);
  }

Recuerde que nuestras interpretaciones de las lecturas de los indicadores técnicos dependerán siempre del comportamiento del mercado medido por la matriz de transición.

//+------------------------------------------------------------------+
//|This function will find an entry opportunity based on our signals |                                                                  |
//+------------------------------------------------------------------+
void find_entry(void)
  {
//--- Store the index of our largest entry
   ulong max_arg = market_behavior.ArgMax();

//--- First we have to know the behavior of the market before we decide to buy or sell
   if(max_arg == 0)
     {
      //--- This means that the market is bullish and we should probably only take buy oppurtunities
      Comment("The observed transition matrix can only be generated by a bullish market");
      bullish_sentiment(0);
     }
   else
      if(max_arg == 1)
        {
         //--- This means that the market is bearish and we should probably only take sell oppurtunities
         Comment("The observed transition matrix can only be generated by a bearish market");
         bearish_sentiment(0);
        }
      else
         if(max_arg == 2)
           {
            //--- This means that the market trends and we should probably join either side of the trend
            Comment("The observed transition matrix can only be generated by a trending market");
            bearish_sentiment(0);
            bullish_sentiment(0);
           }
         else
            if(max_arg == 3)
              {
               //--- This means that the market is mean reverting and we should probably play against the trends on either side
               Comment("The observed transition matrix can only be generated by a mean reverting market");
               bearish_sentiment(-1);
               bullish_sentiment(-1);
              }
  }

Necesitamos una función para ejecutar nuestras órdenes largas.

//+----------------------------------------------------------------+
//|This function will look for oppurtunities to buy                |
//+----------------------------------------------------------------+
void bullish_sentiment(int f_flag)
  {
//--- This function analyses the market for bullish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings above 50 will trigger buy orders.
//--- However if -1 was passed then RSI readings below 50 will trigger buy orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }

//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- The flag is telling us to follow the trend
      if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
        {
         Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- The flag is telling us to bet against the trend
         if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
           {
            Trade.Buy(trading_volume,_Symbol,ask_price,(ask_price - sl_width),(ask_price + sl_width),"Transition Matrix Order");
           }
        }

  }

Esta función ejecutará nuestras órdenes en corto por nosotros. Recordemos que si nuestro mercado es de reversión a la media, entonces interpretaremos los indicadores de forma «opuesta».

//+-------------------------------------------------------------+
//|This function will help us find oppurtunities to sell        |
//+-------------------------------------------------------------+
void bearish_sentiment(int f_flag)
  {
//--- This function analysises the market for bearish sentiment using our technical indicator
//--- It has only 1 parameter, a flag denoting whether we should interpret the indicators in a trend following fashion
//--- or a mean reverting fashion. For example 0 means interpret the indicators in a trend following fashion.
//--- Therefore if we call the function and pass 0, RSI readings below 50 will trigger sell orders.
//--- However if -1 was passed then RSI readings above 50 will trigger sell orders.
//--- First make sure we have no open positions
   if(PositionsTotal() > 0)
     {
      return;
     }
//--- Interpret the flag
   if(f_flag == 0)
     {
      //--- Now we know how to interpret our technical indicators
      if((rsi_readings[0] < 50) && (wpr_readings[0] < -50))
        {
         Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
        }
     }
   else
      if(f_flag == -1)
        {
         //--- Now we know how to interpret our technical indicators
         if((rsi_readings[0] > 50) && (wpr_readings[0] > -50))
           {
            Trade.Sell(trading_volume,_Symbol,bid_price,(bid_price + sl_width),(bid_price - sl_width),"Transition Matrix Order");
           }
        }
  }

Definamos también una función que garantice que nuestra matriz de transición se prepara y calcula según el procedimiento que hemos descrito anteriormente.

//+---------------------------------------------------------------+
//|This function will initialize our transition matrix            |
//+---------------------------------------------------------------+
void initialize_transition_matrix(void)
  {
//--- We need to update our historical price readings and our MA readings
   ma_readings.CopyIndicatorBuffer(ma_handler,0,1,fetch);
   price_readings.CopyRates(_Symbol,PERIOD_CURRENT,COPY_RATES_CLOSE,1,fetch);

//--- Now let us update our transition matrix
   for(int i = 0; i < fetch - look_ahead; i++)
     {
      //--- Did price go from being above the MA but end up beneath the MA?
      if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
        {
         up_and_down += 1;
        }
      //--- Did price go from being above the MA and remain above it?
      else
         if((price_readings[i] > ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
           {
            up_and_up += 1;
           }

         //--- Did price go from being below the MA but end up above it?
         else
            if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] > ma_readings[i + look_ahead]))
              {
               down_and_up += 1;
              }

            //--- Did price go from being below the MA and remain below it?
            else
               if((price_readings[i] < ma_readings[i]) && (price_readings[i + look_ahead] < ma_readings[i + look_ahead]))
                 {
                  down_and_down += 1;
                 }
     }

//--- Let us see our counts so far
   Print("Up and up: ",up_and_up,"\nUp and down: ",up_and_down,"\nDown and up: ",down_and_up,"\nDown and down: ",down_and_down);
   double sum_of_counts = up_and_up + up_and_down + down_and_up + down_and_down;
   Print("Sum of counts: ",(sum_of_counts),"\nObservations made: ",total_count,"\nDifference:[the difference should always be 0] ",(total_count - sum_of_counts));
//--- Now we will calculate the transition matrix
//--- The matrix position (0,0) stores the probaility that after making a move up, the market will continue rising
//--- The matrix position (0,1) stores the probability that after making a move down, price will reverse and start rising
//--- The matrix position (1,0) stores the probability that after making a move up, price will reverse and start falling
//--- The matrix position (1,1) stores the probabilty that after making a move down, price will continue falling
   transition_matrix[0][0] = up_and_up / (up_and_up + up_and_down);
   transition_matrix[0][1] = down_and_up / (up_and_up + up_and_down);
   transition_matrix[1][0] = up_and_down / (down_and_up + down_and_down);
   transition_matrix[1][1] = down_and_down / (down_and_up + down_and_down);
//--- Show the transition matrix
   Print("Our transition Matrix");
   Print(transition_matrix);
//--- Now we need to make sense of the transition matrix
   analyse_transition_matrix();
//--- Now we need to update the flag
   transition_matrix_initialized = true;
  }

También necesitamos una función auxiliar para interpretar nuestra matriz de transición.

//+-------------------------------------------------------------+
//|This function will analyse our transition matrix             |
//+-------------------------------------------------------------+
void analyse_transition_matrix(void)
  {
//--- Check if the market is bullish
   if((transition_matrix[0][0] > transition_matrix[1][0])&&(transition_matrix[0][1] > transition_matrix[1][1]))
     {
      market_behavior[0] = 1;
     }
//--- Check if the market is bearish
   else
      if((transition_matrix[0][1] > transition_matrix[1][0])&&(transition_matrix[1][1] > transition_matrix[1][0]))
        {
         market_behavior[1] = 1;
        }
      //--- Check if the market trends
      else
         if(transition_matrix.Trace() > 1)
           {
            market_behavior[2] = 1;
           }
         //--- Check if the market is mean reverting
         else
            if(transition_matrix.Trace() < 1)
              {
               market_behavior[3] = 1;
              }
  }
//+------------------------------------------------------------------+

Nuestro manejador OnTick se asegurará de que todas las funciones que hemos descrito anteriormente sean llamadas en el momento adecuado.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- First we must check if our transition matrix has been initialized
   if(!transition_matrix_initialized)
     {
      initialize_transition_matrix();
     }
//--- Otherwise our transition matrix has been initialized
   else
     {
      //--- Update technical indicator values
      update_technical_indicators();
      //--- If we have no open positions we will use our tranistion matrix to help us interpret our technical indicators
      if(PositionsTotal() == 0)
        {
         find_entry();
        }
     }
  }
//+------------------------------------------------------------------+

Matriz de transición

Fig. 12: Nuestra matriz de transición calculada en MQL5.

Probando nuestro EA

Fig. 13: Nuestro Asesor Experto operando el par AUDJPY.


Conclusión

Este artículo explora la aplicación de las cadenas de Markov en la negociación algorítmica para adaptarse a las cambiantes condiciones del mercado. Comenzando con una introducción al concepto de cadenas de Markov, ilustramos su utilidad para modelar procesos aleatorios afines a la dinámica del mercado. Definiendo los estados del mercado mediante indicadores técnicos, como las medias móviles, demostramos cómo construir una cadena de Markov para analizar las transiciones del mercado. Este enfoque nos permite determinar la probabilidad de futuros movimientos del mercado, ayudándonos a decidir si emplear estrategias de seguimiento de tendencias o de reversión a la media. Con este método, pretendemos crear algoritmos de negociación inteligentes con mayor capacidad de toma de decisiones y, en última instancia, mejorar el rendimiento de la negociación en mercados dinámicos.


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

Sibusiso Steven Mathebula
Sibusiso Steven Mathebula | 26 jul 2024 en 03:38
En el artículo anterior se han utilizado matrices y vectores para optimizar una estrategia de negociación sin utilizar necesariamente el enfoque convencional de las redes neuronales. Parece que (al menos para mí), uno puede realmente construir un EA auto-optimización, sin necesidad de utilizar NN que implican funciones de activación, lo que significa que realmente no necesita funciones de activación o neuronas para auto-optimizar su EA. Lo más probable es que pueda ser corregido, hey. Yo definitivamente podría estar equivocado, yo podría ser realmente realmente estar terriblemente equivocado, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, ........... estar malinterpretando todo acerca de la optimización y NN mate......Yo soy tu vecino, aquí en RSA.
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana | 27 jul 2024 en 11:14
Sibusiso Steven Mathebula estrategia de negociación sin utilizar necesariamente el enfoque convencional de las redes neuronales. Parece que (al menos para mí), uno puede realmente construir un EA auto-optimización, sin necesidad de utilizar NN que implican funciones de activación, lo que significa que realmente no necesita funciones de activación o neuronas para auto-optimizar su EA. Lo más probable es que pueda ser corregido, hey. Definitivamente podría estar equivocado, podría estar realmente muy equivocado, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, yo podría, ........... estar malinterpretando todo acerca de la optimización y NN mate......Soy tu vecino, aquí en RSA.
Hey SIbusiso, ¿Ujani Budi?

Bueno como sabes hay muchas maneras de hacer cualquier cosa. El enfoque que he esbozado aquí está destinado a ayudarle a obtener resultados fiables, rápido. Sin embargo, todo tiene un precio, la matriz de transición que observarás está muy influenciada por la cantidad de datos que has obtenido, pero a medida que obtienes más y más datos la matriz de transición se vuelve estable y deja de cambiar (converge).

Permíteme ponértelo de esta manera, la matriz de transición y el enfoque NN están resolviendo problemas completamente diferentes, están respondiendo a preguntas diferentes. La matriz de transición no predice nada, simplemente resume/nos dice lo que ha ocurrido en el pasado y no nos dice lo que es probable que ocurra en el futuro.

En cambio, la NN nos dice lo que es probable que ocurra en el futuro. Es posible utilizar ambos en un EA.
thiezo
thiezo | 1 ene 2025 en 07:09
Hola Gamuchirai. Este artículo me habla directamente a mí y te doy las gracias por abrir nuestras mentes. Soy muy nuevo en la codificación y aprendo leyendo y codificando de artículos como el tuyo. Mi mayor reto es Python. Ni siquiera sé por dónde empezar sobre todo porque aprendo más rápido si el tema es el comercio porque entonces puedo backtest e incorporar ideas en mi EA. Por favor, indíqueme dónde puedo aprender el lenguaje. Sólo he codificado la versión MQL5 y el problema al que me enfrento es que 'max_arg' sigue siendo 0, por lo que el EA se mantiene alcista. Con mi limitada comprensión, traté de manipular algunos parámetros y me detuve en un punto donde el código colocaría una compra y una venta al mismo tiempo. Puede que me esté perdiendo un detalle crucial. Puedo enviarle mi código copiado y/o modificado si su código funciona correctamente en su lado. Tal vez usted puede detectar el problema. Utilizo datos descargados ya que estoy de vacaciones y por lo tanto trabajo sin conexión. ¿Podría eso causar problemas? Aprecio el trabajo que está haciendo y sus artículos son brillantes. Soy de SA y todo lo que puedo decir es gracias tsano.
Algoritmo de optimización del comportamiento social adaptativo — Adaptive Social Behavior Optimization (ASBO): Método de Schwefel, método de Box-Muller Algoritmo de optimización del comportamiento social adaptativo — Adaptive Social Behavior Optimization (ASBO): Método de Schwefel, método de Box-Muller
Este artículo presenta una fascinante inmersión en el mundo del comportamiento social de los organismos vivos y su influencia en la creación de un nuevo modelo matemático, el ASBO (Adaptive Social Behavior Optimisation). Hoy exploraremos cómo los principios de liderazgo, vecindad y cooperación observados en las sociedades de seres vivos inspiran el desarrollo de algoritmos de optimización innovadores.
Redes neuronales en el trading: Reducción del consumo de memoria con el método de optimización Adam (Adam-mini) Redes neuronales en el trading: Reducción del consumo de memoria con el método de optimización Adam (Adam-mini)
Una forma de mejorar la eficacia del proceso de aprendizaje y la convergencia de los modelos es mejorar los métodos de optimización. Adam-mini es un método de optimización adaptativa desarrollado para mejorar el algoritmo Adam básico.
Análisis causal de series temporales mediante entropía de transferencia Análisis causal de series temporales mediante entropía de transferencia
En este artículo, analizamos cómo se puede aplicar la causalidad estadística para identificar variables predictivas. Exploraremos el vínculo entre causalidad y entropía de transferencia, además de presentar código MQL5 para detectar transferencias direccionales de información entre dos variables.
Redes neuronales en el trading: Red neuronal espacio-temporal (STNN) Redes neuronales en el trading: Red neuronal espacio-temporal (STNN)
En este artículo, hablaremos sobre el uso de transformaciones espacio-temporales para predecir el próximo movimiento de los precios de manera eficaz. Para mejorar la precisión de la predicción numérica en el STNN, hemos propuesto un mecanismo de atención continua que permite al modelo considerar en mayor medida aspectos importantes de los datos.