How to Use the MACD Indicator in BanTA Go: Raw Slices vs Series

To calculate MACD in BanTA Go, call tav.MACD() for raw []float64 slices or banta.MACD() for cached *Series objects that integrate with BarEnv environments.

The MACD indicator in BanTA Go is implemented across two architectural layers in the banbox/banta repository. The library provides both low-level mathematical operations in the tav package and high-level cached abstractions in the main banta package for backtesting workflows.

MACD Architecture in BanTA

BanTA separates indicator calculations into distinct layers to support both standalone analysis and integrated trading strategies.

Raw Slice Layer (tav Package)

The foundational implementation lives in main/tav/indicators.go and operates on primitive Go slices. The MACD function returns two []float64 slices representing the MACD line and signal line. This layer performs direct exponential moving average (EMA) calculations without caching or environment state.

Series Layer with Caching (banta Package)

The higher-level API in main/sta_inds.go returns *Series objects that automatically cache results per BarEnv. When you call banta.MACD(), the library generates a deterministic cache key (format: "_macd" + fast*1000 + slow*100 + smooth*10 + initType) and stores the result in the Series object, making subsequent calls O(1).

Raw Slice Implementation

For standalone calculations without environment setup, use the tav package functions directly from main/tav/indicators.go.

The algorithm follows the standard MACD specification:

  1. Calculate fast EMA (typically 12 periods)
  2. Calculate slow EMA (typically 26 periods)
  3. Subtract slow from fast to create the MACD line
  4. Calculate signal line as EMA of MACD line (typically 9 periods)
package main

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

func main() {
    // Simulated close prices
    closes := []float64{101, 102, 103, 104, 105, 106, 107, 108, 109, 110}
    
    // Standard parameters: fast=12, slow=26, smooth=9
    macdLine, signalLine := tav.MACD(closes, 12, 26, 9)
    
    fmt.Printf("MACD: %v\n", macdLine)
    fmt.Printf("Signal: %v\n", signalLine)
}

For custom initialization types (such as MyTT compatibility), use MACDBy with the initType parameter:

// initType 0 = standard, 1 = MyTT/Chinese platform style
macd, signal := tav.MACDBy(closes, 12, 26, 9, 1)

Series-Based Implementation with Caching

When working within a BarEnv (the OHLCV container defined in main/core.go), use the cached Series API from main/sta_inds.go. This approach stores calculated values in the environment's cache using the Series.To method.

package main

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

func main() {
    // Create environment: exchange, market, symbol, timeframe
    env, _ := banta.NewBarEnv("binance", "spot", "BTCUSDT", "1d")
    
    // Feed OHLCV bars (timestamp in milliseconds)
    env.OnBar(1704067200000, 30000, 31000, 29500, 30000, 9e9, 8e9, 1000, 100)
    env.OnBar(1704153600000, 30000, 32000, 29800, 31500, 9.5e9, 8.5e9, 1200, 110)
    
    // Calculate MACD on Close series
    macdSeries, signalSeries := banta.MACD(env.Close, 12, 26, 9)
    
    // Retrieve latest values (index 0 = most recent)
    fmt.Printf("Current MACD: %.4f\n", macdSeries.Get(0))
    fmt.Printf("Current Signal: %.4f\n", signalSeries.Get(0))
    
    // Access historical values
    histMacd := macdSeries.Range(0, 5)
    fmt.Println("Last 5 MACD values:", histMacd)
}

The caching mechanism checks res.Cached() (defined in main/types.go lines 48-57) before recalculating. If uncached, it computes EMABy for fast and slow periods, subtracts them using Series.Sub, then calculates the signal EMA.

Initialization Types: Standard vs MyTT

BanTA supports two EMA initialization methods via the initType parameter:

  • initType = 0: Standard initialization used by international platforms (default)
  • initType = 1: MyTT initialization used by Chinese technical analysis platforms

The difference affects only the first EMA value calculation; subsequent values use identical smoothing formulas. Use MACDBy instead of MACD to specify this parameter:

// MyTT-style calculation
macdSeries, signalSeries := banta.MACDBy(env.Close, 12, 26, 9, 1)

Performance Considerations

The raw slice implementation in tav/indicators.go (lines 172-176 for EMABy, lines 355-383 for MACD) allocates new slices for each calculation with O(n) complexity where n is the data length.

The Series implementation adds O(1) cache lookup overhead but avoids recalculating indicators on historical bars when processing real-time data streams. The cache key incorporates all parameters (fast, slow, smooth, initType) to prevent collision between different MACD configurations on the same Series.

Summary

  • Raw slices: Use tav.MACD(data, fast, slow, smooth) in main/tav/indicators.go for standalone calculations without environment setup.
  • Cached Series: Use banta.MACD(series, fast, slow, smooth) in main/sta_inds.go when working with BarEnv objects to enable automatic result caching.
  • Custom initialization: Append By to function names (MACDBy) and pass initType (0 or 1) for MyTT compatibility.
  • Cache keys: Generated as "_macd" + fast*1000 + slow*100 + smooth*10 + initType in the Series implementation.
  • Result access: Call Series.Get(0) for the latest value or Series.Range(start, end) for historical slices.

Frequently Asked Questions

What is the difference between tav.MACD and banta.MACD?

tav.MACD operates on raw []float64 slices and returns two float64 slices, suitable for data analysis outside trading environments. banta.MACD operates on *Series objects from a BarEnv, caches results using a deterministic key, and returns *Series pointers that maintain state across bar updates.

How does the caching mechanism work in BanTA MACD?

The Series implementation generates a cache key using the formula "_macd" + fast*1000 + slow*100 + smooth*10 + initType. It calls obj.To(key) to retrieve or create a cache slot, checks res.Cached() to avoid redundant calculation, and stores both MACD and signal values in the result object.

When should I use initType 1 instead of 0?

Use initType = 1 when replicating indicators from MyTT or Chinese trading platforms that use alternative EMA initialization. Use initType = 0 (default) for compatibility with international standards like TA-Lib or pandas-ta. The parameter affects only the first EMA value in the sequence.

How do I access the MACD histogram in BanTA?

BanTA returns the MACD line and signal line separately. Calculate the histogram by subtracting the signal from the MACD: histogram := macdSeries.Sub(signalSeries) or manually subtract the slices when using the raw API.

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 →