Урок №10: Наш первый советник

Уважаемые коллеги, на этом уроке мы напишем наш первый советник, познакомимся с некоторыми основными функциями языка MQL4, а также напишем несколько своих.

Приступим, и для начала запустим MetaEditor…

 

MetaEditor

 

и создадим шаблон эксперта, ничего не меняя, просто нажимаем кнопку «Далее»…

Мастер создания советника

Обязательно заполняем, как минимум, поле «Имя» — это название нашего будущего советника, поля «Автор» и «Ссылка» не обязательны к заполнению. Добавляем параметры «Lots», «TakeProfit», «StopLoss», обращая внимание тип данных каждого параметра. После заполнения нажимаем кнопку «Далее»:

Мастер создания советника

На следующей форме мастера мы также ничего не отмечаем, просто нажимаем кнопку «Далее»:

Мастер создания советника

И очередное окно мы оставляем без заполнения, нажимаем кнопку «Готово»:

Мастер создания советника

Итак, мы получили шаблон будущего эксперта, с которым будем работать.

MetaEditor

 

Итак, мы предусмотрим наш будущий советник для работы с 4-х и 5-значным брокером и тут два варианта как это реализовать. Можно заменить вид параметра «input» на «extern» для того, чтобы можно было из кода советника модифицировать эти значения (значения с видом параметра «input» изменять в коде эксперта нельзя!)

//--- input parameters
extern double   Lots=0.1;
extern int      TakeProfit=70;
extern int      StopLoss=40;

или добавить глобальные переменные «TP» и «SL», в которые скопируем значения «TakeProfit» и  «StopLoss» и работать уже с ними.

Я выбираю первый вариант, т.е. меняю «input» на «extern«.

Далее в функции «OnInit» добавляю следующий код:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if (Digits == 3 || Digits == 5)
   {
       TakeProfit *= 10;
       StopLoss   *= 10;
   }
   
   return(INIT_SUCCEEDED);
}

Поясняю:

Функция Digits возвращает количество десятичных знаков после запятой, определяющее точность измерения цены символа текущего графика, таким образом:

  1. Я проверяю количество знаков после запятой у текущей валютной пары и если количество знаков равно 3 или равно 5, то делаю вывод, что мой брокер работает с пятизначными котировками.
  2. Следующим шагом я умножаю TakeProfit и StopLoss на 10. Запись «TakeProfit *= 10;» эквивалентна записи «TakeProfit = TakeProfit * 10;«

 

Продолжим.

Теперь нужно определиться как мы будем открывать позиции, в какую сторону и в какой момент? Покупать или продавать?

Осмелюсь предложить воспользоваться для этих целей индикатором «MovingAverage» и у нас будет следующий алгоритм:

  1. Если текущая цена будет выше, чем значение индикатора, то будем покупать.
  2. Если текущая цена будет ниже, чем значение индикатора, то будем продавать.
  3. Закрытие позиций будет происходить либо по TakeProfit, либо по StopLoss.

Язык программирования MQL4 предоставляет нам ряд функций для работы с индикаторами, и как раз одна из них позволяет получить нам текущее значения индикатора:

Moving Average

Итак, у нас есть функция iMA, из справочника(вызывается по клавише «F1») нам известны её параметры, часть из которых я сразу же переношу во внешние параметры советника для последующей оптимизации параметров самого индикатора:

//+-----------------------------------------------------------------------------+
extern double             Lots          = 0.1;
extern int                TakeProfit    = 70;
extern int                StopLoss      = 40;
 // Магический номер открытого ордера
extern int                Magic         = 12345;
//+-----------------------------------------------------------------------------+
extern string             MA            = "Настройки индикатора Moving average";
extern int                ma_period     = 20;
extern int                ma_shift      = 8;
extern ENUM_MA_METHOD     ma_method     = MODE_SMA;
extern ENUM_APPLIED_PRICE applied_price = PRICE_CLOSE;
extern int                shift         = 1;
//+-----------------------------------------------------------------------------+

Обратите внимание на то, что я заменил тип «int» у двух параметров на перечисления «ENUM_MA_METHOD» и «ENUM_APPLIED_PRICE». Сделал я это для удобства восприятия самих параметров, согласитесь, что прочитать, к примеру, у параметра ma_method значение «MODE_SMA» гораздо понятней, чем наблюдать простое число. Кроме того я заранее добавил ещё один параметр «Magic». Он нам понадобится для открытия ордера.

Теперь переходим к функции «OnTick»(она выполняется каждый раз при поступлении новой котировки по валютной паре) и получим значение индикатора MovingAverage:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   double ma = iMA(NULL, 0, ma_period, ma_shift, ma_method, applied_price, shift);
}
//+------------------------------------------------------------------+

Мы объявляем переменную «ma» и записываем в неё значение функции «iMA» с заданными параметрами, значения которых определены в параметрах самого эксперта.

Также нам понадобится несколько функций:

  1. Функция подсчёта количества количества ордеров на покупку.
  2. Функция подсчёта количества количества ордеров на продажу.

Добавим их в код советника:

//+------------------------------------------------------------------+
//| Возвращает количество позиций на покупку                         | //+------------------------------------------------------------------+
int CountBuy()
{
   int order_total=0;
   for(int cnt=0; cnt 

Отлично, теперь разберём каждую строчку функции «CountBuy()«:

int CountBuy() // Объявление функции без параметров, тип возвращаемого значения - int(целое число)
{
   // Объявление переменной order_total с типом int
   // В этой переменной будет храниться счётчик количества открытых позиций, находящихся в рынке
   int order_total=0;

   // Объявляем цикл, для того чтобы пересчитать все ордера, которые мы открыли.
   // Функция OrdersTotal() возвращает общее количество открытых и отложенных ордеров.
   for(int cnt=0; cnt 

Точно аналогичное описание применимо и для функции «CountSell()«. Тут всё тоже самое, кроме одного условия:

if (OrderType()==OP_SELL ...

т.е. проверяется тип ордера «на продажу».

Далее вносим следующие дополнения в тело функции «OnTick()»:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Объявляем переменную для работы с тикетом ордера
   int ticket;
   
   // Объявляем перменные для расчёта цен TakeProfit и StopLoss
   double tp, sl;
   
   // Получаем значение индикатора "MovingAverage"
   double ma = iMA(NULL, 0, ma_period, ma_shift, ma_method, applied_price, shift);
   
   // Если текущая цена выше значения индикатора И количество открытых позиций равно нулю,
   if (Ask > ma && CountBuy() == 0)
   {
            // то открываем ордер на покупку с записью его тикета в переменную "ticket"
            ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 5, 0, 0, "", Magic, 0, Blue);
            
            // Если по какой-то причине ордер открыть не удалось, 
            if (ticket 

т.е. мы объявили переменные ticket, sl, tp для дальнейшей работы с ними, а также добавили код для открытия ордера на покупку.

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Объявляем переменную для работы с тикетом ордера
   int ticket;
   
   // Объявляем перменные для расчёта цен TakeProfit и StopLoss
   double tp, sl;
   
   // Получаем значение индикатора "MovingAverage"
   double ma = iMA(NULL, 0, ma_period, ma_shift, ma_method, applied_price, shift);
   
   // Если текущая цена выше значения индикатора И количество открытых позиций на покупку равно нулю,
   if (Ask > ma && CountBuy() == 0)
   {
            // то открываем ордер на покупку с записью его тикета в переменную "ticket"
            // Ask - это текущая цена для ордера на покупку
            ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, 5, 0, 0, "", Magic, 0, Blue);
            
            // Если по какой-то причине ордер открыть не удалось, 
            if (ticket 

На этом, пожалуй, всё. Простейший советник написан, остаётся его скомпилировать и обратить внимание на журнал ошибок:

Компиляция советника

Ну и конечно же его стоит протестировать, поэтому запускаем терминал и открываем форму тестера стратегий:

Тестер стратегий

После компиляции наш первый советник в окне «Навигатор» и доступен для тестирования на форме тестера стратегий.

Выбираем параметры запуска, т.е. указываем, что тестировать будем советник «First_Expert.ex4», а также валютную пару и период:

Тестер стратегий

Нажимаем кнопку «Старт» и смотрим результат:

Результат работы тестера стратегий

На вкладке «График» тестера стратегий можно посмотреть график баланса.

График работы тестера стратегий

 

Ну что-ж, для первого раза вполне неплохо, на этом наш урок закончен.

По ссылке доступен для скачивания полный код нашего с вами советника First_Expert.

 

Written by 

Один комментарий к “Урок №10: Наш первый советник”