Советники: New_Bar (условие, определяющее появление нового бара, реализованно в виде микросоветника) - страница 3

 

Владимир, вот код

/********************************************************************\
|   Expert tick function                                             |
\********************************************************************/
void OnTick()
 {
//---
  Sleep(250);
  if(iOpen(_Symbol, PERIOD_CURRENT, 0) && iVolume(_Symbol, PERIOD_CURRENT, 0) <= 1)
    Print("Ура! Появился новый бар! ",TimeTradeServer());
  datetime tm = iTime(_Symbol, PERIOD_CURRENT, 0);
  static datetime time = tm;
  if(time == tm)
    return;
  long volume = iVolume(_Symbol, PERIOD_CURRENT, 0);
  if(volume > 1)
    Print("***** Вот и ошибка? volume = ", volume);
  time = tm;
 }/******************************************************************/

и вот результат работы

2022.06.29 12:51:55.339 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 2
2022.06.29 12:52:57.520 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 3
2022.06.29 12:53:55.377 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 2
2022.06.29 12:54:55.449 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 3
2022.06.29 12:55:55.417 Test_NewBar (GBPUSD,M1) Ура! Появился новый бар! 2022.06.29 12:55:55
2022.06.29 12:56:55.444 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 3
2022.06.29 12:57:55.458 Test_NewBar (GBPUSD,M1) Ура! Появился новый бар! 2022.06.29 12:57:55
2022.06.29 12:58:55.450 Test_NewBar (GBPUSD,M1) Ура! Появился новый бар! 2022.06.29 12:58:55
2022.06.29 12:59:55.656 Test_NewBar (GBPUSD,M1) ***** Вот и ошибка? volume = 2

Конечно задержка в четверть секунды многовато, но ведь мне надо было показать, что задержка может пропустить первый тик, а задержка может произойти в самый неожиданный момент…

 
Yuriy Bykov #:

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

Поэтому я для себя реализовывал более сложный код для определения нового бара, в основе которого примерно та же идея, как и в этом ответе, только вместо iTime используется iVolume.

Да не нужно здесь сложного кода.
Все элементарно и очевидно.
В баре есть только один уникальный атрибут - это время открытия. Это его PK(Primary Key). Поэтому через него и надо идентифицировать бар и реализовывать функцию IsNewBar(). Эта функция состоит из 4 строк кода:
  • Статическая переменная time_open_last_bar = 0
  • получение time_open_cur_bar через iTime
  • сравнивание time_open_cur_bar с time_open_last_bar
  • time_open_last_bar = time_open_cur_b
Volume не уникален для бара и поэтому не годится для его идентификации. 
Все просто.

 

Реализация.
Для текущего ТФ и символа сгодится такая функция: 

bool IsNewBar() {
   static datetime last_bar_time_open = 0;
   datetime cur_bar_time_open = iTime(NULL, 0, 0);
   bool result = cur_bar_time_open != last_bar_time_open;
   last_bar_time_open = cur_bar_time_open;
   return result;
}

для мультивалютных сов и с разными ТФ лучше реализовать через класс, когда для каждого символа и (или) ТФ нужен свой экземпляр класса.
Например так:

class CIsNewBar {
private:
   datetime          last_bar_time_open;
   ENUM_TIMEFRAMES   tf;
   string            symbol;
public:
                     CIsNewBar(ENUM_TIMEFRAMES _tf = 0, string _symbol = NULL) {
      symbol = _symbol;
      tf = _tf;
      last_bar_time_open = iTime(symbol, tf, 0);
   };
                    ~CIsNewBar();
   bool              IsNewBar() {
      datetime cur_bar_time_open = iTime(symbol, tf, 0);
      bool result = cur_bar_time_open != last_bar_time_open;
      last_bar_time_open = cur_bar_time_open;
      return result;
   }
};

код не проверял. Вдруг где-то накосячил. Хотя вряд ли. ))

 
Nikolai Semko #:

Реализация.

код не проверял. Вдруг где-то накосячил. Хотя вряд ли. ))

А если в одном вызове OnTick() надо проверить в нескольких местах событие наступления нового бара? Ваш код, похоже, позволяет сделать это только один раз. После этого IsNewBar() будет возвращать false внутри того же вызова OnTick().  

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

P.S. Посмотрел, что я потом переделал немного реализацию, сделав ее одинаковой для MT5/MT4, избавившись от CHashMap и упростив.

 
Yuriy Bykov #:

А если в одном вызове OnTick() надо проверить в нескольких местах событие наступления нового бара? Ваш код, похоже, позволяет сделать это только один раз. После этого IsNewBar() будет возвращать false внутри того же вызова OnTick().

Как это так - в нескольких местах?
Идентификация нового бара должна происходить в OnTick и обработка этого события должна происходить в одном месте. Иначе нужно менять алгоритм. 
Ну если никак не обойти этого (не представляю ситуации - когда это может быть нужно), то создавайте несколько экземпляров класса с одними и теми же параметрами TF и Symbol - для каждого места свой.

ЗЫ: заглянул в ваш код краем глаза и увидел там циклы и поиск символа. Явный перебор с алгоритмом. Какой же там перфоманс?! Жесть!

 
Alexey Viktorov #:

Владимир, вот код

и вот результат работы

Конечно задержка в четверть секунды многовато, но ведь мне надо было показать, что задержка может пропустить первый тик, а задержка может произойти в самый неожиданный момент…

Добрый вечер, Алексей! Задержку и мне приходилось ставить, чтобы сымитировать вне штатную ситуацию. У меня правда была 900 мск. Видел такие пропуски по несколько минут на таймфрейме М1. В общем подвожу результат всего сказанного в обсуждении:

  1. Решение по определению появления нового бара не верное - согласен. Но меня устраивает.
  2. В однострочном коде реализовать условие для идеального определения появления нового бара - не возможно. Да и бог с ним!
  3. Спасибо всем за активное участие в дискуссии!!!
С уважением ко всем, Владимир.
 
Nikolai Semko #:

Как это так - в нескольких местах?
Идентификация нового бара должна происходить в OnTick и обработка этого события должна происходить в одном месте. Иначе нужно менять алгоритм. 
Ну если никак не обойти этого (не представляю ситуации - когда это может быть нужно), то создавайте несколько экземпляров класса с одними и теми же параметрами TF и Symbol - для каждого места свой.

ЗЫ: заглянул в ваш код краем глаза и увидел там циклы и поиск символа. Явный перебор с алгоритмом. Какой же там перфоманс?! Жесть!

Зачем??? Разве не достаточно объявить bool переменную и ей присвоить значение функции IsNewBar? И потом во всех местах где нужно только проверить значение этой переменной.

НО!!! Артём Тришкин писал мне класс для мультивалютников и где-то я уже давал этот класс для применения и обсуждения. 

 
Alexey Viktorov #:

Зачем??? Разве не достаточно объявить bool переменную и ей присвоить значение функции IsNewBar? И потом во всех местах где нужно только проверить значение этой переменной.

НО!!! Артём Тришкин писал мне класс для мультивалютников и где-то я уже давал этот класс для применения и обсуждения. 

Да, можно и так.

Причем, лучше чтобы она(переменная) находилась внутри класса CIsNewBar, была бы паблик и выставлялась бы внутри метода IsNewBar() значением result, а метод IsNewBar() в таком случае может вызываться только из OnTick и поэтому логичнее его переименовать в NewTick или OnTick, а саму переменную в IsNewBar.

class CIsNewBar {
private:
   datetime          last_bar_time_open;
   ENUM_TIMEFRAMES   tf;
   string            symbol;
public:
                     CIsNewBar(ENUM_TIMEFRAMES _tf = 0, string _symbol = NULL) {
      symbol = _symbol;
      tf = _tf;
      last_bar_time_open = iTime(symbol, tf, 0);
      IsNewBar = false;
   }
                    ~CIsNewBar();
   bool              OnTick() {
      datetime cur_bar_time_open = iTime(symbol, tf, 0);
      IsNewBar = cur_bar_time_open != last_bar_time_open;
      last_bar_time_open = cur_bar_time_open;
      return IsNewBar;
   }
   bool IsNewBar;
};
 

Доброго времени, коллеги!

Я пишу код только месяц. Но хочу поделится своим способом определения нового бара.

datetime LastTime;
datetime NowTime;

void OnTick()
{

        NowTime = iTime(NULL, PERIOD_CURRENT, 0);//Получаем текущее время

        if(LastTime != NowTime) //Если время предыдущего действия не равно текущему времени, значит это новая свеча.
         {
        ///
                Тело функции выполняемое один раз за свечу...
        ///
         }
        LastTime = NowTime;

Как по мне это самый коротки вариант.

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

От гуру послушал бы рекомендаций.

 
Petr Zharuk #:

Доброго времени, коллеги!

Я пишу код только месяц. Но хочу поделится своим способом определения нового бара.

Как по мне это самый коротки вариант.

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

От гуру послушал бы рекомендаций.

ровно тоже самое, что я давал здесь (вариант функции - когда используется только текущий ТФ и текущий символ), только с глобальной переменной, а не статической, и не в виде функции, а в теле OnTick. 
Статическая переменная по сути тоже самое, что и глобальная, только удобнее, т.к. не "засоряет" шапку и зона видимости только в пределах функции, что безопаснее. 
Использование функции в данной ситуации только лишь улучшает читаемость кода, но можно конечно же обойтись и без функции. На скорость не влияет.

void OnTick()
{
        static datetime LastTime = 0;
        datetime NowTime = iTime(NULL, PERIOD_CURRENT, 0);//Получаем текущее время

        if(LastTime != NowTime) //Если время предыдущего действия не равно текущему времени, значит это новая свеча.
         {
        ///
                Тело функции выполняемое один раз за свечу...
        ///
         }
        LastTime = NowTime;
Причина обращения: