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

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 or 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 and 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 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

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 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

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 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

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 lines 856-861, which accepts []float64 slices and returns three []float64 slices representing the K, D, and J lines.

Python Implementation

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 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 State-caching series API including KDJ, Stoch, and RSI 697-715
tav/indicators.go Parallel-computation functions including KDJ, SMA, and EMA 856-861
python/ta/index.go Python wrapper for state-caching API 25-30
python/tav/index.go Python wrapper for parallel-computation API 172-179
core.go Core Series and BarEnv infrastructure -
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 lines 697-715 and 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 includes RSI, Stochastic, CCI, and MACD, while the parallel computation package in 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.

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 →