Trading  ·  BraivIQ AI Engineering Playbook

Building An Incremental Technical Indicator Engine In Code: Streaming RSI, MACD, Bollinger Bands And VWAP Without Recomputing The World

A live trading chart carries a dozen indicators - moving averages, RSI, MACD, Bollinger Bands, VWAP - and every one of them must update on every tick, on every timeframe, for every instrument on screen, without the chart stuttering and without the numbers drifting from what a reference calculation would give. The textbook definitions describe each indicator as a function over a whole price series; a production engine cannot afford to recompute a whole series each tick, so it must express every indicator as a stateful reducer that updates in constant time as new data arrives. That reframing is where most of the engineering lives, and getting it wrong produces the classic chart bugs: indicators that repaint, values that drift after hours of streaming, and signals on a forming bar that vanish when the bar closes. This playbook, from a team that specialises in trading charts, is a code-side account of building an incremental indicator engine: reducers with O(1) updates, warm-up periods, the forming-bar contract, chaining indicators without recomputation, multi-timeframe consistency, numerical determinism, and the testing that proves your streaming values match the reference.

 ·  13 min read  ·  By BraivIQ Engineering

Building An Incremental Technical Indicator Engine In Code: Streaming RSI, MACD, Bollinger Bands And VWAP Without Recomputing The World

O(1) per tick - Every indicator expressed as a stateful reducer that updates in constant time rather than recomputing the series  ·  Warm-up - An indicator is not valid until it has seen enough bars - and the engine must say so, not emit a plausible wrong number  ·  Forming bar - A value computed on a partial bar is provisional and must be replaced at close - or the chart repaints  ·  Reference-tested - Streaming values must match a from-scratch reference calculation on replayed data, to the last decimal

Look at any professional trading chart and count the numbers being computed live: a couple of moving averages, an RSI, a MACD with its signal line and histogram, Bollinger Bands, a VWAP, perhaps an ATR - each recalculated on every tick, on every timeframe the trader has open, for every instrument on screen. The textbook defines each of these as a function over the whole price series: an exponential moving average is a weighted sum back to the beginning of time, a Bollinger Band is the standard deviation of the last twenty closes, RSI is a ratio of smoothed gains and losses. A naive engine implements the textbook - recompute the function over the series on every update - and it works in a demo with one chart and dies in production, where the recomputation is multiplied by indicators, timeframes and instruments until the chart stutters and the tick backlog grows. The production answer is to express every indicator as a stateful reducer: an object holding just enough state that a new bar updates the indicator in constant time. That reframing is simple to state and is where almost all the engineering lives, because done carelessly it produces the classic chart bugs traders learn to distrust - indicators that repaint, values that drift after hours of streaming, signals on a forming bar that vanish when it closes. This is a domain we specialise in, and this playbook is a code-side account of building an indicator engine that is fast, consistent and provably correct.

Indicators As Stateful Reducers

The core pattern is to give each indicator a small, explicit state and an update function that consumes one completed bar and returns the new value in constant time, and the pleasure of the approach is that each classical indicator reduces cleanly. A simple moving average keeps a ring buffer of the last N closes and a running sum: on a new bar, add the new close, subtract the one falling out of the window, divide - no loop over the window. An exponential moving average keeps only its previous value: the new EMA is a fixed blend of the new close and the old EMA, which is why EMAs are the cheapest indicator to stream and the building block for many others. RSI keeps two smoothed averages - of gains and of losses over the period - updated with the same blend on each bar's change, and derives the ratio; the state is two numbers. Bollinger Bands need a rolling mean and a rolling standard deviation, and the trap here is numerical: computing variance as the difference of a running sum of squares and the squared running mean is fast and catastrophically unstable in floating point over long streams, so the engine should maintain a Welford-style running variance over the window, or recompute the window's variance from the ring buffer exactly, trading a little compute for correctness. VWAP keeps cumulative price-times-volume and cumulative volume since the session start and divides - trivial, except that it must reset at session boundaries, which drags the venue's session calendar into the indicator layer. In every case the state is tiny, the update is a handful of arithmetic operations, and a chart with a hundred indicator instances costs nothing per tick. The discipline is to write each reducer as a pure function of state and bar, so it is testable in isolation and composable with everything else.

  • SMA - ring buffer of the last N values plus a running sum; add the new, subtract the departing, divide.
  • EMA - one number of state; new value is a fixed blend of the new close and the previous EMA.
  • RSI - two smoothed averages (gains, losses) updated per bar; derive the ratio; state is two numbers.
  • Bollinger - rolling mean plus a numerically stable rolling variance (Welford-style or exact from the buffer), never sum-of-squares minus mean-squared.
  • VWAP - cumulative price-volume and volume since session open; reset on the venue's session boundary.

Warm-Up, The Forming Bar, And Chaining

Three semantics turn a set of reducers into an engine traders can trust. The first is warm-up: a 20-period average has no defined value on bar five, and an engine that emits a plausible-looking number computed over fewer bars is lying in a way that shows up as a wrong reading at the left edge of every chart and a wrong signal in every backtest. Each reducer must know how many bars it needs and report not-yet-valid until it has seen them, and consumers must handle that state explicitly rather than treating zero or null as a value. The second is the forming bar, and it is the source of the most-hated chart bug. A live chart shows the current, incomplete bar updating tick by tick, and traders expect indicators to move with it - but a value computed on a partial bar is provisional, because the bar's close will change until it closes. The engine must therefore support two operations: a provisional update that computes the indicator as if the forming bar were complete, without committing state, and a commit on bar close that advances the state for real. An engine that commits state on every tick of a forming bar corrupts its history - the EMA has absorbed twenty intermediate closes that never existed - and an engine that never shows provisional values feels dead. Get the two-operation contract right and indicators move live and never repaint. The third is chaining: many indicators are functions of other indicators - MACD is the difference of two EMAs, its signal line is an EMA of the MACD, its histogram the difference of those - and the engine should build them as a graph of reducers feeding each other, so the two underlying EMAs are computed once and shared rather than each derived indicator maintaining its own copy, and so provisional and commit propagate through the graph consistently. A dependency graph of reducers, evaluated once per bar in topological order, is the shape of a production indicator engine.

Multi-Timeframe Consistency, Determinism, And Testing

Because a chart shows many timeframes, each timeframe runs its own set of reducers fed by that timeframe's bars, and the consistency property from the aggregation layer carries through: if the hourly bars are derived from the minute bars, the hourly RSI computed from them is exactly the RSI a from-scratch calculation on hourly data would give. Where it gets subtle is the forming bar across timeframes - the current hourly bar is forming for sixty minute-bars, so the hourly indicators are provisional for an hour while the minute indicators commit sixty times, and the engine must keep those two states cleanly separate. Determinism is the property that makes all of this verifiable: given the same sequence of bars, the engine must produce byte-identical indicator values every time, which rules out anything that depends on arrival timing, wall-clock time or floating-point operation order that varies between runs, and argues for fixed evaluation order and, where an instrument's precision demands it, decimal rather than binary floating point. And testing is what turns 'should be right' into 'is right': maintain a from-scratch reference implementation of every indicator - the slow textbook version - and, on recorded bar histories, assert that the streaming engine's committed values match the reference at every bar to a tight tolerance, that provisional values equal what the reference would give with the forming bar included, and that a restart from a checkpoint reproduces the same values as an uninterrupted run. Drift, repainting and warm-up errors all fail these tests immediately; an engine that passes them on real history is one whose numbers a trader can act on.

The Bottom Line

A production trading chart computes dozens of indicators live across timeframes and instruments, and the engineering that makes it possible is the reframing of every indicator from a function over a series into a stateful reducer that updates in constant time: a ring buffer and running sum for the SMA, one number for the EMA, two smoothed averages for RSI, a numerically stable rolling variance for Bollinger Bands, cumulative price-volume with session resets for VWAP. Around the reducers sit the semantics that make the engine trustworthy - explicit warm-up so it never emits a plausible wrong number, a provisional-update-and-commit contract for the forming bar so indicators move live and never repaint, and a dependency graph so chained indicators like MACD share their underlying EMAs and propagate provisional and committed states consistently. Across timeframes the consistency of the aggregation layer carries through, determinism makes every value reproducible, and the decisive discipline is testing the streaming engine against a from-scratch reference on replayed history, so that drift, repainting and warm-up errors are caught before a trader ever sees them. Fast, consistent, provably correct indicator computation is exactly the specialism we bring to trading charts, and it is what separates a chart traders trust from one they second-guess.

References & Further Reading

  • TradingView - lightweight-charts documentation: series and real-time updates (the forming-bar contract from the chart's side): https://tradingview.github.io/lightweight-charts/docs
  • GitHub topics - technical indicators in TypeScript (open-source reference implementations to test against): https://github.com/topics/indicators?l=typescript
  • QuestDB - time-series analytics and window functions for financial data: https://questdb.com/docs/
  • Wikipedia - algorithms for calculating variance (Welford's online algorithm and numerical stability): https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
  • Open Web Solutions - trading dashboard development in 2026 with real-time charting: https://openwebsolutions.in/blog/high-performance-trading-dashboard-react-websockets/