TAlib

Technical analysis library for Elixir.

Indicators

All 133 TA-Lib functions are covered — see PARITY.md for the function-by-function map. The Hilbert-transform family and MAMA are faithful translations of the TA-Lib C reference, cross-verified series for series.

See docs/BENCHMARKS.md for a performance and numerical-agreement comparison against TA-Lib.

Bar-based indicators take maps such as %{open: o, high: h, low: l, close: c, volume: v} with the keys they need — extra keys are ignored.

Installation

The package can be installed by adding talib to your list of dependencies in mix.exs:

def deps do
  [
    {:talib, "~> 0.7"}
  ]
end

Conventions

Examples

iex> TAlib.Indicators.MA.sma([1330.95, 1334.65, 1340.0], 3)
1335.2

iex> TAlib.Indicators.MA.sma_list([1, 2, 3, 4, 5], 3)
[nil, nil, 2.0, 3.0, 4.0]

iex> TAlib.Indicators.RSI.rsi([6, 1, 4, 2, 5, 3], 3) |> Float.round(4)
42.8571

iex> TAlib.Indicators.MACD.macd_all(prices)
%{macd: 20.36, signal: 12.28, histogram: 8.08}

More examples can be found in the module documentation (Hexdocs) and in the test suite.

Streaming

Batch functions walk their whole input — the wrong shape for a live feed. Fourteen indicator families (the MAs, StdDev, Bollinger Bands, ATR, RSI, MACD, Stochastic fast and slow, Williams %R, Donchian, Aroon) also come as O(1) streaming state: warm up once from history, then advance one bar at a time.

# Warm up from history; values equals the batch series exactly
{state, values} = TAlib.Indicators.MA.sma_init(history, 20)

# One new price, one constant-time step — ~0.1 µs, vs recomputing
# the whole series per tick
{state, value} = TAlib.Indicators.MA.sma_next(state, price)

In a GenServer, keep the state in the process and advance it in handle_info/2 as ticks arrive; the states are small immutable structs, safe to keep and pass around. Because init replays the batch arithmetic operation for operation, the streamed series continues the batch series bit for bit — the same values the audit checked against the C library (see docs/BENCHMARKS.md, "Streaming: O(1) per tick").

Reflections

This library slept from 2019 until late 2026, then went from a handful of indicators to full TA-Lib parity in a dozen releases. A few things the journey taught, in case they're useful:

The deliberate trade-offs: nil instead of 0 for missing data (0 is a legitimate reading), chronological inputs oldest-first, and no unstable-period machinery — outputs start where the math starts.