English
preview
Альтернатива временным барам (Часть 3): Индикатор в подокне для отображения альтернативных баров в MQL5

Альтернатива временным барам (Часть 3): Индикатор в подокне для отображения альтернативных баров в MQL5

MetaTrader 5 — Торговые системы |
46 0
Patrick Murimi Njoroge
Patrick Murimi Njoroge

Содержание

  1. Введение
  2. Проектирование — требования к средству просмотра
  3. Архитектура
  4. Источники данных и режимы отрисовки
  5. Модуль построения баров — повторное использование CBarConstructor
  6. Чтение CSV-файла
  7. Панель графического интерфейса — CViewerPanel
  8. Ядро индикатора — AlternativeBarsViewer
  9. Вырожденные бары
  10. Разметка панели и именование объектов
  11. Подключение средства просмотра
  12. Заключение
  13. Прикрепленные файлы


Введение

В двух предыдущих статьях серии были построены все десять типов альтернативных баров, определенных в главе 1 книги Лопеса де Прадо "Advances in Financial Machine Learning": стандартные бары (временные, тиковые, объемные, долларовые) — в первой части, а бары серий — во второй. Обе статьи завершались на одном этапе: бары записывались в CSV-файл, но просмотреть их в терминале было нельзя.

Естественным решением будет открыть CSV-файл в Python и построить график на его основе. Это работает для небольших объемов истории. При объемах альтернативных баров, накапливающихся во время работы в реальном времени, — несколько тысяч долларовых баров за активную сессию, десятки тысяч тиковых баров за неделю, — и matplotlib, и Plotly достигают ограничений отрисовки. Браузер или область отрисовки графика теряет производительность еще до полной отрисовки данных. Переключение между платформами во время прогонов по параметрам оказывается настолько медленным, что нарушает цикл обратной связи. К моменту отрисовки графика советник уже сгенерировал очередную партию баров.

Более серьезная проблема в том, что статические графики могут вообще не показать критически важные ошибки. Неправильно настроенный порог дисбаланса может приводить к закрытию баров за три тика в периоды низкой волатильности. Три тика формируют бар, у которого цены открытия, максимума, минимума и закрытия практически совпадают, — плоскую свечу без информации о направлении движения. На построенном в Python графике из тысячи баров такой бар выглядит как небольшой элемент где-то посередине. В подокне MetaTrader он отображается как золотая свеча сразу после закрытия. Благодаря этому сбой калибровки становится заметным до того, как исказит данные в последующем пайплайне формирования признаков. Настоящая ценность визуализации в терминале не в удобстве: при калибровке в реальном времени пространство параметров становится наглядным, что недоступно в той же мере при офлайн-анализе.

В этой статье реализован AlternativeBarsViewer — пользовательский индикатор, который отрисовывает десять типов альтернативных баров в виде свечей с цветовой маркировкой в отдельном подокне. В данной реализации повторно используется точно та же иерархия CBarConstructor, что и в BarBuilderEA, поэтому свечи в подокне гарантированно совпадают с результатами советника и реализации на Python. Он поддерживает три источника данных: реальные тики (CopyTicksRange), синтетические тики на основе OHLC графика и CSV-файл, экспортированный советником. Кроме того, в нем предусмотрены два режима отображения, между которыми можно переключаться во время работы с помощью кнопки или горячей клавиши.


Проектирование — требования к средству просмотра

Ограничения, определяющие проектное решение, напрямую вытекают из существующей кодовой базы. BarBuilderEA записывает по одному CSV-файлу для каждого типа баров в каталог MQL5\Files\. В имени файла закодированы тип бара и порог — например, EURUSD_dollar_bar_size10000000.csv для порога в десять миллионов долларов. Средство просмотра должно считывать этот файл, не требуя от пользователя указывать путь, и перечитывать его при увеличении размера, чтобы бары, добавленные работающим советником, отображались без ручного вмешательства.

Помимо воспроизведения данных из CSV-файла, средство просмотра должно самостоятельно строить бары по потоку тиков в реальном времени или по OHLC-свечам графика. Это другой сценарий использования: источник CSV идеально подходит для проверки завершенного прогона или сравнения результатов MQL5 и Python. Источники тиков и OHLC позволяют просматривать бары по мере формирования — включая текущий незакрытый бар — вообще без запуска советника.

Альтернативные бары не соответствуют временной оси базового графика. Долларовый бар может закрыться в середине минуты, а в течение одной и той же минуты может закрыться несколько тиковых баров. Индикатор сопоставляет каждый альтернативный бар с индексным слотом базового графика и заполняет пять буферов: цены открытия, максимума, минимума, закрытия и цвет. Для отрисовки используется стиль DRAW_COLOR_CANDLES; в MQL5 пропускаются индексы баров, для которых все четыре ценовых буфера содержат EMPTY_VALUE, поэтому слоты графика без соответствующего альтернативного бара остаются пустыми.

Требуется три цветовых состояния. Бычьи бары (цена закрытия ≥ цена открытия) отрисовываются зеленым цветом. Медвежьи бары отрисовываются цветом Tomato. Формирующийся бар (текущий незакрытый бар, видимый только при использовании источников тиков и OHLC) отрисовывается золотым цветом, показывая, что он еще не закрыт. Кнопка-переключатель в левом верхнем углу подокна индикатора позволяет переключаться между двумя режимами компоновки, описанными в следующем разделе.


Архитектура

AlternativeBarsViewer Data Pipeline

Рис. 1. Трехслойная схема пайплайна данных AlternativeBarsViewer.

  • Верхний ряд — BarBuilderEA записывает данные закрытого бара SBar в CSV-файл при каждом закрытии бара. Имя файла однозначно определяется символом, типом бара и порогом. Кроме того, советник записывает состояние EWM в двоичный файл, чтобы калибровка баров дисбаланса и серий сохранялась после перезапуска терминала.
  • Средний ряд — AlternativeBarsViewer содержит экземпляр CBarConstructor того же типа, что и советник. В зависимости от InpTickSource тики поступают из CopyTicksRange, синтезируются из OHLC-свечей графика (по четыре тика из каждой) либо загружаются при повторном чтении CSV-файла советника. Разбор CSV-файла выполняется непосредственно внутри функции UpdateFromCsv() — отдельный класс считывателя не требуется.
  • Нижний ряд — функция RenderBars() проецирует сохраненный массив баров в пять буферов индикатора. CViewerPanel поддерживает небольшую панель поверх графика в левом верхнем углу подокна, непосредственно под кнопкой переключения режима. Кнопка-переключатель в левом верхнем углу подокна индикатора меняет режим отрисовки во время работы.


Источники данных и режимы отрисовки

Три источника данных

Входной параметр InpTickSource задает один из трех способов получения данных:

  • SRC_TICKS — вызывает функцию CopyTicksRange с периодом анализа продолжительностью InpLookbackDays дней. Это самый точный источник: каждый реальный тик проходит через тот же цикл ProcessTick(), который использует советник, в результате чего формируются побитово идентичные бары. При его использовании также отображается текущий формирующийся бар. При первом вызове загружается полная история; последующие вызовы в OnCalculate обрабатывают только новые тики, используя метку для исключения дубликатов с точностью до миллисекунды.
  • SRC_OHLC — синтезирует тики из OHLC-свечей графика. Каждая закрытая свеча преобразуется в четыре тика в порядке "цена открытия → цена минимума → цена максимума → цена закрытия" для медвежьей свечи или "цена открытия → цена максимума → цена минимума → цена закрытия" для бычьей свечи. Это грубое приближение: тиковое правило определяет направление по изменениям цены между синтетическими событиями. Однако загрузка происходит мгновенно, история тиков не требуется, а источник полезен для быстрого исследования порогов на медленных компьютерах или в Тестере стратегий.
  • SRC_CSV — считывает CSV-файл, экспортированный BarBuilderEA. Файл перечитывается при изменении его размера, поэтому новые бары от работающего советника отображаются автоматически. При использовании этого источника отображаются только закрытые бары (CSV-файл не содержит состояния формирующегося бара). Преимущество в том, что бары уже вычислены, а средство просмотра только читает данные — воспроизводить тики вообще не нужно.

Два режима отрисовки

Перечисление ENUM_RENDER_MODE определяет, как массив закрытых баров проецируется на индексную ось графика:

  • RENDER_TIME — каждый бар размещается в слоте сетки графика, соответствующем времени его закрытия. Слот находится бинарным поиском по кэшированному массиву g_chart_time[]. Когда два альтернативных бара попадают в один слот графика, то более поздний бар перезаписывает более ранний. Это правильное поведение: оно отражает состояние цены на правой границе слота. При редком расположении баров остаются видимые разрывы; при плотном (например, для тиковых баров на дневном графике) происходит много перезаписей.
  • RENDER_INDEX — бары последовательно размещаются в крайних правых слотах графика: самые новые — справа, самые старые — левее. Нет ни разрывов, ни коллизий. Метки временной оси становятся ориентирами положения, а не обозначениями фактического времени закрытия. Этот режим предпочтителен для калибровки параметров, когда форма баров и интервалы между ними важнее календарного выравнивания.

INDEX mode – sequential packing, no gaps

Рис. 2a. Режим INDEX (последовательное размещение, без коллизий).

TIME mode – calendar alignment, gaps and collisions

Рис. 2b. Режим TIME (с календарным выравниванием, видны разрывы).

Режим переключается во время работы кликом по кнопке "View: Time" / "View: Index" в левом верхнем углу подокна индикатора либо нажатием клавиши T, когда график находится в фокусе ввода с клавиатуры. Повторно подключать индикатор не нужно; RenderBars() сразу заново заполняет буферы и вызывает ChartRedraw().

//+------------------------------------------------------------------+
//| Render the bar store into the plot buffers per the active mode   |
//+------------------------------------------------------------------+
void RenderBars(void)
  {
   if(g_rates_total <= 0)
      return;

   ArrayInitialize(g_open,  EMPTY_VALUE);
   ArrayInitialize(g_high,  EMPTY_VALUE);
   ArrayInitialize(g_low,   EMPTY_VALUE);
   ArrayInitialize(g_close, EMPTY_VALUE);
   ArrayInitialize(g_color, 0.0);

   int total = ArraySize(g_bars);

   if(g_render_mode == RENDER_TIME)
     {
      for(int i = 0; i < total; i++)
        {
         int s = SlotForTime(g_bars[i].time);
         PutCandle(s, g_bars[i],
                   g_bars[i].close >= g_bars[i].open ? COL_BULL : COL_BEAR);
        }
      if(g_has_forming)
         PutCandle(g_rates_total - 1, g_forming, COL_FORMING);
     }
   else // RENDER_INDEX — pack newest into the rightmost slots
     {
      int reserve = g_has_forming ? 1 : 0;
      int slots   = g_rates_total - reserve;
      int ndraw   = (total < slots) ? total : slots;
      int first   = total - ndraw;
      int right   = g_rates_total - 1 - reserve;

      for(int j = 0; j < ndraw; j++)
        {
         int s = right - (ndraw - 1 - j);
         int b = first + j;
         PutCandle(s, g_bars[b],
                   g_bars[b].close >= g_bars[b].open ? COL_BULL : COL_BEAR);
        }
      if(g_has_forming)
         PutCandle(g_rates_total - 1, g_forming, COL_FORMING);
     }
  }

В обоих режимах формирующийся бар всегда занимает слот g_rates_total - 1 (крайнюю правую позицию графика). В режиме RENDER_INDEX при размещении закрытых баров этот слот остается свободным, если есть формирующийся бар. В режиме RENDER_TIME формирующийся бар перезаписывает содержимое текущего слота графика — это правильное поведение для графика в реальном времени.



Модуль построения баров — повторное использование CBarConstructor

Средство просмотра создает экземпляр конструктора баров с помощью той же фабричной функции, которая используется в BarBuilderEA. Входные параметры — InpBarType, InpBarSize, InpExpTicksInit, InpEwmSpan и т.д. — точно соответствуют входным параметрам советника. Это намеренное ограничение архитектуры: если бы средство просмотра принимало другие параметры, нельзя было бы гарантировать совпадение его свечей с результатами советника в CSV.

//+------------------------------------------------------------------+
//| Factory — identical mapping to the EA                            |
//+------------------------------------------------------------------+
CBarConstructor *CreateBarConstructor(void)
  {
   switch(InpBarType)
     {
      case BAR_TIME:   return new CTimeBar((int)InpBarSize);
      case BAR_TICK:   return new CTickBar((int)InpBarSize);
      case BAR_VOLUME: return new CVolumeBar(InpBarSize);
      case BAR_DOLLAR: return new CDollarBar(InpBarSize);

      case BAR_TICK_IMB:
         return new CImbalanceBar(IMB_METRIC_TICK,
                                  InpExpTicksInit, InpExpImbInit, InpEwmSpan);
      case BAR_VOLUME_IMB:
         return new CImbalanceBar(IMB_METRIC_VOLUME,
                                  InpExpTicksInit, InpExpImbInit, InpEwmSpan);
      case BAR_DOLLAR_IMB:
         return new CImbalanceBar(IMB_METRIC_DOLLAR,
                                  InpExpTicksInit, InpExpImbInit, InpEwmSpan);

      case BAR_TICK_RUNS:
         return new CRunsBar(IMB_METRIC_TICK,
                             InpExpTicksInit, InpExpRunBuyInit,
                             InpExpRunSellInit, InpEwmSpan);
      case BAR_VOLUME_RUNS:
         return new CRunsBar(IMB_METRIC_VOLUME,
                             InpExpTicksInit, InpExpRunBuyInit,
                             InpExpRunSellInit, InpEwmSpan);
      case BAR_DOLLAR_RUNS:
         return new CRunsBar(IMB_METRIC_DOLLAR,
                             InpExpTicksInit, InpExpRunBuyInit,
                             InpExpRunSellInit, InpEwmSpan);
     }
   return NULL;
  }

Поддерживаются все десять типов баров. Средство просмотра также предоставляет доступ к состоянию формирующегося бара через методы Open(), High(), Low(), Close() и LastTickTime(), добавленные в CBarConstructor во второй части. Их вызывает функция UpdateForming(), заполняющая данные формирующейся свечи золотого цвета.


Чтение CSV-файла

Средство просмотра считывает CSV-файл советника непосредственно внутри функции UpdateFromCsv(). Отдельный класс для чтения не требуется. Файл открывается с помощью FileOpen (при необходимости — в общей папке Common), строка заголовков пропускается, а затем каждая строка данных разбирается и преобразуется в структуру SBar. Файл перечитывается только при изменении его размера, поэтому работающий советник может дописывать файл без необходимости перезагружать индикатор.

//+------------------------------------------------------------------+
//| CSV source — (re)read the EA's export when the file size changes |
//+------------------------------------------------------------------+
void UpdateFromCsv(const bool full)
  {
   string name  = BuildCsvName();
   int    flags = FILE_READ | FILE_CSV | FILE_ANSI | FILE_SHARE_READ | FILE_SHARE_WRITE;
   if(InpCsvCommon) flags |= FILE_COMMON;

   int fh = FileOpen(name, flags, ',');
   if(fh == INVALID_HANDLE) { ... }

   long size = (long)FileSize(fh);
   if(!full && size == g_csv_size) { FileClose(fh); return; }

   ArrayResize(g_bars, 0);
   g_has_forming = false;

   while(!FileIsEnding(fh))
     {
      string s0 = FileReadString(fh);   // time
      if(FileIsEnding(fh) && StringLen(s0) == 0) break;
      string s1 = FileReadString(fh);   // open
      // ... read remaining columns
      if(s0 == "time") continue;    // skip header

      SBar b;
      b.time = StringToTime(s0);
      b.open = StringToDouble(s1);
      // ... assign other fields
      if(b.time > 0) AppendBar(b);
     }

   g_csv_size = size;
   FileClose(fh);
   // Update panel's file label if needed
   if(g_panel_created)
     {
      string disp_file = name;
      if(StringLen(disp_file) > 22)
         disp_file = "..." + StringSubstr(disp_file, StringLen(disp_file) - 21);
      string full_obj_name = g_panel.GetObjName("_val_file");
      ObjectSetString(ChartID(), full_obj_name, OBJPROP_TEXT, disp_file);
     }
  }

Проверка строк защищает от обработки неполных записей. Строковое обозначение типа бара считывается из последнего столбца, но не сохраняется; оно используется только для проверки согласованности.


Панель графического интерфейса — CViewerPanel

В панели используются семь типов графических объектов MQL5 без внешних библиотек. В качестве фона используется один объект типа OBJ_RECTANGLE_LABEL. Пары объектов OBJ_LABEL отображают пять строк статистики: тип бара, исходный файл, общее количество баров, количество вырожденных баров и временную метку последнего бара. Один объект OBJ_BUTTON запускает немедленное повторное чтение CSV-файла. Для всех объектов угол привязки — CORNER_LEFT_UPPER, поэтому положение панели определяется относительно верхнего левого угла подокна, непосредственно под кнопкой-переключателем.

//+------------------------------------------------------------------+
//| Create — build all panel objects                                 |
//+------------------------------------------------------------------+
bool CViewerPanel::Create(const long   chart_id,
                          const int    subwin,
                          const string filename)
  {
   m_chart  = chart_id;
   m_subwin = subwin;

   string bg = ObjName("_bg");
   if(!ObjectCreate(m_chart, bg, OBJ_RECTANGLE_LABEL, m_subwin, 0, 0))
     {
      PrintFormat("CViewerPanel::Create: ObjectCreate failed for %s, err=%d",
                  bg, GetLastError());
      return(false);
     }
   ObjectSetInteger(m_chart, bg, OBJPROP_CORNER,       CORNER_LEFT_UPPER);
   ObjectSetInteger(m_chart, bg, OBJPROP_XDISTANCE,    m_left_margin);
   ObjectSetInteger(m_chart, bg, OBJPROP_YDISTANCE,    m_top_margin);
   ObjectSetInteger(m_chart, bg, OBJPROP_XSIZE,        m_panel_w);
   ObjectSetInteger(m_chart, bg, OBJPROP_YSIZE,        m_panel_h);
   ObjectSetInteger(m_chart, bg, OBJPROP_BGCOLOR,      C'13,17,23');
   ObjectSetInteger(m_chart, bg, OBJPROP_BORDER_COLOR, C'48,54,61');
   ObjectSetInteger(m_chart, bg, OBJPROP_BORDER_TYPE,  BORDER_FLAT);
   ObjectSetInteger(m_chart, bg, OBJPROP_BACK,         true);
   ObjectSetInteger(m_chart, bg, OBJPROP_SELECTABLE,   false);
   //--- Row labels and value labels created via MakeLabel()
   //--- (see attached source for the full sequence)
   return(true);
  }

Каждый объект-метка использует ANCHOR_LEFT_UPPER, поэтому его свойство OBJPROP_XDISTANCE отсчитывается от левого края подокна. Это означает, что панель правильно смещается при изменении размера графика. Метод Update() изменяет только текстовые свойства меток значений, позволяя избежать затрат на удаление и пересоздание объектов при каждом повторном чтении CSV-файла.

Одна деталь позволяет избежать распространенного визуального сбоя: после клика по кнопке Refresh она остается нажатой до следующего события мыши, если явно не сбросить ее состояние. Update() всегда вызывает:

//--- Reset button visual state after handling click
ObjectSetInteger(m_chart, ObjName("_btn_refresh"), OBJPROP_STATE, false);

Этот сброс происходит внутри метода Update() — после чтения CSV-файла, — а не непосредственно в OnChartEvent. Сброс состояния до завершения чтения не дает пользователю визуального подтверждения того, что клик зарегистрирован.

Жирное начертание шрифта задается в имени гарнитуры GDI ("Courier New Bold"), а не через OBJPROP_BOLD. Флаг OBJPROP_BOLD был добавлен в MT5 Build 2361; подход с использованием имени гарнитуры работает во всех сборках, поддерживающих OBJ_LABEL.


Ядро индикатора — AlternativeBarsViewer

Настройка буферов

Для DRAW_COLOR_CANDLES требуется пять буферов индикатора: четыре ценовых буфера OHLC и один буфер индексов цвета. Буфер индексов цвета привязывается с помощью INDICATOR_COLOR_INDEX, поэтому в MQL5 его значения используются для выбора цвета из массива цветов построения, а не как ценовой ряд. Служебное пустое значение для построения — EMPTY_VALUE (DBL_MAX), заданное через PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE). Определены три цвета: зеленый (бычий), Tomato (медвежий) и золотой (формирующийся).

#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   1
#property indicator_label1  "AltBar"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrLimeGreen,clrTomato,clrGold
#property indicator_width1  1

OnCalculate и обработка тиков

Каждый вызов OnCalculate при prev_calculated == 0 приводит к полному сбросу: конструктор баров удаляется и создается заново, все хранилища очищаются, а данные соответствующего источника загружаются с нуля. При последующих вызовах обрабатываются только новые данные: тики после последней обработанной миллисекунды для SRC_TICKS и вновь закрытые бары графика для SRC_OHLC; для SRC_CSV проверяется размер файла. После каждого частичного или полного обновления вызывается RenderBars(), чтобы обновить отображение.

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

//+------------------------------------------------------------------+
//| Feed one chart bar as 4 synthetic ticks (O/H/L/C)                |
//| Approximation for the OHLC source. Real volume (if available),   |
//| otherwise tick volume, assigned to the close tick.               |
//+------------------------------------------------------------------+
void FeedBarAsTicks(const int i,
                    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[])
  {
   double vol     = (volume[i] > 0) ? (double)volume[i] : (double)tick_volume[i];
   double ask_off = (double)spread[i] * _Point;
   bool   up      = (close[i] >= open[i]);

   double seq[4];
   seq[0] = open[i];
   seq[1] = up ? low[i]  : high[i];
   seq[2] = up ? high[i] : low[i];
   seq[3] = close[i];

   for(int k = 0; k < 4; k++)
     {
      MqlTick t;
      ZeroMemory(t);
      t.time     = time[i];
      t.time_msc = (long)time[i] * 1000 + k;
      t.bid      = seq[k];
      t.ask      = seq[k] + ask_off;
      t.last     = seq[k];
      t.volume   = (k == 3) ? (ulong)vol : 0;

      g_tick_num++;
      SBar bar;
      if(g_bar.ProcessTick(t, g_tick_num, bar))
         AppendBar(bar);
     }
  }

Порядок синтетических тиков — цена открытия → цена минимума → цена максимума → цена закрытия для медвежьего бара и цена открытия → цена максимума → цена минимума → цена закрытия для бычьего — влияет на классификатор тикового правила при построении баров дисбаланса и серий. Это приближение с известным ограничением: по данным OHLC нельзя определить последовательность тиков внутри бара в реальном потоке котировок. Для исследования параметров этого приближения достаточно; для точной калибровки используйте SRC_TICKS.

Удаление дубликатов тиков

SRC_TICKS использует миллисекундную отметку, чтобы избежать повторной обработки тиков из предыдущего вызова OnCalculate. Тики идентифицируются по time_msc. Поскольку несколько тиков могут приходиться на одну миллисекунду, вместе с временной меткой сохраняется число уже обработанных тиков с этой меткой:

//--- Remember where we stopped, counting ties at the last millisecond
ulong last = (ulong)ticks[got - 1].time_msc;
int   cnt  = 0;
for(int i = got - 1;
    i >= 0 && (ulong)ticks[i].time_msc == last;
    i--)
   cnt++;
g_last_msc       = last;
g_last_msc_count = cnt;

При следующем вызове полностью пропускаются тики с временной меткой раньше g_last_msc, а также ровно g_last_msc_count тиков с меткой g_last_msc (они составляли последний обработанный пакет). Это гарантирует, что при последовательных вызовах OnCalculate каждый тик передается в ProcessTick() ровно один раз, независимо от перекрытия окон получения тиков.


Вырожденные бары

Simulated dollar bars with degenerate bars highlighted

Рис. 3. Иллюстрация из трех панелей со смоделированными долларовыми барами и выделением вырожденных баров.

  • Панель (a) — гистограмма тикового объема для 160 смоделированных долларовых баров. Вырожденные бары (принудительно ограниченные тремя тиками) отображаются красным цветом; желтая пунктирная линия обозначает порог InpMinTickVolume, равный 5.
  • Панель (b) — отрисовка свечей OHLC. Бычьи бары отображаются зеленым цветом, медвежьи — оранжево-красным, а вырожденные бары — серым. Цветовая маркировка сразу выделяет вырожденные бары без необходимости отдельно проверять тиковый объем.
  • Панель (c) — увеличенный вид окна из девяти баров с одним вырожденным баром в центре. Плоская серая свеча четко выделяется на фоне соседних, даже если окружающие бары небольшие.

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

Для этих двух причин требуются разные способы исправления. При естественном периоде низкой активности никаких действий предпринимать не нужно — бар корректен, просто менее информативен. Если порог откалиброван неверно, необходимо увеличить значение параметра InpExpTicksInit в BarBuilderEA. На практике эти случаи помогает различить счетчик вырожденных баров на панели: если его значение растет только в определенные часы сессии, причина структурная. Если такие бары равномерно распределены по всем часам, порог необходимо скорректировать.


Разметка панели и именование объектов

CViewerPanel layout — graphical objects in the indicator subwindow

Рис. 4. Разметка CViewerPanel — графические объекты в подокне индикатора.

  • Фон — OBJ_RECTANGLE_LABEL размером 200 × 140 пикселей, привязанный к верхнему левому углу подокна и расположенный непосредственно под кнопкой переключения режима (YDISTANCE = 60).
  • Строки статистики — пять строк, каждая с меткой слева (OBJ_LABEL, серого цвета) и значением справа (OBJ_LABEL, с цветовой маркировкой). После создания изменяются только метки значений; метки строк задаются один раз и больше не меняются.
  • Кнопка Refresh — OBJ_BUTTON, занимающая всю ширину панели за вычетом отступа в 16 пикселей. Ее свойству OBJPROP_STATE присваивается значение false после каждого успешного обновления данных, поэтому кнопка не остается нажатой.

Все имена объектов имеют общий префикс (по умолчанию BV_), благодаря чему вызовы ObjectDelete в Destroy() обращаются к точному набору объектов панели без просмотра полного списка объектов графика. Префикс задается в конструкторе CViewerPanel и может быть переопределен, если на одном графике требуется несколько экземпляров панели.


Подключение средства просмотра

Процедура настройки зависит от выбранного источника данных:

Источник тиков или OHLC

  1. Подключите AlternativeBarsViewer к любому графику целевого символа. Задайте значения InpBarType и InpBarSize (а также параметры EWM для баров дисбаланса или серий) в соответствии с конфигурацией, которую вы хотите проанализировать.
  2. Присвойте параметру InpTickSource значение SRC_TICKS для построения баров с точностью до тика и отображения формирующегося бара либо SRC_OHLC для быстрого синтетического приближения.
  3. Для SRC_TICKS присвойте параметру InpLookbackDays количество дней истории тиков, которые нужно загрузить. Более длинный период анализа дает больше баров, но требует больше времени при первой загрузке.
  4. Присвойте параметру InpRenderMode значение RENDER_INDEX (по умолчанию) для отображения без коллизий либо RENDER_TIME, если важно календарное выравнивание. Режим можно изменить в любой момент с помощью кнопки-переключателя или клавиши T.

Источник CSV

  1. Запустите BarBuilderEA на любом графике с нужным типом баров и параметрами. При инициализации советник выводит имя выходного CSV-файла в лог на вкладке "Эксперты": BarBuilderEA: type=dollar_bar, bar_size=10000000, output=EURUSD_dollar_bar_size10000000.csv.
  2. Подключите AlternativeBarsViewer к тому же графику (или к любому графику по тому же символу). Присвойте параметру InpTickSource значение SRC_CSV. Оставьте InpCsvFile пустым для использования автоматически сформированного имени файла либо введите точную строку из лога на вкладке "Эксперты".
  3. Если советник записывает данные в общую папку Common (FILE_COMMON), присвойте параметру InpCsvCommon значение true. По умолчанию BarBuilderEA записывает данные в локальную папку терминала MQL5\Files\.

На одном графике могут одновременно работать несколько экземпляров AlternativeBarsViewer в отдельных подокнах — по одному для каждого типа баров или набора параметров. Общая временная ось базового графика обеспечивает синхронизацию подокон в режиме RENDER_TIME, упрощая параллельное сравнение типов баров. В режиме RENDER_INDEX каждое подокно независимо отображает свою последовательность баров. Выравнивание между подокнами теряет смысл, но в каждом подокне бары расположены равномерно и легко читаются.


Заключение

Альтернативные бары бесполезны, если их калибровку невозможно отслеживать в режиме реального времени. CSV-вывод BarBuilderEA точен и полон, но для его просмотра приходится выходить из терминала. При объемах баров, накапливающихся во время работы в реальном времени, визуализация на Python теряет производительность еще до полной отрисовки истории. AlternativeBarsViewer решает обе проблемы: повторно использует тот же механизм построения баров, что и советник, передает ему данные из трех источников и отрисовывает результат штатными средствами в отдельном подокне.

Эти два режима отрисовки предназначены для разных задач. RENDER_TIME сохраняет календарное выравнивание и позволяет напрямую сопоставлять редко расположенные бары с динамикой цены на базовом графике. RENDER_INDEX устраняет разрывы и коллизии, что важно для баров дисбаланса и серий: иначе из-за их неравномерного расположения калибровочные паттерны были бы незаметны. Для переключения между ними достаточно одного клика по кнопке; менять параметры не требуется.

В следующей статье серии мы реализуем вычисление микроструктурных признаков непосредственно по потоку альтернативных баров, заменив шину сигналов на основе CSV работающим в реальном времени пайплайном формирования признаков. Он будет передавать сигналы OHLC и дисбаланса в модель ONNX без записи промежуточных файлов.


13. Прикрепленные файлы

 

Файл

Расположение

Описание

 1. AlternativeBarsViewer.mq5 MQL5\Indicators\ Ядро индикатора. Строит альтернативные бары по реальным тикам, синтетическим тикам на основе OHLC или CSV-файлу советника. Отрисовывает данные через DRAW_COLOR_CANDLES в двух переключаемых режимах компоновки. Делегирует обновления графического интерфейса классу CViewerPanel.
 2. CViewerPanel.mqh MQL5\Include\AlternativeBars\ Панель графического интерфейса. Тринадцать графических объектов, привязанных к верхнему левому углу подокна; пять строк статистики с обновлением только текста при каждом повторном чтении через Update()
 3. BarBuilderEA.mq5 MQL5\Experts\ Советник, который строит альтернативные бары по потоку тиков в реальном времени и записывает их в CSV-файл. Сохраняет состояние EWM в двоичном файле между перезапусками. Необходим для режима SRC_CSV.
 4. CBarConstructor.mqh MQL5\Include\AlternativeBars\ Абстрактный базовый класс. Содержит накопитель OHLC, методы SeedBar(), UpdateAccumulator(), FillBar() и механизм сохранения состояния. Предоставляет методы доступа к состоянию формирующегося бара, используемые средством просмотра для отрисовки текущей свечи.
 5. CStandardBars.mqh MQL5\Include\AlternativeBars\ Конкретные конструкторы временных, тиковых, объемных и долларовых баров. CCumSumBar — общий родительский класс для объемных и долларовых баров. Все они наследуются от CBarConstructor.
 6. CImbalanceBars.mqh MQL5\Include\AlternativeBars\ Классификатор по тиковому правилу (CTickRule) и конструктор баров дисбаланса (CImbalanceBar) с адаптацией на основе EWM для тиковых, объемных и долларовых баров дисбаланса.
 7. CRunsBar.mqh MQL5\Include\AlternativeBars\ Конструктор баров серий. Бар закрывается, когда значение накопителя серий покупок или продаж превышает E[T] × max(E[theta+/T], E[theta-/T]). Тиковый, объемный и долларовый варианты. Для долларовых баров серий стоимость оценивается по средней цене между Bid и Ask.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22681

Прикрепленные файлы |
MQL5.zip (25.41 KB)
От начального к среднему уровню: Файл шаблона графика (II) От начального к среднему уровню: Файл шаблона графика (II)
В статье показано, как применять индикаторы к графикам, созданным с помощью объекта OBJ_CHART, используя шаблоны, когда ChartIndicatorAdd в этом контексте не работает. Мы объясняем, как сохранить шаблон с индикатором, получить идентификатор графика и применить ChartApplyTemplate к нужной цели. Вы научитесь интегрировать индикаторы как ресурс, автоматизировать настройку встроенного графика и поймёте некоторые практические ограничения, например то, что индикаторы не появляются в стандартном окне.
Нейронная сеть на практике: Ручное обратное распространение Нейронная сеть на практике: Ручное обратное распространение
В этой статье мы начнём закладывать основы понимания того, как работает обратное распространение. Понимание этих основ необходимо для того, чтобы понять, как различные перцептроны могут по отдельности настраивать свои параметры, даже если в сети их произвольное количество. Цель состоит не в том, чтобы реализовать обратное распространение, а в том, чтобы создать простой механизм, который позволит вам понять, как оно работает.
Инжиниринг признаков для машинного обучения (Часть 4): Реализация временных признаков в MQL5 Инжиниринг признаков для машинного обучения (Часть 4): Реализация временных признаков в MQL5
Применение границ сессий из Python к временным меткам брокера в MQL5 приводит на любом брокере с часовым поясом, отличным от UTC, к ошибочной классификации принадлежности к сессиям на два–три часа и искажает флаги сессий по всей истории бэктеста. Мы реализуем файл CTimeFeatures.mqh, содержащий классы CRingBuffer и CTimeFeatures, с тремя методами, доступными для советников: Initialize (фиксация смещения относительно UTC и настройка частотного фильтра), Update (добавление логарифмической доходности в кольцевые буферы для соответствующих торговых сессий) и Calculate (циклическое кодирование, флаги торговых сессий и волатильность сессий). Результат — плоский массив типа double, совместимый по формату с Python-функцией get_time_features для таймфреймов меньше часа, часовых и дневных таймфреймов.
Нейронная сеть на практике: Начинаем строить цепочку Нейронная сеть на практике: Начинаем строить цепочку
Пришло время начать разбираться в том, как соединять перцептроны в цепочку. Это позволит нам реализовать то, что известно как сеть перцептронов. Эта сеть составляет основу всего того, что вы знаете и используете как ту или иную форму «искусственного интеллекта». Однако построение такой сети связано с рядом трудностей. Здесь мы начнём разбираться, в чём они заключаются.