
Construya Asesores Expertos Auto-Optimizables con MQL5 y Python
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?
Fig. 1: Fotografía de un joven Andrey 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).»
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
#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")
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
#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
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:
- Primero, define todos los estados posibles (Nosotros definimos 2 estados simples, ARRIBA y ABAJO).
- Cuenta cuántas velas corresponden a cada estado.
- Calcula qué proporción de todas las velas en el estado ARRIBA fueron seguidas por otra vela en el mismo estado.
- 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]) / downA 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
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.
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.
Fig. 6: Operación seleccionada por nuestro algoritmo de negociación.
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.
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écnicosRecordemos 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 velasEn 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:
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:
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:
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:
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(); } } } //+------------------------------------------------------------------+
Fig. 12: Nuestra matriz de transición calculada en MQL5.
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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso
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.