Understanding the Series Struct in BanTA: Core Architecture Explained

The Series struct is the fundamental data container that powers every technical analysis calculation in BanTA, serving as a self-contained, cache-aware time-series object that handles everything from raw OHLCV data to complex derived indicators.

The Series struct sits at the heart of the BanTA library (banbox/banta), providing a unified interface for storing, manipulating, and analyzing financial time-series data. Whether you are working with raw price bars or calculating complex multi-component indicators, the Series struct in BanTA provides the architectural foundation that makes the library both performant and extensible.

What Is the Series Struct in BanTA?

At its core, the Series struct is a Go struct defined in main/types.go (lines 48-58) that encapsulates a time-ordered sequence of floating-point values along with the metadata and relationships needed for technical analysis. Unlike a simple slice of floats, the Series struct maintains links to its environment, caches derived calculations, and tracks cross-over events with other series.

Key Fields and Their Purposes

The Series struct contains several specialized fields that enable its sophisticated functionality:

  • Data []float64 – Stores the raw numeric values in time order (oldest to newest), serving as the primary data buffer for prices, volumes, or indicator values.

  • Env *BarEnv – Provides access to the bar environment, including current timestamps, cache limits, and sibling series, enabling automatic timestamp updates and data trimming.

  • Cols []*Series – Supports hierarchical column structures, allowing a series to contain child columns for multi-component indicators like Bollinger Bands.

  • Subs map[string]map[int]*Series – Implements the derivation caching system, storing results of operations (like _add, _sub) keyed by operation name and parameter values.

  • XLogs map[int]*CrossLog – Tracks cross-over events with other series or constants, enabling O(1) cross detection via the Cross() method.

  • More interface{} and DupMore func(interface{}) interface{} – Allow attachment of arbitrary auxiliary data (such as indicator parameters) with proper deep-copy semantics when series are duplicated.

How the Series Struct Powers BanTA's Technical Analysis

The architecture of the Series struct enables several critical capabilities that make BanTA efficient and developer-friendly. Each aspect of the design addresses specific challenges in financial data processing.

Data Storage and Time-Series Management

The Data []float64 field provides the foundational storage mechanism. In main/core.go (lines 31-66), the Append method manages data insertion, automatically handling capacity management and data retention based on the BarEnv configuration. This ensures that memory usage remains bounded while maintaining sufficient history for indicator calculations.

Environment Integration with BarEnv

The Env *BarEnv field creates a bidirectional relationship between data and context. As implemented in main/core.go (lines 100-108) via BarEnv.NewSeries, every series is bound to its environment at creation. This linkage enables the series to access shared timestamps, respect global cache limits, and coordinate with sibling series (such as Open, High, Low, Close, Volume) within the same bar environment.

Hierarchical Column Support

Complex indicators often produce multiple output values. The Cols []*Series field supports this through a parent-child relationship. For example, Bollinger Bands consist of upper, middle, and lower bands. Rather than managing three separate series manually, a parent series can hold these as Cols, providing organized access to multi-component indicators while maintaining the same Series interface for each component.

Intelligent Caching and Derivation

Performance optimization in BanTA relies heavily on the Subs map and the Cached() method. When you perform operations like Add(), Sub(), Mul(), or Div() (implemented in main/core.go lines 39-48), the result is stored in Subs keyed by the operation name (e.g., _add) and the operand value. The Cached() method checks if a derived series already exists for the current bar, preventing redundant calculations and enabling efficient chaining of complex indicators.

Cross-Event Detection

Technical analysis frequently requires detecting when one series crosses above or below another. The XLogs map[int]*CrossLog field stores historical cross events, while the Cross() method (found in main/core.go lines 44-95) provides O(1) access to cross information. This eliminates the need to scan entire series histories when checking for recent crossovers, significantly improving performance for real-time trading systems.

Working with the Series Struct: Practical Examples

The following examples demonstrate how to leverage the Series struct in real-world technical analysis scenarios using the BanTA library.

Creating a Bar Environment and Feeding Data

To begin working with series, you first create a BarEnv and populate it with market data:

// Initialize a BarEnv for a specific symbol and timeframe
env, _ := banta.NewBarEnv("binance", "spot", "BTC/USDT", "1m")

// Feed a new bar (timestamp in milliseconds, followed by OHLCV data)
_ = env.OnBar(1709008800000, 50000, 50500, 49800, 50300, 1200, 60000, 800, 300)

Each call to OnBar internally updates the Series objects (Open, High, Low, Close, Volume) stored within the environment, automatically handling timestamp synchronization and data appending.

Computing Simple Moving Averages

You can perform calculations directly on series using the built-in methods:

// Retrieve the Close series and compute a 20-period simple moving average
close := env.Close
sma := close.Back(20).Mean()   // Returns a derived Series
fmt.Println("20-bar SMA:", sma.Get(0))

The Back(20) method returns a cached sub-series containing the last 20 values, while subsequent arithmetic operations leverage the Subs caching mechanism to avoid redundant calculations.

Building Complex Indicators: Bollinger Bands Example

The Series struct's support for hierarchical columns and derivation caching enables complex multi-component indicators:

// Calculate Bollinger Bands (20-period SMA ± 2 standard deviations)
sma := env.Close.Back(20).Mean()
std := env.Close.Back(20).StdDev()

upper := sma.Add(std.Mul(2))
lower := sma.Sub(std.Mul(2))

fmt.Println("Upper band:", upper.Get(0))
fmt.Println("Lower band:", lower.Get(0))

Each arithmetic operation (Add, Sub, Mul) produces a derived Series cached in the Subs map. If these calculations are repeated within the same bar, BanTA retrieves the cached results instead of recomputing them.

Detecting Crossover Events

The cross-event tracking system enables efficient signal generation:

// Detect bullish crossover (fast MA crosses above slow MA)
fastMA := env.Close.Back(10).Mean()
slowMA := env.Close.Back(30).Mean()

if fastMA.Cross(slowMA) > 0 {
    fmt.Println("Bullish crossover detected")
}

The Cross() method consults the XLogs map to determine crossover status in constant time, avoiding the O(n) cost of scanning historical data.

Source Code Architecture and Key Files

The Series struct implementation spans several key files in the BanTA repository:

  • main/types.go – Contains the Series struct definition (lines 48-58) including all fields (Data, Env, Cols, Subs, XLogs, More, DupMore).

  • main/core.go – Implements the core Series functionality including NewSeries (lines 100-108), Append (lines 31-66), Get/Range (lines 73-98), arithmetic operations (lines 39-48), and Cross detection (lines 44-95).

  • main/chanlun.go and main/tav/indicators.go – Provide higher-level indicator implementations that leverage the Series APIs for complex technical analysis.

  • main/core_test.go – Contains unit tests verifying series behavior including append operations, range queries, caching mechanisms, and cross detection.

Summary

  • The Series struct in BanTA serves as the universal data container for all time-series operations, handling raw OHLCV data and derived indicators through a unified interface.

  • Self-contained architecture allows each Series to manage its own data (Data []float64), environment links (Env *BarEnv), and hierarchical relationships (Cols []*Series).

  • Intelligent caching via the Subs map and Cached() method eliminates redundant calculations by storing derived series results (arithmetic operations, indicators) for reuse within the same bar.

  • O(1) cross detection through the XLogs map enables efficient signal generation without scanning historical data, critical for real-time trading systems.

  • Extensible design via More interface{} and DupMore supports custom metadata and deep-copy semantics, allowing future indicator development without breaking existing APIs.

Frequently Asked Questions

What makes the Series struct different from a simple slice of floats?

Unlike a basic []float64, the Series struct in BanTA encapsulates not just raw values but also environmental context, derivation history, and cross-event tracking. It maintains links to the BarEnv for timestamp synchronization, caches derived calculations in the Subs map to prevent redundant computation, and tracks crossover events in XLogs for O(1) signal detection. This transforms a simple data container into a self-contained technical analysis engine.

How does the Series struct handle performance optimization?

Performance optimization relies heavily on the Subs map and Cached() method. When you perform operations like Add(), Sub(), or Mul(), BanTA stores the result in Subs keyed by the operation name and parameters. If the same calculation is requested again within the current bar, Cached() retrieves the existing result instead of recomputing it. This caching strategy is crucial for complex indicator chains where intermediate values are reused multiple times.

Can the Series struct support custom indicator implementations?

Yes, the Series struct is designed for extensibility through the More interface{} field and the DupMore function pointer. Developers can attach arbitrary metadata—such as indicator parameters, configuration settings, or auxiliary data—to any series. When series are copied or derived, the DupMore function ensures deep-copy semantics for this metadata. This flexibility allows custom indicators to integrate seamlessly with BanTA's core architecture without requiring modifications to the base struct.

Where is the Series struct defined in the BanTA repository?

The Series struct is defined in main/types.go at lines 48-58, where all core fields—including Data, Env, Cols, Subs, XLogs, More, and DupMore—are declared. The implementation of series methods (such as Append, Get, Range, arithmetic operations, and Cross) resides in main/core.go. Additional indicator implementations that leverage the Series API can be found in main/chanlun.go and main/tav/indicators.go.

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 →