English 中文 Español Deutsch 日本語 Português
Объектный подход в MQL

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

MetaTrader 4Торговые системы | 1 октября 2007, 14:14
3 747 0
---
---

Введение

Этот обзор будет интересен скорее всего программистам как начинающим так и профессионалам, работающим в среде 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.61 KB)
TradeSystem.mq4 (17.78 KB)
Traling.mqh (21.5 KB)
Индикатор Taichi - простая идея формализации показаний Ichimoku Kinko Hyo Индикатор Taichi - простая идея формализации показаний Ichimoku Kinko Hyo
Теряетесь в интерпритации сигналов Ichimoku? В данной статье предложены некоторые принципы формализации показаний и сигналов Ichimoku Kinko Hyo. Для иллюстрации применения была выбрана пара EURUSD исключительно из собственных соображений, что ни сколько не ограничивает применение индикатора на других инструментах.
Как преодолеть ограничения тестера стратегий при тестировании хеджевых советников Как преодолеть ограничения тестера стратегий при тестировании хеджевых советников
Идеи по тестированию хеджевых советников с использованием тестера стратегий.
Язык MQL4 для "чайников". Пользовательские индикаторы (часть 1) Язык MQL4 для "чайников". Пользовательские индикаторы (часть 1)
Это четвертая статья из цикла "Язык MQL4 для 'чайников'". Сегодня мы будем учиться писать пользовательские индикаторы. Мы изучим классификацию свойств индикаторов, посмотрим, как эти свойства влияют на сам индикатор, узнаем про новые функции и оптимизацию, и наконец-то напишем несколько своих индикаторов. Кроме того, в конце статьи вас ждут советы по стилю программирования. Если это первая статья "для чайников", которую вы читаете, то, пожалуйста, прочитайте предыдущие статьи, чтобы у вас не возникало никаких вопросов. Кроме того убедитесь, что вы хорошо разобрались в старом материале, так как в этой статье я не буду объяснять основы.
Практическое применение кластерных индикаторов на рынке FOREX Практическое применение кластерных индикаторов на рынке FOREX
Кластерные индикаторы – это набор индикаторов, разделяющих валютные пары на отдельные валюты. Индикаторы позволяют следить за колебаниями валют относительно друг друга, определять потенциал зарождения новых валютных трендов, получать торговые сигналы и сопровождать среднесрочные и долгосрочные позиции.