Загадочный биржевой индикатор - страница 3

 
ivanivan_11:
мне одному ТС напоминает по манере и всему остальному hrenfx?)) побольше бы таких героев (никакого сарказма,все реально). должен же кто-то пинать сервисдеск и разрабов чем-то помимо -нарисуйте мне тут в терминале стрелочку другого цвета или прилепите кнопку не сюда,а вооон-туда)

Нет, не вам одному.

Но в отличие от прошлых инкарнаций hrenfx, TC(я надеюсь, что это не он) не выходит за рамки приличий и сложившихся здесь правил. И получается вполне такой приличный спарринг. Мы не возражаем (и никогда не возражали против конструктивной критики), так как благодаря упомянутому ТС, нашли и исправили несколько косяков, уточнили документацию (когда косяки не оказались косяками)

 
fxsaber:
Можно, конечно, в данном случае переписатьс вариантом from == 0, но это не дело совсем. Надо выправлять CopyTicks. В Сервисдеск снова?
Заявку оформил, но переписал "святое"

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Альтернативные реализации стандартных функций/подходов

fxsaber, 2016.09.27 20:30

Вариант CopyTicks, который быстрее оригинала иногда на несколько порядков (from > 0)

int MyCopyTicks( const string Symb, MqlTick& Ticks[], const uint flags = COPY_TICKS_ALL, const ulong from = 0, const uint count = 0 )
{
  int Res = (from == 0) ? CopyTicks(Symb, Ticks, flags, 0, count) : -1;
  
  if (from > 0)
  {    
    uint count2 = 1;
    
    MqlTick NewTicks[];    
    int Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    
    while ((Amount > 0) && ((ulong)NewTicks[0].time_msc >= from))
    {
      count2 <<= 1;
      
      Amount = CopyTicks(Symb, NewTicks, flags, 0, count2);
    }

    for (int i = 0; i < Amount; i++)
      if ((ulong)NewTicks[i].time_msc >= from)
      {
        Res = ArrayCopy(Ticks, NewTicks, 0, i, (count == 0) ? 2000 : count);
        
        break;
      }    
  }
  
  return(Res);
}
Теперь работает без мерцаний.
 

Билд 1432 - индикатор из первого поста работает без "Загадки".

В копилку биржевых индикаторов

 

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

В причинах разобраться не смог, как и с предыдущей загадкой. В прошлый раз оказалось дело в баге CopyTicks. Хочется думать, что я ошибся, но, похоже, все же проявляется какой-то слабо заметный баг. Локализовать мне его не получилось. Как будто идет некая особенность работы индикатора и CopyTicks. В Сервисдеске довольно серьезно обсуждались вопросы несинхронизированности  CopyTicks и OnCalculate. И вопросы там остались. Поэтому предполагаю, что собака зарыта где-то в этих слабо уловимых особенностях.

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

// Индикатор в виде гистограммы показывает проторгованный оборот BUY и SELL

#property indicator_separate_window

#property indicator_buffers 2
#property indicator_plots 2

#property indicator_label1  "TurnOver_BUY"
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_style1 STYLE_SOLID
#property indicator_color1 clrYellow

#property indicator_label2  "TurnOver_SELL"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_style2 STYLE_SOLID
#property indicator_color2 clrRed

long LastTime = 0; // time_msc-время последнего тика (самого свежего), полученного из истории
int Count = 0;     // Количество тиков в последенем запросе, у которых time_msc == LastTime

double Buffer0[];
double Buffer1[];

// Возвращает свежие тики, пришедшие после предыдущего вызова
int GetFreshTicks( MqlTick &Ticks[], const uint flags = COPY_TICKS_TRADE, const uint count = 100000 )
{
  int Res = 0;

  MqlTick NewTicks[];
  const int NewAmount = CopyTicks(Symbol(), NewTicks, flags, LastTime, count);

  if ((NewAmount > 0) && (Count < NewAmount))
  {
    Res = ArrayCopy(Ticks, NewTicks, 0, Count);

    // Взяли крайнее время из текущей истории
    LastTime = Ticks[Res - 1].time_msc;
    Count = 1;

    // Находим (Count) в текущей истории количество тиков со временем LastTime
    for (int i = Res - 2; i >= 0; i--)
    {
      if (Ticks[i].time_msc < LastTime)
        break;

      Count++;
    }
  }

  return(Res);
}

// Ширина гистограммы
void SetScale()
{
  const int Width = 9 / (1 << (5 - (int)ChartGetInteger(0, CHART_SCALE)));

  if (Width != PlotIndexGetInteger(0, PLOT_LINE_WIDTH))
  {
    PlotIndexSetInteger(0, PLOT_LINE_WIDTH, Width);
    PlotIndexSetInteger(1, PLOT_LINE_WIDTH, Width);

    ChartRedraw();
  }
}

void OnInit()
{
  SetIndexBuffer(0, Buffer0);
  SetIndexBuffer(1, Buffer1);
}

void OnChartEvent( const int id, const long &lparam, const double &dparam, const string &sparam )
{
  if (id == CHARTEVENT_CHART_CHANGE)
    SetScale();
}

// Заполняет бар соответствующими ему тиками
void SetBarData( const datetime TimeBar, const MqlTick &Ticks[], int &Pos, double &TurnOverBuy, double &TurnOverSell )
{
  const datetime NextTime = TimeBar + PeriodSeconds();
  const int Amount = ArraySize(Ticks);

  while (Pos < Amount)
  {
    const MqlTick Tick = Ticks[Pos];

    if (Tick.time >= NextTime)
      break;

    if ((bool)(Tick.flags & TICK_FLAG_BUY))
      TurnOverBuy += (double)Tick.volume;
    else if ((bool)(Tick.flags & TICK_FLAG_SELL))
      TurnOverSell -= (double)Tick.volume;

    Pos++;
  }

  return;
}

// Вычисляет, какие должны быть значения буферов для бара со временем TimeFrom
void ControlBarData( const datetime TimeFrom, double &TurnOverBuy, double &TurnOverSell )
{
  MqlTick Ticks[];

  CopyTicks(Symbol(), Ticks, COPY_TICKS_TRADE, (ulong)TimeFrom * 1000, 100000);
  int i = 0;

  SetBarData(TimeFrom, Ticks, i, TurnOverBuy, TurnOverSell);

  return;
}


int OnCalculate( const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[] )
{
  if (prev_calculated == 0)
  {
    // возьмем тики с начала утренней сессии
    LastTime = (TimeCurrent() - (TimeCurrent() % (24 * 3600))) * 1000;

    Count = 0;
  }

  // Зануляем новые бары
  if (rates_total > prev_calculated)
  {
    ArrayFill(Buffer0, prev_calculated, rates_total - prev_calculated, 0);
    ArrayFill(Buffer1, prev_calculated, rates_total - prev_calculated, 0);
  }

  MqlTick Ticks[];

  // Взяли свеженькие тики
  const int Amount = GetFreshTicks(Ticks);

  if (Amount > 0)
  {
    int Pos;

    const datetime LastTime2 = Ticks[0].time - (Ticks[0].time % PeriodSeconds());

    // Находим бар (Pos), включающий первый свежий тик
    for (Pos = rates_total - 1; Pos >= 0; Pos--)
      if (time[Pos] == LastTime2)
        break;

    if (Pos >= 0)
    {
      int i = 0;

      // Заполняем бары своими тиками
      while ((Pos < rates_total) && (i < Amount))
      {
        SetBarData(time[Pos], Ticks, i, Buffer0[Pos], Buffer1[Pos]);

        Pos++;
      }

      // Какой бар справа будет проверяться
      #define BAR 2 // 2 - второй справа (предпоследний)

      // Этот кусок нужен для проверки правильности посчитанных значений буферов
      // Если образовался новый бар и в тиковой истории УЖЕ ЕСТЬ соответствущие ему значения
      if ((rates_total >= BAR) && (rates_total > prev_calculated) && (Ticks[Amount - 1].time >= time[rates_total - 1]))
      {
        double Buf0 = 0;
        double Buf1 = 0;

        // Вычисляем, какие должны быть значения буферов на посчитанных буферах, согласно ПОЛНОСТЬЮ сформированной тиковой истории для них
        ControlBarData(time[rates_total - BAR], Buf0, Buf1);

        #define TOSTRING(A) " " + #A + " = " + (string)A

        // Если есть различия - выводим
        if ((Buf0 != Buffer0[rates_total - BAR]) || (Buf1 != Buffer1[rates_total - BAR]))
          Alert((string)time[rates_total - BAR] + ":" + TOSTRING(Buf0) + TOSTRING(Buf1) + "," +
                TOSTRING(Buffer0[rates_total - BAR]) + TOSTRING(Buffer1[rates_total - BAR]));
      }

    }
  }

  return(rates_total);
}

Жирным выделил проверку корректности, которая вот так сигналит при проблемах

2016.09.28 21:34:00: Buf0 = 4658.0 Buf1 = -4606.0, Buffer0[rates_total-BAR] = 4841.0 Buffer1[rates_total-BAR] = -5120.0
2016.09.28 21:33:00: Buf0 = 2609.0 Buf1 = -4336.0, Buffer0[rates_total-BAR] = 3370.0 Buffer1[rates_total-BAR] = -4375.0
2016.09.28 21:32:00: Buf0 = 3290.0 Buf1 = -3878.0, Buffer0[rates_total-BAR] = 3312.0 Buffer1[rates_total-BAR] = -3878.0
2016.09.28 21:31:00: Buf0 = 3065.0 Buf1 = -6653.0, Buffer0[rates_total-BAR] = 3606.0 Buffer1[rates_total-BAR] = -9407.0
2016.09.28 21:30:00: Buf0 = 5824.0 Buf1 = -5490.0, Buffer0[rates_total-BAR] = 6969.0 Buffer1[rates_total-BAR] = -5558.0
2016.09.28 21:28:00: Buf0 = 4388.0 Buf1 = -3943.0, Buffer0[rates_total-BAR] = 4459.0 Buffer1[rates_total-BAR] = -12743.0
2016.09.28 21:27:00: Buf0 = 2938.0 Buf1 = -3589.0, Buffer0[rates_total-BAR] = 3723.0 Buffer1[rates_total-BAR] = -4011.0
2016.09.28 21:26:00: Buf0 = 3746.0 Buf1 = -6323.0, Buffer0[rates_total-BAR] = 3808.0 Buffer1[rates_total-BAR] = -6760.0
2016.09.28 21:25:00: Buf0 = 4730.0 Buf1 = -5490.0, Buffer0[rates_total-BAR] = 5862.0 Buffer1[rates_total-BAR] = -5490.0
2016.09.28 21:24:00: Buf0 = 6680.0 Buf1 = -7278.0, Buffer0[rates_total-BAR] = 7689.0 Buffer1[rates_total-BAR] = -7669.0
2016.09.28 21:23:00: Buf0 = 6741.0 Buf1 = -7476.0, Buffer0[rates_total-BAR] = 8006.0 Buffer1[rates_total-BAR] = -8598.0
2016.09.28 21:22:00: Buf0 = 4519.0 Buf1 = -10453.0, Buffer0[rates_total-BAR] = 6975.0 Buffer1[rates_total-BAR] = -16083.0

Buf0 и Buf1 - это что должно быть, согласно истории. А BufferN[rates_total-BAR] - это что по итогу посчиталось во время реал-тайма.

Жирным выделил не различия, а совпадения. Они бывают, как для первого, так и для второго буферов.

В общем, запутанно и сумбурно довольно написал. Но исследовал эту загадку, как только мог. До истины не докопался даже близко.

Вроде, CopyTicks работает, как надо (1432) здесь. А баг вылезает почти сразу.

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

Файлы:
TurnOver.mq5  6 kb
 
fxsaber:

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

В причинах разобраться не смог, как и с предыдущей загадкой. В прошлый раз оказалось дело в баге CopyTicks. Хочется думать, что я ошибся, но, похоже, все же проявляется какой-то слабо заметный баг. Локализовать мне его не получилось. Как будто идет некая особенность работы индикатора и CopyTicks. В Сервисдеске довольно серьезно обсуждались вопросы несинхронизированности  CopyTicks и OnCalculate. И вопросы там остались. Поэтому предполагаю, что собака зарыта где-то в этих слабо уловимых особенностях.

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

Жирным выделил проверку корректности, которая вот так сигналит при проблемах

Buf0 и Buf1 - это что должно быть, согласно истории. А BufferN[rates_total-BAR] - это что по итогу посчиталось во время реал-тайма.

Жирным выделил не различия, а совпадения. Они бывают, как для первого, так и для второго буферов.

В общем, запутанно и сумбурно довольно написал. Но исследовал эту загадку, как только мог. До истины не докопался даже близко.

Вроде, CopyTicks работает, как надо (1432) здесь. А баг вылезает почти сразу.

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

Подождите, когда у Всех будет 1432
 
fxsaber:

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

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

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

#include <TypeToBytes.mqh> // https://www.mql5.com/ru/code/16280

long LastTime = 0; // time_msc-время последнего тика (самого свежего), полученного из истории
int Count = 0;     // Количество тиков в последенем запросе, у которых time_msc == LastTime

// Возвращает следующие тики (после предыдущего вызова)
int GetFreshTicks( MqlTick &Ticks[], const uint flags = COPY_TICKS_TRADE, const uint count = 100000 )
{
  int Res = 0;

  MqlTick NewTicks[];
  const int NewAmount = CopyTicks(Symbol(), NewTicks, flags, LastTime, count);

  if ((NewAmount > 0) && (Count < NewAmount))
  {
    Res = ArrayCopy(Ticks, NewTicks, 0, Count);

    // Взяли крайнее время из текущей истории
    LastTime = Ticks[Res - 1].time_msc;
    Count = 1;

    // Находим (Count) в текущей истории количество тиков со временем LastTime
    for (int i = Res - 2; i >= 0; i--)
    {
      if (Ticks[i].time_msc < LastTime)
        break;

      Count++;
    }
  }
  
  return(ArrayResize(Ticks, Res));
}

// Сравнение двух массивов
template <typename T>
bool ArrayEqual( const T &Array1[], const T &Array2[] )
{
  const int Amount = MathMin(ArraySize(Array1), ArraySize(Array2));
  bool Res = (Amount > 0);

  if (Res)
    for (int i = 0; i < Amount; i++)
      if (_R(Array1[i]) != Array2[i]) // https://www.mql5.com/ru/code/16280
      {
        Res = false;
        
        ExpertRemove();

        break;
      }

  return(Res);
}

void OnTick()
{
  // возьмем тики с начала утренней сессии
  Count = 0;
  LastTime = (TimeCurrent() - (TimeCurrent() % (24 * 3600))) * 1000;
  
  MqlTick Ticks[];    // История, собранная по частям
  MqlTick NewTicks[]; // массив для следующей части тиков
  
  // Собираем историю по частям  
  while (GetFreshTicks(NewTicks, COPY_TICKS_TRADE, 100000) > 0)
    ArrayCopy(Ticks, NewTicks, ArraySize(Ticks));
    
  if (ArraySize(Ticks) > 0)    
  {
    // Взяли ВСЮ историю тиков
    Print(CopyTicks(_Symbol, NewTicks, COPY_TICKS_TRADE, Ticks[0].time_msc, 10000000)); // 10000000 - большое число, чтобы все выкачать.
    
    // Проверка на совпадение собранной по частям истории с самой историей
    Print(ArrayEqual(NewTicks, Ticks) ? "Equal" : "Not Equal");
  }    
}

Результат

2016.09.30 16:02:54.661 Test (Si-12.16,M1)      Not Equal
2016.09.30 16:02:54.661 Test (Si-12.16,M1)      ExpertRemove() function called
2016.09.30 16:02:54.621 Test (Si-12.16,M1)      333740
2016.09.30 16:02:54.121 Test (Si-12.16,M1)      Equal
2016.09.30 16:02:54.071 Test (Si-12.16,M1)      333736
2016.09.30 16:02:53.791 Test (Si-12.16,M1)      Equal
2016.09.30 16:02:53.741 Test (Si-12.16,M1)      333723

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

В общем, никак не победить баги CopyTicks. И заметьте, советник работает в режиме ленты (COPY_TICKS_TRADE). Т.е. даже с лентой не получается работать.

 
fxsaber:

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


А разве кто-то гарантировал, что тики собранные онлайн будут совпадать с историей? История строится по тикам на сервере, а на клиент может успевать приходить меньше. Например, тут кто-то экспериментировал с запуском параллельно нескольких терминалов подключенных к одному счету - результат: увеличенный пропуск тиков на каждом из терминалов.
 
Stanislav Korotky:
А разве кто-то гарантировал, что тики собранные онлайн будут совпадать с историей? История строится по тикам на сервере, а на клиент может успевать приходить меньше. Например, тут кто-то экспериментировал с запуском параллельно нескольких терминалов подключенных к одному счету - результат: увеличенный пропуск тиков на каждом из терминалов.
Они не собираются онлайн, а берутся частями через CopyTicks.
 
fxsaber:

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

...

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

1434 - проблему не решил.

 
fxsaber:

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

На очень вялом рынке в 1434 этот баг себя не проявляет.
Причина обращения: