BanTA State-Caching vs Parallel Computation Modes: A Technical Comparison
BanTA provides two execution strategies for technical analysis indicators: state-caching mode maintains per-series history for incremental O(1) updates during live trading, while parallel computation mode processes entire float64 slices in batch for research workloads.
The banbox/banta repository implements these dual execution paths to optimize for fundamentally different use cases—real-time streaming data versus historical bulk analysis. Understanding the architectural distinction between state-caching (state-aware) and parallel (batch) modes is essential for selecting the correct API surface and achieving optimal performance in your trading applications.
How State-Caching Mode Works
State-caching mode is designed for incremental computation where new market bars arrive one at a time, such as in live trading or event-driven backtesting environments.
Core Mechanism
In this mode, each technical indicator operates on a *Series object that persists internal state across calculation cycles. When a new candle arrives, the library updates the Series incrementally rather than recomputing the entire history.
The caching mechanism lives in core.go, where the Series.Cached() method determines whether the series already covers the current environment time:
func (s *Series) Cached() bool {
return s.Time >= s.Env.TimeStop // cached when the series covers the env's latest bar
}
(see [core.go lines 269-271](https://github.com/banbox/banta/blob/main/core.go#L269-L271))
Indicators that require mutable state—such as ADX, DM, or running-sum calculations—store intermediate values in the Series.More field. When environments are cloned, this state is preserved via Series.DupMore, ensuring continuity across symbol and timeframe boundaries.
API Usage
State-caching indicators are accessed through the main package using state-aware *Series objects:
ma5 := ta.SMA(e.Close, 5) // state-cached SMA
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
In sta_inds.go, the SMA implementation maintains a running sum inside Series.More, allowing O(1) updates per new bar rather than O(N) recalculation (see [sta_inds.go lines 87-93](https://github.com/banbox/banta/blob/main/sta_inds.go#L87-L93)).
How Parallel Computation Mode Works
Parallel computation mode—implemented in the tav sub-package—processes entire input arrays in a single pass without maintaining per-candle state.
Batch Processing Architecture
Unlike the state-caching approach, parallel mode receives plain []float64 slices and computes the full indicator series from scratch. This mode discards all intermediate state between calls, returning a new slice containing the complete result vector.
The tav package duplicates the mathematical logic from the main indicators but operates exclusively on slices. For example, the DV2 indicator in tav/indicators.go iterates over the whole input array once:
func DV2(h, l, c []float64, period, maLen int) []float64 { … } // batch version
(see [tav/indicators.go lines 85-87](https://github.com/banbox/banta/blob/main/tav/indicators.go#L85-L87))
When to Use Parallel Mode
Use this mode for research, bulk-historical analysis, or vectorized operations where you need the full time series output at once. The API surface uses the tav prefix:
ma5 := tav.SMA(closeArr, 5) // slice-based SMA
atr := tav.ATR(highArr, lowArr, closeArr, 14)
xArr := tav.Cross(ma5, ma30)
Performance Characteristics and Trade-offs
State-caching mode minimizes CPU usage for streaming data by reusing historic calculations. Memory overhead is minimal—only the cached series values and the More state field are retained. This makes it ideal for high-frequency updates where only the latest bar changes.
Parallel computation mode incurs the full O(N) computational cost on every call because the entire series is recomputed from raw inputs. However, it avoids object allocation overhead for Series instances and performs well for one-off bulk calculations, behaving similarly to TA-Lib implementations.
Implementation Details and Key Files
The dual-mode architecture spans several critical files in the repository:
types.go— Defines theSeriesstruct (the state holder) andCrossLogcore.go— ImplementsSeries.Cached(),Series.To(), and the environment logic driving state-cachingsta_inds.go— Contains state-aware implementations for indicators like SMA, ADX, and ATRtav/indicators.go— Houses parallel, slice-based implementations of the same mathematical functions
Both modes share identical mathematical cores; the divergence lies strictly in state management—either preserving it via Series.More or discarding it for functional purity.
Practical Code Examples
State-Caching for Live Trading
Use this pattern when processing real-time bar updates:
var envMap = make(map[string]*ta.BarEnv)
func OnBar(symbol, timeframe string, bar *ta.Kline) {
key := fmt.Sprintf("%s_%s", symbol, timeframe)
e := envMap[key]
if e == nil {
e = &ta.BarEnv{TimeFrame: timeframe, BarNum: 1}
envMap[key] = e
}
e.OnBar(bar.Time, bar.Open, bar.High, bar.Low, bar.Close,
bar.Volume, bar.Quote, bar.BuyVolume, bar.TradeNum)
ma5 := ta.SMA(e.Close, 5) // state-cached SMA
atr := ta.ATR(e.High, e.Low, e.Close, 14).Get(0)
// …
}
(see [readme.md lines 69-92](https://github.com/banbox/banta/blob/main/readme.md#L69-L92))
Parallel Mode for Research
Use this pattern for historical vectorized analysis:
highArr := []float64{1.01, 1.01, 1.02, 0.996}
lowArr := []float64{0.99, 1.00, 1.00, 0.98}
closeArr := []float64{1.00, 1.01, 1.00, 0.99}
ma5 := tav.SMA(closeArr, 5) // slice-based SMA
atr := tav.ATR(highArr, lowArr, closeArr, 14)
xArr := tav.Cross(ma5, ma30)
(see [readme.md lines 20-34](https://github.com/banbox/banta/blob/main/readme.md#L20-L34))
Summary
- State-caching mode maintains a per-symbol, per-timeframe
Seriesobject that remembers past results, enabling O(1) incremental updates for live trading scenarios - Parallel computation mode operates on raw
[]float64slices without persistent state, recomputing full vectors from scratch for batch analysis - State-caching uses the main package API (
ta.Indicator()) while parallel mode requires thetavsub-package (tav.Indicator()) - Both implementations share the same mathematical logic but differ in whether they store intermediate state in
Series.More
Frequently Asked Questions
Can I mix state-caching and parallel modes in the same application?
Yes. According to the banbox/banta source code, both modes can coexist in the same codebase. Use state-caching (ta package) for real-time data processing and parallel mode (tav package) for offline research or initial historical data seeding. The mathematical results are identical, though floating-point accumulation may differ slightly due to running-sum versus full-recalculation algorithms.
Why does state-caching use the Series.More field?
The Series.More field in types.go provides generic storage for indicator-specific state that must persist across bar updates. Complex indicators like ADX or Wilder's smoothing require remembering previous values, which More stores as a raw interface. When environments are cloned via Series.DupMore, this state copies forward, ensuring continuity without global variables.
Is parallel mode actually parallelized with goroutines?
Despite the name, "parallel" in BanTA refers to batch vectorization rather than concurrent goroutine execution. The tav functions process entire arrays in single-pass loops without per-candle state, but they do not automatically spawn multiple threads. The parallelism describes the data flow (processing all bars simultaneously) rather than CPU threading.
Which mode should I use for backtesting large historical datasets?
For bulk backtesting where all historical bars are available upfront, parallel computation mode (tav package) is typically preferred. It avoids the memory overhead of maintaining Series objects for every indicator and symbol combination. However, if your backtest processes bars sequentially like live trading, state-caching provides better performance by avoiding redundant recalculation of unchanged history.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →