Получение последнего тика по символу

В главе про таймсерии, в разделе Работа с массивами реальных тиков, уже была представлена встроенная структура MqlTick, содержащая поля со значениями цен и объемов для конкретного символа, известными на момент каждого изменения котировок. В режиме онлайн MQL-программа может запросить последние полученные цены и объемы с помощью функции SymbolInfoTick, использующей эту же структуру.

bool SymbolInfoTick(const string symbol, MqlTick &tick)

Для символа с заданным именем symbol функция заполняет переданную по ссылке структуру tick. В случае успеха возвращается true.

Как известно, индикаторы и эксперты автоматически вызываются терминалом по приходу нового тика, если в них описаны соответствующие обработчики OnCalculate и OnTick. Однако информация о сути изменений цены, объеме последней сделки и времени генерации тика не передаются напрямую в обработчики — более подробную информацию позволяет получить как раз функция SymbolInfoTick.

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

Возьмем за основу индикатор EventTickSpy.mq5 и преобразуем его в SymbolTickSpy.mq5, который будет запрашивать на каждом "мультивалютном" тике структуру MqlTick для соответствующего символа, а затем рассчитывать и выводить на график все спреды.

Добавим новый входной параметр Index. Он потребуется для нового способа рассылки уведомлений: мы будем посылать в пользовательском событии только номер изменившегося символа (см. далее).

#define TICKSPY 0xFEED // 65261
 
input string SymbolList = 
   "EURUSD,GBPUSD,XAUUSD,USDJPY,USDCHF"// List of symbols, comma separated (example)
input ushort Message = TICKSPY;          // Custom message id
input long Chart = 0;                    // Receiving chart id (do not edit)
input int Index = 0;                     // Index in symbol list (do not edit)

Также добавим массив Spreads для хранения спредов по символам и переменную SelfIndex, для того чтобы запомнить позицию символа текущего графика в списке (если он там встретится — а это, как правило, так). Последнее нужно, чтобы вызывать нашу функцию "обработки" нового тика из OnCalculate в исходной копии индикатора — для неё проще и правильнее взять готовый индекс для _Symbol явным образом, а не отсылать через событие самим себе.

int Spreads[];
int SelfIndex = -1;

Введенные структуры данных инициализируем в OnInit. В остальном OnInit остался без изменений, включая запуск подчиненных экземпляров индикатора на сторонних символах (здесь эти строки опущены).

void OnInit()
{
   ...
         const int n = StringSplit(SymbolList, ',', Symbols);
         ArrayResize(Spreadsn);
         for(int i = 0i < n; ++i)
         {
            if(Symbols[i] != _Symbol)
            {
               ...
            }
            else
            {
               SelfIndex = i;
            }
            Spreads[i] = 0;
         }
   ...
}

В обработчике OnCalculate генерируем пользовательское событие на каждом тике, если копия индикатора работает на чужом символе (при этом идентификатор графика Chart, на который следует отсылать уведомления, не равен 0). Обратите внимание, что в событии заполняется только параметр lparam, равный Index (dparam равен 0, sparam — NULL). Если Chart равно 0, значит мы в главной копии индикатора, работающей на символе графика _Symbol, и если он найден во входном списке символов, вызываем напрямую OnSymbolTick с соответствующим индексом SelfIndex.

int OnCalculate(const int rates_totalconst int prev_calculatedconst intconst double &price[])
{
   if(prev_calculated)
   {
      if(Chart > 0)
      {
         EventChartCustom(ChartMessageIndex0NULL);
      }
      else if(SelfIndex > -1)
      {
         OnSymbolTick(SelfIndex);
      }
   }
  
   return rates_total;
}

В приемной части алгоритма событий в OnChartEvent также вызываем OnSymbolTick, но на этот раз номер символа из списка получаем в lparam (то, что было отослано как параметр Index из другой копии индикатора).

void OnChartEvent(const int idconst long &lparamconst double &dparamconst string &sparam)
{
   if(id == CHARTEVENT_CUSTOM + Message)
   {
      OnSymbolTick((int)lparam);
   }
}

Сама функция OnSymbolTick запрашивает полную информацию по тику с помощью SymbolInfoTick и вычисляет спред как разницу между ценами Ask и Bid, деленную на размер пункта (свойство SYMBOL_POINT мы рассмотрим позднее).

void OnSymbolTick(const int index)
{
   const string symbol = Symbols[index];
   
   MqlTick tick;
   if(SymbolInfoTick(symboltick))
   {
      Spreads[index] = (int)MathRound((tick.ask - tick.bid)
         / SymbolInfoDouble(symbolSYMBOL_POINT));
      string message = "";
      for(int i = 0i < ArraySize(Spreads); ++i)
      {
         message += Symbols[i] + "=" + (string)Spreads[i] + "\n";
      }
      
      Comment(message);
   }
}

Новый спред обновляет соответствующую ячейку в массиве Spreads, после чего весь массив выводится на график в комментарии. Вот как это выглядит.

Текущие спреды по запрошенному списку символов
Текущие спреды по запрошенному списку символов

Вы можете сравнить в реальном времени соответствие информации в комментарии и в окне Обзора рынка.