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

 

Обратите внимание на фильтр по символу и по магику в цикле перебора позиций. Если фильтра нет, но вы тралите вообще все открытые позиции на всех символах, а это плохо.

Ну а так,на первый взгляд, все вроде бы в норме.

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
 
Andrei Novichkov:

Обратите внимание на фильтр по символу и по магику в цикле перебора позиций. Если фильтра нет, но вы тралите вообще все открытые позиции на всех символах, а это плохо.

Ну а так,на первый взгляд, все вроде бы в норме.

Большое спасибо, Андрей! Про Magic всё понятно, потому что на одном символе, действительно, могут быть открыты сразу несколько позиций, но возник встречный вопрос. Советник будет перебирать открытые позиции сразу по всем символам, если ему явно не указать на текущий символ? И это даже не смотря на то, что он установлен на конкретную валютную пару, например, EURUSD? Честно говоря, не совсем пойму вот этот момент.

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

 
MrBrooklin:

Большое спасибо, Андрей! Про Magic всё понятно, потому что на одном символе, действительно, могут быть открыты сразу несколько позиций, но возник встречный вопрос. Советник будет перебирать открытые позиции сразу по всем символам, если ему явно не указать на текущий символ? И это даже не смотря на то, что он установлен на конкретную валютную пару, например, EURUSD? Честно говоря, не совсем пойму вот этот момент.

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


Да. По всем открытым позам по всем символам...
Последовательно по открытым позам.
Вот простой трал уже есть в учебнике.

https://book.mql4.com/ru/build/trading
 
MrBrooklin:

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

  1. Создаем советник для автоматизации работы по трейлингу (сопровождению) уровня Stop Loss уже открытой позиции с заданными уровнями Take Profit и Stop Loss.
  2. В советнике создаем блок входных параметров с двумя параметрами: задать «уровень трейлинга» и задать «шаг трейлинга».
  3. При поступлении новых котировок обрабатываем их функцией OnTick( ). Трейлинг работает только при поступлении нового тика по текущему символу.
  4. Создадим и запустим в работу цикл перебора всех позиций.
  5. Если вдруг не обнаруживаем ни одной открытой позиции, то возвращаемся к циклу
  6. Обновим котировки.
  7. Если есть открытая позиция, то продолжаем.
  8. Определяем тип открытой позиции: Buy или Sell.
  9. Если открыта позиция Buy, то определяем, где находится текущая цена относительно открытой позиции.
  10. Если текущая цена выше цены открытия позиции, то проверяем на какой уровень она поднялась.
  11. Если текущая цена достигла «уровня трейлинга» заданного во входных параметрах, то переносим Stop Loss на уровень без убытка равного цене открытия позиции Buy. В противном случае ничего не делаем.
  12. Если текущая цена превысила «уровень трейлинга» на величину равную «шагу трейлинга», то тогда Stop Loss перемещается с уровня цены открытия позиции Buy на величину равную «шагу трейлинга» и так до тех пор, пока цена не достигнет заданного для данной позиции уровня Take Profit.
  13. Если цена развернется и достигнет уровня уже перемещенного Stop Loss, то позиция закрывается.
  14. Если открыта позиция Sell, то определяем, где находится текущая цена относительно открытой позиции.
  15. Если текущая цена ниже цены открытия позиции, то проверяем на какой уровень она опустилась.
  16. Если текущая цена достигла «уровня трейлинга» заданного во входных параметрах, то переносим Stop Loss на уровень без убытка равного цене открытия позиции Sell. В противном случае ничего не делаем.
  17. Если текущая цена превысила «уровень трейлинга» на величину равную «шагу трейлинга», то тогда Stop Loss перемещается с уровня цены открытия позиции Sell на величину равную «шагу трейлинга» и так до тех пор, пока цена не достигнет заданного для данной позиции уровня Take Profit.
  18. Если цена развернется и достигнет уровня уже перемещенного Stop Loss, то позиция закрывается.

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

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

С теорией неплохо, теперь практика. Получится сделать?

 
Aliaksandr Hryshyn:

С теорией неплохо, теперь практика. Получится сделать?

Попытаюсь. Но Вы сами понимаете, что для этого нужен совершенно другой уровень знаний, которых у меня пока ещё нет.

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

 
Aleksey Masterov:

Да. По всем открытым позам по всем символам...
Последовательно по открытым позам.
Вот простой трал уже есть в учебнике.

https://book.mql4.com/ru/build/trading

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

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

 

Пока продолжим про функции.

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

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

Но мы отвлеклись, перейдем снова на функции.

Если функция решает слишком много задач в целом, - то следуя аналогии, это очень умный боец, который если что-то с ним пойдет не так, может погубить все предприятие. Если спросить что делает такая функция, ответ может быть длинным. Если результат этой функции вдруг перестанет быть верным, то разобраться, что же вызывает ошибку в ней будет крайне сложно (потому что задач много, кода много, большое количество вызовов подпроцедур и где именно ошибка понять сложно).

Если функция вычисляет корректный результат по понедельникам, средам и воскресеньями, а в остальные дни в зависимости от "настроения" - то можем ли мы положится на такую функцию? Представьте что допустим системная функция OrderSend открывала бы позиции только по четвергам, и если определен некий магический параметр со значением 13. И сказанное вовсе не бред или фантазии. Такое поведение можно устроить по щелчку пальцев, достаточно функцию сделать зависимой от неких параметров во внешней среде.

Допустим функция: 

double sum(double a, double b)
{
   return a+b;
}

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

Посмотрим на антипример теперь:

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

Результат вроде бы тот же, ведь с всегда равен нулю. Или не всегда? А что если кто-то где-то меняет c? Что тогда? А если кто-то где-то также использует внешнюю переменную с, но уже для своих целей и у него переменная c другого типа, допустим string? Объединить обе эти две функции уже нельзя (компилятор не даст объявить две переменные с одинаковым именем). Разобраться с их общими зависимостями тоже сложно. Вообще не понятно что с этим делать. Я вот например до сих пор не знаю надежного и простого способа заставить работать такие функции вместе.

Даже если другой функции нет, и внешнюю переменную читает только одна функция, то скопировать ее в другое место становится не так просто. Нужно скопировать и эту функцию и ее зависимость. А если мы копируем эти функции в общий файл? Там этих функций набирается 50, 100 штук. И каждая вместе с собой копирует еще кучу своих зависимых переменных. Получается запутанный клубок связанных переменных не понятно с какими функциями. А зачем все это? Какие задачи это решает? Зачем создавать лишние зависимости когда в подавляющем большинстве случаев можно обойтись без них?

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

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

Что делает этот код я не знаю, потому что функции даже не написаны. Но если бы я его читал, то по всей видимости это означало бы, что в случае успешного выбора первого <попавшегося> отложенного ордера с направлением ORDER_TYPE_BUY он бы отменялся (первая функция выбирает, вторая отменяет). Так как код запускался бы каждый тик, то не зависимо от количества отложенных ордеров, каждый из них рано или поздно был бы отменен. Также это означает, что любая попытка установки отложенного ордера на покупку будет пресекаться - ордер будет тут же сниматься. При этом ордера на продажу будут выставляться без проблем.

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

Применительно к МКЛ нужно сказать еще немного о чистых функциях. Так как это прикладной язык, то сложно писать что-либо не опираясь на данные предоставляемые из терминала. В конце концов в этом и состоит главная задача: правильно взаимодействовать с торговым окружением. Формально, любое торговое окружение изменчиво: меняются цены, количество ордеров, баланс и пр. пр. пр. Следовательно любая функция взаимодействующая с таким изменчивым торговым окружением не является чистой. Потому что внешнее торговое окружение тоже можно можно принять как за некую глобальную переменную, которая постоянно меняется. Но когда мы пишем OrdersTotal() мы не рассчитываем что данная функция всегда будет возврать одно и тоже значение. Вместо этого мы ожидаем, что она будет возвращать количество отложенных ордеров, которое естественно будет разным. Поэтому, применительно к MQL, мы будем считать функции чистыми и годными для повторного использования, даже в том случае, если они будут вызывать функции внешнего API, вроде OrdersTotal(). Это будет наше разумное послабление.

 
Vasiliy Sokolov:

Пока продолжим про функции...

Огромное Вам спасибо, Василий, за те бесценные знания, которыми делитесь не только со мной, но и с теми начинающими программистами, которые читают или будут читать данную тему!

С таким же, огромным уважением, Владимир.

 

Продолжаю изучение языка программирования MQL5. Пока серьезных замечаний со стороны участников темы по алгоритму написания кода советника Trailing_Stop не было (про символ и Magic помню, позже добавлю в алгоритм!), создал входные параметры советника и написал код цикла, запускающего перебор открытых позиций.

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

Бился до полуночи, но так и не смог победить. Не могу никак понять, с чем связана проблема.


Код советника написан на английском языке, а комментарии к коду на русском языке, чтобы облегчить усвоение материала. В данном советнике постарался всё расписать, как ранее и обещал, в изложении доступном для ученика 1-го класса школы программирования.

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

//+------------------------------------------------------------------+
//|                                                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 version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
 
MrBrooklin:


i равно кол-ву открытых позиций, столько и циклов будет с печатью 

Print("Запущен цикл");
нужно убрать знак "=" в 
   for(i; i>=0; i--)
зачем вам проходить цикл когда кол-во открытых позиций равно 0. на этот нулевой вызов и выходила вторая печать