English
preview
Создание пользовательского тикового графика в MQL5

Создание пользовательского тикового графика в MQL5

MetaTrader 5 — Индикаторы |
80 0
ALGOYIN LTD
Israel Pelumi Abioye

Содержание



Введение

MetaTrader 5 организует ценовые данные с использованием фиксированных временных интервалов, что зачастую скрывает истинную интенсивность рыночной активности. Одноминутная свеча, сформированная из сотен тиков, может выглядеть точно так же, как и свеча, сформированная всего из нескольких сделок. Для скальперов и систем, работающих на основе тиков, это становится серьёзным ограничением, поскольку MetaTrader 5 не предоставляет встроенный график тиковых баров.

В этой статье рассказывается, как создавать свечи, формируемые по рыночной активности, которые закрываются после N тиков, а не по истечении заданного временного интервала. Вы получите чёткую и реализуемую задачу: разработать советник, который создаёт и настраивает отдельный пользовательский символ, объединяет поступающие тики в OHLC-бары заданного пользователем размера (количество тиков на бар), присваивает барам монотонно возрастающие временные метки и обновляет формирующуюся свечу в режиме реального времени с помощью функции CustomRatesUpdate. В качестве входных параметров указываются имя пользовательского символа, количество тиков на бар и опция автоматического открытия графика. В результате в MetaTrader 5 появляется тиковый график в реальном времени, на котором каждая свеча отражает интенсивность рыночной активности, а не прошедшее время.

 

Обзор проекта и план реализации

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

Что мы создаем

Мы создаём в MQL5 пользовательский тиковый график, в котором каждая свеча формируется на основе фиксированного количества тиков, а не времени. На стандартном графике MetaTrader 5 свечи формируются автоматически на основе временных интервалов, например одной или пяти минут, поэтому ценовое движение группируется по времени, а не по фактической торговой активности. В нашем случае новая свеча будет создаваться только после заданного количества тиков, например 10 тиков на бар. Это означает, что каждая свеча формируется исключительно на основе рыночной активности, благодаря чему график более точно отражает реальную интенсивность торговли.

Советник начинает регистрировать каждый входящий тик в режиме реального времени сразу после запуска на торговом инструменте, например EURUSD. Каждый тик означает новое обновление цены, и мы используем счётчик, чтобы сгруппировать эти тики в группы фиксированного размера, а не ориентироваться на время. Логика OHLC используется для формирования полной свечи, как только группа тиков достигает заданного размера.

Представьте себе свечу на 10 тиков по паре EURUSD, которая начинается с цены 1,08721. Цена колеблется вверх и вниз по мере поступления последующих тиков, достигая таких значений, как 1,08725, опускаясь до 1,08718 и поднимаясь до 1,08730. Первый тик определяет цену открытия. Эти движения определяют максимум и минимум свечи. Текущая цена, например 1,08726, становится ценой закрытия при появлении десятого тика. После этого свеча завершается, и следующий тик сразу же открывает новую.

Рисунок 1. Тиковый график MT5

План реализации

В этом разделе мы опишем пошаговый процесс реализации пользовательского тикового графика на языке MQL5. Это даст чёткий план действий: как перейти от получения необработанных рыночных данных к построению структурированных свечей на основе фиксированного количества тиков.

Создание пользовательского символа:

Это первый шаг в реализации пользовательского тикового графика в MQL5. Прежде чем обрабатывать тики или строить свечи, нам нужен отдельный символ, в котором будут храниться и отображаться сгенерированные нами тиковые данные и свечи. Именно поэтому мы создаём пользовательский символ, который будет служить местом назначения для всех создаваемых нами свечей на основе тиков. Мы создадим функцию, которая будет запускаться при инициализации советника. Сам советник будет установлен на график символа, с которым мы хотим работать, например XAUUSD. С этого момента все поступающие тики по XAUUSD будут использоваться для построения нашего пользовательского тикового графика.

Цель этой функции — проверить, существует ли уже пользовательский символ. Если он уже существует, мы просто активируем его, чтобы он стал видимым в окне «Обзор рынка» и был готов к использованию. Если его нет, мы создаём его, используя текущий символ, в данном случае XAUUSD, в качестве шаблона. После создания символа мы настраиваем его так, чтобы он вел себя как настоящий торговый инструмент. Сюда входит настройка таких параметров, как точность котировок, минимальное изменение цены и размер тика, чтобы поведение цены соответствовало XAUUSD. Кроме того, мы задаём описательную информацию, такую как имя символа и сведения о валюте, чтобы он корректно интегрировался с платформой.

Например, если наш пользовательский символ называется MT5_TICK_101, после завершения этого этапа он появится в окне «Обзор рынка». Это подтверждает, что символ был успешно создан и теперь доступен для получения данных по XAUUSD. Важно отметить, что создание пользовательского символа не приводит к автоматическому появлению видимых свечей на графике, поскольку их отображение происходит только после обработки тиковых данных и их отправки в механизм построения графиков.

Рисунок 2. Создание пользовательского символа

Открытие графика пользовательского символа:

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

Например, когда советник запускается на символе XAUUSD, он создает и открывает график для пользовательского символа, такого как MT5_TICK_101. На данном этапе график будет пустым, поскольку агрегирование тиков ещё не было выполнено, а данные баров ещё не переданы в движок графика. В MetaTrader 5 для визуализации недостаточно просто создать график; свечи появляются только тогда, когда генерируемые котировки активно обновляются с помощью механизма обновления отображения, такого как CustomRatesUpdate. Таким образом, на этом этапе внимание уделяется исключительно подготовке визуального рабочего пространства, в котором позже будут отображаться свечи на основе тиков.

Рисунок 3. Открытие графика пользовательского символа

Создание тиковых баров:

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

Мы отслеживаем, сколько тиков было обработано для текущего бара, с помощью счетчика. Бар считается завершенным, когда этот счетчик достигает заданного предела, например, десяти тиков. Чтобы платформа правильно распознала этот бар, мы затем фиксируем окончательные значения OHLC и присваиваем ему корректную метку времени. Затем функция CustomRatesUpdate используется для отправки этого завершенного бара в пользовательский символ, который отвечает за обновление и отображение свечи на графике.

Важно отметить, что на этом этапе фиксируются только завершенные бары; текущий тик здесь не учитывается, поскольку активный тик всё еще обрабатывается в реальном времени до закрытия бара. Например, если мы работаем с XAUUSD, а количество тиков на бар задано равным 10, советник возьмет 10 последовательных обновлений цены, отследит их движение, а затем сформирует одну свечу. Сразу после этого процесс сбрасывается, и с появлением следующего тика начинается новый бар.

Рисунок 4. Создание баров на основе тиков

Обновление текущего тикового бара:

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

В отличие от предыдущего этапа, где мы сосредоточивались на завершении формирования свечи после фиксированного количества тиков, здесь мы имеем дело с формированием этой же свечи в реальном времени. Каждый поступающий тик участвует в обновлении значений OHLC в реальном времени. Цена открытия задается по первому тику, тогда как максимум и минимум продолжают корректироваться по мере движения цены. Цена закрытия постоянно обновляется по последней цене до момента окончательного формирования бара. На данном этапе функция CustomRatesUpdate вызывается в OnTick при каждом тике. Это означает, что график обновляется при каждом тике, благодаря чему формирующаяся свеча всегда остается видимой и актуальной.

Однако эта модификация по-прежнему связана с нашей логикой подсчёта тиков. Мы отслеживаем количество тиков, чтобы определить, когда бар сформировался полностью, даже если обновление происходит при каждом тике. Как только достигается необходимое количество тиков и формируется новый бар, процесс автоматически переходит к следующему циклу. Например, тики с 1-го по 10-й в 10-тиковом баре, создаваемом по XAUUSD, постоянно обновляют одну и ту же свечу в реальном времени. CustomRatesUpdate продолжает отправлять обновления в реальном времени до 10-го тика, после чего бар завершается и начинается новый, благодаря чему свеча постоянно отражает состояние рынка.

 

Создание пользовательского тикового графика в MQL5

Теперь, когда у нас есть чёткое представление о том, что мы хотим создать и как будет работать система, в этой главе мы перейдём от концепции к практической реализации. Шаг за шагом мы начнём писать код пользовательского тикового графика на MQL5, преобразуя проект в рабочий код.

Создание пользовательского символа

Как и указано в плане реализации, на первом этапе создаётся и настраивается пользовательский символ, на котором будет размещён тиковый график.

Пример:

input string InpCustomSymbol = "TICK_101";   // Name of the custom symbol to create/use
input bool   InpOpenChart    = true;         // Whether to automatically open the chart
input int    InpTicksPerBar  = 30;          // Number of ticks that will form one candle

//+------------------------------------------------------------------+
//| Create or prepare custom symbol                                  |
//+------------------------------------------------------------------+
bool CreateCustomSymbol()
  {
//--- If symbol already exists, just select it and continue
   if(SymbolInfoInteger(InpCustomSymbol, SYMBOL_EXIST))
     {
      SymbolSelect(InpCustomSymbol, true);  // Make it visible in Market Watch
      return true;
     }

//--- Create new custom symbol using current symbol as template
   if(!CustomSymbolCreate(InpCustomSymbol, "", _Symbol))
     {
      Print("Failed to create custom symbol: ", GetLastError());
      return false;
     }

//--- Copy important trading properties from the original symbol
   CustomSymbolSetInteger(InpCustomSymbol, SYMBOL_DIGITS, _Digits);  // Decimal precision
   CustomSymbolSetDouble(InpCustomSymbol, SYMBOL_POINT, _Point);     // Smallest price step
   CustomSymbolSetDouble(InpCustomSymbol, SYMBOL_TRADE_TICK_SIZE,SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE));

//--- Set descriptive metadata
   CustomSymbolSetString(InpCustomSymbol, SYMBOL_DESCRIPTION, "Tick Chart - " + _Symbol);
   CustomSymbolSetString(InpCustomSymbol, SYMBOL_CURRENCY_BASE,SymbolInfoString(_Symbol, SYMBOL_CURRENCY_BASE));
   CustomSymbolSetString(InpCustomSymbol, SYMBOL_CURRENCY_PROFIT,SymbolInfoString(_Symbol, SYMBOL_CURRENCY_PROFIT));

   SymbolSelect(InpCustomSymbol, true);  // Make visible
   Print("Custom symbol created: ", InpCustomSymbol);

   return true;
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Ensure custom symbol exists
   if(!CreateCustomSymbol())
      return INIT_FAILED;
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Пояснение:

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

На этом этапе создаётся и подготавливается пользовательский символ, который станет основой для нашего тикового графика. Во время инициализации советник сначала задаёт имя символа — в данном случае TICK_101. Этот символ представляет пользовательскую рыночную среду, где будут храниться все сгенерированные ценовые данные и свечи. Внутри функции создания сначала с помощью SymbolInfoInteger проверяется, существует ли этот символ. Если он уже существует, советник просто активирует его с помощью SymbolSelect, после чего он становится видимым в окне «Обзор рынка» и готовым к использованию. Это позволяет избежать дублирования символов и гарантирует, что советник работает в единой согласованной среде.

Текущий символ графика (например, XAUUSD) используется в качестве шаблона для создания символа с помощью CustomSymbolCreate, если создаваемый символ ещё не существует. Благодаря этому новый символ остаётся полностью настраиваемым и при этом наследует реалистичную рыночную структуру. После создания символ настраивается таким образом, чтобы функционировать как полноценный торговый инструмент. Соответствующие функции CustomSymbol используются для настройки таких важных параметров, как точность цены (количество знаков), минимальное изменение цены (пункт) и размер тика. Благодаря этому обновления цен пользовательского символа гарантированно будут точными и соответствующими реальной рыночной активности.

Советник также задаёт описательные метаданные, включая понятное описание и информацию о валюте, чтобы символ корректно интегрировался в платформу. После завершения настройки функция SymbolSelect вызывается повторно, чтобы убедиться, что символ активен и отображается в окне «Обзор рынка». Наконец, во время выполнения OnInit весь этот процесс запускается автоматически. Если на каком-либо этапе настройка завершится неудачно, советник останавливается; в противном случае пользовательский символ полностью подготовлен и готов принимать и отображать рыночные данные, сформированные на основе тиков, на следующих этапах.

Открытие графика пользовательского символа

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

Пример:

//+------------------------------------------------------------------+
//| Check if a chart is already open                                 |
//+------------------------------------------------------------------+
bool IsChartOpen(string symbol, ENUM_TIMEFRAMES tf)
  {
   long id = ChartFirst();  // Get first chart ID

//--- Loop through all open charts
   while(id != -1)
     {
      //--- If both symbol and timeframe match, chart is already open
      if(ChartSymbol(id) == symbol && ChartPeriod(id) == tf)
         return true;
      id = ChartNext(id);  // Move to next chart
     }

   return false;  // No matching chart found
  }

Пояснение:

Эта функция проверяет, что в MetaTrader 5 уже не открыт такой же график, прежде чем создавать новый. После получения символа и периода, например TICK_101 и M1, выполняется просмотр всех открытых графиков терминала, чтобы проверить, открыт ли уже такой график. Функция ChartFirst() запускает процесс, возвращая идентификатор первого открытого графика в MetaTrader 5. Это можно сравнить с началом обхода с первого графика в списке всех графиков, используемых в данный момент. Функция возвращает первый график из внутреннего списка (например, EURUSD, XAUUSD или TICK_101)

Функция использует оператор while(id != -1) для запуска цикла после получения идентификатора первого графика. Это означает, что функция будет просматривать графики по одному, пока не останется ни одного непроверенного. Эта функция пошагово переходит между открытыми графиками, поскольку MetaTrader хранит их в связанной структуре. Эта функция проверяет два важнейших элемента каждого графика. ChartSymbol(id) и ChartPeriod(id) используются для сравнения символа и таймфрейма графика соответственно. Например, если мы ищем символ TICK_101 на таймфрейме M1, программа будет сравнивать каждый открытый график именно с этой комбинацией.

Далее функция сразу возвращает значение true, если обнаруживает совпадение, что означает, что график TICK_101 на таймфрейме M1 уже открыт. Это указывает программе, что, поскольку график уже существует и находится в активном состоянии, нет необходимости открывать новый. Если текущий график не соответствует, с помощью ChartNext(id) выполняется переход к следующему графику. Этот процесс повторяется до тех пор, пока не будут проверены все открытые графики. Если после полного просмотра соответствующий график не найден, функция в итоге возвращает значение false, что означает, что график TICK_101 не открыт и его необходимо создать.

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

Пример:

//+------------------------------------------------------------------+
//| Check if a chart is already open                                 |
//+------------------------------------------------------------------+
bool IsChartOpen(string symbol, ENUM_TIMEFRAMES tf)
  {
   long id = ChartFirst();  // Get first chart ID

//--- Loop through all open charts
   while(id != -1)
     {
      //--- If both symbol and timeframe match, chart is already open
      if(ChartSymbol(id) == symbol && ChartPeriod(id) == tf)
         return true;

      id = ChartNext(id);  // Move to next chart
     }

   return false;  // No matching chart found
  }

//+------------------------------------------------------------------+
//| Open custom chart if not already open                            |
//+------------------------------------------------------------------+
void OpenCustomChart()
  {
//--- Prevent opening multiple duplicate charts
   if(IsChartOpen(InpCustomSymbol, PERIOD_M1))
     {
      Print("Chart already open");
      return;
     }

//--- Open chart (M1 is just a container, not actually used for logic)
   long id = ChartOpen(InpCustomSymbol, PERIOD_M1);

   if(id == 0)
      Print("Failed to open chart: ", GetLastError());
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
// Ensure custom symbol exists
   if(!CreateCustomSymbol())
      return INIT_FAILED;

//--- Optionally open chart
   if(InpOpenChart)
      OpenCustomChart();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Пояснение:

При активации советник обеспечивает открытие графика TICK_101 во время инициализации; если автоматическое открытие отключено, пользователь должен открыть график самостоятельно. Перед открытием каких-либо графиков система проверяет, не открыт ли уже TICK_101 на таймфрейме M1. Если это так, программа сразу же приостанавливает работу, чтобы предотвратить дублирование, и выводит сообщение о том, что график уже открыт.

Затем код использует функцию ChartOpen для открытия графика, если он ещё не открыт. В данном случае M1 не влияет на логику, основанную на тиках; он используется исключительно в качестве таймфрейма графика. Он просто предоставляет визуальную область для отображения пользовательского тикового графика. Наконец, советник проверяет значение параметра InpOpenChart в обработчике события запуска. Если этот параметр имеет значение true, график откроется автоматически при запуске советника на таком символе, как XAUUSD. Это выполняется вызовом функции OpenCustomChart(). Пользователь полностью сам решает, открывать ли график автоматически; если значение равно false, этот шаг полностью пропускается.

Создание тиковых баров

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

Пример:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   MqlTick tick;

//--- Get latest tick from broker
   if(!SymbolInfoTick(_Symbol, tick))
      return;

//--- Ignore invalid price
   if(tick.bid <= 0)
      return;

  }
//+------------------------------------------------------------------+

Пояснение:

Эта функция запускается при поступлении каждого нового тика (событие OnTick). Именно здесь мы получаем рыночные данные в режиме реального времени и определяем, как их обрабатывать в рамках нашего нашего подхода, основанного на тиках. Мы используем MqlTick для сохранения самых свежих рыночных данных, предоставляемых брокером. Все компоненты котировок в режиме реального времени, включая цену Bid, цену Ask, последнюю цену, объем и временную метку, содержатся в этой структуре, однако получить к ней доступ можно только после запроса новых данных с рынка.

Затем с помощью функции SymbolInfoTick получаются последние тиковые данные для текущего символа, например XAUUSD. Значения в режиме реального времени поступают из этого источника. С помощью этой функции брокер сразу предоставляет цены Bid и Ask; они не рассчитываются вручную. Эта функция гарантирует, что мы постоянно работаем с самым актуальным состоянием рынка, получая самые свежие значения Bid и Ask в режиме реального времени при каждом выполнении OnTick. Мы проверяем данные после их получения, определяя, превышает ли цена Bid ноль. Это мера предосторожности, призванная гарантировать, что некорректные или пустые рыночные обновления не будут обрабатываться; если данные некорректны, функция немедленно завершает работу с помощью оператора return.

На этом этапе мы уделяем основное внимание цене Bid, поскольку именно она является основным ориентиром, определяющим, как мы формируем структуру OHLC каждой свечи. Каждое изменение цены оценивается на основе Bid, и именно это определяет формирование значений открытия, максимума, минимума и закрытия в режиме реального времени. Теперь, когда мы можем непрерывно получать самые свежие рыночные данные, следующим логичным шагом станет разбиение этого входящего потока цен на компоненты OHLC. Цена открытия устанавливается по первой корректной цене, максимальная и минимальная цены обновляются в зависимости от движения цены, а цена закрытия постоянно обновляется с использованием самого последнего значения Bid.

Пример:

//+------------------------------------------------------------------+
//| Tick-based candle state                                          |
//+------------------------------------------------------------------+
int      tick_count    = 0;   // Counts how many ticks have formed the current bar
double   open_price    = 0;   // Open price of current bar
double   high_price    = 0;   // Highest price reached in current bar
double   low_price     = 0;   // Lowest price reached in current bar
double   close_price   = 0;   // Latest price (updates every tick)
datetime bar_time      = 0;   // Time assigned to the current bar
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   MqlTick tick;

//--- Get latest tick from broker
   if(!SymbolInfoTick(_Symbol, tick))
      return;

//--- Ignore invalid price
   if(tick.bid <= 0)
      return;

   double   price = tick.bid;
   datetime now   = tick.time;

//--- If first tick of new bar, initialize OHLC
   if(tick_count == 0)
     {
      open_price = price;
      high_price = price;
      low_price  = price;
      bar_time   = now;  // starting time of this bar
     }

//--- Update High and Low
   if(price > high_price)
      high_price = price;
   if(price < low_price)
      low_price  = price;

//--- Always update Close
   close_price = price;

//--- Increase tick counter
   tick_count++;

//--- If tick limit reached, close bar
   if(tick_count >= InpTicksPerBar)
     {
      tick_count = 0;        // reset for next bar
     }

  }
//+------------------------------------------------------------------+

Пояснение:

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

Когда появляется новый тик, мы фиксируем время тика, а также последнюю цену Bid в качестве опорной цены. Цена принимается за цену открытия свечи, если это первый тик новой свечи, то есть счетчик тиков равен 0. Время бара задается так, чтобы обозначить начало свечи в TICK_101, а максимум и минимум одновременно инициализируются тем же значением. Каждая новая цена сравнивается с текущими максимальными и минимальными значениями по мере появления новых тиков. Максимум обновляется, если новая цена превышает текущий максимум. Минимум обновляется, если новая цена ниже текущего минимума. Свеча постоянно отражает последние изменения на рынке, поскольку цена закрытия обновляется с учетом последнего тика.

Мы можем отслеживать ход формирования свечи, поскольку каждый новый тик увеличивает счетчик тиков на единицу. Этот счетчик продолжает увеличиваться до тех пор, пока не достигнет максимального значения, указанного в параметре InpTicksPerBar. Свеча считается завершённой после достижения необходимого количества тиков. После этого счетчик тиков обнуляется, и текущий бар фиксируется. Этот сброс сигнализирует о начале формирования новой свечи, что позволяет TICK_101 приступить к созданию последующей свечи со следующего поступившего тика. Вся обработка тиков и отслеживание цен лишь собирают и систематизируют данные, необходимые для формирования каждой свечи, но сами по себе ничего не отображают на графике. Чтобы значения OHLC действительно отображались для пользовательского символа, необходимо использовать функцию CustomRatesUpdate, которая отправляет подготовленные данные свечи в TICK_101 для отображения в режиме реального времени.

Пример:
//+------------------------------------------------------------------+
//| Tick-based candle state                                          |
//+------------------------------------------------------------------+
int      tick_count    = 0;   // Counts how many ticks have formed the current bar
double   open_price    = 0;   // Open price of current bar
double   high_price    = 0;   // Highest price reached in current bar
double   low_price     = 0;   // Lowest price reached in current bar
double   close_price   = 0;   // Latest price (updates every tick)
datetime bar_time      = 0;   // Time assigned to the current bar
datetime last_bar_time = 0;   // Time of the last completed bar (used to ensure uniqueness)
//+------------------------------------------------------------------+
//| Finalize and store completed candle                              |
//+------------------------------------------------------------------+
void CommitCompletedBar()
  {
//--- Ensure bar time is always increasing
   if(bar_time <= last_bar_time)
      bar_time = last_bar_time + 1;

   last_bar_time = bar_time;

   MqlRates rates[1];

//--- Final OHLC values for completed bar
   rates[0].time        = bar_time;
   rates[0].open        = open_price;
   rates[0].high        = high_price;
   rates[0].low         = low_price;
   rates[0].close       = close_price;
   rates[0].tick_volume = InpTicksPerBar;  // fixed tick count
   rates[0].spread      = 0;
   rates[0].real_volume = 0;

//--- Store final bar
   if(!CustomRatesUpdate(InpCustomSymbol, rates))
      Print("CommitCompletedBar failed: ", GetLastError());
  }
//--- If tick limit reached, close bar
if(tick_count >= InpTicksPerBar)
  {
   CommitCompletedBar();  // finalize candle
   tick_count = 0;        // reset for next bar
  }

Пояснение:

Эта функция отвечает за фиксацию завершенной свечи и отправку ее в пользовательский символ для отображения на графике. Она вызывается, когда достигнуто необходимое количество тиков для свечи. В начале выполнения функции система проверяет, что время свечи последовательно увеличивается. Любое время, которое не увеличивается, корректируется, поскольку MetaTrader требует, чтобы каждый бар имел уникальную последовательную метку времени. Это гарантирует, что свечи на TICK_101 будут правильно выровнены и не будут дублироваться. Следующая свеча размещается в соответствии с откорректированным временем, которое затем сохраняется как последняя контрольная точка.

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

Для обеспечения единообразия по всем барам тиковый объем затем задается исходя из заранее установленного количества тиков на свечу. Поскольку в этой особой структуре на основе тиков спред и реальный объем не требуются, их значения устанавливаются равными нулю. С помощью функции CustomRatesUpdate завершенная свеча передается в пользовательский символ. На этом ключевом этапе свеча появляется на графике благодаря передаче завершенных данных OHLC в TICK_101. Если это обновление по какой-либо причине завершается неудачей, отображается сообщение об ошибке, чтобы можно было выявить проблему.

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

Обновление текущего тикового бара

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

Пример:
//+------------------------------------------------------------------+
//| Update current (forming) candle                                  |
//+------------------------------------------------------------------+
void UpdateCurrentBar()
  {

   MqlRates rates[1];

//--- Populate current candle structure
   rates[0].time        = bar_time;
   rates[0].open        = open_price;
   rates[0].high        = high_price;
   rates[0].low         = low_price;
   rates[0].close       = close_price;
   rates[0].tick_volume = (long)tick_count;  // number of ticks so far
   rates[0].spread      = 0;
   rates[0].real_volume = 0;

//--- Push update to chart (this redraws candle live)
   CustomRatesUpdate(InpCustomSymbol, rates);
  }
//--- Update live (forming) candle
UpdateCurrentBar();

//--- If tick limit reached, close bar
if(tick_count >= InpTicksPerBar)
  {
   CommitCompletedBar();  // finalize candle
   tick_count = 0;        // reset for next bar
  }

Пояснение:

Данная функция в режиме реального времени обновляет формирующуюся в данный момент свечу. Еще до завершения формирования свечи гарантируется, что каждый новый тик мгновенно отражается в текущей свече, отображаемой на графике пользовательского символа TICK_101. Первым шагом в функции является создание структуры, которая будет содержать текущее состояние формирующейся свечи. Перед передачей на график все значения OHLC и сопутствующие данные формируются в этой структуре, которая служит временным хранилищем. Первым задаётся значение времени бара, которое обозначает время начала текущей свечи. Это время не изменяется в течение всего времени формирования свечи и используется для её идентификации на графике. Далее определяется цена открытия. Это самая первая цена, с которой началась текущая свеча, и она остается неизменной до момента завершения свечи.

Затем задаются значения максимума и минимума. Они отражают максимальную и минимальную цены, достигнутые на данный момент в ходе формирования свечи. Они постоянно обновляются в других частях логики, а здесь просто передаются в структуру, чтобы график мог отображать актуальное состояние. Также указывается цена закрытия, которая отражает самую последнюю рыночную цену. В отличие от цен «открытия», «максимума» и «минимума», цена «закрытия» постоянно меняется по мере поступления новых тиков, что делает её самой динамичной частью свечи.

Функция использует CustomRatesUpdate, чтобы передать обновленные данные свечи в пользовательский символ после того, как все значения будут готовы. Именно этот решающий этап приводит к мгновенному обновлению свечи на графике, обеспечивая визуальное отображение динамики цен в режиме реального времени. В основном потоке обработки тиков эта функция вызывается при каждом поступающем тике сразу после обновления значений OHLC. Это означает, что каждый отдельный тик мгновенно обновляет формирующуюся свечу на графике, обеспечивая её полную синхронизацию с реальным движением рынка.

 

Заключение

Следуя шагам, описанным в этой статье, вы получите практичное и проверяемое решение для построения тиковых графиков в MetaTrader 5. Представленный советник:

  • создает или активирует пользовательский символ (например, TICK_101) с торговыми свойствами, скопированными из исходного инструмента;
  • при необходимости открывает отдельный график для этого символа;
  • собирает входящие тики и поддерживает состояние значений «открытие», «максимум», «минимум» и «закрытие», одновременно считая тики;
  • фиксирует и отправляет завершенный бар каждые N тиков с уникальной меткой времени;
  • передает формирующийся бар на график при каждом тике, чтобы он оставался точным.

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

Проект поддерживается в Algo Forge.

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

Прикрепленные файлы |
MT5_TICK_CHART.mq5 (7.84 KB)
Нейронная сеть на практике: Градиент Нейронная сеть на практике: Градиент
В статье объясняется, почему и как используется градиент при обучении перцептрона, исходя из метода наименьших квадратов и правила цепочки для получения частных производных. Мы покажем реализацию вычисления градиента в классе C_Neuron на языке MQL5 и проверим её на примерах с 1 и 2 входами. Вы научитесь настраивать веса и смещение с помощью градиентного спуска, а также подготовитесь к прямому и обратному распространению.
От начального к среднему уровню: Объекты и подокна (I) От начального к среднему уровню: Объекты и подокна (I)
В статье подробно рассматриваются создание и размещение объекта OBJ_CHART в подокнах, при этом особое внимание уделяется различиям между главным окном и подокнами. Показывается, как интегрировать индикаторы с помощью ресурсов (#resource, ChartIndicatorAdd) и определять нужное подокно по краткому имени индикатора. В результате код становится более стабильным, переносимым и удобным для повторного использования.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
От базового к среднему уровню: Ресурсы От базового к среднему уровню: Ресурсы
В этой статье вы познакомитесь с концепцией, которая во многих случаях может оказаться чрезвычайно полезной и значительно упростить обмен вашими приложениями и проектами. Хотя эту концепцию непросто полностью объяснить в одной статье, того, что будет здесь изложено и объяснено, уже хватит, чтобы в будущем мы смогли сделать многое, в том числе и то, что иначе было бы невозможно. Именно поэтому эта статья ещё не была опубликована: чтобы у вас был вспомогательный материал и начальная база для изучения.