English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации - Моделирование рынка (Часть 03):  Внесение корректировок (I)

Разработка системы репликации - Моделирование рынка (Часть 03): Внесение корректировок (I)

MetaTrader 5Примеры | 13 июля 2023, 10:21
525 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье, "Разработка системы репликации - Моделирование рынка (Часть 02): Первые эксперименты (II), мы построили систему, способную генерировать 1-минутный бар с приемлемым временем обработки для использования его в моделировании рынка. Однако мы понимали, что не можем контролировать происходящее. Наши возможности были ограничены выбором одних моментов и корректировкой других. При запуске системы, у нас оставалось мало возможностей.

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

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


Планирование

Данный этап планирования довольно простой, ведь, если смотреть на то, как работала система в прошлой статье, становится ясно, что нам нужно сделать немедленно. Необходимо создать форму управления для паузы, воспроизведения и, прежде всего, выбора конкретного времени для начала исследования.

В текущем виде мы всегда будем начинать с первого торгового тикета. Предположим, мы хотим провести исследование, начиная с пятого часа работы рынка, то есть с 14:00 (учитывая, что рынок открывается в 9:00). В данном случае нам пришлось бы ждать 5 часов репликации и только потом проводить нужный анализ. Это совершенно невыполнимо, так как если мы попытаемся остановить репликацию, она закроется, и нам придется начинать все сначала, с первого торгового тикета.

Теперь ясно, что нам нужно сделать немедленно, ведь то, что происходит сейчас - демотивирует и препятствует его использованию, даже если он интересен.

Хорошо, сейчас у нас есть общее направление, и мы можем приступать к реализации.


Реализация

Реализация будет довольно любопытная, поскольку нам придется пройти разные пути, от самых простых до самых разнообразных, чтобы действительно достигнуть реализации системы управления. Тем не менее, все шаги легко понять, если внимательно прочитать объяснения и следовать статьям по порядку, без пропусков какой-то из них и без попыток перейти на несколько этапов вперед.

Вопреки мнению многих, мы не собираемся использовать DLL-библиотеки в системе. Для реализации системы репликации мы будем просто использовать язык MQL5. Идея заключается в том, чтобы максимально использовать возможности MetaTrader 5, и показать насколько далеко мы можем продвигаться внутри платформы при создании необходимых функций. Обращение к внешней реализации немного портит веселье в работе с MQL5, создавая впечатление, что он не может удовлетворить наши потребности.

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


Как создать очень простой советник.

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

Но почему именно советник? Мы могли бы использовать индикатор вместо советника, который работал бы точно так же. Однако я хочу использовать советник, потому что он понадобится нам позже для создания репликации ордеров. Кроме того, мы попытаемся использовать ту же систему ордеров, которая применялась в другой моей серии статей, под названием "Разработка торгового советника с нуля". Но пока нам не надо беспокоиться о системе ордеров: нам предстоит ещё много работы, прежде чем мы доберемся до нее.

Полный код нашего базового советника можно увидеть ниже:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
int OnInit()
{
        Control.Init();
                
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+

Обратите внимание, что код максимально прост, но его достаточно, чтобы управлять работой сервиса. Теперь давайте рассмотрим конкретную часть кода, а именно класс объекта управления, выделенный выше. На этой ранней стадии разработки данный код не очень сложный. Мы внедряем только одну кнопку, которая позволяет воспроизвести или приостановить сервис репликации. Давайте рассмотрим данный класс подробнее.

Первое, на что следует обратить внимание - это код, который показываем ниже:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <Market Replay\Interprocess.mqh>
//+------------------------------------------------------------------+
#define def_ButtonPlay  "Images\\Market Replay\\Play.bmp"
#define def_ButtonPause "Images\\Market Replay\\Pause.bmp"
#resource "\\" + def_ButtonPlay
#resource "\\" + def_ButtonPause
//+------------------------------------------------------------------+
#define def_PrefixObjectName "Market Replay _ "

Первый важный момент - это выделенный СИНИМ цветом заголовочный файл, который позже рассмотрим более детально. Далее у нас есть несколько определений объектов bitmap, которые будут представлять кнопки воспроизведения и паузы. Ничего сложного тут нет. После этих пунктов мы переходим к коду класса, который довольно компактный. Полный код можно увидеть ниже.

class C_Controls
{
        private :
//+------------------------------------------------------------------+
                string  m_szBtnPlay;
                long            m_id;
//+------------------------------------------------------------------+
                void CreateBtnPlayPause(long id)
                        {
                                m_szBtnPlay = def_PrefixObjectName + "Play";
                                ObjectCreate(id, m_szBtnPlay, OBJ_BITMAP_LABEL, 0, 0, 0);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_XDISTANCE, 5);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_YDISTANCE, 25);
                                ObjectSetInteger(id, m_szBtnPlay, OBJPROP_STATE, false);
                                ObjectSetString(id, m_szBtnPlay, OBJPROP_BMPFILE, 0, "::" + def_ButtonPause);
                                ObjectSetString(id, m_szBtnPlay, OBJPROP_BMPFILE, 1, "::" + def_ButtonPlay);
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_Controls()
                        {
                                m_szBtnPlay = NULL;
                        }
//+------------------------------------------------------------------+
                ~C_Controls()
                        {
                                ObjectDelete(ChartID(), m_szBtnPlay);
                        }               
//+------------------------------------------------------------------+
                void Init(void)
                        {
                                if (m_szBtnPlay != NULL) return;
                                CreateBtnPlayPause(m_id = ChartID());
                                GlobalVariableTemp(def_GlobalVariableReplay);
                                ChartRedraw();
                        }
//+------------------------------------------------------------------+
                void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
                        {
                                u_Interprocess Info;
                                
                                switch (id)
                                {
                                        case CHARTEVENT_OBJECT_CLICK:
                                                if (sparam == m_szBtnPlay)
                                                {
                                                        Info.s_Infos.isPlay = (bool) ObjectGetInteger(m_id, m_szBtnPlay, OBJPROP_STATE);
                                                        GlobalVariableSet(def_GlobalVariableReplay, Info.Value);
                                                }
                                                break;
                                }
                        }
//+------------------------------------------------------------------+
};

Здесь у нас две основные функции: Init и DispatchMessage. Они выполняют всю работу, необходимую для функционирования советника на этой ранней стадии. Чтобы лучше объяснить некоторые из этих деталей, давайте рассмотрим фрагменты этих двух функций ниже. Начнем с функции Init.

void Init(void)
{
        if (m_szBtnPlay != NULL) return;
        CreateBtnPlayPause(m_id = ChartID());
        GlobalVariableTemp(def_GlobalVariableReplay);
        ChartRedraw();
}

При вызове функции Init, она сначала проверяет, были ли уже созданы элементы управления. Если это уже произошло, функция возвращается. Это важно, поскольку при изменении периода графика или любом изменении, которое требует от советника перезагрузки графика (что бывает довольно часто), состояние сервиса репликации не будет изменено. То есть, если сервис запущен, он будет продолжать в состоянии паузы; если он в состоянии воспроизведения, он будет продолжать отправлять тики.

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

После этого нам нужно будет применить объекты на экран. Это важно, потому что если не выполним обновления, советник загрузится, но сервис застрянет, что заставит нас думать, что система дала сбой. На самом деле, мы ждем, пока MetaTrader 5 обновит для нас график, чтобы объекты построились и мы могли воспроизвести репликацию рынка.

Вы заметили, как всё просто? Теперь давайте проанализируем код функции DispatchMessage, который на данном этапе также прост. Код представлен ниже:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        u_Interprocess Info;
                        
        switch (id)
        {
                case CHARTEVENT_OBJECT_CLICK:
                        if (sparam == m_szBtnPlay)
                        {
                                Info.s_Infos.isPlay = (bool) ObjectGetInteger(m_id, m_szBtnPlay, OBJPROP_STATE);
                                GlobalVariableSet(def_GlobalVariableReplay, Info.Value);
                        }
                        break;
        }
}

Мы используем MetaTrader 5 для того, чтобы он управлял всем за нас. Мы используем объединение под названием u_Interprocess, для установки глобальной переменной терминала с помощью проверки состояния кнопки bitmap. Таким образом, мы устанавливаем глобальную переменную терминала для передачи информации сервисному процессу, который отвечает за создание репликации.

Поэтому мы всегда будем запускать систему репликации в состоянии паузы. После того как советник с его объектами загружен на график, мы можем воспроизвести или приостановить репликации рынка по своему усмотрению. Это сделает ситуацию немного интереснее.

Файл Interprocess.mqh - как мы его понимаем

Как вы уже догадались, при переходе от использования скриптов к использованию советника, мы внесли изменения и в сервис репликации. Перед рассмотрением этих изменений, давайте взглянем на файл Interprocess.mqh, который полностью показан в его текущей стадии развития в коде ниже:
#define def_GlobalVariableReplay "Replay Infos"
//+------------------------------------------------------------------+
union u_Interprocess
{
        double Value;
        struct st_0
        {
                bool isPlay;
                struct st_1
                {
                        char Hours;
                        char Minutes;
                }Time[3];
        }s_Infos;
};

Это простое определение предоставляет нам имя, но это не просто имя. Это будет имя глобальной терминальной переменной, которая будет использоваться на этом этапе для обеспечения связи между советником и сервисом. Та часть, которая может оказаться сложной для тех, у кого меньше опыта в программировании, - это объединение.

Давайте разберемся, что на самом деле представляет собой это объединение, а затем поймем, как оно используется для передачи информации между советником и сервисом. Чтобы понимать всю сложность вопроса, необходимо знать, сколько битов представляет каждый вид данных при его использовании. Чтобы облегчить себе жизнь, предлагаю ознакомиться с приведенной ниже таблицей:

Тип Количество битов
Bool 1 бит
Char или UChar 8 бит
Short или UShort 16 бит
Int или UInt 32-бит
Long или ULong 64-бит

В этой таблице указаны типы целых чисел со знаком и без него, затем количество бит (только не путайте биты с байтами). Бит - это наименьшая единица информации, представляющая собой включенное или выключенное состояние, или 1 и 0 в двоичной системе. Байт представляет собой набор битов.

Глядя на эту таблицу, некоторые люди могут не понимать следующую мысль: в переменной типа uchar у нас будет 8 переменных типа bool. То есть, переменная uchar соответствует «объединению» (хотя это слово не самое подходящее) 8 переменных bool. В коде это будет выглядеть следующим образом:

union u_00
{
        char info;
        bool bits[8];
}Data;

Длина этого объединения составляет 8 бит, или 1 байт. Вы можете изменить содержание информации, записав биты в массиве и выбрав определенную позицию. Например, чтобы сделать Data.info равным 0x12, можно выполнить один из двух вариантов, показанных ниже:

Data.info = 0x12;

ou 

Data.bits[4] = true;
Data.bits[1] = true;

Так или иначе, мы получим тот же результат, если в переменной Data.info все начальные биты установлены в 0. Это то, что представляет собой объединение.

Теперь давайте вернемся к нашему исходному коду. Самый большой тип, встречающийся в 64-битной системе, - это тип long (со знаком) или ulong (без знака). Мы напоминаем о различии между значениями со знаком и без него потому, что в случае значения со знаком можно представлять отрицательные значения, а без знака - только положительные. Таким образом, в этом случае мы получили бы что-то такое:

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

Плавающие числа - это переменные, значение которых не является точным, но всё же может быть использовано для представления вычисляемых значений. В основном, существует 2 типа:

Тип Количество битов
Float 32-бит
Double 64-бит

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

Посмотрев на тип, используемый в глобальных переменных терминала, можно видеть, что они бывают только плавающего типа, точнее типа double, т.е. 64 бита. Теперь возникает вопрос: у какого целочисленного типа есть такая же длина? Именно так, как вы ответили: тип long, у которого также 64 бита. Поэтому при соединении типа long и double, мы можем одновременно представлять две совершенно разные вещи,

но здесь возникает немного сложный вопрос: как вы узнаете, какой тип используется? Чтобы решить эту проблему, мы используем не полный тип, а лишь его фрагменты и присваиваем этим фрагментам имена. Таким образом, рождается объединение, которое вы можете увидеть в коде файла Interprocess.mqh.

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

Обратите внимание, что всё это делается в соответствии с очень простыми и понятными правилами. Это было бы очень трудно сделать, если бы мы попытались напрямую создать плавающие значения, а затем понять их смысл.

Таким образом, вы должны были понять, что такое объединение и как мы будем его использовать. Но помните: если вы хотите использовать глобальные переменные терминала, тип которых double и имеет 64 бита, то создаваемое объединение не может превышать эти самые 64 бита, в обратном случае часть информации будет потеряна.


Как создается сервис репликации

Данная часть, пожалуй, больше всего потребует от вас внимания, чтобы понять происходящее Если вы делаете что-то без понимания, то вам будет очень легко запутаться. Хотя это звучит просто, но есть детали, которые при неправильном понимании, могут заставить вас задаться вопросом: почему система работает так, как описано и продемонстрировано, но вы не можете заставить ее работать на вашей собственной рабочей платформе.

Давайте посмотрим на файл сервиса репликации. В настоящее время он по-прежнему остается довольно компактным и простым. Вы можете увидеть его целиком в приведенном ниже коде:

#property service
#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
#include <Market Replay\C_Replay.mqh>
//+------------------------------------------------------------------+
input string    user01 = "WINZ21_202110220900_202110221759"; //Arquivo com ticks
//+------------------------------------------------------------------+
C_Replay        Replay;
//+------------------------------------------------------------------+
void OnStart()
{
        ulong t1;
        int delay = 3;
        long id;
        u_Interprocess Info;
        bool bTest = false;
        
        if (!Replay.CreateSymbolReplay(user01)) return;
        id = Replay.ViewReplay();
        Print("Aguardando permissão para iniciar replay ...");
        while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(750);
        Print("Serviço de replay iniciado ...");
        t1 = GetTickCount64();
        while ((ChartSymbol(id) != "") && (GlobalVariableGet(def_GlobalVariableReplay, Info.Value)))
        {
                if (!Info.s_Infos.isPlay)
                {
                        if (!bTest)     bTest = (Replay.Event_OnTime() > 0); else       t1 = GetTickCount64();
                }else if ((GetTickCount64() - t1) >= (uint)(delay))
                {
                        if ((delay = Replay.Event_OnTime()) < 0) break;
                        t1 = GetTickCount64();
                }
        }
        Replay.CloseReplay();
        Print("Serviço de replay finalizado ...");
}
//+------------------------------------------------------------------+

Если вы возьмете этот код и просто создадите файл "WINZ21_202110220900_202110221759"который используется как основа для создания репликации, и попытаетесь запустить его, то вы увидите, что ничего не произойдет, и даже если вы используете файл, который находится во вложении, и попытаетесь запустить его из этого кода, то в таком варианте тоже ничего не произойдет. Но почему? Причина заключается в коде id = Replay.ViewReplay(); данный код делает кое-что, что нужно понять, чтобы действительно использовать систему репликации. Что бы вы ни делали, если вы не понимаете происходящее, то ничто не будет иметь смысла, но прежде чем рассмотреть код внутри ViewReplay(), давайте сначала разберемся с потоком данных в приведенном выше коде.

Чтобы понять, как это работает на практике, давайте разобьем его на более мелкие части, начиная со следующего фрагмента:

if (!Replay.CreateSymbolReplay(user01)) return;

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

id = Replay.ViewReplay();

Данная строка загрузит советника, но более подробно это будет рассмотрено позже, так что давайте двигаться дальше.

while (!GlobalVariableCheck(def_GlobalVariableReplay)) Sleep(750);

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

t1 = GetTickCount64();

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

while ((ChartSymbol(id) != "") && (GlobalVariableGet(def_GlobalVariableReplay, Info.Value)))

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

        u_Interprocess Info;

//...

        if (!Info.s_Infos.isPlay)

Здесь мы проводим проверку состояния, о котором было сообщено трейдером или пользователем репликации. Если мы находимся в режиме воспроизведения, этот тест будет провален. Но если мы будем находиться в режиме паузы, то всё получится. Обратите внимание, как мы используем объединение для захвата нужного бита в значении double. Без этого объединения такое было бы немыслимо.

Как только мы перейдем в режим паузы, мы выполним следующую строку:

if (!bTest) bTest = (Replay.Event_OnTime() > 0); else t1 = GetTickCount64();

Данная строка позволяет отправлять только первый торговый тик на актив. Это важно по ряду причин, которые мы рассмотрим ниже. Как только мы сделаем это в любое другое время, при котором сервис репликации будет «приостановлен», мы будем фиксировать текущее значение счетчика. Правда, это «приостановленное"» состояние не относится к тому, что сервис действительно приостановлен. Он просто не отправляет тики на актив репликации, поэтому я и говорю, что он «приостановлен».

Но если пользователь или трейдер хотят начать или возобновить выполнение репликации рынка, тогда надо будет просмотреть новую строку кода. Её можно увидеть чуть ниже:

else if ((GetTickCount64() - t1) >= (uint)(delay))

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

if ((delay = Replay.Event_OnTime()) < 0) break;

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

Эти функции будут выполняться до тех пор, пока не будет отправлен последний тик или не будет закрыт график актива репликации. Когда это произойдет, выполним следующую строку:

Replay.CloseReplay();

Она окончательно положит конец репликации.

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


ViewReplay класса C_Replay - почему это так важно?

Данный код можно увидеть ниже:

long ViewReplay(void)
{
        m_IdReplay = ChartOpen(def_SymbolReplay, PERIOD_M1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        return m_IdReplay;
}

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

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

После этого, загружаем определенный шаблон и применяем его к только что открывшемуся окну графика. Важно отметить, что данный шаблон довольно специфичен, это не обычный шаблон. Для создания этого шаблона, в случае если вы его удалили (он будет в приложении), необходимо скомпилировать советника системы репликации рынка и применить его к любому активу. Затем нужно всего лишь сохранять этот график как шаблон с именем «Market Replay». Если этот файл не существует или в нем отсутствует советник, то вся система выйдет из строя, что бы мы ни делали.

В некотором роде можно было бы это решить, если бы вместо советника использовался индикатор. В таком случае мы бы вызывали этот индикатор через MQL5 (чисто теоретически), но, как я уже упоминал в начале статьи, у меня есть свои причины использовать советник вместо индикатора. Поэтому, чтобы решить максимально простым способом проблему загрузки, мы используем шаблон, содержащий советника системы репликации.

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

Теперь мы возвращаем идентификатор графика актива репликации,так как у нас не будет другого способа сделать это в другом месте, и нам нужна эта информация, чтобы сервис знал, когда график был закрыт.

Все остальные функции класса C_Replay достаточно просты для понимания, так что мы не уделим этому внимание в данной статье.


Заключение

На видео ниже можно увидеть, как система загружается и как она работает на практике.



В следующей статье мы создадим систему управления положением, чтобы можно было выбирать время начала воспроизведения. До встречи!


Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/10706

Прикрепленные файлы |
Market_Replay.zip (10282.77 KB)
Нейросети — это просто (Часть 49): Мягкий Актор-Критик (Soft Actor-Critic) Нейросети — это просто (Часть 49): Мягкий Актор-Критик (Soft Actor-Critic)
Мы продолжаем рассмотрение алгоритмов обучения с подкреплением в решении задач непрерывного пространства действий. И в данной статье предлагаю познакомиться с алгоритмом Soft Аctor-Critic (SAC). Основное преимущество SAC заключается в способности находить оптимальные политики, которые не только максимизируют ожидаемую награду, но и имеют максимальную энтропию (разнообразие) действий.
Изучаем PrintFormat() и берем готовые к использованию примеры Изучаем PrintFormat() и берем готовые к использованию примеры
Статья будет полезна как новичкам, так и уже опытным разработчикам. В ней мы рассмотрим работу функции PrintFormat(), разберём примеры форматирования строк и напишем шаблоны для вывода различной информации в журнал терминала.
Разработка системы репликации - Моделирование рынка (Часть 04): Внесение корректировок (II) Разработка системы репликации - Моделирование рынка (Часть 04): Внесение корректировок (II)
Сегодня мы продолжим разработку системы и управления. Без возможности управления сервисом сложно двигаться вперед и совершенствовать систему.
Нейросети — это просто (Часть 48): Методы снижения переоценки значений Q-функции Нейросети — это просто (Часть 48): Методы снижения переоценки значений Q-функции
В предыдущей статье мы познакомились с методом DDPG, который позволяет обучать модели в непрерывном пространстве действий. Однако, как и другие методы Q-обучения, DDPG склонен к переоценки значений Q-функции. Эта проблема часто приводит к обучению агента с неоптимальной стратегией. В данной статье мы рассмотрим некоторые подходы преодоления упомянутой проблемы.