# Understanding the Series Struct in BanTA: Core Architecture Explained

> Discover the Series struct in BanTA's core architecture. This key data container manages OHLCV data and derived indicators for efficient technical analysis.

- Repository: [banbox/banta](https://github.com/banbox/banta)
- Tags: internals
- Published: 2026-02-26

---

**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`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/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:

```go
// 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:

```go
// 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:

```go
// 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:

```go
// 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`](https://github.com/banbox/banta/blob/main/main/types.go)** – Contains the `Series` struct definition (lines 48-58) including all fields (`Data`, `Env`, `Cols`, `Subs`, `XLogs`, `More`, `DupMore`).

- **[`main/core.go`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/main/chanlun.go)** and **[`main/tav/indicators.go`](https://github.com/banbox/banta/blob/main/main/tav/indicators.go)** – Provide higher-level indicator implementations that leverage the `Series` APIs for complex technical analysis.

- **[`main/core_test.go`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/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`](https://github.com/banbox/banta/blob/main/main/core.go)**. Additional indicator implementations that leverage the Series API can be found in [`main/chanlun.go`](https://github.com/banbox/banta/blob/main/main/chanlun.go) and [`main/tav/indicators.go`](https://github.com/banbox/banta/blob/main/main/tav/indicators.go).