How to Use BanTA State-Caching Mode for Real-Time Trading

BanTA’s state-caching mode enables incremental indicator updates by attaching a BarEnv to a symbol-timeframe pair, allowing trading bots to process new candles in microseconds without recomputing entire histories.

BanTA (Ban Technical Analysis) is an open-source technical analysis library written in Go and available at banbox/banta. Unlike traditional libraries that recompute indicators from scratch on every call, BanTA offers a state-caching mode specifically designed for low-latency, real-time trading systems. This article explains the internal mechanics of the caching architecture and provides production-ready implementations in both Go and Python.

Understanding BanTA Execution Models

BanTA supports two distinct execution strategies that determine how indicator values are calculated across historical data.

Parallel Mode treats every calculation as stateless. When you call an indicator function, the library recomputes values for the entire input series from the first bar to the last. This is ideal for backtesting large datasets where you need complete histories, but it introduces unnecessary latency for live trading.

State-Caching Mode maintains persistent state between bars. When a new candle arrives, only the incremental update is calculated. The library stores previous results in Series objects attached to a BarEnv (environment), allowing microsecond-level indicator updates essential for high-frequency strategies.

How State-Caching Works in BanTA

The state-caching architecture relies on three core components working in concert: the environment manager (BarEnv), the value containers (Series), and the indicator functions that interact with both.

The BarEnv Lifecycle

Every symbol-timeframe pair requires its own BarEnv instance. This environment acts as the central coordinator for incoming market data.

When a new bar arrives, you invoke BarEnv.OnBar, which validates chronological ordering and delegates to OnBar2. According to the source in core.go (lines 36-70), OnBar2 performs three critical operations:

  1. Appends the new OHLCV values to the environment’s built-in series
  2. Advances the internal timestamp (TimeStop)
  3. Invokes TrimOverflow to enforce memory bounds

TrimOverflow (lines 88-98 in core.go) maintains the cache size according to BarEnv.MaxCache (default 1500 bars). When storage exceeds 1.5 * MaxCache, the oldest values are discarded to prevent unbounded memory growth during long-running trading sessions.

Series Caching Mechanism

Indicators in BanTA operate on Series objects rather than raw slices. Each Series maintains its own timestamp (s.Time) and cached values.

The caching logic centers on Series.Cached (lines 69-71 in core.go). This method returns true when s.Time >= s.Env.TimeStop, indicating the series already contains a computed value for the current bar. When cached, indicator functions return the stored result immediately without recalculation.

For new bars, indicators compute values and call Series.Append (lines 31-66 in core.go). This method:

  • Validates the series is not already cached
  • Pushes the new value onto the internal slice
  • Handles multi-column results (e.g., KDJ outputs three series)
  • Updates s.Time to match the current environment timestamp

Handling Stateful Indicators

Some indicators require persistent internal state beyond simple value caching. Examples include ADX, DM (Directional Movement), and other rolling-window calculations that maintain intermediate sums or previous trend directions.

BanTA solves this through the Series.More field, an interface{} storage for arbitrary state structs. As implemented in sta_inds.go (lines 79-88), stateful indicators check Series.More on first execution. If nil, they initialize a state struct (e.g., dmState); subsequent calls reuse this struct, allowing the algorithm to continue calculations from the previous bar’s state.

Implementing Real-Time Trading with BanTA State-Caching

The following production-ready examples demonstrate how to integrate BanTA into live trading loops. Both examples assume you receive real-time candle data from an exchange WebSocket or REST API.

Go Implementation

In Go, maintain a map of BarEnv instances keyed by symbol-timeframe pairs. This pattern ensures thread-safe isolation between different trading pairs.

import (
	"fmt"
	ta "github.com/banbox/banta"
)

var envMap = make(map[string]*ta.BarEnv)

func OnBar(symbol, timeframe string, bar *ta.Kline) {
	// Obtain or create the BarEnv for this symbol-timeframe pair
	key := fmt.Sprintf("%s_%s", symbol, timeframe)
	env, exists := envMap[key]
	if !exists {
		env = &ta.BarEnv{
			TimeFrame: timeframe,
			BarNum:    1,
			MaxCache:  2000, // Increase for longer lookback periods
		}
		envMap[key] = env
	}

	// Push the new candle - updates all built-in series and advances time
	env.OnBar(bar.Time, bar.Open, bar.High, bar.Low, bar.Close,
		bar.Volume, bar.Quote, bar.BuyVolume, bar.TradeNum)

	// Compute indicators - automatically reuse cached values
	ma5 := ta.SMA(env.Close, 5)
	ma30 := ta.SMA(env.Close, 30)
	atr := ta.ATR(env.High, env.Low, env.Close, 14).Get(0)

	// Trading logic
	if ta.Cross(ma5, ma30) == 1 {
		cur := env.Close.Get(0)
		stop := cur - atr
		fmt.Printf("Open long at %.4f, stop-loss %.4f\n", cur, stop)
	} else if ta.Cross(ma5, ma30) == -1 {
		fmt.Printf("Close long at %.4f\n", env.Close.Get(0))
	}

	// Multi-column indicator example
	kdj := ta.KDJ(env.High, env.Low, env.Close, 9, 3, 3).Cols
	k, d := kdj[0], kdj[1]
	_ = k
	_ = d
}

Python Implementation

The Python wrapper (bbta) generated with gopy exposes the same caching semantics. The API mirrors the Go version closely.

from bbta import ta

# Create environment per symbol/timeframe

env = ta.BarEnv(TimeFrame="1m")

# Feed candles as they arrive from exchange

for ts, o, h, l, c, v in klines:
    # Push new bar - updates internal series and timestamp

    env.OnBar(ts, o, h, l, c, v, 0, 0, 0)  # quote, buyVol, tradeNum set to 0

    # Compute indicators (cached automatically)

    ma5 = ta.Series(ta.SMA(env.Close, 5))
    ma30 = ta.Series(ta.SMA(env.Close, 30))
    atr = ta.ATR(env.High, ta.Low(env.Low), env.Close, 14).Get(0)

    # Signal detection

    if ta.Cross(ma5, ma30) == 1:
        cur = env.Close.Get(0)
        print(f"Open long at {cur:.2f}, stop-loss {cur-atr:.2f}")
    elif ta.Cross(ma5, ma30) == -1:
        print(f"Close long at {env.Close.Get(0):.2f}")

    # Multi-column indicator

    k, d, j = ta.KDJ(env.High, env.Low, env.Close, 9, 3, 3).Cols

Configuring Cache Limits and Performance

Memory management in BanTA state-caching mode is controlled through BarEnv.MaxCache. The default value of 1500 bars suits most intraday strategies, but high-timeframe position trading may require larger buffers.

When the cache exceeds 1.5 * MaxCache, TrimOverflow automatically discards the oldest values. This prevents memory leaks during weeks of continuous operation. For ultra-low-latency systems, keep MaxCache minimal to improve cache locality, while ensuring it covers your longest indicator lookback period (e.g., 200 bars for a 200-period SMA).

Summary

  • BanTA state-caching mode eliminates redundant calculations by incrementally updating indicators as new bars arrive, making it ideal for real-time trading bots.
  • The BarEnv struct manages symbol-timeframe state, while Series objects store cached indicator values and timestamps.
  • OnBar and OnBar2 handle incoming market data, TrimOverflow enforces memory limits, and Series.Cached prevents duplicate computations.
  • Stateful indicators leverage Series.More to persist intermediate calculations across bars (e.g., ADX, DM).
  • Both Go and Python APIs support identical caching semantics, allowing sub-microsecond indicator updates in production trading systems.

Frequently Asked Questions

What is the difference between BanTA’s parallel mode and state-caching mode?

Parallel mode recomputes every indicator value from the first bar to the current bar on each call, which is computationally expensive but simple for backtesting. State-caching mode maintains persistent Series objects attached to a BarEnv, updating only the newest candle incrementally. This reduces latency from milliseconds to microseconds, making it essential for live trading systems where every tick matters.

How does BanTA prevent memory leaks during long-running trading sessions?

BanTA implements automatic cache management through BarEnv.MaxCache and the TrimOverflow method in core.go (lines 88-98). When the number of stored bars exceeds 1.5 times the MaxCache value (default 1500), the oldest values are discarded. This bounded buffer strategy ensures that memory usage remains predictable even when trading bots run for weeks or months continuously.

Can I use BanTA state-caching mode with Python for real-time trading?

Yes. BanTA provides a Python wrapper (bbta) generated with gopy that exposes the same state-caching API as the Go version. You create a ta.BarEnv, feed candles via OnBar, and call indicator functions like ta.SMA or ta.ATR. The underlying Go objects handle caching automatically, so Python users receive identical performance benefits for real-time trading applications.

Which indicators in BanTA require special handling in state-caching mode?

Most indicators work automatically with the standard Series caching mechanism, but stateful indicators like ADX, DM (Directional Movement), and similar rolling-window algorithms require persistent intermediate state. These indicators store their internal state in the Series.More field (an interface{} storage) as seen in sta_inds.go (lines 79-88). The first bar initializes a state struct (e.g., dmState), and subsequent bars reuse this struct to continue calculations from the previous step.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →