English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Рецепты MQL5 - ОСО-ордера

Рецепты MQL5 - ОСО-ордера

MetaTrader 5Примеры | 9 февраля 2015, 12:02
4 734 3
Denis Kirichenko
Denis Kirichenko

Введение

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


1. Сущность ОСО-ордеров

ОСО-ордера (one-cancels-the-other order) – это связка пары отложенных ордеров.

Между собой они связаны функцией взаимоотмены: если сработал первый, то не должен сработать второй, и наоборот.

Рис. 1. Связка ОСО-ордеров

Рис. 1. Связка ОСО-ордеров

На рис.1 приведена простая схема взаимозависимости ордеров. Она отражает сущностное определение: связка живет до тех пор, пока живут оба ордера. С точки зрения логики любой [один] ордер из пары есть необходимое, но недостаточное условие для существования связки.

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


2. Программирование связки ордеров

Для программирования задач, связанных с контролем над ОСО-ордерами, на мой взгляд, очень подходит ООП-инструментарий.

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


2.1. Класс CiOcoObject

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

По традиции создадим новый объект на основе родного для MQL5 базового класса CObject.

Тогда новый класс может выглядеть примерно так:

//+------------------------------------------------------------------+
//| Class CiOcoObject                                                |
//| Purpose: a class for OCO-orders                                  |            
//+------------------------------------------------------------------+
class CiOcoObject : public CObject
  {
   //--- === Data members === --- 
private:
   //--- тикеты связки
   ulong             m_order_tickets[2];
   //--- флаг инициализации
   bool              m_is_init;
   //--- id
   uint              m_id;

   //--- === Methods === --- 
public:
   //--- конструктор/деструктор
   void              CiOcoObject(void){m_is_init=false;};
   void             ~CiOcoObject(void){};
   //--- конструктор копирования
   void              CiOcoObject(const CiOcoObject &_src_oco);
   //--- оператор присваивания
   void              operator=(const CiOcoObject &_src_oco);

   //--- инициализация/деинициализация
   bool              Init(const SOrderProperties &_orders[],const uint _bunch_cnt=1);
   bool              Deinit(void);
   //--- получить id
   uint              Id(void) const {return m_id;};

private:
   //--- типы ордеров
   ENUM_ORDER_TYPE   BaseOrderType(const ENUM_ORDER_TYPE _ord_type);
   ENUM_BASE_PENDING_TYPE PendingType(const ENUM_PENDING_ORDER_TYPE _pend_type);
   //--- задать id
   void              Id(const uint _id){m_id=_id;};
  };

Каждая связка ОСО-ордеров будет иметь свой идентификатор. Его значение создается посредством обращения к генератору случайных чисел (объекту класса CRandom).

С точки зрения интерфейса интерес представляют методы инициализации и деинициализации связки. Первый создает (инициализирует) связку, второй – удаляет (деинициализирует).

В качестве аргумента метод CiOcoObject::Init() принимает массив структур типа SOrderProperties. Этот вид структуры представляет собой свойства ордера, входящего в связку, т.е. ОСО-ордера.


2.2 Структура SOrderProperties

Рассмотрим состав полей вышеобозначенной структуры.

//+------------------------------------------------------------------+
//| Структура свойств ордера                                         |
//+------------------------------------------------------------------+
struct SOrderProperties
  {
   string            symbol;           // символ
   ENUM_PENDING_ORDER_TYPE order_type; // тип ордера
   double            volume;           // объем ордера
   uint              price_offset;     // отступ для цены исполнения,пп
   uint              limit_offset;     // отступ для цены лимита,пп
   uint              sl;               // stop loss,пп
   uint              tp;               // take profit,пп
   ENUM_ORDER_TYPE_TIME type_time;     // тип по истечению
   datetime          expiration;       // истечение
   string            comment;          // комментарий
  };

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

В структуре используется перечисление типа ENUM_PENDING_ORDER_TYPE:


//+------------------------------------------------------------------+
//| Тип отложенного ордера                                           |
//+------------------------------------------------------------------+
enum ENUM_PENDING_ORDER_TYPE
  {
   PENDING_ORDER_TYPE_BUY_LIMIT=2,       // Buy-limit
   PENDING_ORDER_TYPE_SELL_LIMIT=3,      // Sell-limit
   PENDING_ORDER_TYPE_BUY_STOP=4,        // Buy-stop
   PENDING_ORDER_TYPE_SELL_STOP=5,       // Sell-stop
   PENDING_ORDER_TYPE_BUY_STOP_LIMIT=6,  // Buy-stop-limit
   PENDING_ORDER_TYPE_SELL_STOP_LIMIT=7, // Sell-stop-limit
  };

По большому счету оно дублирует стандартное перечисление ENUM _ORDER_TYPE, но с его помощью можно однозначно выбирать только отложенные ордера, вернее типы таких ордеров.

Тогда при запуске программы и выборе соответствующего типа ордера во входных параметрах ошибок не будет (рис.2).


Рис.2 Поле "Тип" с выпадающим списком возможных типов ордера

Рис. 2. Поле "Тип" с выпадающим списком возможных типов ордера


Если же использовать стандартное перечисление ENUM _ORDER_TYPE, то можно было бы задать тип рыночного ордера ORDER_TYPE_BUY или ORDER_TYPE_SELL, что совсем не нужно, т.к. работа идет с отложенными ордерами.


2.3. Инициализация связки

Как уже ранее отмечалось, инициализацией связки ордеров занимается метод CiOcoObject::Init().

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

Не буду приводить код всего метода. Отмечу, что здесь важно правильно рассчитать все цены (открытие, стоп, профит, лимит), чтобы метод торгового класса CTrade::OrderOpen() выполнил торговый приказ. Для этого нужно учитывать 2 момента: направление ордера (покупка или продажа) и расположение цены исполнения ордера относительно текущей (выше или ниже).

Этот метод вызывает пару приватных методов: BaseOrderType() и PendingType(). Первая определяет направление ордера, а вторая – тип отложенного ордера.

Если ордер размещен, то в массив m_order_tickets[] заносится его тикет.

Для тестирования данного метода я использовал простой скрипт Init_OCO.mq5.

#property script_show_inputs
//---
#include "CiOcoObject.mqh"
//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
sinput string Info_order1="+===--Ордер 1--====+";   // +===--Ордер 1--====+
input ENUM_PENDING_ORDER_TYPE InpOrder1Type=PENDING_ORDER_TYPE_SELL_LIMIT; // Тип
input double InpOrder1Volume=0.02;                  // Объем
input uint InpOrder1PriceOffset=125;                // Отступ для цены исполнения, пп
input uint InpOrder1LimitOffset=50;                 // Отступ для цены лимита, пп
input uint InpOrder1SL=250;                         // Стоп-лосс, пп
input uint InpOrder1TP=455;                         // Профит, пп
input string InpOrder1Comment="OCO Order 1";        // Комментарий
//---
sinput string Info_order2="+===--Ордер 2--====+";   // +===--Ордер 2--====+
input ENUM_PENDING_ORDER_TYPE InpOrder2Type=PENDING_ORDER_TYPE_SELL_STOP; // Тип
input double InpOrder2Volume=0.02;                  // Объем    
input uint InpOrder2PriceOffset=125;                // Отступ для цены исполнения, пп
input uint InpOrder2LimitOffset=50;                 // Отступ для цены лимита, пп
input uint InpOrder2SL=275;                         // Стоп-лосс, пп
input uint InpOrder2TP=300;                         // Профит, пп
input string InpOrder2Comment="OCO Order 2";        // Комментарий

//--- globals
CiOcoObject myOco;
SOrderProperties gOrdersProps[2];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- свойства 1-го ордера
   gOrdersProps[0].order_type=InpOrder1Type;
   gOrdersProps[0].volume=InpOrder1Volume;
   gOrdersProps[0].price_offset=InpOrder1PriceOffset;
   gOrdersProps[0].limit_offset=InpOrder1LimitOffset;
   gOrdersProps[0].sl=InpOrder1SL;
   gOrdersProps[0].tp=InpOrder1TP;
   gOrdersProps[0].comment=InpOrder1Comment;

//--- свойства 2-го ордера
   gOrdersProps[1].order_type=InpOrder2Type;
   gOrdersProps[1].volume=InpOrder2Volume;
   gOrdersProps[1].price_offset=InpOrder2PriceOffset;
   gOrdersProps[1].limit_offset=InpOrder2LimitOffset;
   gOrdersProps[1].sl=InpOrder2SL;
   gOrdersProps[1].tp=InpOrder2TP;
   gOrdersProps[1].comment=InpOrder2Comment;

//--- инициализация связки
   if(myOco.Init(gOrdersProps))
      PrintFormat("Id новой ОСО-связки: %I32u",myOco.Id());
   else
      Print("Ошибка выставления ОСО-связки!");
  }

В нем можно задать различные свойства будущих ордеров связки. Всего в MetaTrader 5 имеется 6 различных типов отложенных ордеров.

Тогда может быть 15 вариантов (комбинаций) связок (при условии, что типы ордеров в связке разные).

C(k,N) = C(2,6) = 15

С помощью скрипта были протестированы все варианты. Приведу пример для связки "Buy-stop - Buy-stop-limit".

В параметрах скрипта нужно явно указать типы ордеров (рис.3).


Рис.3 Связка ордера типа "buy-stop" с ордером типа "buy-stop-limit"

Рис. 3. Связка ордера типа "buy-stop" с ордером типа "buy-stop-limit"

В журнале "Эксперты" появится такая информация:

QO      0       17:17:41.020    Init_OCO (GBPUSD.e,M15) Код результата выполнения запроса: 10009
JD      0       17:17:41.036    Init_OCO (GBPUSD.e,M15) Тикет нового ордера: 24190813
QL      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) Код результата выполнения запроса: 10009
JH      0       17:17:41.286    Init_OCO (GBPUSD.e,M15) Тикет нового ордера: 24190814
MM      0       17:17:41.379    Init_OCO (GBPUSD.e,M15) Id новой ОСО-связки: 3782950319

Однако с помощью скрипта полноценно поработать с ОСО-ордерами не получится, если не прибегать к зацикливанию.


2.4. Деинициализация связки

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

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

//+------------------------------------------------------------------+
//| Деинициализация связки                                           |
//+------------------------------------------------------------------+
bool CiOcoObject::Deinit(void)
  {
//--- если связка инициализирована
   if(this.m_is_init)
     {
      //--- проверить свои ордера 
      for(int ord_idx=0;ord_idx<ArraySize(this.m_order_tickets);ord_idx++)
        {
         //--- текущий ордер связки
         ulong curr_ord_ticket=this.m_order_tickets[ord_idx];
         //--- другой ордер связки
         int other_ord_idx=!ord_idx;
         ulong other_ord_ticket=this.m_order_tickets[other_ord_idx];

         //---
         COrderInfo order_obj;

         //--- если текущего ордера нет
         if(!order_obj.Select(curr_ord_ticket))
           {
            PrintFormat("Ордера #%d нет в списке активных ордеров.",curr_ord_ticket);
            //--- попытка удалить другой ордер                 
            if(order_obj.Select(other_ord_ticket))
              {
               CTrade trade_obj;
               //---
               if(trade_obj.OrderDelete(other_ord_ticket))
                  return true;
              }
           }
        }
     }
//---
   return false;
  }

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

Расширим возможности скрипта, в котором выставлялась пара ордеров. Для этих целей создадим тестовый советник Control_OCO_EA.mq5.

По большому счету советник будет отличаться от скрипта только тем, что в его коде будет присутствовать блок обработки события Trade():

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- деинициализация ОСО-связки
   if(myOco.Deinit())
     {
      Print("Связки ордеров больше нет!");
      //--- сбросить связку
      CiOcoObject new_oco;
      myOco=new_oco;
     }
  }

На видео можно увидеть, как обе программы работают в терминале MetaTrader 5.




Однако в обеих тестовых программах есть свои недостатки.

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

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


3. Советник-контроллер

Чтобы выставлять и настраивать параметры ордеров связки, создадим на графике Панель "Управление ОСО-ордерами".

Она войдет в состав советника-контроллера (рис.4). Исходный код представлен в файле советника Panel_OCO_EA.mq5.


Рис.4 Панель для создания ОСО-ордеров

Рис. 4. Панель для создания ОСО-ордеров - исходное состояние


Чтобы выставить связку ОСО-ордеров, нужно выбрать тип для будущего ордера и заполнить поля.

Тогда единственная на панели кнопка изменит свою надпись (текстовое свойство, рис.5).


Рис.5 Панель для создания ОСО-ордеров - новая связка

Рис. 5. Панель для создания ОСО-ордеров - новая связка


Объекты каких классов были задействованы при создании Панели?

Это классы Стандартной библиотеки:

  • CAppDialog - главный диалог приложения;
  • CPanel - прямоугольная метка;
  • CLabel - текстовая метка;
  • CComboBox - поле с выпадающим списком;
  • CEdit - поле ввода;
  • CButton - кнопка.

Естественно, что автоматом вызывались методы классов-родителей.

Теперь о коде. Нужно сказать, что та часть Стандартной библиотеки, что отведена под нужды создания панелей индикации и диалогов управления, довольно обширна.

Например, чтобы поймать событие закрытия выпадающего списка, придется заглянуть глубоко в стек вызовов (рис. 6).


Рис.6 Стек вызовов

Рис. 6. Стек вызовов


Для специфических событий разработчик задает свои макросы и нотацию в файле %MQL5\Include\Controls\Defines.mqh.

Для целей создания ОСО-связки я придумал пользовательское событие ON_OCO.

#define ON_OCO (101) // событие "создание ОСО-связки" 

Вся работа по заполнению параметров будущих ордеров и генерирование связки проходит в теле обработчика OnChartEvent().

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- обработка всех события графика главным диалогом
   myDialog.ChartEvent(id,lparam,dparam,sparam);

//--- обработка выпадающих списков
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE)
     {
      //--- если это список Панели
      if(!StringCompare(StringSubstr(sparam,0,7),"myCombo"))
        {
         static ENUM_PENDING_ORDER_TYPE prev_vals[2];
         //--- индекс списка
         int combo_idx=(int)StringToInteger(StringSubstr(sparam,7,1))-1;

         ENUM_PENDING_ORDER_TYPE curr_val=(ENUM_PENDING_ORDER_TYPE)(myCombos[combo_idx].Value()+2);
         //--- запомнить изменение типа ордера
         if(prev_vals[combo_idx]!=curr_val)
           {
            prev_vals[combo_idx]=curr_val;
            gOrdersProps[combo_idx].order_type=curr_val;
           }
        }
     }

//--- обработка полей ввода
   else if(id==CHARTEVENT_OBJECT_ENDEDIT)
     {
      //--- если это поле ввода Панели
      if(!StringCompare(StringSubstr(sparam,0,6),"myEdit"))
        {
         //--- найти объект
         for(int idx=0;idx<ArraySize(myEdits);idx++)
           {
            string curr_edit_obj_name=myEdits[idx].Name();
            long curr_edit_obj_id=myEdits[idx].Id();
            //--- если имена совпадают
            if(!StringCompare(sparam,curr_edit_obj_name))
              {
               //--- получить текущее значение поля
               double value=StringToDouble(myEdits[idx].Text());
               //--- определить индекс массива gOrdersProps[]
               int order_num=(idx<gEditsHalfLen)?0:1;
               //--- определить номер поля структуры gOrdersProps
               int jdx=idx;
               if(order_num)
                  jdx=idx-gEditsHalfLen;
               //--- заполнить поле структуры gOrdersProps
               switch(jdx)
                 {
                  case 0: // объем
                    {
                     gOrdersProps[order_num].volume=value;
                     break;
                    }
                  case 1: // исполнение
                    {
                     gOrdersProps[order_num].price_offset=(uint)value;
                     break;
                    }
                  case 2: // лимит
                    {
                     gOrdersProps[order_num].limit_offset=(uint)value;
                     break;
                    }
                  case 3: // стоп
                    {
                     gOrdersProps[order_num].sl=(uint)value;
                     break;
                    }
                  case 4: // профит
                    {
                     gOrdersProps[order_num].tp=(uint)value;
                     break;
                    }
                 }
              }
           }
         //--- флаг создания ОСО-связки
         bool is_to_fire_oco=true;
         //--- проверить заполнение структур 
         for(int idx=0;idx<ArraySize(gOrdersProps);idx++)
           {
            //---  если задан тип ордера 
            if(gOrdersProps[idx].order_type!=WRONG_VALUE)
               //---  если задан объем  
               if(gOrdersProps[idx].volume!=WRONG_VALUE)
                  //---  если задан отступ для цены исполнения
                  if(gOrdersProps[idx].price_offset!=(uint)WRONG_VALUE)
                     //---  если задан отступ для цены лимита
                     if(gOrdersProps[idx].limit_offset!=(uint)WRONG_VALUE)
                        //---  если задан stop loss
                        if(gOrdersProps[idx].sl!=(uint)WRONG_VALUE)
                           //---  если задан take profit
                           if(gOrdersProps[idx].tp!=(uint)WRONG_VALUE)
                              continue;

            //--- сбросить флаг создания ОСО-связки 
            is_to_fire_oco=false;
            break;
           }
         //--- создать ОСО-связку?
         if(is_to_fire_oco)
           {
            //--- заполнить поля комментария
            for(int ord_idx=0;ord_idx<ArraySize(gOrdersProps);ord_idx++)
               gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+1);
            //--- изменить свойства кнопки
            myButton.Text("Новая связка");
            myButton.Color(clrDarkBlue);
            myButton.ColorBackground(clrLightBlue);
            //--- отвечать на действия пользователя 
            myButton.Enable();
           }
        }
     }
//--- обработка клика по кнопке
   else if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- если это кнопка создания ОСО-связки
      if(!StringCompare(StringSubstr(sparam,0,6),"myFire"))
         //--- если отвечать на действия пользователя
         if(myButton.IsEnabled())
           {
            //--- сгенерировать событие "создание ОСО-связки"
            EventChartCustom(0,ON_OCO,0,0.0,"OCO_fire");
            Print("Поступил приказ на создание новой связки.");
           }
     }
//--- обработка приказ на инициализацию новой связки 
   else if(id==CHARTEVENT_CUSTOM+ON_OCO)
     {
      //--- инициализация ОСО-связки
      if(gOco.Init(gOrdersProps,gOcoList.Total()+1))
        {
         PrintFormat("Id новой ОСО-связки: %I32u",gOco.Id());
         //--- скопировать связку
         CiOcoObject *ptr_new_oco=new CiOcoObject(gOco);
         if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC)
           {
            //--- добавить в список
            int node_idx=gOcoList.Add(ptr_new_oco);
            if(node_idx>-1)
               PrintFormat("Всего связок: %d",gOcoList.Total());
            else
               PrintFormat("Ошибка добавления ОСО-связки %I32u в список!",gOco.Id());
           }
        }
      else
         Print("Ошибка выставления ОСО-ордеров!");

      //--- сбросить свойства
      Reset();
     }
  }

Код обработчика не мал. Здесь я бы выделил несколько блоков.

Сначала главному диалогу отдается обработка всех события графика.

Затем идут блоки обработки различных событий:

  • Изменение выпадающих списков - для определения типа ордеров;
  • Редактирование полей ввода - для заполнения свойств ордеров;
  • Клик по кнопке - для генерации события ON_OCO;
  • Непосредственно реакция на событие ON_OCO - создание связки ордеров.

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

В теле обработчика OnTrade() проверяется необходимость удаления связки и закрытия оставшегося ордера.


Заключение

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

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


Прикрепленные файлы |
init_oco.mq5 (3.17 KB)
control_oco_ea.mq5 (3.92 KB)
panel_oco_ea.mq5 (15.34 KB)
ciocoobject.mqh (13.9 KB)
crandom.mqh (2.59 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (3)
Serhiy Dotsenko
Serhiy Dotsenko | 18 февр. 2015 в 12:12

Коллеги, кто-нибудь видел где-нибудь примеры использования библиотек которые лежат в MQL5\Include\Canvas\Charts, есть одна идея, как-раз можно использовать стандартную библиотеку, только без примеров, как-то туговато идёт освоение ))

Anatoli Kazharski
Anatoli Kazharski | 18 февр. 2015 в 12:22
thejobber:

Коллеги, кто-нибудь видел где-нибудь примеры использования библиотек которые лежат в MQL5\Include\Canvas\Charts, есть одна идея, как-раз можно использовать стандартную библиотеку, только без примеров, как-то туговато идёт освоение ))

Раздел Scripts\Examples\Canvas.
Serhiy Dotsenko
Serhiy Dotsenko | 18 февр. 2015 в 12:26
tol64:
Раздел Scripts\Examples\Canvas.
Спасибо Анатолий, думал что это только в индикаторах может быть ))
Изучаем класс CCanvas. Реализация прозрачности графических объектов Изучаем класс CCanvas. Реализация прозрачности графических объектов
Надоела угловатая графика скользящих средних? Вы хотите рисовать в терминале что-то более красивое, чем простой прямоугольник с заливкой? Рисовать красиво в терминале можно. Для этого есть класс для создания пользовательской графики - CCanvas. С помощью этого класса можно реализовать прозрачность, смешивать цвета и получать иллюзию прозрачности при помощи наложения и смешивания цвета.
Разнонаправленная торговля и хеджирование позиций в MetaTrader 5 с помощью API HedgeTerminal, часть 2 Разнонаправленная торговля и хеджирование позиций в MetaTrader 5 с помощью API HedgeTerminal, часть 2
Статья описывает новый подход в вопросах хеджирования позиций и ставит точку в спорах между пользователями платформ MetaTrader 4 и MetaTrader 5 в этом вопросе. Она является продолжением первой части: "Разнонаправленная торговля и хеджирование позиций в MetaTrader 5 с помощью панели API HedgeTerminal". Во второй части описывается интеграция пользовательских экспертов с HedgeTerminalAPI - специальной библиотекой виртуализации, позволяющей торговать разнонаправлено, находясь в комфортном программном окружении, позволяющем легко и просто управлять своими позициями.
Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным
В данной статье описывается специальный алгоритм, позволяющий эффективно получать доступ к элементам по их уникальному ключу. В качестве ключа может быть использован любой базовый тип данных, например ключом могут быть строки или целочисленные переменные. Такой контейнер данных принято называть словарем или ассоциативным массивом. С его помощью решать многие задачи становиться гораздо проще и эффективней.
Разнонаправленная торговля и хеджирование позиций в MetaTrader 5 с помощью панели HedgeTerminal, часть 1 Разнонаправленная торговля и хеджирование позиций в MetaTrader 5 с помощью панели HedgeTerminal, часть 1
Статья описывает новый подход в вопросах хеджирования позиций и ставит точку в спорах между пользователями платформ MetaTrader 4 и MetaTrader 5 в этом вопросе. На примере простых схем и диаграмм, общедоступным языком рассказывается об алгоритмах, которые делают такое хеджирование надежным. Статья посвящена описанию новой панели - HedgeTerminal, которая, по сути, является полноценным торговым терминалом внутри самого терминала MetaTrader 5. С ее помощью, благодаря предлагаемой виртуализации торговли, можно управлять своими торговыми позициями так, как это принято в MetaTrader 4.