Принципы построения пользовательских
программ на языке MQL 4 рассматриваются на примере создания простейшей экспертной
системы на основе стандартного индикатора MACD.
В данной экспертной системе также рассмотрены примеры реализации таких функций как
выставление уровней тейкпрофита с поддержкой трейлинг-стопа и большинство предохраняющих
условий для безопасной работы. В предлагаемом примере торговля идет с открытием и
контролем одной позиции.
Принципы торговли:
вход в Long (BUY) – индикатор MACD ниже нуля, идет
снизу вверх, а его сверху вниз пересекает сигнальная линия.
вход в Short (SELL) – индикатор MACD выше нуля, идет
cверху вниз, а его снизу вверх пересекает сигнальная линия.
выход из Long – по тейкпрофиту, по трейлинг-стопу или при пересечении MACD со своей сигнальной линией (MACD выше нуля,
идет cверху вниз, а его снизу вверх пересекает сигнальная линия).
выход из Short – по тейкпрофиту, по трейлинг-стопу или при пересечении MACD со своей сигнальной линией (MACD ниже нуля,
идет снизу вверх, а его сверху вниз пересекает сигнальная линия).
Важное замечание: Для исключения из анализа незначительных(мелкие 'бугорки' на графике) изменений индикатора MACD введем дополнительный контроль за
размером рисуемых 'бугров' в виде следующего условия – величина индикатора должна составлять не менее 5 единиц минимальной цены (5*Point, что для USD/CHF
равно 0.0005, для USD/JPY = 0.05).
1-й этап – создание описания эксперта
В окне "Navigator" устанавливаем курсор мыши на разделе "Expert Advisors", нажимаем на правую кнопку мыши и в
появившемся меню выбираем команду "Create". Мастер первоначальных настроек эксперта предложит ввести некоторые данные: "Name" –
название программы, "Author" – автор программы, "Link" – ссылка на веб-сайт.
2-й этап – создание первичной структуры программы
Код тестового эксперта будет занимать всего несколько страниц, но и такой объем зачастую
бывает труден для восприятия. Особенно, если мы с вами не профессиональные программисты... Так ведь? Иначе этого описания не
пришлось бы писать :)
Для ознакомления со структурой стандартного эксперта взглянем на предлагаемое описание:
Инициализация переменных
Первичные проверки данных
проверка графика, количество баров на графике
проверка значений внешних переменных Lots, S/L, T/P, T/S
Установка внутренних переменных для быстрого доступа к данным
Проверка торгового терминала – пустой ли? Eсли да, то:
проверки: если ли деньги на счету и тд...
можно встать в длинную позицию(BUY)?
открыть длинную позицию и выйти
можно встать в короткую позицию(SELL)?
открыть короткую позицию и выйти
выход из эксперта...
Контроль открытых ранее позиций в цикле
если это длинная позиция
нужно ли закрыть?
нужно ли передвинуть трейлинг-стоп?
если это короткая позиция
нужно ли закрыть?
нужно ли передвинуть трейлинг-стоп?
Получилось достаточно просто, всего 4 основных блока.
Теперь попробуем по шагам сформировать куски кода под каждый раздел структурной схемы:
Инициализация переменных Все переменные, которые будут
использоваться в программе-эксперте, должны быть предварительно
описаны в соответствии с синтаксисом языка MetaQuotes Language 4. Поэтому в начале программы вставляем
блок инициализации переменных
В MetaQuotes Language 4 введено такое понятие, как дополнительные
пользовательские переменные, которые могут быть установлены извне,
без вмешательства в исходный текст программы-эксперта. Это придает
дополнительную гибкость. Переменная MATrendPeriod как раз и
является такой пользовательской переменной. В начале программы
вставляем описание этой переменной.
extern double MATrendPeriod=26;
Первичные проверки данных Этот кусок кода обычно
кочует из одного эксперта в другой с мелкими изменениями –
практически стандартный блок проверок:
// первичные проверки данных // важно удостовериться что эксперт работает на нормальном графике и // пользователь правильно выставил внешние переменные (Lots, StopLoss, // TakeProfit, TrailingStop) // в нашем случае проверяем только TakeProfit if(Bars<100) { Print("bars less than 100"); return(0); } if(TakeProfit<10) { Print("TakeProfit less than 10"); return(0); // проверяем TakeProfit }
Установка внутренних переменных для быстрого доступа к
данным В коде программы приходится достаточно часто
обращаться к значениям индикаторов или оперировать вычисляемыми
значениями.
int start() { double MacdCurrent, MacdPrevious, SignalCurrent; double SignalPrevious, MaCurrent, MaPrevious; int cnt, ticket, total;
Для облегчения кодирования и ускорения доступа
применяется предварительное помещение данных во внутренние
переменные.
Теперь вместо громадной записи iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0) в тексте программы можно
использовать MacdCurrent.
Проверка торгового терминала – пустой ли? Если да,
то: В нашем эксперте мы используем только позиции, открытые
по маркету и не трогаем отложенные ордеры. Но для безопасности
лучше внесем проверку торгового терминала на наличие выставленных
ордеров:
// теперь надо определиться - в каком состоянии торговый терминал? // проверим, есть ли ранее открытые позиции или ордеры? total=OrdersTotal(); if(total<1) {
проверки: если ли деньги на счету и тд... Перед
анализом рыночной ситуации желательно проверить состояние своего
счета – есть ли свободные деньги для открытия позиции?
// нет ни одного открытого ордера // на всякий случай проверим, если у нас свободные деньги на счету? // значение 1000 взято для примера, обычно можно открыть 1 лот if(AccountFreeMargin()<(1000*Lots)) { Print("We have no money. Free Margin = ", AccountFreeMargin()); return(0); }
проверка возможности встать в длинную
позицию (BUY) Условие входа в длинную позицию: MACD ниже
нуля, идет снизу вверх, а его сверху вниз пересекает сигнальная
линия. Как это записывается на MQL 4
(обратите внимание что работа идет с сохраненными ранее в
переменных значениями индикаторов):
// проверяем на возможность встать в длинную позицию (BUY) if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious) { ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0, Ask+TakeProfit*Point,"macd sample", 16384,0,Green); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice()); } else Print("Error opening BUY order : ",GetLastError()); return(0); }
Выше говорилось о дополнительном контроле за
размером рисуемых 'бугров'. Переменная MACDOpenLevel является
'пользовательской' переменной, которую можно менять, не трогая
текста программы, для гибкости контроля. В начале программы
вставляем описание этой переменной (заодно и описание
переменной, используемой ниже).
проверка возможности встать в короткую
позицию (SELL) Условие входа в короткую позицию: MACD выше
нуля, идет сверху вниз, а его снизу вверх пересекает сигнальная
линия. Как это записывается:
// проверяем на возможность встать в короткую позицию (SELL) if(MacdCurrent>0 && MacdCurrent MacdPrevious>SignalPrevious && MacdCurrent>(MACDOpenLevel*Point) && MaCurrent { ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0, Bid-TakeProfit*Point,"macd sample", 16384,0,Red); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice()); } else Print("Error opening SELL order : ",GetLastError()); return(0); }
// здесь мы завершили проверку на возможность открытия новых позиций. // новые позиции открыты не были и просто выходим return(0); }
Контроль открытых ранее позиций в цикле
// переходим к важной части эксперта - контролю открытых позиций // 'важно правильно войти в рынок, но выйти - еще важнее...' for(cnt=0;cnt { OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); if(OrderType()<=OP_SELL && // это открытая позиция? OP_BUY или OP_SELL OrderSymbol()==Symbol()) // инструмент совпадает?
{
"Cnt" – это переменная цикла, которая должна быть
описана в начале программы следующим образом :
int cnt = 0;
Eсли это длинная позиция
if(OrderType()==OP_BUY) // открыта длинная позиция {
нужно ли закрыть? Условие выхода из длиной
позиции: при пересечении MACD со своей сигнальной линией,
когда MACD выше нуля, идет вверху вниз, а его снизу вверх
пересекает сигнальная линия.
// проверим, может уже пора закрываться? if(MacdCurrent>0 && MacdCurrent MacdPrevious>SignalPrevious && MacdCurrent>(MACDCloseLevel*Point)) { OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // закрываем позицию return(0); // выходим }
нужно ли передвинуть
трейлинг-стоп? Трейлинг-стоп выставляем только тогда,
когда у позиции уже есть прибыль, превышающая величину
трейлинг-стопа в пунктах и если новый уровень стопа лучше
предыдущего.
нужно ли закрыть? Условие выхода из короткой
позиции: при пересечении MACD со своей сигнальной линией,
когда MACD ниже нуля, идет снизу вверх, а его сверху вниз
пересекает сигнальная линия.
// проверим, может уже пора закрываться? if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) { OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // закрываем позицию return(0); // выходим }
нужно ли передвинуть
трейлинг-стоп? Трейлинг-стоп выставляем только тогда,
когда у позиции уже есть прибыль, превышающая величину
трейлинг-стопа в пунктах и если новый уровень стопа лучше
предыдущего.
Закрываем все оставшиеся открытые операторные скобки
} } } return(0); }
Вот так, шаг за шагом и написан эксперт...
3-й этап – сборка результирующего кода программы
Откроем настройки эксперта (кнопка F7 или строка меню "Properties..."). Перед нами появится
окно, в котором необходимо выставить внешние настройки параметров работы:
Соберем весь код из предыдущего раздела воедино...
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ intstart() { doubleMacdCurrent, MacdPrevious, SignalCurrent; doubleSignalPrevious, MaCurrent, MaPrevious; intcnt=0, ticket, total; // первичные проверки данных // важно удостовериться что эксперт работает на нормальном графике и // пользователь правильно выставил внешние переменные (Lots, StopLoss, // TakeProfit, TrailingStop) // в нашем случае проверяем только TakeProfit if(Bars<100) { Print("bars less than 100"); return(0); } if(TakeProfit<10) { Print("TakeProfit less than 10"); return(0); // проверяем TakeProfit } // ради упрощения и ускорения кода, сохраним необходимые // данные индикаторов во временных переменных MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0); MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0); SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1); MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0); MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
total=OrdersTotal(); if(total<1) { // нет ни одного открытого ордера if(AccountFreeMargin()<(1000*Lots)) { Print("We have no money. Free Margin = ", AccountFreeMargin()); return(0); } // проверяем на возможность встать в длинную позицию (BUY) if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious) { ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point, "macd sample",16384,0,Green); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice()); } elsePrint("Error opening BUY order : ",GetLastError()); return(0); } // проверяем на возможность встать в короткую позицию (SELL) if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious) { ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0,Bid-TakeProfit*Point, "macd sample",16384,0,Red); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice()); } elsePrint("Error opening SELL order : ",GetLastError()); return(0); } return(0); } // переходим к важной части эксперта - контролю открытых позиций // 'важно правильно войти в рынок, но выйти - еще важнее...' for(cnt=0;cnt<total;cnt++) { OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); if(OrderType()<=OP_SELL && // это открытая позиция? OP_BUY или OP_SELL OrderSymbol()==Symbol())// инструмент совпадает? { if(OrderType()==OP_BUY)// открыта длинная позиция { // проверим, может уже пора закрываться? if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && MacdCurrent>(MACDCloseLevel*Point)) { OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // закрываем позицию return(0); // выходим } // проверим - может можно/нужно уже трейлинг стоп ставить? if(TrailingStop>0) { if(Bid-OrderOpenPrice()>Point*TrailingStop) { if(OrderStopLoss()<Bid-Point*TrailingStop) { OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop, OrderTakeProfit(),0,Green); return(0); } } } } else// иначе это короткая позиция { // проверим, может уже пора закрываться? if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) { OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // закрываем позицию return(0); // выходим } // проверим - может можно/нужно уже трейлинг стоп ставить? if(TrailingStop>0) { if((OrderOpenPrice()-Ask)>(Point*TrailingStop)) { if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0)) { OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop, OrderTakeProfit(),0,Red); return(0); } } } } } } return(0); } // конец.
Для финальной настройки эксперта необходимо лишь указать значения внешних
переменных "Lots = 1", "Stop Loss (S/L) = 0" (не используется), "Take Profit (T/P) = 120" (для часовок
подходит), "Trailing Stop (T/S) = 30". Конечно же, значения вы можете поставить свои.
Нажмите на кнопку "Compile" и, если не
обнаружатся ошибки (кстати, в редактор MetaEditor можно скопировать текст из вышеприведенной распечатки программы),
нажмите на кнопку "Save", чтобы эксперт сохранился.
Статья посвящена бесконфликтной торговле нескольких экспертов на одном терминале МТ 4 и рассчитана на пользователя, обладающего базовыми навыками работы с терминалом и программирования на MQL 4.
ИНТЕРЕСНО, ЭНТО ТЕСТ ДЛЯ ВЫЯВЛЕНИЯ ДУМАЮЩИХ, И ОТДЕЛЕНИЯ ИДИОТОВ ОТ ДЕЙСТВИТЕЛЬНО ЧТО-ТО ХОТЯЩИХ ПОНЯТЬ? ИЛИ ТУПАЯ ШУТКА АВТОРА?МОЖЕТ АВТОР БЫЛ В ЗЮ-ЗЮ, И РЕШИЛ ПРИ НАПИСАНИИ ЭНТОГО СОВЕТНИКА ДЕЙСТВИТЕЛЬНО СНАЧАЛА ЗАКРЫТЬ ПОЗИЦИЮ, А ПОТОМ СТАВИТЬ ТРЕЙЛИНГ-СТОП????? ЧЕСТНО ГОВОРЯ, САМ ПОТРАТИЛ 3 ЧАСА, ПЫТАЯСЬ ПОНЯТЬ В ЧЕМ ХРЕНЬ...
Так кто ж тогда тупой и в зюзю??? Логика такова, что сначала исполняются наиболее значимые события. Сначала проверяем не пора ли закрыть ордер и если пора, то закрываем. Если нет, то проверяем не пора ли двигать стопы, если пора - двигаем... Далее уже проверяем критерии открытия новой позиции.
Что вы будете делать, если у вас открыта позиция Бай, а рынок резко идёт в противоположную сторону? Вы, с вашим IQ, сначала откроете Селл, а потом будете двигать стопы? И в последнюю очередь закроете ордер Бай???
А вот советник, построенный по логике, в которую вы не вкурили сначала закроет ордер Бай (ведь рынок пошёл против), потом попробует двинуть стоп, а если нет ордера, которому требуется двигать стоп, только тогда откроет Селл. И то, сначала проверив критерии открытия. И если сигнала от индикатора на Селл нету, то даже при резком таком скачке цен он не откроет позицию.
Похоже, вы этот тест не прошли...
т.е. зачем производить проверку на тип ордеров, если в следующей строке она все равно производится на определенный тип OP_BUY или OP_SELL?
2) переменная cnt дважды инициализированна нулем и в принципе можно ли ее заменить на обычную "круговую" i не объявляя (дабы сэкономить кучу тактов процессора)))?
Здравствуйте. Сам в программировании не силен, могу поменять переменные, не более того.Что нужно добавить, чтобы сделки на покупку совершались и при пересечении сигнальной линии гистограммой выше 0?Соответственно добавим сделок и на продажу. И добавим стоп-лосс. Заранее благодарен.
ИНТЕРЕСНО, ЭНТО ТЕСТ ДЛЯ ВЫЯВЛЕНИЯ ДУМАЮЩИХ, И ОТДЕЛЕНИЯ ИДИОТОВ ОТ ДЕЙСТВИТЕЛЬНО ЧТО-ТО ХОТЯЩИХ ПОНЯТЬ? ИЛИ ТУПАЯ ШУТКА АВТОРА?МОЖЕТ АВТОР БЫЛ В ЗЮ-ЗЮ, И РЕШИЛ ПРИ НАПИСАНИИ ЭНТОГО СОВЕТНИКА ДЕЙСТВИТЕЛЬНО СНАЧАЛА ЗАКРЫТЬ ПОЗИЦИЮ, А ПОТОМ СТАВИТЬ ТРЕЙЛИНГ-СТОП????? ЧЕСТНО ГОВОРЯ, САМ ПОТРАТИЛ 3 ЧАСА, ПЫТАЯСЬ ПОНЯТЬ В ЧЕМ ХРЕНЬ...