
Объектный подход в MQL
Введение
Этот обзор будет интересен скорее всего программистам как начинающим так и профессионалам, работающим в среде 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).





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования