Написание советника для торговли

В программировании советников как и в любом деле главное идея, а реализация может быть разной.
На каком-то форуме просили сделать советника для торговли на новостях, что бы он в определенное время выставлял два стоп ордера на заданном от цены расстоянии. После срабатывания одного второй удаляется. Если за 30 минут после открытия не сработал ни один ордер, удаляем оба. Это будет техзадание на разработку советника.

Открываем MetaEditor, редактор языка MetaQuotes Language 4 (MQL4).

В меню жмем Файл/создать, выбираем советник. В следующем окне даем нашему Граалю имя, «автор» и «ссылка» это что бы все знали кто сотворил это чудо и где вас можно найти. Ниже можно выставить переменные которые будут входными параметрами вашего советника. Зададим несколько свойств, Delta – расстояние от цены для выставления стоп ордеров, стоплос и тейкпрофит. Кнопка добавить, имя Delta (в MQL4 регистр имеет значение, Delta и delta это разные переменные), тип int – целые числа и параметр который эксперт будет иметь по умолчанию.

Написание советника для торговли
//+------------------------------------------------------------------+
//| TradeOnNews.mq4                                                  |
//| Copyright © 2008, MetaQuotes Software Corp.                      |
//|                                         |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, MetaQuotes Software Corp."
#property link "http://www.metaquotes.net"

//---- input parameters
extern int Delta=15;
extern int StopLoss=20;
extern int TakeProfit=30;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
 {
//----

//----
  return(0); 
 }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
 {
//----

//----
  return(0);
 }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
 {
//----

//----
  return(0);
 }
//+------------------------------------------------------------------+

Тело функции находится между открывающей и закрывающей скобками {}.

А вот код советника сделанного по нашему техзаданию.

//+------------------------------------------------------------------+
//|                                                  TradeOnNews.mq4 |
//|                      Copyright © 2008, MetaQuotes Software Corp. |
//|                                         |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2008, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net"

//---- input parameters
extern string StartTime      = "15:30";  // время открытия маркет ордера (по времени MetaTrader)
extern string CloseTime      = "16:00";  // Время закрытия ордеров
extern int    Delta          = 15;  // расстояние от цены для выставления стоп ордеров
extern int    StopLoss       = 20;
extern int    TakeProfit     = 30;
extern double Lot            = 0.1;

int day, EP;
string Symb;

double   Mas_Ord[31][13];   // Массив ордеров
int      Mas_Tip[6];            // Массив колич. ордеров всех типов // [] тип орд: 0=B,1=S,2=BL,3=SL,4=BS,5=SS
int      Mas_Tip_T[6];          // Массив колич. ордеров всех типов - открытых сегодня
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   Symb = Symbol();    // Название фин.инстр.
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   int total = OrdersTotal(); // сколько всего ордеров открыто
   Orders(); // вызываем функцию учета ордеров
   if(total > 1) // если ордеров больше одного - в нашем случае два
    {
     for(int i = 0; i < total; i ++)
      {
       if(OrderSelect(i, SELECT_BY_POS) == false) continue; // если ордер не выбран, возвращаемся в начало цыкла
       if(Mas_Tip[0] > 0                 // если ордеров бай больше нуля - есть бай ордер
          && OrderType() == OP_SELLSTOP) // и есть селстоп
          OrderDelete(OrderTicket());    // удаляем селстоп

       if(Mas_Tip[1] > 0                // если ордеров сел больше нуля - есть сел ордер
          && OrderType() == OP_BUYSTOP) // и есть байстоп
          OrderDelete(OrderTicket());   // удаляем байстоп
      }
    }
   if(total == 1 && Mas_Tip[4] > 0) // если открыт один ордер и этот ордер байстоп
    {
     Expiration(); // вызываем функцию Expiration для расчета продолжительности жизни отложеного ордера
     OrderSend(Symb,OP_SELLSTOP,Lot,Bid-Delta*Point,3,Bid-Delta*Point + StopLoss*Point,Bid-Delta*Point - TakeProfit*Point,NULL,0,EP,Red); // открываем селстоп
    }
   if((total == 0) && (TimeCurrent() > StrToTime(StartTime)) && (TimeCurrent() < StrToTime(StartTime) + 60))
    { // если нет ни одного открытого ордера и текущее время перевалило за время открытия и текущее время меньше за время открытия плюс 60 секунд
     Expiration(); // вызываем функцию Expiration для расчета продолжительности жизни отложеного ордера
     OrderSend(Symb,OP_BUYSTOP,Lot,Ask+Delta*Point,3,Ask+Delta*Point - StopLoss*Point,Ask+Delta*Point + TakeProfit*Point,NULL,0,EP,Blue); //открываем байстоп
    }
   return(0);
  }
//+------------------------------------------------------------------+
// заполняем масив даными об ордерах - сколько, каких и тд.          |
//+------------------------------------------------------------------+
int Orders()//Інформація про ордера... сколько, каких, цена открытия, стоплос и тд.
 {
  int Qnt = 0;                          // Счётчик количества ордеров
  Qnt = 0;                              // Обнуление счётчика ордеров
  ArrayInitialize(Mas_Ord,0);           // Обнуление массива
  ArrayInitialize(Mas_Tip,0);           // Обнуление массива
  ArrayInitialize(Mas_Tip_T,0);         // Обнуление массива
  for(int i = 0; i < OrdersTotal(); i++)// По рыночн. и отлож. ордерам
   {
    if((OrderSelect(i,SELECT_BY_POS) == true) && (OrderSymbol() == Symb))//Если есть следующ.  //.. и наша вал.пара
     {
      Qnt++;                                        // Колич. ордеров
      Mas_Ord[Qnt][1] = OrderOpenPrice();           // Курс открытия орд
      Mas_Ord[Qnt][2] = OrderStopLoss();            // Курс SL
      Mas_Ord[Qnt][3] = OrderTakeProfit();          // Курс ТР
      Mas_Ord[Qnt][4] = OrderTicket();              // Номер ордера
      Mas_Ord[Qnt][5] = OrderLots();                // Количество лотов
      Mas_Tip[OrderType()]++;                       // Кол. ордеров типа
      Mas_Ord[Qnt][6] = OrderType();                // Тип ордера
      Mas_Ord[Qnt][7] = OrderMagicNumber();         // Магическое число
      if(OrderComment() == "") Mas_Ord[Qnt][8] = 0; // Если нет коммент
       else Mas_Ord[Qnt][8] = 1;                    // Если есть коммент
      Mas_Ord[Qnt][9] = OrderOpenTime();            // Время открвтия ордера
      Mas_Ord[Qnt][10] = OrderCloseTime();          // Время закрытия ордера
      Mas_Ord[Qnt][11] = OrderExpiration();         // Возвращает дату истечения для выбранного отложенного ордера.
      if(day == TimeDayOfYear(OrderOpenTime()))     // работа с ордером ...
       {
        Mas_Tip_T[OrderType()]++;                   // Кол. ордеров типа... открытых сегодня
       }
     }
   }
  Mas_Ord[0][0] = Qnt;                         // Колич. ордеров
  return;
 }
//+------------------------------------------------------------------+
//                                                                   |
//+------------------------------------------------------------------+
int Expiration()
 { // если время открытия больше чем время закрытия - додаем одни сутки в секундах(60*60*24)
  if(StrToTime(StartTime) > StrToTime(CloseTime)) EP = 60*60*24 + StrToTime(CloseTime);
   else EP = StrToTime(CloseTime); // если время открытия не больше чем время закрытия - не додаем
 }
//+------------------------------------------------------------------+

Рассмотрим код эксперта подробнее.
Добавим еще пару переменных.

extern string    StartTime    = "15:30";  // время открытия маркет ордера (по времени MetaTrader)
extern string   CloseTime    = "16:00";  // Время закрытия ордеров
extern int       Delta=15;
extern int       StopLoss=20;
extern int       TakeProfit=30;
extern double Lot            = 0.1;  //размер лота

Зарезервированное слово «extern» означает что эта будет видна в настройках советника. string – значение строкового типа, строки всегда берутся в двойные кавычки. Переменные типа double – числа с дробью.
Ниже объявим несколько глобальных переменных, так как они объявлены вне функций, их можно использовать в любой функции. При вызове функции данные в этих переменных сохраняются.

int day, EP;
string Symb;

double   Mas_Ord[31][13];   // Массив ордеров
int      Mas_Tip[6];            // Массив колич. ордеров всех типов // [] тип орд: 0=B,1=S,2=BL,3=SL,4=BS,5=SS
int      Mas_Tip_T[6];          // Массив колич. ордеров всех типов - открытых сегодня

Если переменная объявлена внутри функции, использовать ее можно только в ней и при вызове функции данная переменная обнуляется.
В функции init() – узнаем на какой график кинули советника(какая валютная пара) и запоминаем в переменой Symb.

Symb = Symbol();// Название фин.инстр. в переменой Symb запоминаем валютную пару по которой будем торговать.

Алгоритм функции start() я строю по принципу реальной торговли. Сначала проверяем есть ли открытые ордера и если пора закрывать, то закрываем. Потом смотрим нет ли чего модифицировать. Если все что нужно закрыть, закрыто. Все что нужно модифицировать, модифицировано. Проверяем нет ли условий по нашей ТС для покупки или продажи.
Узнаем сколько у нас открытых ордеров – вызываем функцию OrdersTotal(). OrdersTotal() возвращает общее количество открытых ордеров всех типов.

int total = OrdersTotal(); // сколько всего ордеров открыто

Запоминаем в переменой total количество открытых ордеров.

Немного теории.
Операции отношения: == — равно, != — не равно, > — больше, < — меньше, >= — больше или равно, <= — меньше или равно.
Логические отношения: ! – не, || — или, && — и.

Оператор if (если): if(total == 0) action; – если(ордеров = 0) действие; — если(выражение) { действие 1; действие 2; действие n;}. Если (выражение истинно) выполняется первый оператор после прямых скобок (), если (выражение ложно) оператор после прямых скобок() пропускается и выполняется следующий. Если нужно выполнить больше одного оператора помещаем их в фигурные скобки {}. К оператору if можно добавить часть else:
if(total == 0) action 1; — если (выражение истинно) выполняется действие 1; — если (выражение ложно)
else action 2; — выполняется действие 2;
вариант с несколькими операторами:

if(выражение)
 {   // если истинно
  действие 1;
  действие 2;
 действие n;
}
 else
  {  // если ложно
    действие 3;
    действие 4;
    действие n;
  }

Вернемся к советнику: если еще не открыт ни один ордер и наступило время торговли

Написание советника для торговли

TimeCurrent() – возвращает последнее известное время сервера (время последней котировки) в виде количества секунд прошедших после 00:00 1 января 1970 года.
StrToTime(string) – преобразование строки, содержащей время или дату в формате ”yyyy.mm.dd[hh:mi]”, в число типа datetime (количество секунд, прошедших с 01.01.1970).
Если все условия истинны – выполняется тело оператора if.

 Expiration(); // вызываем функцию Expiration для расчета продолжительности жизни отложеного ордера

int Expiration()
 { // если время открытия больше чем время закрытия - додаем одни сутки в секундах(60*60*24)
  if(StrToTime(StartTime) > StrToTime(CloseTime)) EP = 60*60*24 + StrToTime(CloseTime);
    else EP = StrToTime(CloseTime); // если время открытия не больше чем время закрытия - не додаем
 }

Функция Expiration() записывает в переменную ЕР время закрытия отложенного ордера. После отработки кода функции Expiration(), нас возвращает назад в функцию start(), в тот же место строкой ниже. И запускается торговая функция OrderSend которая выставляет ордер байстоп.

Написание советника для торговли

Point – размер пункта текущей валютной пары (например EUR/USD 0.0001). Delta*Point = 15*0.0001 = 0.0015.
Ордер выставлен, конец функции start(). С приходом нового тика функция start() запускается сначала. Снова узнаем сколько открытых ордеров.
int total = OrdersTotal();
в переменную total теперь записывается 1 (есть один открытый ордер).
Вызывается функция Orders();. В которой всевозможная информация об открытых ордерах заносится в массив.
Массив – набор переменных одного типа, имеющих общее название.

Написание советника для торговли

Одномерный массив Mas_Tip[6] содержит 7 ячеек. Индекс первого элемента массива 0, если в массиве 7 элементов – индекс остатнего элемента будет 6.

Написание советника для торговли

Двухмерный массив Mas[9][9]. При обращении к двухмерному массиву (помещению или извлечению данных), указывается два индекса. На пересечении этих двух индексов находится нужная нам ячейка.

i++  // это значит i увеличить на 1, это выражение можно записать и так i = i + 1;
i--  // уменьшить на 1, тот же результат получим если запишем i = i – 1;

for(выражение1; выражение2; выражение3) оператор; // for – оператор цикла.
for(int i = 0; i < 5; i++) «оператор»;
выражение1 выполняется один раз при старте цикла, переменой i задается начальное значение. Проверяется выражение2 если оно ложно (равно или больше 5), «оператор» не выполняется цикл заканчивается и управление передается следующему оператору.
Если выражение2 истинно (i меньше 5), выполняется «оператор» и выражение3. Снова проверяется выражение2, цикл повторяется до тех пор пока выражение2 не станет ложно. Нужно быть внимательным при написании кода цикла, может быть ситуация когда выражение2 никогда не станет ложным, тогда цикл никогда не закончится (вечный цикл) и программа зависнет.

Написание советника для торговли

Вернемся в советник, функция Orders(). С помощью цикла перебираем все наши открытые ордера и заносим их данные в массив. Что бы получить информацию о ордере нужно сначала его выбрать с помощью функции OrderSelect.
После окончании работы функции Orders(), возвращаемся в start(). У нас был открыт один байстоп ордер, проверяем условия для открытия второго ордера.

Написание советника для торговли

Если открыт только один ордер (total == 1) и этот ордер байстоп (Mas_Tip[4] > 0), открываем второй (селстоп).
И остатний этап. Теперь имеем два ордера (total > 1). С каждым тиком проверяем не сработал ли отложенный ордер. Если сработал байстоп и преобразовался в бай (Mas_Tip[0] > 0), удаляем селстоп — OrderDelete(OrderTicket());.
Если сработал селстоп и преобразовался в сел (Mas_Tip[1] > 0), удаляем байстоп — OrderDelete(OrderTicket());.

Исходник: TradeOnNews 0

Важно! Этот советник не годится для торговле на реале. В нем много «подводных камней», которые необходимо устранить.

Написание элементарного индикатора — https://forexlab.ru/mql-writing-basic-indicator/

You May Also Like