Объектный подход в MQL

Автор: lexy Вторник, Сентябрь 9th, 2014 Нет комментариев

Рубрика: Разное

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

Итак,

Одно из неудобств MQL, на мой взгляд программиста, является отсутствие объектного подхода в построении модели торговой системы. И два выхода, которые предлагают нам разработчики это 1) использовать вызов внешних функций, или 2) использование так называемого параметра ордера MAGIC для идентификации принадлежности ордера.

В принципе, если на одном счете работает только одна система, то в идентификации ордеров нет необходимости. Но когда есть программная возможность «прицепить» к одному счету несколько МТС, то без использования MAGIC нам уже не обойтись никак. Даже вызывая внешние функции у нас есть потребность в их определении. Конечно, можно строить массив OrderTicket и идентифицировать принадлежность этого массива только к одной ТС, но как мы знаем, в некоторых дилинговых центрах ticket ордера при операции swap меняется (точнее говоря закрывается один и открывается новый). Поэтому без использования числа MAGIC как вы видите нам не обойтись никак.

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

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

Итак давайте разберемся с этой моделью

А). Сигнальная система (СС).

Объекты этого модуля занимаются обработкой и трактовкой приходящих котировок. В качестве «объекта» сигнальной системы обычно выступает совокупность индикаторов. Например скользящие средние. Результатом обработки котировок и значений индикаторов «объект» (или проще семафорподает сигналы на вход / выход, или на модификацию ордеров, доливку и т.д.

Семафор формирует свой сигнал и передает его другому объекту из модуля Входа/выхода (ВВ).

Задание семафора в рамках языка MQL очень простое.

1. Определяем глобальный идентификатор семафора с помощью #define. Желательно, чтоб номера шли не подряд 1,2,3,4… а через 5-10, для того, чтоб можно было в эксперте использовать один сигнал на несколько процессов (см. во втором модуле)

//+------------------------------------------------------------------+
//|                                                      Signals.mqh |
//|                                    Copyright © 2007 Сергеев Алексей |
//|                                                los@we.kherson.ua |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Сергеев Алексей "
#property link      "mailto: los@we.kherson.ua"
#property library

#define BLACKSYS   10
#define BORCHAN    20
#define ELDER      80
#define ENVELOP    90

2. Затем в глобальной функции этого модуля мы должны подключить его обработчик.

int CheckSignal(bool bEntry, int SignalID)
{
      switch (SignalID)
      {
                  case BLACKSYS:             return (BlackSys(bEntry)); break;
                  case BORCHAN:              return (BorChan(bEntry)); break;
                  case ELDER:                   return (Elder(bEntry)); break;
                  case ENVELOP:              return (Envelop(bEntry)); break;
                  default:                                     return (-1);
      }
}

3. Ну и последним шагом мы должны соответственно описать функции.

Например для обработки сигналов объекта, который наследует свойства индикатора Envelop.

int Envelop(bool bEntry)
{
      int MA=21;
      double Deviation=0.6;
      int Mode=MODE_SMA;//0-sma, 1-ema, 2-smma, 3-lwma
      int Price=PRICE_CLOSE;//0-close, 1-open, 2-high, 3-low, 4-median, 5-typic, 6-wieight

      double envH0, envL0, m0;
      double envH1, envL1, m1;
      envH0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 0);
      envL0=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 0);
      envH1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_UPPER, 1);
      envL1=iEnvelopes(NULL, 0, MA, Mode, 0, Price, Deviation, MODE_LOWER, 1); 

      m0 = (Low[0]+High[0])/2;          m1 = (Low[1]+High[1])/2;
      //----- условия для совершения операции
      if (bEntry)   //для открытия
      {
                  if (envH0<m0 && envH1<m1) return (OP_SELL);
                  if (envL0>m0 && envL1>m1) return (OP_BUY);
      }
      else //для закрытия
      {
                  if (envH0<m0 && envH1<m1) return (OP_BUY);
                  if (envL0>m0 && envL1>m1) return (OP_SELL);
      }

   return (-1); //нет сигнала
}

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

Б). У объектов блока ВВ самые минимальные задачи:

Во-первых его объекты взаимодействуют с объектами-сигналами – наблюдают за ними. Цикл жизни и взаимодействия таков:

Проверка семафора -> в случае каких-либо положительных сигналов открываем/закрываем/модифицируем позиции -> Передача управления объектам в модуле ПП.

Все объекты модуля ВВ – имеют приставки Process…. Которые уже более конкретно определяют его поведение.

Например:

ProcessAvgLim         //  -   объект обрабатывает сигналы с открытием отложенных лимит-ордеров и усреднением позиций
ProcessTurn           //  -  объект обрабатывает сигналы с переворотом позиций

Каждый экземпляр класса торговой системы (как все мы понимаем и этим руководствуемся в своих моделях) должен обладать своими индивидуальными характеристиками, такими как профит, стоплос, должен быть свой Money Management, а также другие дополнительные параметры, которые реализуются в различных вариантах трейлинга и т.д.

 

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

double SysPar[nSignal][11];

#define _TP        0 //Профит
#define _NullTP    1 // уровень профита, после которого ставим в безубыток
#define _NullTP2   2 // уровень профита, после которого ставим в безубыток
#define _TS        3 // расстояние трейлинг стопа
#define _NullSL    4 // уровень, при достижении которого переводим ожидаемый профит в точку открытия
#define _SL        5 // уровень, при достижении которого переводим ожидаемый профит в точку открытия
#define _dSL       6 // начальный шаг по уровню открытию следующего ордера в поддержку позы
#define _dStep     7 // Во сколько раз увеличиваем шаг по уровню открытия следующего
#define _dLot      8 // Во сколько раз (по отношению к последнему) увеличиваем лот на следующем
#define _nLot      9 // Во сколько раз (по отношению к начальному) увеличиваем лот на следующем

string SysParName[nSignal];

где в качестве nSignal передаем тот самый идентификатор объекта-сигналов.

Например:

SysPar[ENVELOP][_TS] = 40.0;    // расстояние трейлинг-стопа
SysPar[ENVELOP][_NullSL] = 20.0;// уровень, при достижении которого переводим ожидаемый профит в точку открытия
SysPar[ENVELOP][_SL] = 70;      //изменение стоп лоса

Количество задаваемых параметров этого массива-структуры вы можете увеличивать на свое усмотрение.

Итак, после задания параметров мы вызываем функцию обработки семафоров. Или другими словами – взаимодействуем с сигнальной системой. Выполняется это в любимой функции start()

void start()
{
      ProcessAvgLim(ENVELOP, ENVELOP, Green, Red);
… …

Как видим на схеме, в нашей торговой системе зарегистрировано 4 семафора и 3 наблюдателя. Каждый семафор основан на своем варианте трактовки котировок.

Например, Семафор 1 отсылает сигналы анализируя индикатор MACD. Наблюдатель 1 в свою очередь получая эти сигналы открывает ордера по простой схеме ProcessSimple.

Наблюдатели 2 и 3 немного усложнены. Каждый контролирует сигналы двух семафоров. И подход в открытии ордеров соответственно разный.

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

Вся «ответственность» за состоянием открытых ордеров ложится на объекты модуля

Поддержки позиций (ПП)

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

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


На форуме есть интересная библиотека трейлингов Библиотека функций и советники для трейлинга / Юрий Дзюбан. Все виды трейлингов из этой библиотеки легко присоединяются к системе.

Для простоты восприятия объекты поддержки начинаются с префикса Trailing…

Схема жизни следующая:

Вызов передача управления от наблюдателя к сопровождению происходит в той же функции start()

void start()
{
      ProcessSimple(MACD, MACD, Black, Plum); TrailingSimple(MACD, Black, Plum);
      ProcessAvgLim(ENVELOP, ENVELOP, Green, Red);  TrailingAvgLim(ENVELOP, Green, Red);
}

 

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

В конце объяснения мне еще раз хочется попросить разработчиков в расширении возможностей языка MQL. И в качестве примера привести вариант реализации классов объектов на языке с++.

struct SystemParam
{
    double TP;        // профит
    double NullTP;    // уровень профита, после которого ставим в безубыток
    double NullTP2;    // уровень профита, после которого ставим в безубыток серию ордеров одного направления
    double TS;        // расстояние трейлинг-стопа
    double NullSL;    // уровень убытка, при котором переводим ожидаемый профит просто в безубыток
    double SL;        // стоплос
    double dSL;        // шаг по уровню открытию следующего ордера в поддержку позиции
    double dStep;    // во сколько раз увеличиваем шаг по уровню открытия следующего ордера
    double dLot;        // во сколько раз увеличиваем лот на следующем ордере
}

class MTS
{
    public:
    string m_NameTS;    // имя системы (для создания комментариев ордера)
    int m_SignalID;     // идентификатор торговых сигналов (для опроса семафора)

    long int Tickets[1000];    // массив тикетов ордеров, отбираемых по m_SignalID (MAGIC)

    SystemParam SysPar;    // Параметры торговой системы
    color ClrBuy;        // цвет для указания ордера BUY
    color ClrSell;        // цвет для указания ордера SELL

    // Инициализация
    void MyMTS ();            // стандартная функция, задающая начальные значения системы
    void MyMTS (int aSignalID, int nProcessMode, int nTrailingMode); // стандартная функция,
                                    // задающая начальные значения системы

    // Реализация
    int CheckSignal();     // функция проверки состояния сигналов рынка

    // Обработка
    int m_nProcessMode;         // идентификатор варианта наблюдения
    int m_nTrailingMode;         // идентификатор варианта сопровождения
    void Process();        // функция ВВ – обработка CheckSignal()
    void Trailing();        // функция ПП – сопровождение ордеров

    // Спец функции
    bool CreatTicketArray(int dir);    // создание массива тикетов, отбираемых по m_SignalID (MAGIC)
                    // и желаемому типу dir: buy, sell, buylim, buystop, sellim, sellstop
    bool ArrangeOrderBy(int iSort);// упорядочивание масства Tickets по параметру (дата, профит, цена …)

};

…

MTS MyTS; // наша торговая система
…

int init()
{
…
    MyTS.m_SignalID = SIGNAL_MACD; // наша система основывается на сигналах MACD
    MyTS.m_NameTS = "MACD";
    MyTS.SysPar.TP = 500;
    MyTS.SysPar.NullTP = 20;
    MyTS.SysPar.TS = 50;
    MyTS.SysPar.SL = 1000;

    MyTS.SetProcess (MODE_AVGLIM);
    MyTS.SetTrailing (MODE_AVGLIM);
…
}

void start()
{
…
    MyTS.Process ();
    MyTS.Trailing ();
…
}

…

void MTS::Process()
{
…
    int Signal = CheckSignal(true, m_SignalID); //вызов глобальной функции по обработке сигналов
    if (Signal == -1) return; // если нет сигнала то просто ничего не делаем

//----- для совершения покупки
    if(Signal == OP_BUY)
    {
    }

    if(Signal == OP_SELL)
    {
    }
…
}

…
// глобальный обработчик семафоров

int CheckSignal(bool bEntry, int SignalID)
{
    switch (SignalID)
    {
        case ELDER:    return (Elder(bEntry)); break;
        case ENVELOP:    return (Envelop(bEntry)); break;
        case LAGUER:    return (Laguer(bEntry)); break;
        case MACD:    return (Macd(bEntry)); break;
        …
    }
}

// вызов конкретного семафора
int Macd(bool bEntry)
{
    double MACDOpen=3;
    double MACDClose=2;
    double MA=26;
    int MODE_MA    = MODE_EMA; // метод вычисления средних
    int PRICE_MA   = PRICE_CLOSE; // метод вычисления средних
    int PERIOD     = PERIOD_H1; // на каком периоде работать

    //параметры средних
    double MacdCur, MacdPre, SignalCur;
    double SignalPre, MaCur, MaPre;

    //---- получить значение
    MacdCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,0);   MacdPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_MAIN,1);
    SignalCur=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,0);   SignalPre=iMACD(NULL,0,8,17,9,PRICE_MA,MODE_SIGNAL,1);
    MaCur=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,0);   MaPre=iMA(NULL,0,MA,0,MODE_MA,PRICE_MA,1);

    //----- условия для совершения операции
    if (bEntry)   //для открытия bEntry==true
    {
        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre)
          return (OP_BUY);
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre)
          return (OP_SELL);
    }
    else //для закрытия bEntry==false
    {
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDClose*Point))
          return (OP_BUY);
        if(MacdCur>0 && MacdCur<SignalCur && MacdPre>SignalPre && MacdCur>(MACDOpen*Point) && MaCur<MaPre)
          return (OP_BUY);

        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDClose*Point))
          return (OP_SELL);
        if(MacdCur<0 && MacdCur>SignalCur && MacdPre<SignalPre && MathAbs(MacdCur)>(MACDOpen*Point) && MaCur>MaPre)
          return (OP_SELL);
    }

    return (-1); //нет сигнала
}

Логика системы в рамках языка MQL будет отличаться не на много. Все функции стают глобальными. А чтоб отличать ордера одной ТС от другой, то придется во все функции, которые работают с ордерами, внести дополнительный параметр SignalID (он же MAGIC).

С уважением, Алексей

Прикрепленные файлы:
 Signals.mqh (30.6 Kb)
 TradeSystem.mq4 (17.8 Kb)
 Traling.mqh (21.5 Kb)
Источник: mql4.com

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты