
Создание самооптимизирующихся советников на языках MQL5 и Python
Краткое содержание
Разработчики алгоритмической торговли сталкиваются со значимой проблемой адаптации к постоянно и непредсказуемо меняющимся с течением времени рыночным условиям. По мере изменения этих условий должны меняться и используемые стратегии. Например, стратегия возврата к среднему значению может оказаться оптимальной, когда рынки демонстрируют волатильность в определенных пределах. Однако, когда рынки начинают устойчиво двигаться в одном направлении, более подходящей становится стратегия следования за трендом.
Зачастую мы, как разработчики, реализуем единственную торговую стратегию и пытаемся обеспечить ее универсальное применение во всех рыночных условиях. К сожалению, такой подход не может гарантировать постоянный успех. Как вариант, возможно также закодировать несколько торговых стратегий в одну программу, что позволит конечному пользователю по своему усмотрению выбирать наиболее подходящую стратегию вручную.
Поэтому очевидно, что необходимо создавать программы, способные самостоятельно выбирать и переключаться между различными стратегиями в зависимости от преобладающих на рынке условий. Чтобы добиться этого, понадобится количественный метод измерения силы трендов или возвратов к средним значениям на рынке. Оценив силу каждого изменения, наш советник потенциально может выбрать оптимальную стратегию, которой и будет следовать.
В статье показано, как можно грамотно достичь нашей цели с помощью матрицы перехода для моделирования поведения рынка и выбора, какую стратегию использовать — следования за рынком или возврата к среднему значению. Начнем с разработки глубокого понимания матриц переходов. Затем рассмотрим, как эти математические инструменты можно использовать для создания интеллектуальных торговых алгоритмов с улучшенными возможностями принятия решений.
Кто такой Андрей Марков?
Рис. 1. Фотография Андрея Маркова в молодости.
Рис. 2. Условная модель Маркова для транспортного предприятия и маршруты, случайно используемые его клиентами.
Проинтерпретируем приведенную выше цепь Маркова. Мы можем наблюдать, что 40% садящихся во Франкфурте пассажиров обычно сходят в Мюнхене, а остальные 60%, как правило, едут до Кельна. Среди пассажиров в Кельне 30% обычно возвращаются во Франкфурт, а 70% обычно направляются в Берлин. Модель наглядно отображает самые популярные маршруты, используемые клиентами.
Кроме того, обратите внимание: есть направления без прямого сообщения. Отсутствие сообщения между некими двумя городами означает, что за 70 лет существования компании никому из клиентов не возникло необходимости проехать из одного из них в другой напрямую. Поэтому вы, как руководитель, можете с уверенностью заключить, что добавление автобусов на маршрут «Франкфурт—Берлин» может оказаться не такими прибыльным по сравнению с другими популярными маршрутами, такими как «Франкфурт—Кельн».
Суть в том, что матрица перехода показывает различные возможности перехода из одного состояния в другое. По мнению Андрея Маркова, вероятность какого-либо состояния зависит только от его текущего состояния. Это помогает понять, как меняется система и какое следующее состояние она, вероятнее всего, перейдет. Прежде чем применить матрицы перехода к финансовым рынкам, сначала мы должны определить все возможные состояния, в которых может находиться рынок.
Построение стратегии: определение состояний рынка
Один из эффективных способов определения состояний рынка — использование технических индикаторов. В приведенном ниже примере мы применили скользящую среднюю к символу из терминала MetaTrader 5. Можем определить состояния следующим образом: «Всякий раз при закрытии свечи выше скользящей средней состояние будет ВВЕРХ (1 на графике), а при ее закрытии ниже скользящей средней это будет состояние ВНИЗ (2 на графике)».
Рис.3. Схематическая диаграмма, показывающая состояние рынка как 1 или 2.
Мы можем построить цепь Маркова, чтобы смоделировать переход рынка от закрытия выше скользящей средней к закрытию ниже нее. Другими словами, цепь Маркова, моделирующая взаимосвязь между скользящей средней и ценой закрытия, могла бы ответить на такой вопрос, как: «Если одна свеча закрывается над скользящей средней, какова вероятность, что следующая свеча тоже закроется над скользящей средней?» Если такая вероятность превышает 0.5, такой рынок может подходить для стратегий следования за трендом. В противном случае более вероятно, что рынок подходит для стратегий возврата к среднему значению.
Начало работы: создание первой матрицы перехода
#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
Затем нужно определить наши учетные данные для входа и задать другие необходимые глобальные переменные, такие как символ, которым хотим торговать, и таймфрейм, который хотим использовать.
#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
Теперь можно войти в систему.
#Login if(mt5.initialize(login=login,password=password,server=server)): print("Logged in successfully") else: print("Failed to login")
Теперь определим торговый объем.
#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()
После получения данных можем приступить к расчету матрицы переходов, чтобы увидеть, как будет развиваться рынок для 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
Рис. 4. Наш датафрейм в текущем формате.
Необходимо определить, на каком расстоянии друг от друга должны находиться интересующие нас свечи. В этом примере хочется найти ответ на вопрос: если текущая свеча закроется над скользящей средней, какова вероятность закрытия следующей свечи также над скользящей средней? Если интересуют вероятности переходов на более длительных временных горизонтах, именно этот параметр следует увеличить для удовлетворения потребностей вашей конкретной стратегии.
#Define how far ahead we are looking look_ahead = 1
Рассчитать матрицу переходов легко:
- Сначала определяем все возможные состояния (мы определили 2 простых состояния: ВВЕРХ и ВНИЗ).
- Подсчитываем, сколько свечей попадает в каждое соответствующее состояние.
- Рассчитываем, какова доля всех свечей в состоянии ВВЕРХ, за которыми следовала свеча в том же состоянии.
- Рассчитываем, какова доля всех свечей в состоянии ВНИЗ, за которыми следовала свеча в том же состоянии.
#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Затем объединим данные в датафрейм.
transition_matrix = pd.DataFrame({ "UP":[up_and_up,(1-down_and_down)], "DOWN":[(1-up_and_up),down_and_down] },index=['UP','DOWN'])
Рассмотрим полученную матрицу переходов.
transition_matrix
Рис. 5. Наша матрица переходов.
Вместе проинтерпретируем матрицу переходов. Наша матрица сообщает, что при закрытии текущей свечи выше скользящей средней существует вероятность 88%, что следующая свеча тоже закроется выше скользящей средней, и 12% — что следующая свеча закроется ниже скользящей средней. Это надежный признак того, что тенденции на этом конкретном рынке не так часто меняются местами. Следовательно, на этом рынке будет удобно использовать стратегии следования за трендом.
Теперь, когда мы построили свою матрицу переходов, можно создать остальную часть алгоритма, который будет использовать эту матрицу для принятия решений относительно покупки или продажи определенной ценной бумаги.
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,:])
Далее определим функцию для получения текущего состояния рынка.
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)
Наконец, определим функцию для выбора действия с учетом текущего состояния рынка и вероятности перехода.
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)
Теперь можно увидеть наш алгоритм в действии.
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)
Рынок находится ниже скользящей средней и демонстрирует сильные тренды, надо продавать.
Открытие позиции на продажу.
Рис. 6. Сделка, которую выбрал наш торговый алгоритм.
Рис. 7. Сделка, которую наш торговый алгоритм выбрал на следующий день.
Но это еще не все, что можно сказать о матрицах переходов. Однако это неплохое введение в тему. Прежде чем завершить обсуждение, важно уточнить, какие переменные влияют на нашу матрицу переходов и как мы при необходимости можем управлять такой матрицей.
ВВЕРХ | ВНИЗ | |
---|---|---|
ВВЕРХ | 0.926 | 0.074 |
ВНИЗ | 0.043 | 0.957 |
Как вы можете заметить, когда мы выбрали в качестве символа EURUSD, вероятность наблюдать 2 свечи подряд над скользящей средней составляла 88%, но при том новом символе, который мы выбрали, то есть Boom 1000 Index, вероятность увидеть 2 свечи подряд над скользящей средней возросла до 93%. Поэтому выбранный символ оказывает на матрицу переходов несомненное влияние.
Параметры технического индикатораНапомним, что мы использовали технические индикаторы для более легкого определения состояний рынка относительно индикатора. Поэтому изменения периода скользящей средней существенно повлияют на матрицу переходов. Для простой иллюстрации этого вернемся к нашим исходным условиям моделирования EURUSD, на в этот раз единственным отличием будет использование периода 2, тогда как в первоначальном примере мы использовали период 20. Все прочие переменные останутся неизменными.
ВВЕРХ | ВНИЗ | |
---|---|---|
ВВЕРХ | 0.456 | 0.544 |
ВНИЗ | 0.547 | 0.453 |
Обратите внимание, что вероятности переходов теперь стремятся к соотношению 50:50 в отношении движения в любом из двух направлений. Это в неявной форме говорит нам о том, что по мере увеличения периода скользящей средней наши вероятности переходов отступают от соотношения 50:50.
Промежуток между свечамиВ обсуждении нас интересовала только взаимосвязь между свечами, следующими одна за другой. Однако по мере увеличения расстояния между рассматриваемыми свечами наша матрица переходов тоже меняется. Вернемся снова к начальным условиям, которые мы использовали для моделирования EURUSD. Однако на этот раз увеличим разрыв между 2 свечами до 100. Таким образом, все прочие переменные будут такими же, за исключение размера разрыва между 2 свечами.
ВВЕРХ | ВНИЗ | |
---|---|---|
ВВЕРХ | 0.503 | 0.497 |
ВНИЗ | 0.507 | 0.493 |
Рекомендации
Нет абсолютно «правильного» или «неправильного» способа проектирования цепи Маркова. Но для того чтобы ваше приложение соответствовало нашим рассуждениям, при построении цепей Маркова необходимо следовать описанному ниже шаблону разработки:transition_matrix = pd.DataFrame({ "UP":["UP AND UP","UP AND DOWN"], "DOWN":["DOWN AND UP","DOWN AND DOWN"] },index=['UP','DOWN'])
Наша матрица переходов разработана для того, чтобы быстро показать, нужно нам следовать за трендом или играть против него.
Стратегии следования за трендом могут оказаться наиболее эффективными, когда наибольшие вероятности содержатся в главной диагонали. Это означает, что рынок склоняется подхватывать тренд и оставаться в нем:
Рис. 8. Следующая за трендом матрица переходов.
И наоборот, стратегии возврата к среднему значению максимально эффективны, когда наибольшие вероятности содержатся в недиагональных элементах матрицы. Это означает, что рынок склонен возвращаться к уровням равновесия:
Рис. 9. Матрица переходов для возврата к среднему значению.
Кроме того, если наибольшие вероятности находятся в нижней строке, это означает, что рынок медвежий:
Рис. 10. Матрица переходов для медвежьего рынка.
Наконец, если наибольшие вероятности находятся в верхней строке, это означает, что рынок бычий:
Рис. 11. Матрица переходов для бычьего рынка.
Реализация в MQL5
Приступим теперь к реализации стратегии с помощью MQL5 таким образом, чтобы можно было тщательно протестировать ее с использованием реальных рыночных данных.
Сначала загрузим необходимые библиотеки.
//+------------------------------------------------------------------+ //| 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. Воспользуемся матрицей переходов, чтобы решить следует применить стратегии следования тренду или стратегии возврата к среднему значению. Gamuchirai Zororo Ndawana Friday 19 July 2024, 10:09 Selebi Phikwe Botswana */ //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh>//Trade class CTrade Trade;
Далее определим входные параметры, которые конечный пользователь сможет редактировать.
//+------------------------------------------------------------------+ //| 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
Кроме того, везде в приложении нам понадобятся некоторые глобальные переменные.
//+------------------------------------------------------------------+ //|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
Для нашего советника нужно задать функцию инициализации. Эта функция будет гарантировать, что пользователь передал допустимые входные данные и настроил технические индикаторы.
//+------------------------------------------------------------------+ //| 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); }
Кроме того, программе необходима процедура, которой нужно следовать при каждой ее деинициализации.
//+------------------------------------------------------------------+ //| 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(); }
Создадим также функцию для обновления технических индикаторов и получения текущих рыночных цен.
//+------------------------------------------------------------------+ //|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); }
Не забывайте, что интерпретация показаний технических индикаторов всегда будет зависеть от поведения рынка, измеряемого матрицей переходов.
//+------------------------------------------------------------------+ //|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); } }
Нужна функция для исполнения ордеров на покупку.
//+----------------------------------------------------------------+ //|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"); } } }
Эта функция будет исполнять для нас ордера на продажу. Напомним, что если рынок возвращается к среднему значению, то мы будем интерпретировать индикаторы «противоположным» образом.
//+-------------------------------------------------------------+ //|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"); } } }
Определим также функцию, которая обеспечит подготовку матрицы переходов и ее расчет в соответствии с описанной выше процедурой.
//+---------------------------------------------------------------+ //|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; }
Кроме того, для интерпретации матрицы переходов понадобится вспомогательная функция.
//+-------------------------------------------------------------+ //|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; } } //+------------------------------------------------------------------+
Обработчик OnTick обеспечит в соответствующие моменты вызов всех описанных выше функций.
//+------------------------------------------------------------------+ //| 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(); } } } //+------------------------------------------------------------------+
Рис. 12. Матрица переходов, рассчитанная на MQL5.
Рис. 13. Наш советник торгует парой AUDJPY.
Заключение
В статье рассматривается применение цепей Маркова в алгоритмическом трейдинге с целью адаптации к меняющимся рыночным условиям. Начиная со знакомства с концепцией цепей Маркова, мы иллюстрируем их пользу при моделировании случайных процессов наподобие рыночной динамики. Определяя состояния рынка с помощью таких технических индикаторов, как скользящая средняя, мы показываем, как построить цепь Маркова для анализа рыночных переходов. Такой подход позволяет определить вероятность будущих движений рынка, помогая решить, какие стратегии лучше использовать — следования за трендом или возврата к среднему значению. С помощью этого метода мы предполагаем создавать интеллектуальные торговые алгоритмы с расширенными возможностями в области принятия решений, улучшая в конечном итоге результаты торговли на динамически развивающихся рынках.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15040
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Ну, как вы знаете, есть много способов сделать что-либо. Подход, который я изложил здесь, призван помочь вам быстро получить надежные результаты. Однако все имеет свою цену, матрица переходов, которую вы будете наблюдать, сильно зависит от того, сколько данных вы собрали, но по мере того, как вы собираете все больше и больше данных, матрица переходов становится стабильной и перестает меняться (она сходится).
Позвольте мне объяснить вам это так: матрица переходов и NN-подход решают совершенно разные проблемы, они отвечают на разные вопросы. Матрица переходов ничего не предсказывает, она просто обобщает/рассказывает нам, что произошло в прошлом, и не говорит нам, что может произойти в будущем.
NN, с другой стороны, говорит нам о том, что, скорее всего, произойдет в будущем. В одном советнике можно использовать и то, и другое.