preview
Creating a Custom Tick Chart in MQL5

Creating a Custom Tick Chart in MQL5

MetaTrader 5Indicators |
1 120 0
ALGOYIN LTD
Israel Pelumi Abioye

Table of Contents



Introduction

MetaTrader 5 organizes price data using fixed time intervals, which often hides the true intensity of market activity. A one-minute candle formed from hundreds of ticks can appear identical to one created from only a few trades. For scalpers and tick-driven systems, this becomes a major limitation because MetaTrader 5 does not provide a native tick-bar chart.

This article shows how to produce activity‑driven candles that close after N ticks instead of after a clock interval. You will get a clear, executable goal: an EA that creates and configures a separate custom symbol, aggregates incoming ticks into OHLC bars of a user‑defined size (ticks‑per‑bar), assigns monotonic bar timestamps, and updates the forming candle in real time using CustomRatesUpdate. Inputs include the custom symbol name, ticks‑per‑bar, and an option to auto‑open the chart. The result is a live tick chart in MetaTrader 5 where each candle reflects market activity density rather than elapsed time.

 

Project Overview and Implementation Plan

Before implementing this in MQL5, it is important to clearly define what we are building. The development process is considerably more intentional and seamless when the end product is clearly visible. It also makes it easier for you to follow each step of the logic as we proceed.

What We Are Building

We are building a custom tick chart in MQL5 where each candle is formed based on a fixed number of ticks instead of time. In a standard MetaTrader 5 chart, candles are generated automatically based on time intervals such as one minute or five minutes, meaning price movement is grouped by time rather than actual trading activity. In our case, a new candle will only be created after a specified number of ticks, for example, 10 ticks per bar. This means each candle is built purely from market activity, making the chart more responsive to real trading intensity.

The EA starts recording each incoming tick in real time as soon as it is initiated on a symbol like EURUSD. Every tick denotes a new price update, and we utilize a counter to arrange these ticks into fixed blocks rather than depending on time. OHLC logic is used to create a complete candle once a block reaches the specified size.

Imagine a 10-tick candle on EURUSD that begins at 1.08721. The price goes up and down as the subsequent ticks come in, reaching values like 1.08725, falling to 1.08718, and culminating at 1.08730. The initial tick establishes the open. The candle's peak and low points are defined by these motions. The current price, such as 1.08726, becomes the close when the tenth tick happens. After that, the candle is finished, and a new one is started right away by the subsequent tick.

Figure 1. MT5 Tick Chart

Implementation Plan

In this section, we will outline the step-by-step process we will follow to implement the custom tick chart in MQL5. This will give a clear roadmap of how we move from receiving raw market data to building structured candles based on a fixed tick size.

Creating the Custom Symbol:

This is the first step in implementing the custom tick chart in MQL5. Before we can process ticks or build candles, we need a separate symbol where our generated tick data and candles will be stored and displayed. That is why we create a custom symbol, which will serve as the destination for all the tick-based candles we create. We will create a function that runs during EA initialization. The EA itself will be attached to the chart of the symbol we would like to work with, for example, XAUUSD. From that point, all incoming ticks from XAUUSD will be used to build our custom tick chart.

The purpose of this function is to check whether the custom symbol already exists. If it already exists, we simply activate it so it becomes visible in Market Watch and ready for use. If it does not exist, we create it using the current symbol, in this case XAUUSD, as a template. Once the symbol is created, we configure it to behave like a real trading instrument. This includes setting properties such as price precision, minimum price movement, and tick size so that the price behavior matches XAUUSD. We also assign descriptive information like symbol name and currency details so it integrates properly with the platform.

For instance, if our custom symbol is called MT5_TICK_101, it will appear in the Market Watch window after this phase. This confirms that the symbol was created successfully and its current availability for receiving data from XAUUSD. It is important to note that creating the symbol does not automatically produce visible candles on the chart, as display only occurs once tick data is processed and sent to the chart engine.

Figure 2. Creating the Custom Symbol

Opening the Custom Symbol Chart:

The EA creates a special chart for the unique tick-based symbol in this stage. It ensures a single regulated viewing environment by determining whether a chart for the symbol already exists and opening one automatically if it doesn't.

For example, when the EA starts on XAUUSD, it creates and opens a chart for a custom symbol such as MT5_TICK_101. At this stage, the chart will appear blank because no tick aggregation has been processed yet and no bar data has been pushed to the chart engine. In MetaTrader 5, creating a chart is not sufficient for visualization; candles only appear when generated rates are actively updated using a display update mechanism such as CustomRatesUpdate. This step, therefore, focuses only on preparing the visual workspace where tick-based candles will later be rendered.

Figure 3. Opening the Custom Symbol Chart

Creating Tick-Based Bars:

In this step, we start creating candles by allocating incoming ticks according to the number of ticks per bar into fixed-size batches. We take the price of each new tick and use it to update the values of the current bar. The sequence's first tick establishes the open, and when subsequent ticks occur, we constantly compare prices to update the high and low, with the most recent price serving as the close.

We keep track of how many ticks have been processed for the current bar by keeping a counter. The bar is deemed finished whenever this counter hits the specified limit, such as ten ticks. To make sure the platform recognizes it correctly, we then record the final OHLC values and give the bar a legitimate time. The CustomRatesUpdate function is then used to send this finished bar to the custom symbol, which is responsible for updating and displaying the candle on the chart.

It is important to note that this step only finalizes completed bars; it does not include the currently forming tick, since the active tick is still being processed in real time before the bar is closed. For example, if we are working with XAUUSD and the tick size is set to 10, the EA will take 10 consecutive price updates, track their movement, and then finalize one candle. Immediately after that, the process resets, and a new bar begins with the next incoming tick.

Figure 4. Creating Tick-Based Bars

Updating the Live Tick Bar:

In this step, we ensure that the currently forming candle is continuously updated as every new tick arrives. This is where the chart becomes fully dynamic, because each price change is immediately reflected in the active bar.

Unlike the previous step, where we focus on completing a candle after a fixed number of ticks, here we are dealing with the live formation of that same candle. Every tick that comes in contributes to updating the OHLC values in real time. The open is set at the first tick, while the high and low continue to adjust as price moves. The close is constantly refreshed with the latest price until the bar is finalized. At this stage, CustomRatesUpdate is called in OnTick on every tick. This means the chart is updated on every single tick, ensuring the forming candle is constantly visible and up-to-date.

This modification is still connected to our tick counter logic, though. We keep track of the tick count to determine when a bar is entirely created, even if the update occurs on each tick. The process automatically moves on to the next cycle once the necessary number of ticks is reached and a new bar is generated. For instance, ticks 1 through 10 on a 10-tick bar created on XAUUSD constantly update the same candle in real time. CustomRatesUpdate continues to push live updates until tick 10, at which point the bar is finished and a new one starts, ensuring that the candle constantly represents the market.

 

Creating a Custom Tick Chart in MQL5

We will go from concept to practical implementation in this chapter now that we have a clear idea of what we want to create and how the system will operate. Step by step, we will start writing the custom tick chart in MQL5, converting the design into functional code.

Creating the Custom Symbol

Just as discussed in the implementation plan, the first stage creates and configures the custom symbol that will host the tick chart.

Example:

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);
  }
//+------------------------------------------------------------------+

Explanation:

The input parameters define the custom symbol name, control whether the chart opens automatically, and set the number of ticks used to form each candle in the tick-based chart system. 

This step is responsible for creating and preparing the custom symbol that will act as the foundation for our tick-based chart. During initialization, the EA first defines a symbol name, in this case TICK_101, which represents the custom market environment where all generated price data and candles will be stored. Inside the creation function, the first check is to determine whether this symbol already exists using SymbolInfoInteger. If it already exists, the EA simply activates it using SymbolSelect, which makes it visible in Market Watch and ready for use. This prevents duplicate symbols and ensures the EA works with a single, consistent environment.

The current chart symbol (such as XAUUSD) is utilized as a template to generate the symbol if it doesn't already exist using CustomSymbolCreate. This keeps the new symbol completely customizable while allowing it to inherit a realistic market structure. The symbol is set up to function like an actual trading instrument after it is created. The relevant CustomSymbol routines are used to set important features like price precision (digits), minimum price movement (point), and tick size. By doing this, price updates on the custom symbol are guaranteed to be precise and in line with actual market activity.

The EA also assigns descriptive metadata, including a readable description and currency information, so the symbol integrates properly within the platform. Once everything is configured, SymbolSelect is called again to ensure the symbol is active and visible in Market Watch. Finally, during OnInit, this entire process is executed automatically. If the setup fails at any point, the EA stops; otherwise, the custom symbol is fully prepared and ready to receive and display the tick-based market data in the next stages.

Opening the Custom Symbol Chart

Following the implementation plan, the next stage prepares and opens the dedicated chart that will display the live tick-based candles.

Example:

//+------------------------------------------------------------------+
//| 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
  }

Explanation:

This feature makes sure that a duplicate chart is not opened in MetaTrader 5 before generating a new one. After receiving a symbol and period, such as TICK_101 and M1, it looks through all the terminal's open charts to see if that particular chart is already available. ChartFirst() starts the process by returning the ID of the first open chart in MetaTrader 5. This can be compared to starting from the first chart in the list of all charts that are currently in use. The function returns the first chart in the internal list (for example, EURUSD, XAUUSD, or TICK_101)

The function uses while(id != -1) to initiate a loop after obtaining the first chart ID. This implies that it will keep reviewing charts one at a time until there are none left. The function navigates across open charts step-by-step because MetaTrader maintains them in a connected structure. The function verifies two crucial elements for every chart. ChartSymbol(id) and ChartPeriod(id) are used to compare the chart's symbol and timeframe, respectively. For instance, the program will compare each open chart to this precise combination if we are looking for TICK_101 on the M1 period.

Next, the function instantly returns true if it finds a match, indicating that a chart for TICK_101 on M1 is already open. This instructs the program that since the chart already exists and is active, there is no need to open a new one. It uses ChartNext(id) to go to the next chart if the current one does not match. Until every open chart has been examined, this process is repeated. The function ultimately returns false, indicating that the TICK_101 chart is not open and needs to be created if no matching chart is discovered after scanning everything.

The next step is to incorporate the ability to determine whether a custom symbol chart, such as TICK_101, is open so that, if it is not, the computer opens the chart automatically.

Example:

//+------------------------------------------------------------------+
//| 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);
  }
//+------------------------------------------------------------------+

Explanation:

When the EA is activated, it makes sure that the TICK_101 chart is opened during initialization; when it is disabled, the user is responsible for doing so. The system checks to see if TICK_101 on the M1 timeframe is already active before opening any charts. If it is, it pauses right away to prevent duplication and logs a message saying the chart is already op. 

The code then uses ChartOpen to open the chart if it isn't already open. In this instance, M1 has no bearing on the tick-based logic; it is merely utilized as a chart timeframe. It merely offers a visual area for the display of the personalized tick chart. Lastly, the EA verifies the value of InpOpenChart in the startup event handler. The chart will open automatically when the EA begins running on a symbol like XAUUSD if it is true. This is done by calling OpenCustomChart(). The user has complete discretion over whether the chart opens automatically; if it is false, in which case this step is entirely omitted.

Creating Tick-Based Bars

This stage builds candles strictly from a fixed number of ticks instead of time, ensuring each bar reflects real market activity, while also initializing real-time tick capture as the foundation for continuous bar formation.

Example:

//+------------------------------------------------------------------+
//| 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;

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

Explanation:

This function runs each time a new tick arrives (OnTick event). This is the primary location where we get real-time market data and determine how to handle it for our unique tick-based approach. We utilize MqlTick to save the most recent market data that the broker provides. All real-time pricing components, including bid, ask, last price, volume, and timestamp, are contained in this structure, but we can only access it once we have requested new data from the market.

The most recent update for the current symbol, such as XAUUSD, is then obtained using SymbolInfoTick. The real-time values originate from this source. The broker provides the bid and ask prices immediately using this function; they are not computed by hand. This function ensures that we are constantly working with the most recent state of the market by retrieving the most recent bid and ask values in real time every time OnTick executes. We verify the data after it has been retrieved by determining whether the bid price is higher than zero. This is a precautionary measure to make sure that no corrupted or empty market updates are being processed; the function uses return to quit right away if the data is invalid.

Our main focus in this step is the bid price, because it is the primary reference that determines how we build the OHLC structure of each candle. Every movement in price is evaluated based on the bid, and this is what drives the formation of the open, high, low, and close values in real time. Now that we can retrieve the latest market data continuously, the next logical step is to separate this incoming price flow into OHLC components. The open is set from the first valid price, the high and low are updated based on price movement, and the close is continuously refreshed using the most recent bid value.

Example:

//+------------------------------------------------------------------+
//| 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
     }

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

Explanation:

Instead of depending on time-based intervals, each candle in TICK_101 is produced strictly from a defined number of ticks, meaning the chart is totally driven by market activity. To determine candle formation, the system keeps track of different internal values that represent the condition of the current candle. These include a counter for how many ticks have arrived, as well as values that hold the starting price, the highest price reached, the lowest price reached, and the most recent price. There is also a reference time used to identify the commencement of each candle.

When a new tick appears, we record the timing of the tick as well as the most recent bid price as the reference price. The price becomes the candle's open if this is the first tick of a new candle, meaning the tick counter is 0. The bar time is set to indicate the beginning of the candle in TICK_101, and both the high and low are initialized to the same value at the same time. Every new price is compared to the current high and low values as more ticks appear. The high is updated if the new price exceeds the current high. The low is changed if it is less than the existing low. The candle constantly reflects the most recent market change because the close price is updated with the most recent tick.

We can monitor the candle's progress since each incoming tick raises the tick counter by one. Until it hits the specified maximum in InpTicksPerBar, this counter keeps increasing. The candle is deemed finished once the necessary number of ticks has been attained. The tick counter is then reset to zero, and the current bar is finalized. This reset signals the start of a new candle, enabling TICK_101 to begin creating the subsequent one from the subsequent tick that comes in. All the tick processing and price tracking only collect and organize the data needed to form each candle, but they do not display anything on the chart by themselves. To actually make the OHLC values visible on the custom symbol, we must use CustomRatesUpdate, which sends the prepared candle data to TICK_101 so it can be rendered in real time.

Example:
//+------------------------------------------------------------------+
//| 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
  }

Explanation:

This function is responsible for finalizing a completed candle and sending it to the custom symbol so it can be displayed on the chart. It is called when the required number of ticks for a candle has been reached. The system checks that the candle's time advances in a consistent order at the beginning of the function. Any time that does not advance is corrected since MetaTrader mandates that every bar have a distinct, sequential timestamp. This guarantees that the candles on TICK_101 stay correctly aligned and don't duplicate. The next candle is placed according to the adjusted time, which is then saved as the most recent reference.

The finished candle data is then stored in a structure. Before the completed bar is submitted to the chart, all OHLC values and other attributes are defined using this structure. The finished candle values are then assigned by the function. The open, high, low, and close prices that were gathered during the tick processing stage come after the time. These values indicate the last stage of price movement for that tick sequence because the candle is no longer moving currently.

To ensure uniformity across all bars, the tick volume is subsequently allocated depending on the predetermined number of ticks per candle. Since spread and real volume are not necessary in this unique tick-based structure, they are set to zero. Using CustomRatesUpdate, the finished candle is transmitted to the custom symbol. This crucial stage enables the candle to appear on the chart by pushing the completed OHLC data into TICK_101. An error notice is given so that the problem can be found if this update fails for any reason.

Next, the code checks whether the tick counter has reached the limit for the candle. Next, the precise number of ticks needed to finish a single bar is represented by this limit. The candle has gathered enough price updates to be finished once the counter equals or exceeds this value. The function in charge of shutting and storing the finished candle is called by the system when this condition is satisfied. This guarantees that all OHLC data is locked in and transmitted to the custom symbol for chart display. The tick counter is reset to zero as soon as the candle is finalized. To ensure continuous and clean candle production without mixing data between bars, this reset is crucial since it sets up the system to begin a fresh candle from the next incoming tick.

Updating the Live Tick Bar

The last step is updating the live tick bar, where the forming candle is continuously refreshed with each incoming tick so that the OHLC values reflect real-time market movement before the candle is completed.

Example:
//+------------------------------------------------------------------+
//| 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
  }

Explanation:

The candle that is now forming is updated in real time by this function. Even before the candle is finished, it guarantees that each new tick is instantly reflected in the live candle shown on the custom symbol TICK_101. The first step in the function is to build a structure that will contain the forming candle's current state. Before being transferred to the chart, all OHLC values and associated data are produced in this structure, which serves as a temporary container. The first value assigned is the bar time, which represents the starting time of the current candle. This time does not change during the life of the candle and is used to identify it on the chart. Next, the open price is assigned. This represents the very first price that started the current candle and remains unchanged until the candle is completed.

The high and low values are then assigned. These reflect the highest and lowest prices reached so far during the formation of the candle. They are continuously updated elsewhere in the logic, and here they are simply passed into the structure so the chart can display the latest state. The close price is also assigned, and this represents the most recent market price. Unlike the open, high, and low, the close is constantly changing as new ticks arrive, making it the most dynamic part of the candle.

The function utilizes CustomRatesUpdate to push the updated candle data into the custom symbol after all values are ready. This crucial phase is what causes the chart's candle to instantaneously redraw, providing a real-time visual update on price movement. In the main tick processing flow, this function is called on every incoming tick right after updating the OHLC values. This means that every single tick immediately refreshes the forming candle on the chart, keeping it fully in sync with real market movement.   

 

Conclusion

By following the steps in this article, you obtain a practical, verifiable solution for tick‑based charting in MetaTrader 5. The delivered EA:

  • creates or activates a custom symbol (eg, TICK_101) with trading properties copied from the source instrument;
  • optionally opens a dedicated chart for that symbol;
  • collects incoming ticks and maintains open/high/low/close state while counting ticks;
  • seals and sends a completed bar every N ticks with a unique timestamp;
  • pushes the forming bar to the chart on every tick so it stays accurate.

Acceptance criteria you can check in the terminal: the custom symbol appears in Market Watch, its chart displays a continually updating live candle, and each candle closed contains exactly the configured number of ticks and an increasing timestamp. This architecture gives you an activity‑driven view of price action, improving visibility of momentum and micro‑structure. From here you can extend the EA with volume handling, persistent storage, different aggregation rules (eg, tick‑volume weighted), or hooks for strategy testing that require tick‑accurate bar boundaries.

Attached files |
MT5_TICK_CHART.mq5 (7.84 KB)
Adaptive Malaysian Engulfing Indicator (Part 1): Pattern Detection and Retest Validation Adaptive Malaysian Engulfing Indicator (Part 1): Pattern Detection and Retest Validation
Implement the Malaysian Engulfing concept in MQL5 with two coordinated indicators. One applies strict, body‑based engulfing rules for precise pattern detection; the other uses a state-driven model to monitor what follows—pullbacks and timed retests—directly on the chart. The result is a repeatable, rule-based workflow that replaces visual guesswork with programmable logic.
The MQL5 Standard Library Explorer (Part 11): How to Build a Matrix-Based Market Structure Indicator in MQL5 The MQL5 Standard Library Explorer (Part 11): How to Build a Matrix-Based Market Structure Indicator in MQL5
Learn to engineer an MQL5 indicator that converts trend, momentum, and volatility into a single raw score using a matrix.mqh (ALGLIB). The article covers a separate‑window oscillator to validate the core mathematics, then a main‑chart indicator that plots non‑repainting buy/sell arrows when the score crosses user‑defined thresholds. An optional long‑term EMA filter, a minimum‑bar cooldown, and built‑in alerts make the tool practical for live trading.
Exploring Conformal Forecasting of Financial Time Series Exploring Conformal Forecasting of Financial Time Series
In this article, we will consider conformal predictions and the MAPIE library that implements them. This approach is one of the most modern ones in machine learning and allows us to focus on risk management for existing diverse machine learning models. Conformal predictions, by themselves, are not a way to find patterns in data. They only determine the degree of confidence of existing models in predicting specific examples and allow filtering for reliable predictions.
MetaTrader 5 Machine Learning Blueprint (Part 15): How to Calibrate Profit-Taking and Stop-Loss Targets from Synthetic Data MetaTrader 5 Machine Learning Blueprint (Part 15): How to Calibrate Profit-Taking and Stop-Loss Targets from Synthetic Data
This article applies the Optimal Trading Rule from AFML Chapter 13 to set profit targets and stop-losses without in-sample calibration. We model post-entry P&L with a discrete Ornstein–Uhlenbeck process, run a 100,000-path search, and implement Python, multiprocessing, and a Numba @njit parallel kernel (242× faster). The result is an optimal (PT, SL) under three forecast specifications, constrained by the prop-firm daily loss limit.