English 中文 Español Deutsch 日本語 Português
preview
Создание самооптимизирующихся советников на языках MQL5 и Python

Создание самооптимизирующихся советников на языках MQL5 и Python

MetaTrader 5Примеры |
799 3
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

Краткое содержание

Разработчики алгоритмической торговли сталкиваются со значимой проблемой адаптации к постоянно и непредсказуемо меняющимся с течением времени рыночным условиям. По мере изменения этих условий должны меняться и используемые стратегии. Например, стратегия возврата к среднему значению может оказаться оптимальной, когда рынки демонстрируют волатильность в определенных пределах. Однако, когда рынки начинают устойчиво двигаться в одном направлении, более подходящей становится стратегия следования за трендом.

Зачастую мы, как разработчики, реализуем единственную торговую стратегию и пытаемся обеспечить ее универсальное применение во всех рыночных условиях. К сожалению, такой подход не может гарантировать постоянный успех. Как вариант, возможно также закодировать несколько торговых стратегий в одну программу, что позволит конечному пользователю по своему усмотрению выбирать наиболее подходящую стратегию вручную.

Поэтому очевидно, что необходимо создавать программы, способные самостоятельно выбирать и переключаться между различными стратегиями в зависимости от преобладающих на рынке условий. Чтобы добиться этого, понадобится количественный метод измерения силы трендов или возвратов к средним значениям на рынке. Оценив силу каждого изменения, наш советник потенциально может выбрать оптимальную стратегию, которой и будет следовать.

В статье показано, как можно грамотно достичь нашей цели с помощью матрицы перехода для моделирования поведения рынка и выбора, какую стратегию использовать — следования за рынком или возврата к среднему значению. Начнем с разработки глубокого понимания матриц переходов. Затем рассмотрим, как эти математические инструменты можно использовать для создания интеллектуальных торговых алгоритмов с улучшенными возможностями принятия решений.


Кто такой Андрей Марков?

XIX век был эпохой блистательных открытий, таких как изобретение Александром Грэмом Беллом телефона, создание Томасом Эдисоном лампы накаливания или разработка Гульельмо Маркони первого радиоприемника. Однако среди всех этих научных достижений немногие имеют большее значение для разработчиков алгоритмов, чем вклад блестящего российского математика Андрея Маркова.

Марков

Рис. 1. Фотография Андрея Маркова в молодости.

Марков работал над многими задачами, которые требовали от него моделирования совершенно случайных процессов вроде нашей проблемы с непредсказуемостью динамики рынка. Он формально описал структуру, известную сегодня как «цепь Маркова». Попробуем разобраться в ней интуитивно.

Представьте, что вы управляете предприятием общественного транспорта, которое больше 70 лет обеспечивает автобусное сообщение в Германии. Предприятие рассматривает расширение своего автобусного парка, а вы, как руководитель, должны решить, на какие маршруты следует направить дополнительные автобусы, а в какие направления нет смысла инвестировать.

Подход к задаче как к цепи Маркова мог бы упростить для менеджера процесс принятия решений. Представим, что следующая диаграмм отображает цепь Маркова для всех завершенных поездок, которые предприятие выполнило за свою 70-летнюю историю.


Модель Маркова

Рис. 2. Условная модель Маркова для транспортного предприятия и маршруты, случайно используемые его клиентами.

Проинтерпретируем приведенную выше цепь Маркова. Мы можем наблюдать, что 40% садящихся во Франкфурте пассажиров обычно сходят в Мюнхене, а остальные 60%, как правило, едут до Кельна. Среди пассажиров в Кельне 30% обычно возвращаются во Франкфурт, а 70% обычно направляются в Берлин. Модель наглядно отображает самые популярные маршруты, используемые клиентами.

Кроме того, обратите внимание: есть направления без прямого сообщения. Отсутствие сообщения между некими двумя городами означает, что за 70 лет существования компании никому из клиентов не возникло необходимости проехать из одного из них в другой напрямую. Поэтому вы, как руководитель, можете с уверенностью заключить, что добавление автобусов на маршрут «Франкфурт—Берлин» может оказаться не такими прибыльным по сравнению с другими популярными маршрутами, такими как «Франкфурт—Кельн».

Суть в том, что матрица перехода показывает различные возможности перехода из одного состояния в другое. По мнению Андрея Маркова, вероятность какого-либо состояния зависит только от его текущего состояния. Это помогает понять, как меняется система и какое следующее состояние она, вероятнее всего, перейдет. Прежде чем применить матрицы перехода к финансовым рынкам, сначала мы должны определить все возможные состояния, в которых может находиться рынок.



Построение стратегии: определение состояний рынка

Один из эффективных способов определения состояний рынка — использование технических индикаторов. В приведенном ниже примере мы применили скользящую среднюю к символу из терминала MetaTrader 5. Можем определить состояния следующим образом: «Всякий раз при закрытии свечи выше скользящей средней состояние будет ВВЕРХ (1 на графике), а при ее закрытии ниже скользящей средней это будет состояние ВНИЗ (2 на графике)».


Определение состояний рынка

Рис.3. Схематическая диаграмма, показывающая состояние рынка как 1 или 2.

Мы можем построить цепь Маркова, чтобы смоделировать переход рынка от закрытия выше скользящей средней к закрытию ниже нее. Другими словами, цепь Маркова, моделирующая взаимосвязь между скользящей средней и ценой закрытия, могла бы ответить на такой вопрос, как: «Если одна свеча закрывается над скользящей средней, какова вероятность, что следующая свеча тоже закроется над скользящей средней?» Если такая вероятность превышает 0.5, такой рынок может подходить для стратегий следования за трендом. В противном случае более вероятно, что рынок подходит для стратегий возврата к среднему значению.


Начало работы: создание первой матрицы перехода

Для начала импортируем свои стандартные библиотеки на языке Python для связи с терминалом MetaTrader5 и проведения анализа данных.

#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
EURUSD имеет минимальный объем: 0.01
Теперь нужно указать, какой объем данных из терминала MetaTrader5 нам понадобится.
#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

Рассчитать матрицу переходов легко:

  1. Сначала определяем все возможные состояния (мы определили 2 простых состояния: ВВЕРХ и ВНИЗ).
  2. Подсчитываем, сколько свечей попадает в каждое соответствующее состояние.
  3. Рассчитываем, какова доля всех свечей в состоянии ВВЕРХ, за которыми следовала свеча в том же состоянии.
  4. Рассчитываем, какова доля всех свечей в состоянии ВНИЗ, за которыми следовала свеча в том же состоянии.
#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. Сделка, которую наш торговый алгоритм выбрал на следующий день.

Но это еще не все, что можно сказать о матрицах переходов. Однако это неплохое введение в тему. Прежде чем завершить обсуждение, важно уточнить, какие переменные влияют на нашу матрицу переходов и как мы при необходимости можем управлять такой матрицей.


Символ
Первая переменная, которая влияет на нашу матрицу переходов, — очевидно, выбранный символ. Например, если мы оставим все остальные переменные без изменений и просто выберем новый символ, Boom 1000 Index, то вот как будет выглядеть матрица переходов.



ВВЕРХ ВНИЗ
ВВЕРХ 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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Sibusiso Steven Mathebula
Sibusiso Steven Mathebula | 26 июл. 2024 в 03:38
В приведенной выше статье матрицы и векторы были использованы для оптимизации торговой стратегии без обязательного применения традиционного подхода с использованием нейронных сетей. Похоже, что (по крайней мере, для меня), можно построить самооптимизирующийся советник, не обязательно используя NN, включающие функции активации, то есть вам не нужны функции активации или нейроны для самооптимизации вашего советника. Скорее всего, меня могут поправить, эй. Я могу определенно ошибаться, я могу действительно ужасно ошибаться, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, ........... неправильно понимать все об оптимизации и NN mate...... Я ваш сосед, здесь, в ЮАР.
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana | 27 июл. 2024 в 11:14
Sibusiso Steven Mathebula торговой стратегии без обязательного применения традиционного подхода с использованием нейронных сетей. Похоже, что (по крайней мере, для меня), можно построить самооптимизирующийся советник, не обязательно используя NN, включающие функции активации, то есть вам не нужны функции активации или нейроны для самооптимизации вашего советника. Скорее всего, меня могут поправить, эй. Я могу определенно ошибаться, я могу действительно ужасно ошибаться, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, я могу, ........... неправильно понимать все об оптимизации и NN mate...... Я ваш сосед, здесь, в ЮАР.
Эй, SIbusiso, Ujani Budi?

Ну, как вы знаете, есть много способов сделать что-либо. Подход, который я изложил здесь, призван помочь вам быстро получить надежные результаты. Однако все имеет свою цену, матрица переходов, которую вы будете наблюдать, сильно зависит от того, сколько данных вы собрали, но по мере того, как вы собираете все больше и больше данных, матрица переходов становится стабильной и перестает меняться (она сходится).

Позвольте мне объяснить вам это так: матрица переходов и NN-подход решают совершенно разные проблемы, они отвечают на разные вопросы. Матрица переходов ничего не предсказывает, она просто обобщает/рассказывает нам, что произошло в прошлом, и не говорит нам, что может произойти в будущем.

NN, с другой стороны, говорит нам о том, что, скорее всего, произойдет в будущем. В одном советнике можно использовать и то, и другое.
thiezo
thiezo | 1 янв. 2025 в 07:09
Привет, Гамучирай. Эта статья обращена непосредственно ко мне, и я благодарю вас за то, что вы открыли наши умы. Я совсем новичок в кодинге и учусь, читая и кодируя из таких статей, как ваша. Моя самая большая проблема - это Python. Я даже не знаю, с чего начать, тем более что я быстрее учусь, если речь идет о торговле, потому что тогда я могу проводить бэктесты и внедрять идеи в свой советник. Пожалуйста, подскажите, где я могу изучить этот язык. Я кодировал только версию на MQL5, и проблема, с которой я столкнулся, заключается в том, что 'max_arg' остается равным 0, поэтому советник остается бычьим. С моим ограниченным пониманием я попробовал манипулировать несколькими параметрами и остановился на том, что код будет ставить покупки и продажи одновременно. Возможно, я упускаю какую-то важную деталь. Я могу отправить вам скопированный код или модифицированный код, если ваш код работает правильно на вашей стороне. Возможно, вы сможете заметить проблему. Я использую загруженные данные, поскольку нахожусь в отпуске и поэтому работаю в автономном режиме. Может ли это вызвать проблемы? Я ценю работу, которую вы делаете, и ваши статьи великолепны. Я из ЮАР и все, что я могу сказать, это спасибо Tsano.
Возможности Мастера MQL5, которые вам нужно знать (Часть 35): Регрессия опорных векторов Возможности Мастера MQL5, которые вам нужно знать (Часть 35): Регрессия опорных векторов
Регрессия опорных векторов — это идеалистический способ поиска функции или "гиперплоскости" (hyper-plane), который наилучшим образом описывает взаимосвязь между двумя наборами данных. Мы попытаемся использовать его при прогнозировании временных рядов в пользовательских классах Мастера MQL5.
Удаленный профессиональный риск-менеджер Forex на Python Удаленный профессиональный риск-менеджер Forex на Python
Делаем удаленный профессиональный риск-менеджер Для Forex на Python, разворачиваем его на сервере по шагам. В процессе статьи поймем, как программно управлять рисками на Форекс, и как больше не слить депозит на Форекс.
Применение теории игр Нэша с фильтрацией НММ в трейдинге Применение теории игр Нэша с фильтрацией НММ в трейдинге
Настоящая статья посвящена применению теории игр Джона Нэша, в частности теории равновесия Нэша, в трейдинге. В ней обсуждается, как трейдеры могут использовать скрипты Python и платформу MetaTrader 5 для выявления и использования неэффективности рынка спомощью принципов Нэша. В статье приводится пошаговое руководство по реализации этих стратегий, включая использование скрытых Марковских моделей (HMM) и статистического анализа, для повышения эффективности торговли.
Алгоритм успешного ресторатора —  Successful Restaurateur Algorithm (SRA) Алгоритм успешного ресторатора — Successful Restaurateur Algorithm (SRA)
Алгоритм успешного ресторатора (SRA) — инновационный метод оптимизации, вдохновленный принципами управления ресторанным бизнесом. В отличие от традиционных подходов, SRA не отбрасывает слабые решения, а улучшает их, комбинируя с элементами успешных. Алгоритм показывает конкурентоспособные результаты и предлагает свежий взгляд на балансирование между исследованием и эксплуатацией в задачах оптимизации.