Trading  ·  BraivIQ AI Engineering Playbook

Building A Bar Aggregation Engine In Code: From Raw Ticks To Time, Tick, Volume And Renko Bars For Multi-Timeframe Charts

Every candle a trader looks at is manufactured. Markets do not emit one-minute bars; they emit a torrent of individual ticks - trades and quotes with prices, sizes and timestamps - and somewhere between the feed and the chart an aggregation engine turns that torrent into the open-high-low-close bars that every chart, indicator and pattern detector consumes. Get it right and nobody notices. Get it subtly wrong - a bar boundary misaligned by a timezone, a late tick dropped, a higher timeframe that disagrees with the lower one it should be built from - and every chart in the building shows a false market. This is a domain we specialise in, and this playbook is a code-side account of how a bar aggregation engine is actually built: the tick-to-bar state machine, time bars with session and boundary handling, tick, volume, range and Renko bars that close on activity rather than the clock, deriving higher timeframes from lower ones so every timeframe agrees, the forming-candle contract with the chart, the hard problems of late and out-of-order data, and the correctness discipline - deterministic replay and property tests - that lets you trust every bar on screen.

 ·  13 min read  ·  By BraivIQ Engineering

Building A Bar Aggregation Engine In Code: From Raw Ticks To Time, Tick, Volume And Renko Bars For Multi-Timeframe Charts

Ticks → bars - Markets emit individual trades and quotes; every OHLC candle is manufactured from them by an aggregation engine  ·  State machine - Open on first tick, extend high and low, update close, accumulate volume, close on the boundary - the core loop  ·  4 bar families - Time bars close on the clock; tick, volume and range bars close on activity; Renko closes on price movement  ·  Every timeframe agrees - Higher timeframes must be derivable from lower ones - the property test that catches most aggregation bugs

Traders talk about one-minute bars and daily candles as though the market produced them, but it does not. A market emits ticks - a stream of individual trades and quote updates, each with a price, a size and a timestamp, arriving at rates that can reach millions per second across an exchange - and every bar on every chart is manufactured from that stream by a piece of software that almost nobody thinks about until it is wrong. That software is the bar aggregation engine, and it sits at the root of the entire charting and analytics stack: the chart renders its output, every technical indicator is computed from it, every pattern detector reads it, every backtest replays it. When it is right, nobody notices. When it is subtly wrong - a bar boundary misaligned by a timezone offset, a late tick silently dropped, a daily candle that does not equal the aggregation of the minute candles beneath it - every chart in the building shows a market that did not happen, and decisions get made on it. Building this engine correctly is a domain we specialise in, and this playbook is a code-side account of how it is actually done: the core state machine, the four families of bars, the multi-timeframe derivation that keeps every chart consistent, the forming-candle contract, the genuinely hard problems of late and disordered data, and the discipline that lets you trust every bar on screen.

The Core State Machine: Tick To Bar

At its heart, aggregation is a small state machine per instrument per timeframe, and writing it down precisely is the first step to getting it right. A bar has an open, high, low, close, volume and - for anything beyond the simplest use - a trade count and the timestamps of its first and last tick. When a tick arrives, the engine determines which bar it belongs to. If no bar is currently forming for that bucket, a new bar opens: open, high, low and close are all set to the tick price, volume to the tick size. If a bar is forming and the tick belongs to it, the bar extends: high becomes the greater of high and the tick price, low the lesser, close becomes the tick price, volume accumulates. If the tick belongs to a later bucket, the forming bar closes - it is emitted as complete and immutable - and a new bar opens for the tick's bucket. The subtleties live in that 'determines which bar it belongs to' step and in what happens at the boundaries: a tick exactly on a boundary must be assigned consistently (conventionally to the bar that starts at that instant), the engine must decide whether to emit empty bars for buckets with no ticks (a chart usually wants a gap, an indicator usually wants a carried-forward close - so emit a flag, not a fake), and the distinction between a completed bar and the forming bar must be explicit in the data model, because downstream consumers treat them differently. Written as pure functions over an explicit state, this machine is small, fast and testable, which is exactly what you want at the root of the stack.

The Four Families Of Bars

Which bar a tick belongs to depends on the bar type, and there are four families, each with its own boundary rule and its own traps. Time bars - the familiar one-minute, five-minute, hourly and daily candles - bucket ticks by clock interval, and their difficulty is entirely about time: boundaries must be aligned to a canonical epoch so that every consumer agrees a five-minute bar starts at 09:30 and not 09:31, timestamps must be normalised to a single timezone (exchange-local for session logic, UTC for storage, never the server's local time), and session boundaries matter - a daily bar is defined by the trading session, not by midnight, so the engine needs a session calendar per venue with open, close, half-days and holidays. Tick bars close after a fixed number of trades, volume bars after a fixed cumulative size, and range bars after price has travelled a fixed distance; all three close on activity rather than the clock, so their boundaries are data-driven, their duration varies, and their forming bar can stay open across a quiet hour or close ten times in a busy second - which means the engine's state machine must be driven purely by the tick stream with no reliance on wall-clock timers. Renko bricks close when price moves a fixed brick size from the previous brick's close, ignoring time and volume entirely, and they introduce a genuinely different problem: a single large tick can complete several bricks at once, so the engine must emit multiple bars from one input, and reversal rules (how far price must retrace to open a brick in the opposite direction) must be explicit and configurable. Supporting all four from one engine means abstracting the boundary rule behind an interface while sharing the open-extend-close core - and testing each rule's edge cases separately, because their failure modes do not overlap.

  • Time bars - bucket by clock interval; align boundaries to a canonical epoch, normalise timezones, and define daily bars by the venue's session calendar, not midnight.
  • Tick and volume bars - close after N trades or N units of size; purely stream-driven, variable duration, no wall-clock timers.
  • Range bars - close when price has travelled a fixed distance; boundaries are price-driven and a bar can span hours or milliseconds.
  • Renko bricks - close on fixed price movement from the prior brick; one tick can emit several bricks, and reversal rules must be explicit.
  • One core, pluggable boundary rules - share the open-extend-close machine, abstract the 'which bar does this tick belong to' decision.

Multi-Timeframe Consistency And The Forming-Candle Contract

A chart offers many timeframes, and the single most important correctness property of an aggregation engine is that they agree: the daily bar must equal the aggregation of the hourly bars within its session, which must equal the aggregation of the minute bars within each hour, all the way down to ticks. The clean way to guarantee this is to derive higher timeframes from lower ones rather than aggregating each independently from ticks: build the base timeframe (typically one minute, or one second for intraday-heavy venues) from the tick stream, and build every higher timeframe by aggregating completed base bars - open from the first, high as the maximum, low as the minimum, close from the last, volume as the sum. Derivation makes consistency structural rather than coincidental, and it turns the property into a test you can run: for any window, aggregate the lower bars and assert equality with the higher bar, on real recorded data, continuously. The other contract the engine must honour is with the chart itself: the forming candle. A live chart shows the current bar updating tick by tick, and the engine must emit that forming bar as a distinct, mutable, clearly-flagged object - updated in place as ticks arrive, then replaced by an immutable completed bar at the boundary - so that the chart can update the last candle in place and append on rollover, and so that indicators can choose whether to include the incomplete bar. A forming bar that is indistinguishable from a completed one is how charts repaint and how backtests cheat: an indicator computed on a bar that later changed will show a signal that never existed at the time. Make the forming state explicit in the type, and both problems disappear.

The Bottom Line

Every candle a trader sees is manufactured from a torrent of ticks by a bar aggregation engine that sits at the root of the entire charting and analytics stack, and its errors are the most consequential kind - invisible on the surface and present in every chart, indicator, pattern and backtest downstream. Building it correctly is concrete engineering: a small, pure, testable state machine that opens a bar on the first tick, extends high and low, updates close, accumulates volume and closes on the boundary; pluggable boundary rules for the four bar families - time bars with epoch-aligned boundaries, normalised timezones and per-venue session calendars, tick and volume bars driven purely by the stream, range bars driven by price distance, and Renko bricks that can emit several bars from one tick; higher timeframes derived from lower ones so that every timeframe agrees by construction and the agreement can be asserted as a continuous property test; a forming-candle contract that keeps the mutable live bar explicitly distinct from immutable completed bars, so charts do not repaint and backtests do not cheat; and the discipline for real feeds - exchange timestamps not arrival time, a bounded reordering window, explicit revision of finalised bars, deduplication on sequence, and idempotent replay that yields byte-identical bars from the same tick log. That last property is the whole standard: an engine whose every bar can be reproduced from the record is one you can trust, and building charts on exactly that foundation is the specialism we bring to trading systems.

References & Further Reading

  • TradingView - lightweight-charts documentation: series data, the last bar and real-time updates (the forming-candle contract from the chart's side): https://tradingview.github.io/lightweight-charts/docs
  • QuestDB - time-series aggregation and sampling by interval for financial data: https://questdb.com/docs/
  • Algovantis - optimizing market data acquisition for low-latency algorithmic trading (feed ordering and sequencing): https://algovantis.com/market-data-acquisition-strategies-for-low-latency-algo-trading/
  • Open Web Solutions - trading dashboard development in 2026 with real-time charting (WebSocket tick pipelines): https://openwebsolutions.in/blog/high-performance-trading-dashboard-react-websockets/
  • BrightCoding - lightweight-charts is insanely fast (incremental last-bar updates): https://www.blog.brightcoding.dev/2026/06/18/stop-using-bloated-chart-libraries-lightweight-charts-is-insanely-fast