# How to Calculate KDJ Momentum Indicators Using BanTA: Go and Python Guide

> Learn to calculate KDJ momentum indicators with BanTA in Go and Python. Explore state-caching for live bots and parallel computation for backtesting.

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

---

**BanTA computes KDJ momentum indicators through two execution models—state-caching for live trading bots and parallel computation for bulk backtesting—using the `KDJ()` function in either [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) or [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) depending on your architecture.**

BanTA is a high-performance technical analysis library written in Go with zero external dependencies. This guide explains how to calculate the **KDJ momentum indicator** using BanTA's dual execution models, referencing the actual implementation in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) and [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go).

## Understanding BanTA's Dual Execution Models

BanTA provides **two distinct execution models** for technical analysis calculations:

**State-caching (event-driven) mode** uses a `BarEnv` object to store historic series for a symbol and timeframe. This mirrors TradingView's Pine Script behavior and updates cached values on each new candle, making it ideal for **live-trading bots** requiring incremental updates.

**Parallel-computation mode** exposes pure functions that accept plain `[]float64` slices and return full-length result arrays. This TA-Lib-style approach is optimized for **bulk backtesting** and research workloads where you process entire historical datasets at once.

## How KDJ Works in BanTA

The **KDJ indicator** is a momentum oscillator that extends the Stochastic oscillator with an additional J line. BanTA implements the standard calculation pipeline:

1. **RSV (Raw Stochastic Value)**: `RSV = 100 × (close − lowestLow) / (highestHigh − lowestLow)` over the lookback period. This uses the same logic as BanTA's `Stoch` function in [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) at line 838.

2. **K line**: RMA (Running Moving Average) or SMA of RSV over `sm1` periods, seeded with 50.

3. **D line**: RMA or SMA of K over `sm2` periods, also seeded with 50.

The choice between **RMA** and **SMA** smoothing is controlled by the `maBy` argument, which defaults to `"rma"` for exponential-like smoothing behavior.

## Implementation Examples

### State-Caching Mode for Live Trading

Use this approach when processing real-time market data streams where bars arrive sequentially.

**Go Implementation**

```go
package main

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

var envMap = make(map[string]*ta.BarEnv)

func OnBar(symbol, timeframe string, k *ta.Kline) {
	key := fmt.Sprintf("%s_%s", symbol, timeframe)
	env, ok := envMap[key]
	if !ok {
		env = &ta.BarEnv{TimeFrame: timeframe, BarNum: 1}
		envMap[key] = env
	}
	// feed the new candle
	env.OnBar(k.Time, k.Open, k.High, k.Low, k.Close, k.Volume, k.Quote, k.BuyVolume, k.TradeNum)

	// KDJ (period=9, sm1=3, sm2=3)
	kLine, dLine, _ := ta.KDJ(env.High, env.Low, env.Close, 9, 3, 3)

	fmt.Printf("K=%0.2f D=%0.2f (latest)\n", kLine.Get(0), dLine.Get(0))
}

```

This implementation references the `KDJ` function in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) lines 697-715, which accepts `*Series` pointers for high, low, and close prices, plus integer parameters for period, sm1, and sm2 smoothing windows.

**Python Implementation**

```python
from bbta import ta

# 1️⃣ create a BarEnv (one per symbol/timeframe)

env = ta.BarEnv(TimeFrame="5m")

# simulate incoming candles (timestamp, o, h, l, c, v)

candles = [
    (1672531200000, 100, 102, 99, 101, 1200),
    (1672531260000, 101, 103, 100, 102, 1300),
    # … more candles …

]

for ts, o, h, l, c, v in candles:
    env.OnBar(ts, o, h, l, c, v, 0, 0, 0)

    # KDJ with default RMA smoothing

    k, d, _ = ta.KDJ(env.High, env.Low, env.Close, 9, 3, 3)

    print(f"K={k.Get(0):.2f}  D={d.Get(0):.2f}")

```

The Python binding wraps the Go implementation in [`python/ta/index.go`](https://github.com/banbox/banta/blob/main/python/ta/index.go) lines 25-30, exposing the same state-caching API to Python users.

### Parallel Computation Mode for Backtesting

Use this approach when processing complete historical datasets where you need full arrays returned immediately.

**Go Implementation**

```go
package main

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

func main() {
	high := []float64{1.02, 1.04, 1.03, 1.05, 1.06, 1.07, 1.05}
	low  := []float64{0.98, 0.99, 1.00, 1.01, 1.02, 1.00, 1.01}
	close:= []float64{1.00, 1.02, 1.01, 1.04, 1.05, 1.03, 1.04}

	k, d, _ := tav.KDJ(high, low, close, 9, 3, 3)

	fmt.Printf("K series: %v\nD series: %v\n", k, d)
}

```

This calls the `KDJ` function in [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) lines 856-861, which accepts `[]float64` slices and returns three `[]float64` slices representing the K, D, and J lines.

**Python Implementation**

```python
from bbta import tav

high  = [1.02, 1.04, 1.03, 1.05, 1.06, 1.07, 1.05]
low   = [0.98, 0.99, 1.00, 1.01, 1.02, 1.00, 1.01]
close = [1.00, 1.02, 1.01, 1.04, 1.05, 1.03, 1.04]

k, d, _ = tav.KDJ(high, low, close, 9, 3, 3)

print("K:", k)
print("D:", d)

```

The Python wrapper in [`python/tav/index.go`](https://github.com/banbox/banta/blob/main/python/tav/index.go) lines 172-179 exposes the parallel computation API to Python users, accepting Python lists or NumPy arrays and returning computed results.

## Key Source Files and API Reference

| File | Purpose | Lines |
|------|---------|-------|
| [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) | State-caching series API including `KDJ`, `Stoch`, and `RSI` | 697-715 |
| [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) | Parallel-computation functions including `KDJ`, `SMA`, and `EMA` | 856-861 |
| [`python/ta/index.go`](https://github.com/banbox/banta/blob/main/python/ta/index.go) | Python wrapper for state-caching API | 25-30 |
| [`python/tav/index.go`](https://github.com/banbox/banta/blob/main/python/tav/index.go) | Python wrapper for parallel-computation API | 172-179 |
| [`core.go`](https://github.com/banbox/banta/blob/main/core.go) | Core `Series` and `BarEnv` infrastructure | - |
| [`chanlun.go`](https://github.com/banbox/banta/blob/main/chanlun.go) | Additional caching infrastructure | - |

The `KDJ` function signature in state-caching mode accepts `*Series` pointers for price data and returns three `*Series` objects (K, D, J), while the parallel version accepts and returns `[]float64` slices.

## Summary

- **BanTA provides two execution models** for KDJ calculations: state-caching via `BarEnv` for live trading, and parallel computation via the `tav` package for backtesting.
- **The KDJ implementation** follows the standard formula: RSV calculation followed by smoothed K and D lines using RMA (default) or SMA, with source code located in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) lines 697-715 and [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) lines 856-861.
- **Go developers** import `github.com/banbox/banta` for state-caching or `github.com/banbox/banta/tav` for parallel mode.
- **Python users** access the same functionality through `bbta.ta.KDJ()` for event-driven workflows and `bbta.tav.KDJ()` for batch processing.

## Frequently Asked Questions

### What parameters does BanTA's KDJ function accept?

The `KDJ` function accepts five required parameters: `high`, `low`, and `close` price series (either `*Series` objects for state-caching or `[]float64` slices for parallel mode), followed by three integers: `period` (the RSV lookback window, typically 9), `sm1` (the K smoothing period, typically 3), and `sm2` (the D smoothing period, typically 3). The state-caching version returns three `*Series` pointers (K, D, J), while the parallel version returns three `[]float64` slices.

### How does BanTA's KDJ calculation differ from standard Stochastic?

While BanTA's **RSV calculation** uses the same formula as a standard Stochastic oscillator (`RSV = 100 × (close − lowestLow) / (highestHigh − lowestLow)`), the KDJ indicator adds the **J line** calculated as `3K − 2D`. Additionally, BanTA allows you to choose between **RMA** (Running Moving Average, default) and **SMA** smoothing for the K and D lines via the `maBy` argument, with both methods seeding initial values at 50 to ensure consistent behavior across series.

### Which execution mode should I use for live trading versus backtesting?

For **live trading bots** processing streaming market data, use the **state-caching mode** via `BarEnv` (Go) or `bbta.ta` (Python). This maintains running state across bars and computes only incremental updates, mirroring TradingView's Pine Script behavior and minimizing CPU overhead per tick. For **backtesting and research** requiring batch processing of complete historical datasets, use the **parallel-computation mode** via the `tav` package (Go) or `bbta.tav` (Python), which processes entire arrays without maintaining state and is optimized for vectorized operations across large datasets.

### Does BanTA support other momentum indicators besides KDJ?

Yes, BanTA implements numerous momentum indicators in both execution modes. The state-caching API in [`sta_inds.go`](https://github.com/banbox/banta/blob/main/sta_inds.go) includes **RSI**, **Stochastic**, **CCI**, and **MACD**, while the parallel computation package in [`tav/indicators.go`](https://github.com/banbox/banta/blob/main/tav/indicators.go) provides vectorized versions of **SMA**, **EMA**, **RSI**, and **Stochastic** calculations. All indicators follow the same dual-mode architecture, allowing seamless switching between event-driven live trading and batch backtesting workflows without changing your underlying calculation logic.