English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
Разработка торгового советника с нуля (Часть 20): Новая система ордеров (III)

Разработка торгового советника с нуля (Часть 20): Новая система ордеров (III)

MetaTrader 5Торговые системы | 22 июля 2022, 12:55
1 026 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка торгового советника с нуля (Часть 19) мы сосредоточились на демонстрации изменений кода, которые были реализованы для обеспечения работы новой системы ордеров. Как только эти изменения будут реализованы, я смогу на 100% работать над реальной проблемой. Эта проблема заключается в реализации системы ордеров, которая на 100% наглядна и понятна для тех, кто торгует без необходимости знать, сколько стоит тик, или где должен быть размещен ордер, чтобы заработать X сумму, или где находится стоп-лосс, чтобы избежать потери Y.

Создание такой системы требует хорошего владения MQL5, а также понимания того, как на самом деле работает платформа MetaTrader 5 и какие ресурсы она нам предоставляет.


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

1.0.1. Дизайн индикаторов

Идея здесь (это даже больше, чем просто идея) в том, что я на самом деле собираюсь сделать, а именно показать то, как реализовать систему в этой статье. Как создать что-то похожее на рисунок на изображении ниже:

И ведь довольно легко узнать, что это такое, даже без моего объяснения нам удалось понять, что есть кнопка для закрытия, есть значение и точка для облегчения перетаскивания ордера. Но это еще не все, когда Stop Loss становится Stop Gain, система отобразит это так, как показано ниже:

То есть будет очень легко узнать: когда, сколько, где и стоит ли вообще сохранять определенную позицию или нет.

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

В случае отложенного ордера это будет выглядеть так:

Для одной позиции внешний вид будет отличаться, что можно увидеть ниже:

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

Продолжая планирование, мы можем заметить, что в основном у нас будет 5 объектов в каждом индикаторе, а это означает, что нам нужно будет заставить MetaTrader 5 обрабатывать 5 объектов одновременно для каждого индикатора. В случае отложенного OCO-ордера, MetaTrader 5 должен будет справиться с 15 объектами. Точно так же, как и в случае OCO-позиции, MetaTrader 5 должен справиться с 15 объектами (и это на ордер или позицию), а это значит, что если у вас есть 4 отложенных OCO-ордера и 1 открытая OCO-позиция, то MetaTrader 5 должен будет иметь дело с 25 объектами, не считая остальных, которые также будут на графике активов. И всё это при использовании только одного актива на платформе.

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


1.0.2. Выбор объектов

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

У нас есть два способа это сделать, и, к счастью, MetaTrader 5 охватывает оба: первый — использование позиции по временным и ценовым координатам, а второй — использование декартовых координат X и Y.

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

В другой статье мы уже просмотрели эту систему и показывали, как выбирать объекты, подробнее см. Несколько индикаторов на графике (Часть 05).

Раз мы завершили это планирование, то наконец можем перейти к самому кодированию: пришло время применить всё на практике.


2.0. Реализация

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


2.0.1. Создание интерфейсной основы

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


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

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


2.0.1.1. Класс C_Object_Base

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

class C_Object_Base
{
        public  :
//+------------------------------------------------------------------+
void Create(string szObjectName, ENUM_OBJECT typeObj)
{
        ObjectCreate(Terminal.Get_ID(), szObjectName, typeObj, 0, 0, 0);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_SELECTABLE, false);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_SELECTED, false);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BACK, true);
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TOOLTIP, "\n");
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BACK, false);
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TOOLTIP, "\n");
        PositionAxleY(szObjectName, 9999);
};

// ... Остальной код класса

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

void PositionAxleX(string szObjectName, int X)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XDISTANCE, X);
};
//+------------------------------------------------------------------+
virtual void PositionAxleY(string szObjectName, int Y)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y);
};

Код позиционирования по оси Y будет зависеть от каждого конкретного объекта, но даже если у объекта нет конкретного кода, класс предоставляет общий. У нас есть общий способ указания цвета объекта, который совершается с помощью кода.

virtual void SetColor(string szObjectName, color cor)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, cor);
}

И также у нас есть способ определения размеров объектов.

void Size(string szObjectName, int Width, int Height)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XSIZE, Width);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE, Height);
};

Таким образом, мы закончили класс C_Object_Base, но к нему мы вернемся позже.


2.0.1.2. Класс C_Object_BackGround

Теперь мы создадим два других класса для поддержки наших графических объектов. Первый из них — это класс C_Object_BackGround. Он создаст фоновое поле для последующего получения других элементов. Его код довольно прост. Можно увидеть его ниже в полном объеме:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Base.mqh"
//+------------------------------------------------------------------+
class C_Object_BackGround : public C_Object_Base
{
        public:
//+------------------------------------------------------------------+
		void Create(string szObjectName, color cor)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_RECTANGLE_LABEL);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clrNONE);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, cor);
                        }
//+------------------------------------------------------------------+
virtual void PositionAxleY(string szObjectName, int Y)
                        {
                                int desl = (int)(ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE) / 2);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y - desl);
                        }
//+------------------------------------------------------------------+
};

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


2.0.1.3. Класс C_Object_TradeLine

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

#property copyright "Daniel Jose"
#include "C_Object_BackGround.mqh"
//+------------------------------------------------------------------+
class C_Object_TradeLine : public C_Object_BackGround
{
        private :
                static string m_MemNameObj;
        public  :
//+------------------------------------------------------------------+

// ... Внутренный код класса

//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+
string C_Object_TradeLine::m_MemNameObj = NULL;
//+------------------------------------------------------------------+

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

Следующее, на что мы обратим внимание - это код создания объекта.

void Create(string szObjectName, color cor)
{
        C_Object_BackGround::Create(szObjectName, cor);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
        SpotLight(szObjectName);
};

Чтобы реализовать его правильно, мы используем класс C_Object_BackGround, где фактически мы создадим поле, которое будет служить линией. Повторю, это так, ведь если бы использовался другой тип объекта, мы бы не добились того же поведения, которое у нас будет. Единственный объект, который делает всё так, как нам нужно - это тот, который присутствует в классе C_Object_Background, поэтому мы изменим его в соответствии с нашими нуждами и, таким образом, создадим строку.

Далее, мы увидим код, отвечающий за выделение строки.

void SpotLight(string szObjectName = NULL)
{
        if (szObjectName != NULL) ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE, (szObjectName != NULL ? 4 : 3));
        if (m_MemNameObj != NULL) ObjectSetInteger(Terminal.Get_ID(), m_MemNameObj, OBJPROP_YSIZE, 3);
        m_MemNameObj = szObjectName;
};

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

Зная это, приведенный выше код вместе со следующим кодом заставляют старый код выбора исчезнуть, таким образом, MetaTrader 5 сообщит нам, каким индикатором мы работаем.

string GetObjectSelected(void) const { return m_MemNameObj; }

Но есть еще одна функция, на которую стоит обратить внимание. Это та, которая производит позиционирование линии по оси Y. Мы ее покажем чуть ниже.

virtual void PositionAxleY(string szObjectName, int Y)
{
        int desly = (m_MemNameObj == szObjectName ? 2 : 1);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y - desly);
};

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

Таким образом, два наших объекта теперь полностью завершены, но прежде чем мы сможем увидеть их на экране (как показано выше), нам надо будет сделать некоторые вещи в классе C_ObjectsTrade.


2.0.2. Модификация класса C_ObjectsTrade

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

Сначала мы создаем перечисление событий.

enum eEventType {EV_GROUND = 65, EV_LINE};

По мере создания объектов мы должны добавлять сюда новые события, а они должны быть чем-то важным. Однако, у каждого объекта будет только один тип события, и это событие будет генерироваться с помощью MetaTrader 5, наш код только позаботится о том, чтобы событие было правильно обработано.

Как только это будет сделано, мы создадим переменные, которые предоставят нам доступ к каждому из объектов.

C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;

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

Собственно, теперь мы делаем первое изменение в коде из предыдущей статьи.

inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev)
{
        return StringFormat("%s%c%c%c%d%c%c", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)ev);
}

Раньше не было никаких выделенных точек, но теперь они будут помогать MetaTrader 5 и держать нас в курсе происходящего.

Также появилась новая функция.

void SetPositionMinimalAxleX(void)
{
        m_PositionMinimalAlxeX = (int)(ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS) * 0.2);
}

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

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

inline void Select(const string &sparam)
{
        ulong tick;
        double price;
        eIndicatorTrade it;
        eEventType ev;
        string sz = sparam;
                                
        if (!GetInfosOrder(sparam, tick, price, it, ev)) sz = NULL;
        m_TradeLine.SpotLight(sz);
}

Другая функция, которая также претерпела изменения - это та, которая создает индикатор.

inline void CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select)
{
        if (price <= 0) RemoveIndicatorTrade(ticket, it); else
        {
                CreateIndicatorTrade(ticket, it, select);
                PositionAxlePrice(price, ticket, it, -1, -1, 0, false);
        }
}

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

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
{
        color cor1, cor2;
        string sz0;
                                
        switch (it)
        {
                case IT_TAKE    :
                        cor1 = clrPaleGreen;
                        cor2 = clrDarkGreen;
                        break;
                case IT_STOP    :
                        cor1 = clrCoral;
                        cor2 = clrMaroon;
                        break;
                case IT_PENDING:
                default:
                        cor1 = clrGold;
                        cor2 = clrDarkGoldenrod;
                        break;
        }                               
        m_TradeLine.Create(MountName(ticket, it, EV_LINE), cor2);
        if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE));
        m_BackGround.Create(sz0 = MountName(ticket, it, EV_GROUND), cor1);
        switch (it)
        {
                case IT_TAKE:
                case IT_STOP:
                        m_BackGround.Size(sz0, 92, 22);
                        break;
                case IT_PENDING:
                        m_BackGround.Size(sz0, 110, 22);
                        break;
        }
}

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

Ниже мы показываем следующую функцию, которая была изменена и заслуживает внимания.

#define macroDelete(A)  {                                                               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND));       \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE));         \
                        }
                                        
inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it)
                                else
                                {
                                        macroDelete(IT_PENDING);
                                        macroDelete(IT_RESULT);
                                        macroDelete(IT_TAKE);
                                        macroDelete(IT_STOP);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                        }
#undef macroDelete

Она, как и следующая, немного скучна. Функция должна работать так, чтобы вы были тем, кто выбирает каждый из созданных объектов один за другим. И так для каждого из индикаторов. Подумайте об этом. Если мы не будем использовать макрос, облегчающий задачу, то будет кошмар. Кодировать эту функцию было бы крайне утомительно, так как каждый индикатор в конце кода будет иметь 5 объектов. Зная, что каждый набор в OCO-ордере будет иметь 3 индикатора, это привело бы к тому, что нам пришлось бы кодировать с 15 объектами, а в этом случае шансы ошибиться (поскольку разница между ними только в названии), были бы огромными. Таким образом, с помощью макроса код сводится к тому, что выделено в коде: мы будем кодировать только 5 объектов в конце, но это всего лишь первая фаза, чтобы получить результат, который показан выше.

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

#define macroSetAxleY(A)        {                                               \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND), y); \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE), y);    \
                                }
                                                                        
#define macroSetAxleX(A, B)     {                                               \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND), B); \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE), B);    \
                                }

inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
                        {
                                double ad;
                                int x, y;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetAxleY(it);
                                macroSetAxleX(it, m_PositionMinimalAlxeX);
                                if (Leverange == 0) return;
                                if (it == IT_PENDING)
                                {
                                        ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))), x, y);
                                        macroSetAxleY(IT_TAKE);
                                        macroSetAxleX(IT_TAKE, m_PositionMinimalAlxeX + 120);
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)), x, y);
                                        macroSetAxleY(IT_STOP);
                                        macroSetAxleX(IT_STOP, m_PositionMinimalAlxeX + 220);
                                }
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Если вы считаете, что предыдущая функция была скучной, то посмотрите на эту. Здесь работа будет дублироваться, но благодаря выделенным кодам всё становится приемлемым.

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


Заключение

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

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


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

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

Прикрепленные файлы |
DoEasy. Элементы управления (Часть 12): Базовый объект-список, WinForms-объекты ListBox и ButtonListBox DoEasy. Элементы управления (Часть 12): Базовый объект-список, WinForms-объекты ListBox и ButtonListBox
В статье создадим базовый объект списков WinForms-объектов и два новых объекта: ListBox и ButtonListBox.
Разработка торгового советника с нуля (Часть 19): Новая система ордеров (II) Разработка торгового советника с нуля (Часть 19): Новая система ордеров (II)
В данной статье мы будем разрабатывать графическую систему ордеров вида «посмотрите, что происходит». Следует сказать, что мы не начнем с нуля, а модифицируем существующую систему, добавив еще больше объектов и событий на график торгуемого нами актива.
Разработка торговой системы на основе индикатора OBV Разработка торговой системы на основе индикатора OBV
Это новая статья, продолжающая нашу серию для начинающих MQL5-программистов, в которой мы учимся строить торговые системы с использованием самых популярных индикаторов. На этот раз мы будем изучать индикатор балансового объема On Balance Volume (OBV) — узнаем, как его использовать и как создать торговую систему на его основе.
Нейросети — это просто (Часть 21): Вариационные автоэнкодеры (VAE) Нейросети — это просто (Часть 21): Вариационные автоэнкодеры (VAE)
В прошлой статье мы познакомились с алгоритмом работы автоэнкодера. Как и любой другой алгоритм, он имеет свои достоинства и недостатки. В оригинальной реализации автоэнкодер выполняет задачу максимально разделить объекты из обучающей выборки. А о том, как бороться с некоторыми его недостатками мы поговорим в этой статье.