# How to Implement SMA Indicator Using BanTA: Complete Go and Python Guide

> Learn to implement the SMA indicator using BanTA in Go and Python. This guide shows efficient raw slice calculation and cached Series object methods for better performance.

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

---

**To implement an SMA indicator using BanTA, call the `SMA` function from the `tav` package for raw `[]float64` slices, or use the `SMA` method on `*Series` objects for cached, chainable calculations that automatically handle window sums and division by the period.**

BanTA is a high-performance technical analysis library written in Go with Python bindings, designed for financial time-series processing. When you implement SMA indicator using BanTA, you gain access to two distinct architectural layers: a low-level raw slice interface for simple calculations and a high-level **Series API** with built-in caching for complex indicator chaining.

## Understanding BanTA's SMA Architecture

BanTA provides SMA calculations through two complementary APIs that share the same underlying mathematics but differ in memory management and ease of use.

The **Raw Slice API** operates directly on `[]float64` slices, computing window sums with the `Sum` function and dividing by the period to produce the average. This approach is stateless and ideal for one-off calculations.

The **Series API** wraps data in a `*Series` object that maintains a cache of computed values. When you call `SMA` on a Series, BanTA creates a derived series (internally tagged as `_sma`), calculates the rolling sum, and reuses cached results on subsequent accesses. This design minimizes redundant computation when chaining multiple indicators.

## Implementing SMA with the Raw Slice API

### Source Code Location and Logic

The raw slice implementation resides in [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) at lines 61–74. This function accepts a slice of float64 values and an integer period, delegates the window calculation to the `Sum` function, and divides each valid sum by `float64(period)` to produce the simple moving average.

The implementation handles **NaN values** gracefully: if any value within a window is NaN, the resulting SMA value for that position becomes NaN, and calculation resumes only after the NaN exits the window.

### Complete Go Example

```go
package main

import (
	"fmt"
	"github.com/banbox/banta/main/tav"
)

func main() {
	// Example closing prices
	close := []float64{10, 11, 12, 13, 14, 15, 16}
	period := 3

	// Compute SMA – returns a slice of the same length
	sma := tav.SMA(close, period)

	fmt.Println("SMA:", sma)
	// Output: SMA: [NaN NaN 11 12 13 14 15]
}

```

*Explanation:* The first `period-1` entries return `NaN` because the sliding window is not yet full. Once the window contains three values, the function calculates the average (e.g., `(10+11+12)/3 = 11`).

## Implementing SMA with the Series API

### Cached Calculation Architecture

The high-level implementation is located in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) at lines 87–101. This version operates on `*Series` objects defined in [`core.go`](https://github.com/banbox/banta/blob/main/core.go) and [`types.go`](https://github.com/banbox/banta/blob/main/types.go). When you invoke `SMA(series, period)`, BanTA:

1. Creates a new derived series tagged `_sma` that references the parent series
2. Computes the rolling sum using `Sum(series, period)`
3. Divides each valid sum by the period to produce the average
4. Caches the result so subsequent indicator chains reuse the computed values without recalculation

This caching mechanism is particularly efficient when building complex strategies that reference the same SMA multiple times or chain it with other indicators like RSI or Bollinger Bands.

### Complete Go Example

```go
package main

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

func main() {
	// Build a Series from raw data
	close := NewSeries([]float64{10, 11, 12, 13, 14, 15, 16})
	period := 3

	// Compute SMA – result is a cached Series
	sma := SMA(close, period)

	// Print the SMA values
	for i := 0; i < sma.Len(); i++ {
		fmt.Printf("index %d: %.2f\n", i, sma.Get(i))
	}
}

```

*Explanation:* The `NewSeries` function wraps the raw slice in a Series object. The `SMA` function returns a new Series that automatically handles the rolling window logic and caches results. Accessing values via `Get(i)` retrieves the computed SMA or `NaN` for incomplete windows.

## Using SMA in Python

### Python Bindings Structure

BanTA exposes its Go implementation to Python using **gopy**, which compiles the Go functions into a shared library. The Python bindings are located in [`python/tav/index.go`](https://github.com/banbox/banta/blob/main/python/tav/index.go) for the raw slice API and [`python/ta/index.go`](https://github.com/banbox/banta/blob/main/python/ta/index.go) for the Series API.

When you call `banta.ta.SMA(series, period)` from Python, the wrapper forwards the arguments to the Go library, which performs the calculation and returns a Python-accessible Series object containing the SMA values.

### Python Implementation Example

```python
import banta.ta as ta
import numpy as np

# Example closing prices (numpy array or Python list)

close = np.array([10, 11, 12, 13, 14, 15, 16], dtype=float)
period = 3

# Compute SMA – returns a banta Series object

sma = ta.SMA(close, period)

# Convert to a plain list for inspection

print("SMA:", list(sma))

# Output: SMA: [nan, nan, 11.0, 12.0, 13.0, 14.0, 15.0]

```

*Explanation:* The Python API accepts numpy arrays or Python lists and automatically converts them to the internal Series format. The returned object behaves like a list, where the first `period-1` values are `nan` (Python's float nan) indicating insufficient data for the calculation.

## Handling Edge Cases and NaN Values

BanTA's SMA implementation includes robust handling of **NaN values** and incomplete windows. When calculating the simple moving average, if any value within the current window is NaN, the resulting SMA value for that position becomes NaN. The calculation automatically resumes once the NaN value exits the sliding window.

This behavior ensures that missing data does not contaminate subsequent valid calculations. For the Series API, the cache respects these NaN boundaries, ensuring that derived indicators built on top of the SMA receive the correct NaN signals when underlying data is incomplete or missing.

## Key Source Files Reference

The SMA implementation spans several files across the BanTA repository:

- **[`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go)** (lines 61–74): Contains the low-level `SMA` function for `[]float64` slices, implementing the core sum-and-divide logic.
- **[`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go)** (lines 87–101): Implements the high-level `SMA` method for `*Series` objects with caching support.
- **[`python/tav/index.go`](https://github.com/banbox/banta/blob/main/python/tav/index.go)** (lines 25–27): Python binding that exposes the raw slice SMA function to Python via gopy.
- **[`python/ta/index.go`](https://github.com/banbox/banta/blob/main/python/ta/index.go)** (lines 35–36): Python binding for the Series-based SMA calculation.
- **[`core.go`](https://github.com/banbox/banta/blob/main/core.go)** and **[`types.go`](https://github.com/banbox/banta/blob/main/types.go)**: Define the `Series` struct and caching mechanisms used by the high-level API.

## Summary

- BanTA provides two APIs to implement SMA indicator using BanTA: the **Raw Slice API** for simple `[]float64` calculations and the **Series API** for cached, chainable technical analysis.
- The raw implementation in [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) computes window sums and divides by the period, returning `NaN` for incomplete windows.
- The Series implementation in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) creates a derived `_sma` series that caches results, making repeated calculations and indicator chaining computationally efficient.
- Python bindings in [`python/tav/index.go`](https://github.com/banbox/banta/blob/main/python/tav/index.go) and [`python/ta/index.go`](https://github.com/banbox/banta/blob/main/python/ta/index.go) expose both APIs via gopy, accepting numpy arrays and returning Series objects.
- Both implementations handle `NaN` values gracefully, ensuring calculation integrity when data is missing.

## Frequently Asked Questions

### What is the difference between the Raw Slice API and Series API in BanTA?

The **Raw Slice API** operates directly on `[]float64` slices and performs stateless calculations, making it ideal for simple, one-off SMA computations. The **Series API** wraps data in a `*Series` object that maintains a cache of computed values, allowing efficient reuse when chaining multiple indicators or referencing the same SMA calculation repeatedly in your strategy.

### How does BanTA handle incomplete windows when calculating SMA?

BanTA returns **NaN** (Not a Number) for any position where the sliding window does not contain enough data points to satisfy the specified period. For a period of 3, the first two values in the result will be NaN, and valid SMA calculations begin at the third position. This behavior applies consistently across both the Go implementations and Python bindings.

### Can I use BanTA SMA with Python numpy arrays?

Yes, the Python bindings accept **numpy arrays** or standard Python lists as input. When you call `banta.ta.SMA(close, period)`, the library automatically converts the input into the internal Series format, computes the SMA using the compiled Go code, and returns a Series object that behaves like a list and can be converted back to numpy arrays if needed.

### Where is the SMA calculation cached in the Series API?

The cache is stored within the **derived Series object** created when you call `SMA` on a Series. Internally, BanTA tags this derived series as `_sma` and stores the computed rolling sum and division results. Subsequent accesses to the same SMA series object retrieve values from this cache rather than recalculating the window sum, significantly improving performance when the same indicator is referenced multiple times.