Тип цены для построения графиков по символу

Бары на ценовых графиках MetaTrader 5 могут строиться по ценам Bid или Last — это указано в спецификации каждого инструмента. MQL-программа может узнать эту характеристику посредством вызова функции SymbolInfoInteger для свойства SYMBOL_CHART_MODE. Возвращаемое значение является элементом перечисления ENUM_SYMBOL_CHART_MODE.

Идентификатор

Описание

SYMBOL_CHART_MODE_BID

Бары строятся по ценам Bid

SYMBOL_CHART_MODE_LAST

Бары строятся по ценам Last

Режим с ценами Last используется для символов, торгуемых на биржах (в отличие от децентрализованного рынка Forex) — для них доступен стакан цен. Глубину стакана можно узнать по свойству SYMBOL_TICKS_BOOKDEPTH.

Свойство SYMBOL_CHART_MODE пригодится для корректировки сигналов индикаторов или стратегий, которые строятся, например, по цене графика Last, в то время как торговые приказы будут выполняться "по рынку", то есть по ценам Ask или Bid, в зависимости от направления.

Также тип цены бывает востребован при расчете баров пользовательского инструмента: если тот зависит от стандартных инструментов, может иметь смысл учет их настроек по типу цены. Когда пользователь сам вводит формулу синтетического инструмента в окне Собственный символ (вызывается по нажатию кнопки Создать символ в диалоге Символы), он имеет возможность выбирать типы цен согласно спецификациям соответствующих использованных стандартных символов. Однако когда алгоритм расчета формируется в MQL-программе, именно она отвечает за правильный выбор типа цены.

Для начала соберем статистику по использованию цен Bid и Last для построения графиков на конкретном счете. Этим займется скрипт SymbolStatsByPriceType.mq5.

const bool MarketWatchOnly = false;
   
void OnStart()
{
   const int n = SymbolsTotal(MarketWatchOnly);
   int k = 0;
   // проходим по всем доступным символам
   for(int i = 0i < n; ++i)
   {
      if(SymbolInfoInteger(SymbolName(iMarketWatchOnly), SYMBOL_CHART_MODE)
          == SYMBOL_CHART_MODE_LAST)
      {
         k++;
      }
   }
   PrintFormat("Symbols in total: %d"n);
   PrintFormat("Symbols using price types: Bid=%d, Last=%d"n - kk);
}

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

   Symbols in total: 52304
   Symbols using price types: Bid=229, Last=52075

Более практичным примером послужит индикатор SymbolBidAskChart.mq5, предназначенный для отрисовки диаграммы в виде баров, сформированных по цене заданного типа. Это позволит сравнить свечи графика, использующего для своего построения цены из свойства SYMBOL_CHART_MODE, с барами на альтернативном типе цен. Например, можно увидеть бары по цене Bid на графике инструмента по цене Last. Или получить бары по цене Ask, чего стандартные графики терминала не поддерживают.

В качестве основы нового индикатора возьмем уже готовый индикатор IndDeltaVolume.mq5, представленный в разделе Ожидание данных и управление видимостью. В том индикаторе мы закачивали для определенного количества баров BarCount тиковую историю и считали по ним дельту объемов, то есть раздельно объемы для покупок и продаж. В новом индикаторе нам будет достаточно заменить расчетный алгоритм на поиск цен Open, High, Low, Close по тикам внутри каждого бара.

Настройки индикатора включают 4 буфера и 1 диаграмму в виде баров (DRAW_BARS), выводимую в основном окне.

#property indicator_chart_window
#property indicator_buffers 4
#property indicator_plots   1
   
#property indicator_type1   DRAW_BARS
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2
#property indicator_label1  "Open;High;Low;Close;"

Отображение в виде баров выбрано, чтобы облегчить их восприятие при наложении на свечи основного графика (то есть, чтобы были видны оба варианта каждого бара).

Во входные параметры добавлен ChartMode, чтобы пользователь мог выбрать один из трех типов цены (обратите внимание, что Ask является нашим расширением по сравнению со стандартным набором элементов в ENUM_SYMBOL_CHART_MODE).

enum ENUM_SYMBOL_CHART_MODE_EXTENDED
{
   _SYMBOL_CHART_MODE_BID,  // SYMBOL_CHART_MODE_BID
   _SYMBOL_CHART_MODE_LAST// SYMBOL_CHART_MODE_LAST
   _SYMBOL_CHART_MODE_ASK,  // SYMBOL_CHART_MODE_ASK*
};
   
input int BarCount = 100;
input COPY_TICKS TickType = INFO_TICKS;
input ENUM_SYMBOL_CHART_MODE_EXTENDED ChartMode = _SYMBOL_CHART_MODE_BID;

Бывший класс CalcDeltaVolume сменил название на CalcCustomBars, но остался почти без изменений. Среди отличий упомянем новый набор из 4-х буферов и поле chartMode, которое инициализируется в конструкторе из входной переменной ChartMode.

class CalcCustomBars
{
   const int limit;
   const COPY_TICKS tickType;
   const ENUM_SYMBOL_CHART_MODE_EXTENDED chartMode;
   
   double open[];
   double high[];
   double low[];
   double close[];
   ...
public:
   CalcCustomBars(
      const int bars,
      const COPY_TICKS type,
      const ENUM_SYMBOL_CHART_MODE_EXTENDED mode)
      : limit(bars), tickType(type), chartMode(mode) ...
   {
      // регистрируем массивы как индикаторные буфера
      SetIndexBuffer(0open);
      SetIndexBuffer(1high);
      SetIndexBuffer(2low);
      SetIndexBuffer(3close);
      const static string defTitle[] = {"Open;High;Low;Close;"};
      const static string types[] = {"Bid""Last""Ask"};
      string name = defTitle[0];
      StringReplace(name";"types[chartMode] + ";");
      PlotIndexSetString(0PLOT_LABELname);
      IndicatorSetInteger(INDICATOR_DIGITS_Digits);
   }
   ...

В зависимости от режима chartMode вспомогательный метод price возвращает из каждого тика конкретный тип цены.

protected:
   double price(const MqlTick &tconst
   {
      switch(chartMode)
      {
      case _SYMBOL_CHART_MODE_BID:
         return t.bid;
      case _SYMBOL_CHART_MODE_LAST:
         return t.last;
      case _SYMBOL_CHART_MODE_ASK:
         return t.ask;
      }
      return 0// ошибка
   }
   ...

С помощью метода price легко реализовать модификацию основного расчетного метода calc, где заполняются буфера для бара под номером i на основе массива тиков ticks для этого бара.

   void calc(const int iconst MqlTick &ticks[], const int skip = 0)
   {
      const int n = ArraySize(ticks);
      for(int j = skipj < n; ++j)
      {
         const double p = price(ticks[j]);
         if(open[i] == EMPTY_VALUE)
         {
            open[i] = p;
         }
         
         if(p > high[i] || high[i] == EMPTY_VALUE)
         {
            high[i] = p;
         }
         
         if(p < low[i])
         {
            low[i] = p;
         }
         
         close[i] = p;
      }
   }

Остальные фрагменты исходного кода и принципы их работы соответствуют описанию IndDeltaVolume.mq5.

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

int OnInit()
{
   ...
   ENUM_SYMBOL_CHART_MODE mode =
      (ENUM_SYMBOL_CHART_MODE)SymbolInfoInteger(_SymbolSYMBOL_CHART_MODE);
   Print("Chart mode: "EnumToString(mode));
   
   if(mode == SYMBOL_CHART_MODE_BID
      && ChartMode == _SYMBOL_CHART_MODE_LAST)
   {
      Alert("Last price is not available for "_Symbol);
   }
   
   return INIT_SUCCEEDED;
}

Ниже показана копия экрана для инструмента с режимом построения графика по цене Last, на который наложен индикатор с типом цены Bid.

Индикатор с барами по ценам Bid на графике по ценам Last
Индикатор с барами по ценам Bid на графике по ценам Last

Также интересно посмотреть на бары по цене Ask, наложенные на обычный график по ценам Bid.

Индикатор с барами по ценам Ask на графике по ценам Bid
Индикатор с барами по ценам Ask на графике по ценам Bid

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