Самообучение языку MQL5 с полного нуля - страница 71

 
MrBrooklin:

Игорь, желание изучать язык MQL5 не пропадает, а наооборот, только усиливается. Меня просто разрывает, почему такой тупой и не могу понять простые вещи с этим оператором цикла. Главное без оператора цикла код работает идеально, а с циклом полный ступор. Всё-равно докапаюсь до истины. Правда, времени стало гораздо меньше для самообучения, т.к. закончился карантин и на работе появляются редкие моменты, чтобы заниматься изучением языка. В основном разбираюсь с языком вечером и до поздней ночи.

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

  1. Написать код с примением оператора цикла for для перебора всех открытых позиций на всех символах и если таковых нет, то открыть позицию Buy в промежутке времени с 09:00:00 по 09:01:00, а в 23:50:00 закрыть её принудительно, если в течении торгового дня не сработает стоп-лосс. Без оператора цикла for, как уже раньше писал, всё работает идеально. Теперь хочу добиться такого же результата, но уже с применением цикла.
  2. Написать 2-е функции, определяющие направление открытия позиции по первому тику, который появится в промежутке времени с 09:00:00 по 09:01:00. Если первый тик вверх, то должна открыться позиция Buy, если первый тик вниз, то соответственно позиция Sell. Это никакая не стратегия, а просто моя "хотелка", чтобы не использовать условие random.
С уважением, Владимир.

Владимир, Вы в какие-то дебри лезете. Вам дан был шаблон: https://www.mql5.com/ru/forum/352460/page58#comment_18726492 Вы его проигнорировали и решили идти напролом своим умом. Такая дорога трудна и может затянутся на годы. По дороге изучите Си, Си++, Rust, C#, полиморфизм, наследование, разыменовывание указателей, динамическое приведение типов - но Вы уж определитесь с целью: Вам шашечки или ехать. К цели надо идти кратчайшем путем. Если хотите трейлинг - пишите трейлинг. Не надо изучать Си и Си++ для этого. Это все потом в качестве факультатива, если возникнет желание.

Ситуация с циклом for показательная. Это мощная конструкция, которую нужно применять к месту. У Вас неттинг и вспомогательный алгоритм. Поэтому нет перебора позиций, нет никаких мэджиков, поэтому нет и этого цикла. Естественно без него у Вас все работает, а с циклом получается масло масленное. Но Вы упорно пытаетесь его обязательно использовать, видимо это является самоцелью: как можно больше использовать всего и всякого, на про запас, что бы было.

 
Vasiliy Sokolov:

Владимир, Вы в какие-то дебри лезете. Вам дан был шаблон: https://www.mql5.com/ru/forum/352460/page58#comment_18726492 Вы его проигнорировали и решили идти напролом своим умом. Такая дорога трудна и может затянутся на годы. По дороге изучите Си, Си++, Rust, C#, полиморфизм, наследование, разыменовывание указателей, динамическое приведение типов - но Вы уж определитесь с целью: Вам шашечки или ехать. К цели надо идти кратчайшем путем. Если хотите трейлинг - пишите трейлинг. Не надо изучать Си и Си++ для этого. Это все потом в качестве факультатива, если возникнет желание.

Ситуация с циклом for показательная. Это мощная конструкция, которую нужно применять к месту. У Вас неттинг и вспомогательный алгоритм. Поэтому нет перебора позиций, нет никаких мэджиков, поэтому нет и этого цикла. Естественно без него у Вас все работает, а с циклом получается масло масленное. Но Вы упорно пытаетесь его обязательно использовать, видимо это является самоцелью: как можно больше использовать всего и всякого, на про запас, что бы было.

Здравствуйте, Василий!

Спасибо Вам за то, что как всегда вовремя подключаетесь к теме и направляете на путь истинный. Теперь разложу всё по-порядку:

  1. Шаблон в советник был вставлен ровно тогда, когда Вы его любезно мне предоставили, за что отдельное спасибо!
  2. По своему скудоумию решил, что советник с Trailin Stop нужно проверять на тестере стратегий, но как это сделать без автоматического открытия хотя бы одной позиции не додумался. Поэтому и появились дебри с написанием "полноценного" советника. В кавычках написал потому, что как таковой стратегии у советника нет. Просто не хотел для открытия позиции использовать условие random.
  3. Никто из собеседников, кроме Вас, не подсказал, что в данной ситуации для перебора позиций цикл for не нужен. Мой косяк, мог бы давно уже сам догадаться и спросить - а нужен ли вообще цикл? Оказалось, что не нужен. За подсказку ещё раз спасибо!
  4. Чтобы наконец-то перейти к непосредственному написанию трейлинг стопа и при этом не бросить на полпути уже начатое, мне остается только написать код 2-х функций, определяющих направление открытия неттинговой позиции Buy или Sell.
  5. Да, полностью согласен с Вами, что самообучение начинает затягиваться. Но если принять во внимание, что иду по неизведанному и извилистому для меня пути, то надеюсь, что можно сделать на это скидку.
  6. За весь период самообучения получил огромное количество информации и теперь пытаюсь её не только упорядочить, но и применить на деле. Для этого в CodeBase мною был взят советник, доработан и уже две недели стоит на демо-счете одного форекс-дилера. Результат получается именно тот, который и ожидал от него. Не далек тот момент, когда поставлю на реальный счёт. Это уже плюс!
  7. За скоростью самообучения не гонюсь, т.к. уже ранее писал, что от переизбытка информации периодически наступает ступор.
  8. За весь период самообучение понемногу стал запоминать английские слова и фразы, которые появляются после компиляции кода. Уже реже заглядываю в Google Translate. Это тоже положительный момент в самообучении.
  9. Всё, что уже мною написано в советнике выложу ровно тогда, когда будет сформирована полная заготовка под трейлинг стоп. Постараюсь доделать её в субботу. Вот с именно с этого момента и начнётся основная работа по написанию кода трейлинг стопа.

Василий, за наставление и конструктивную критику премного благодарен! Также выражаю огромную благодарность всем участникам данной темы, которые как могут, так и помогают мне в самообучении языку программирования MQL5 с полного нуля.

Самообучение продолжается. To be contined (продолжение следует).

С уважением, Владимир.

 
Андрей:

Всем добрый день!

Хотел немного добавить, тема интересна, мне кажется сама идея "Самообучение языку MQL5 с полного нуля" не совсем верна, если человек 0 в программировании, то ему для начала нужно научиться программировать. Если цель научиться программировать именно на MQL  с нуля, то нужно вначале научиться программировать на С, хотя-бы самый базис, а потом научиться писать на MQL, так как MQL это по сути С и есть, но специализированый, заточенный под определенную задачу, человек не понимает как циклы работают, а уже пытается во всю программы писать. Это все равно, что пытаться выучить феню не зная русского...

Другое дело, что С не самый дружелюбный язык для изучения программирования, он прост, но через чур лаконичен, новичку сложно читать и понимать код, не имея базовых понятий об алгоритмах, типах данных, булевой алгебре. в С три символа подряд могут нести смысл нескольких строк в другом языке.

Чтобы научиться просто программировать с нуля, для базы, чтобы научиться говорить с компьютером на одном (неважно пока каком) языке, я бы посоветовал начать с Pascal'я,  самую простую книгу для начала прорешать на нем (недели 2-3 на расслабоне), потом С (тут после паскаля неделя максимум при усердии, освоить синтаксис, я про базовые знания языка!) а уже потом браться за специализированные языки типа MQL, потому, что MQL - это С, с небольшими заморочками но и упрощениями, так как заточен на одну задачу и очевидное компилятор знает сам. А С в свою очередь, это такой сжатый RARом паскаль, ну а паскаль почти английский =).

Теперь по циклам:

Мне когда учился программировать помогла аналогия с суммой в алгебре, где указываются начальное значение n, конечное значение n и формула с этим n для расчета. 

Здравствуйте, Андрей! С базисом полностью с Вами согласен. У меня его нет и не было. Но, как говорится - Москва не сразу строилась! За советы и подсказки Вам спасибо!

С уважением, Владимир.

 
Roman Shiredchenko:

Да. Согласен - его коды тож гуд! Вы себя накручиваете на пустом месте. Спокойно разбирайтесь в коде без спешки и все. Вот же все элементарно здесь - выбрали и тралим: (куда уж проще - как раз ваша тема выбора НУЖНОЙ позиции... :-))

Кроме этого, включаемые файлы - это значит, что они у вас в коде типа также расположены в виде функций и все.

Спасибо, Роман, за коды и ссылки! Разбираюсь с кодами спокойно и точно никуда не тороплюсь!

С уважением, Владимир.

 

Всем доброго дня и хорошего настроения!

Продолжаю изучение языка программирования MQL5. Сегодня, как и обещал, выкладываю на всеобщее обозрение код шаблона  советника, предназначенного для написания трейлинг стопа. В связи с тем, что код шаблона советника получился достаточно громоздким, поэтому пришлось максимально сократить комментарии. Полную версию советника с подробными комментариями каждой строчки кода, в изложении доступном для ученика 1-го класса школы программирования, опубликую позже в виде файла c рабочим названием Trailing_Stop.mq5, чтобы не плодить на сайте очередную "портянку".

Перед публикацией кода была произведена проверка работы советника в тестере стратегий. Проблемы не выявлены. В советнике применена неттинговая система учёта позиций. Эта система учета подразумевает, что в один момент времени на счете может быть только одна открытая позиция по одному и тому же символу (финансовому инструменту).

На настоящий момент в советнике реализована возможность:

  1. Проверять торговый счет, на который планируется его установить. Если советник будет устанавливаться на демо-счёт, то в окне сообщений появится разрешение на продолжение работы. Если будет предпринята попытка установить советник на реальный счёт, то в окне сообщений появится предупреждение о невозможности продолжения работы, после чего он автоматически удалится с рабочего окна торгового терминала.
  2. Один раз в торговый день ровно в 9 ч. 00 мин. по московскому времени автоматически открывать одну позицию в ту сторону, куда будет направлен первый тик. Например, если в 9 ч. 00 мин. по московскому времени первый тик будет направлен вверх, то значит откроется позиция BUY, если первый тик будет вниз, то значит откроется позиция SELL.
  3. Задавать размер стоп лосса.
  4. Задавать размер лота.
  5. Задавать другие параметры, которые будут необходимы для работы трейлинг стопа.

Кроме того, в советнике предустановлен шаблон, любезно предоставленный Василием Соколовым, который в ходе процесса самообучения будет наполняться кодом.

ПРЕДУПРЕЖДЕНИЕ!!!

Советник разрабатывается согласно плана самообучения для демо-счёта и исключительно в учебных целях! Не предназначен для торговли на реальном счёте и извлечения прибыли!

С уважением, Владимир.

//+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2020, MetaQuotes Software Corp."
#property link        "https://www.mql5.com"
#property description "Советник создан для демо-счёта исключительно в учебных целях!"
#property description "Не предназначен для торговли на реальном счёте и извлечения прибыли!"
#property version     "1.00"
/* Краткое описание советника Trailing_Stop. Код советника разрабатывается в соответствии с
   планом самообучения языку программирования MQL5. В советнике применена неттинговая система
   учёта позиций. Эта система учета подразумевает, что в один момент времени на счете может быть
   только одна открытая позиция по одному и тому же символу (финансовому инструменту). Алгоритм
   работы советника прост: один раз в торговый день ровно в 9 ч. 00 мин. по московскому времени
   будет открываться одна позиция в ту сторону, куда будет направлен первый тик. Например, если
   первый тик будет направлен вверх, то значит откроется позиция BUY, если первый тик будет вниз,
   то значит откроется позиция SELL. У открытой позиции сразу будет устанавливаться ордер Stop Loss
   фиксированного размера для минимизации потерь в том случае, если цена финансового инструмента
   станет двигаться в убыточном направлении. Если цена инструмента достигнет этого уровня, то
   позиция полностью закроется автоматически. Если цена финансового инструмента будет двигаться
   в прибыльном направлении, то тогда автоматически включится в работу Trailing_Stop (Трейлинг Стоп).
   Схема работы Трейлинг Стоп:
   1. С приходом новых котировок советник проверяет, прибыльна ли открытая позиция.
   2. Как только прибыль (в пунктах) станет равной либо большей той величины, которая указана во
      входном параметре советника "Уровень перестановки Stop Loss в безубыток", автоматически
      поступит команда для перемещения ордера Stop Loss на тот уровень цены, по которому открылась
      существующая позиция, т.е. в безубыток.
   3. Если цена и дальше продолжит движение с увеличением прибыльности позиции, то при превышении
      величины, указаной во входном параметре советника "Уровень перестановки Stop Loss в безубыток"
      на величину, которая указана во входном параметре "Шаг трейлинг стопа", Stop Loss вслед за
      текущей ценой автоматически переместится с уровня безубытка на величину этого шага.
   4. Если прибыльность позиции уменьшится, то модификации ордера происходить не будет. Таким
      образом, будет автоматически фиксироваться прибыль торговой позиции.
   Если в течении торгового дня открытая позиция не закроется по Stop Loss или Trailing_Stop, то в
   23 ч. 50 мин. советник принудительно закроет эту позицию.

   ВАЖНО!!! Советник создан для демо-счёта исключительно в учебных целях!
            Не предназначен для торговли на реальном счёте и извлечения прибыли!*/

//--- Создадим входные параметры советника
input ushort BreakevenLevel=100; //Уровень перестановки Stop Loss в безубыток
input ushort TrailingStop=10;    //Шаг трейлинг стопа
input ushort SL=200;             //Стоп-лосс
input double Lot=0.1;            //Лот
input long   Magic_Number=3215;  //Магический номер
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   /* Создадим оператор-переключатель switch, который cравнивает значение выражения с константами во всех
      вариантах case и передает управление оператору с соответствующим значением выражения.*/
   switch(account_type)
     {
      case ACCOUNT_TRADE_MODE_DEMO: //если ключевое слово ACCOUNT_TRADE_MODE_DEMO
         trade_mode="Счёт DEMO";    //значит торговый режим счёта - демо
         break;                     //оператор break прекращает выполнение ближайшего оператора switch
      case ACCOUNT_TRADE_MODE_REAL: //если ключевое слово ACCOUNT_TRADE_MODE_REAL
         trade_mode="Счёт REAL";    //значит торговый режим счёта - реальный
         break;                     //оператор break прекращает выполнение ближайшего оператора switch
     }
   if(account_type==ACCOUNT_TRADE_MODE_REAL) //если торговый режим счёта - реальный
     {
      //--- выводим окно сообщений на торговом терминале и закрываем советник
      MessageBox("Работа на реальном счете запрещена, выходим!","Советник запущен на реальном счете");
      return(-1); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if(account_type==ACCOUNT_TRADE_MODE_DEMO) //если торговый режим счёта - демо
     {
      //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
      MessageBox("Работа на демо-счете разрешена!","Советник запущен на демо-счете");
      return(0); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- сообщим код завершения работы эксперта
   Print(__FILE__,": Код причины деинициализации = ",reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   TimeToStruct((D'1970.01.01 23:50:00'),time_close);
//--- Зададим условия для открытия позиций BUY и SELL   
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   price=NormalizeDouble(price,digits);
   if(PositionSelect(Symbol())==false
      && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
      && time_current.hour==time_open.hour
      && time_current.min>=time_open.min
      && time_current.min<time_open1.min
      && (TickUP()==(price+point)))
     {OpenBUY();}
   else
     {
      if(PositionSelect(Symbol())==false
         && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
         && time_current.hour==time_open.hour
         && time_current.min>=time_open.min
         && time_current.min<time_open1.min
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if(time_current.hour==time_close.hour && time_current.min==time_close.min)
      CloseALL();

//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+

//-- Выбираем позиции по текущему символу. Если позиции нет и выбирать нечего, то выходим!
   if(!PositionSelect(Symbol()))
      return;
//-- Стоп-лосс длинной позиции переставляем в безубыток и тралим его
   if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
     {
      SetBreakevenForBuyPosition(); // установить безубыток для Buy позиции
      TrailingStopLossForBuyPosition(); // перетащить Stop Loss для Buy позиции
     }
//-- Стоп-лосс короткой позиции переставляем в безубыток и тралим его
   else
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
        {
         SetBreakevenForSellPosition(); // установить безубыток для Sell позиции
         TrailingStopLossForSellPosition(); // перетащить Stop Loss для Sell позиции
        }
  }

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
//| Buy Position Open function                                       |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Buy и назовём её OpenBUY
void OpenBUY()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= {0};
   MqlTradeResult result= {0};
//--- Создадим переменные необходимые для работы с сервером
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   request.action=TRADE_ACTION_DEAL;
   request.symbol=Symbol();
   request.volume=Lot;
   request.deviation=2;
   request.magic=Magic_Number;
   request.type=ORDER_TYPE_BUY;
   request.price=NormalizeDouble(price,digits);
   /*Создадим запрос на торговый сервер request.sl, в котором укажем, где должен находиться уровень
     стоп лосс относительно цены, по которой открылась позиция "BUY". Уровень SL должен находиться
     ниже цены, поэтому пишем price-SL*point. Для нормализации уровня SL применим функцию преобразования
     данных NormalizeDouble, где обязательно умножим уровень SL на point (размер одного пункта) и укажем
     digits(количество знаков после запятой).
   */
   request.sl=NormalizeDouble(price-SL*point,digits);
   if(!OrderSend(request,result))
      PrintFormat("OrderSend error %d",GetLastError());
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| Sell Position Open function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Sell и назовём её OpenSELL
void OpenSELL()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= {0};
   MqlTradeResult result= {0};
//--- Создадим переменные необходимые для работы с сервером
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   request.action=TRADE_ACTION_DEAL;
   request.symbol=Symbol();
   request.volume=Lot;
   request.deviation=2;
   request.magic=Magic_Number;
   request.type=ORDER_TYPE_SELL;
   request.price=NormalizeDouble(price,digits);
   /*Точно также создадим запрос на торговый сервер request.sl, в котором укажем, где должен
     находиться уровень стоп лосс относительно цены, по которой открылась позиция "SELL".
     Уровень SL теперь должен находиться выше цены, поэтому пишем price+SL*point. Снова для
     нормализации уровня SL применим функцию преобразования данных NormalizeDouble, где обязательно
     умножим уровень SL на point (размер одного пункта) и укажем digits(количество знаков после запятой).
   */
   request.sl=NormalizeDouble(price+SL*point,digits);
   if(!OrderSend(request,result))
      PrintFormat("OrderSend error %d",GetLastError());
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| All Position Close function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для закрытия всех позиций и назовем её CloseALL
void CloseALL()
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request;
   MqlTradeResult  result;
//--- перебираем все открытые позиции
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- определяем параметры
      ulong  ticket=PositionGetTicket(i);                                            // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);                              // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS);                    // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                               // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);                              // объем позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции
      //--- выводим информацию о позиции
      PrintFormat("#%I64u %s  %s  %.2f  %s [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         //--- обнуляем значения запроса на сервер и результата ответа
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action   =TRADE_ACTION_DEAL;   // тип торговой операции (немедленное исполнение)
         request.position =ticket;              // тикет позиции
         request.symbol   =symbol;              // символ
         request.volume   =volume;              // объём позиции
         request.deviation=2;                   // допустимое отклонение от цены
         request.magic    =Magic_Number;        // MagicNumber позиции
         if(type==POSITION_TYPE_BUY)            // если тип позиции "покупка"
           {
            //--- запрашиваем лучшее предложение цены на "продажу" по текущему символу
            request.price=SymbolInfoDouble(symbol,SYMBOL_BID);
            //--- запрашиваем тип рыночного ордера на "продажу" и закрываем позицию встречным ордером
            request.type =ORDER_TYPE_SELL;
           }
         else // в противном случае
           {
            //--- запрашиваем лучшее предложение цены на "покупку" по текущему символу
            request.price=SymbolInfoDouble(symbol,SYMBOL_ASK);
            //--- запрашиваем тип рыночного ордера на "покупку" и закрываем позицию встречным ордером
            request.type =ORDER_TYPE_BUY;
           }
         //--- выводим информацию о закрытии
         PrintFormat("Close #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправляем запрос
         if(!OrderSend(request,result))
            PrintFormat("OrderSend error %d",GetLastError());  // если отправить запрос не удалось, выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
         //---
        }
     }
  }
//+------------------------------------------------------------------+
//| Tick Up function                                                 |
//+------------------------------------------------------------------+
double TickUP()
  {
//---
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   double price1=NormalizeDouble(price+point,digits);
   return(price1);
  }
//+------------------------------------------------------------------+
//| Tick DOWN function                                               |
//+------------------------------------------------------------------+
double TickDOWN()
  {
//---
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   double price2=NormalizeDouble(price-point,digits);
   return(price2);
  }
  
//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+
  
//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // установить безубыток для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции BUY вслед за ценой                      |
//+------------------------------------------------------------------+
void TrailingStopLossForBuyPosition() // перетащить Stop Loss для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // установить безубыток для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции SELL вслед за ценой                     |
//+------------------------------------------------------------------+
void TrailingStopLossForSellPosition() // перетащить Stop Loss для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
 

Всем доброго дня и хорошего настроения!

Продолжаю изучение языка программирования MQL5. Вчера написал код перестановки стоп лосса в безубыток для позиций Buy и Sell. Выкладываю код пока без подробных комментариев. Полную версию советника с подробным описанием каждой строчки кода, в изложении доступном для ученика 1-го класса школы программирования, опубликую позже в виде файла c рабочим названием Trailing_Stop.mq5. Перед публикацией кода была произведена проверка работы советника в тестере стратегий. Проблемы не выявлены.

С уважением, Владимир.

//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // функция "Установить безубыток для позиции Buy"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Buy
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Buy
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open+BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Buy
         if(type==POSITION_TYPE_BUY && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // функция "Установить безубыток для позиции Sell"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Sell
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Sell
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open-BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Sell
         if(type==POSITION_TYPE_SELL && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
 
MrBrooklin:

Всем доброго дня и хорошего настроения!

Продолжаю изучение языка программирования MQL5. Сегодня, как и обещал, выкладываю на всеобщее обозрение код шаблона  советника, предназначенного для написания трейлинг стопа. В связи с тем, что код шаблона советника получился достаточно громоздким, поэтому пришлось максимально сократить комментарии. Полную версию советника с подробными комментариями каждой строчки кода, в изложении доступном для ученика 1-го класса школы программирования, опубликую позже в виде файла c рабочим названием Trailing_Stop.mq5, чтобы не плодить на сайте очередную "портянку".

...

Очень хорошо. Главная мысль схвачена верно. Особенно понравился процесс разработки:

MrBrooklin:

Всем доброго дня и хорошего настроения!

Продолжаю изучение языка программирования MQL5. Вчера написал код перестановки стоп лосса в безубыток для позиций Buy и Sell. ...

Т.е. Все правильно. Не надо решать задачу всю сразу с наскока в одном месте. А делаете постепенно, как у Вас и произошло. Сначала описываете основную логику в функции OnTick и основные функции типа OpenBUY, OpenSELL, TickUP, TickDown.

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

Теперь Вам нужно отточить эту манеру разработки: продолжайте писать функции, навешивая функционал через них к уже существующему коду. Существующая программа должна работать корректно. Сложность не должна расти. Добавляемые функции должны возвращать простые, ясные результаты. Их названия должны быть такими жи простыми и ясными для Вас. Если не получается так сделать, возможно причина в том, что нужно для решения задачи написать ни одну функции а две или даже три. Поначалу, общий каркас приложения будет тяжело определить. Поэтому лучше обращаться с советом к более опытным товарищам. Со временем Вы научитесь разрабатывать такие шаблоны самостоятельно.

Что бы функция решала возложенные на нее задачи, со временем, Вам потребуется использовать все больше и больше языковых конструкций внутри таких функций. Вот тогда Вы постепенно и главное органично станете расширять свои познания в конструкциях языка, изучите циклы, массивы и даже работу с указателями.

По сути, самое сложное в программировании Вы уже изучили, хотя пока еще не закрепили пройденный материал. Осталось многократно повторить это для закрепления. Дальше ничего принципиально нового не будет, все тоже самое: Общий шаблон -> Описание названий функций и их параметры-> Написание содержимого этих функций -> компоновка этих функций в центральном блоке. Все. Останется только совершенствовать навык и быть последовательным. Разные дополнительные конструкции, которые Вы начнете использовать вроде циклов и массивом - это просто детали, порою хитро сделанные, но не более чем детали.

 
Кстати обратите внимание на общий размер Вашей программы. Он уже вполне значительный. Как Вам? Кстати, новичок такой объем кода написать уже не может: переменные начинают путаться, вложенность скобок зашкаливает, начинают лезть ошибки при компиляции как грибы после дождя. После компиляции программа такого объема у них начинает глючить и никто не может понять в чем дело. А у Вас все работает почему-то), и структурно понятно по функциям, что и как происходит. Красота одним словом.
 

Так как совершенству нет предела, вставлю еще несколько замечаний по коду:

void OnTick()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   TimeToStruct((D'1970.01.01 23:50:00'),time_close);
//--- Зададим условия для открытия позиций BUY и SELL   
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   price=NormalizeDouble(price,digits);
   if(PositionSelect(Symbol())==false
      && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
      && time_current.hour==time_open.hour
      && time_current.min>=time_open.min
      && time_current.min<time_open1.min
      && (TickUP()==(price+point)))
     {OpenBUY();}
   else
     {
      if(PositionSelect(Symbol())==false
         && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
         && time_current.hour==time_open.hour
         && time_current.min>=time_open.min
         && time_current.min<time_open1.min
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if(time_current.hour==time_close.hour && time_current.min==time_close.min)
      CloseALL();
}

Я выделил два нетривиальных места желтым.

1) Обратите внимание, что код повторяется и первом if и в следующем else. Разница только в последней строке и конечном действии (OpenBUY, OpenSell).

2) Условия попадания в блок else не очевидны. Они из-за обилия ?? не просматриваются. В действительности, они зависят лишь от последней строчки:

TickUP()==(price+point)

 Это верный признак того, что здесь не хватате функции.

Нужно написать функцию, которая возвращает истину, если время для открытия позиции соответствует заданному (напишу ее чуть позже)

 
Vasiliy Sokolov:

Очень хорошо. Главная мысль схвачена верно. Особенно понравился процесс разработки:

Т.е. Все правильно. Не надо решать задачу всю сразу с наскока в одном месте. А делаете постепенно, как у Вас и произошло. Сначала описываете основную логику в функции OnTick и основные функции типа OpenBUY, OpenSELL, TickUP, TickDown.

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

Теперь Вам нужно отточить эту манеру разработки: продолжайте писать функции, навешивая функционал через них к уже существующему коду. Существующая программа должна работать корректно. Сложность не должна расти. Добавляемые функции должны возвращать простые, ясные результаты. Их названия должны быть такими жи простыми и ясными для Вас. Если не получается так сделать, возможно причина в том, что нужно для решения задачи написать ни одну функции а две или даже три.

Что бы функция решала возложенные на нее задачи, со временем, Вам потребуется использовать все больше и больше языковых конструкций внутри таких функций. Вот тогда Вы постепенно и главное органично станете расширять свои познания в конструкциях языка, изучите циклы, массивы и даже работу с указателями.

Здравствуйте, Василий! Спасибо Вам большое за своевременные подсказки и поддержку. Очень сильно помогли мне в изучении языка программирования MQL5 Ваши сообщения о роли функций и принципах построения программного кода:

  1. https://www.mql5.com/ru/forum/352460/page28#comment_18636493
  2. https://www.mql5.com/ru/forum/352460/page28#comment_18637800
  3. https://www.mql5.com/ru/forum/352460/page29#comment_18641729
  4. https://www.mql5.com/ru/forum/352460/page52#comment_18694985

Сейчас, когда информация в голове уже приняла структуированный вид, гораздо легче воспринимается код, написанный не только мною, но и другими программистами. Надеюсь, что данная тема будет хорошим подспорьем для всех начинающих изучать язык программирования MQL5 с полного нуля.

С уважением, Владимир.

Причина обращения: