Skip to main content

A fast Technical Analysis library — TA-Lib alternative powered by Rust and PyO3

Project description

⚡ ferro-ta

The Python Technical Analysis Library That Beats TA-Lib — Everywhere

Powered by Rust. Driven by O(n) algorithms. Designed for the speed that modern quantitative trading demands.

Binder Open in Colab Documentation


"Same API as TA-Lib. 3–5× faster. No C compiler needed. Drop it in today."

ferro-ta is a Rust-powered, PyO3-compiled technical analysis library that replaces TA-Lib with a pure-Rust core that runs 3× to 5× faster on every major indicator. It runs as a pre-compiled Python wheel — no C toolchain, no system dependencies, no compilation headaches.


🚀 Why ferro-ta?

TA-Lib ferro-ta
Speed C extension, O(n×period) for STOCH/etc. Rust + O(n) algorithms for most indicators
Installation Requires C compiler + system libs pip install ferro-ta — zero deps
Platforms Linux-only on many CI systems Windows / macOS (Intel + M-series) / Linux
API talib.SMA(close, 20) ferro_ta.SMA(close, 20) — identical
Extra indicators VWAP, SUPERTREND, ICHIMOKU, DONCHIAN, and 10 more
Streaming API Bar-by-bar stateful classes
GPU acceleration Optional PyTorch backend (CUDA / MPS)
WebAssembly Node.js / Browser via WASM
Type stubs Full .pyi + py.typed (PEP 561)

⚡ Performance vs TA-Lib

ferro-ta is optimized for high throughput and often competitive with TA-Lib, thanks to:

  • O(n) sliding max/min (monotonic deque) for STOCH — was O(n×period) in TA-Lib
  • Fused TR loop for ATR — no intermediate allocation, single pass
  • Branchless gain/loss for RSI — diff.max(0.0) instead of if/else
  • O(n) rolling operators for SMA/WMA/BBANDS — sliding window accumulators
  • Fused fast+slow EMA loop for MACD — single pass for both EMAs
  • Zero-copy NumPy bridging — input arrays read directly from buffer without copying

🏆 Reproducible benchmark workflow

We publish benchmark methodology and generated tables in benchmarks/README.md.

  • Cross-library speed suite (62 indicators × available libraries): benchmarks/test_speed.py
  • Head-to-head TA-Lib comparison: benchmarks/bench_vs_talib.py
  • Table generation from results.json: benchmarks/benchmark_table.py
# Reproduce these numbers yourself
pip install ferro-ta ta-lib
python benchmarks/bench_vs_talib.py --sizes 10000 100000 --json benchmark_vs_talib.json
# or with uv:
uv run python benchmarks/bench_vs_talib.py --sizes 10000 100000 --json benchmark_vs_talib.json
uv run python benchmarks/check_vs_talib_regression.py --input benchmark_vs_talib.json

# full cross-library speed suite (100k bars):
uv run pytest benchmarks/test_speed.py --benchmark-only --benchmark-json=benchmarks/results.json -v
# generate markdown table from results:
uv run python benchmarks/benchmark_table.py

🎯 Features

  • No C-compiler required — pre-compiled wheels for Windows, macOS (Intel & Apple Silicon), and Linux
  • Drop-in API compatible with TA-Lib (SMA, EMA, RSI, MACD, BBANDS, and 155+ more)
  • Extended Indicators beyond TA-Lib: VWAP, SUPERTREND, ICHIMOKU, DONCHIAN, PIVOT_POINTS, KELTNER_CHANNELS, HULL_MA, CHANDELIER_EXIT, VWMA, CHOPPINESS_INDEX
  • Streaming / Live-Trading API — bar-by-bar stateful classes (StreamingSMA, StreamingRSI, etc.)
  • NumPy integration — accepts and returns NumPy arrays; reads input buffers without copying data
  • Pandas integration — transparently accepts pandas.Series / DataFrame and returns Series with original index preserved
  • Polars integration — transparently accepts polars.Series and returns polars.Series; install with pip install "ferro-ta[polars]"
  • Indicator pipeline — compose multiple indicators into a reusable pipeline (ferro_ta.pipeline.Pipeline)
  • Configuration defaults — set global parameter defaults, per-indicator overrides, and temporary scopes (ferro_ta.config)
  • Optional GPU backend — pass a PyTorch tensor to ferro_ta.gpu.sma/ema/rsi and get a tensor back (CUDA or MPS); install with pip install "ferro-ta[gpu]"
  • Type stubs (.pyi) + py.typed (PEP 561) for IDE auto-completion and mypy/pyright support
  • WebAssembly binding — use ferro-ta in Node.js or the browser via wasm/ (SMA, EMA, BBANDS, RSI, ATR, OBV, MACD, MOM, STOCHF)
  • Backtesting utilities — minimal vectorized backtester (ferro_ta.backtest) with RSI, SMA crossover, and MACD crossover strategies; optional commission and slippage
  • Plugin registry — register and run custom or built-in indicators by name (ferro_ta.registry)
  • Error model — custom exception hierarchy (FerroTAError, FerroTAValueError, FerroTAInputError) with input validation helpers
  • Sphinx documentation in docs/ and Jupyter notebook examples in examples/
  • OHLCV resampling — time-based and volume-bar resampling, multi-timeframe API (ferro_ta.resampling)
  • Tick aggregation — tick/volume/time bar builders from raw trades (ferro_ta.aggregation)
  • Strategy DSL — expression-based strategy evaluation (ferro_ta.dsl)
  • Signal composition — weighted/rank composite scores and screening (ferro_ta.signals)
  • Portfolio analytics — correlation, volatility, beta, drawdown (ferro_ta.portfolio)
  • Cross-asset analytics — relative strength, spread, Z-score, rolling beta (ferro_ta.cross_asset)
  • Feature matrix — multi-indicator DataFrame for ML pipelines (ferro_ta.features)
  • Charting API — matplotlib and plotly charts with indicator subplots (ferro_ta.viz)
  • Data adapters — pluggable adapter interface with CSV and in-memory implementations (ferro_ta.adapters)
  • Options/IV helpers — IV rank, IV percentile, IV z-score on any IV series (ferro_ta.options)
  • Agentic tools — stable LangChain/agent tool wrappers (ferro_ta.tools), end-to-end workflow orchestrator (ferro_ta.workflow)
  • MCP server — Model Context Protocol server for Cursor/Claude integration; run with python -m ferro_ta.mcp
  • Observability / Loggingferro_ta.enable_debug(), ferro_ta.log_call(), ferro_ta.benchmark() and ferro_ta.traced() decorator for instrumentation
  • API discoveryferro_ta.indicators(category=None) lists all 160+ indicators with metadata; ferro_ta.info(func) returns full parameter docs
  • Structured error codes — every FerroTAError exception now carries a code (FTERR001FTERR006) and an actionable suggestion hint

📦 Installation

pip install ferro-ta

Optional extras:

pip install "ferro-ta[pandas]"   # transparent pandas.Series support
pip install "ferro-ta[polars]"   # transparent polars.Series support
pip install "ferro-ta[gpu]"      # GPU-accelerated SMA/EMA/RSI via PyTorch (CUDA/MPS)
pip install "ferro-ta[options]"  # Options/IV helpers (IV rank, percentile, z-score)
pip install "ferro-ta[mcp]"      # MCP server for Cursor/Claude agent integration
pip install "ferro-ta[all]"      # all optional extras (excluding gpu)

⚡ Quick Start

import numpy as np
from ferro_ta import SMA, EMA, RSI, MACD, BBANDS

close = np.array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.15,
                  43.61, 44.33, 44.83, 45.10, 45.15, 43.61, 44.33])

# Simple Moving Average
sma = SMA(close, timeperiod=5)

# Exponential Moving Average
ema = EMA(close, timeperiod=5)

# Relative Strength Index
rsi = RSI(close, timeperiod=14)

# MACD (returns macd_line, signal_line, histogram)
macd_line, signal, histogram = MACD(close, fastperiod=12, slowperiod=26, signalperiod=9)

# Bollinger Bands (returns upper, middle, lower)
upper, middle, lower = BBANDS(close, timeperiod=5, nbdevup=2.0, nbdevdn=2.0)

Migrating from TA-Lib? Just swap the import — the API is identical:

# Before (TA-Lib)
import talib
sma = talib.SMA(close, timeperiod=20)
rsi = talib.RSI(close, timeperiod=14)

# After (ferro-ta — same call signature, faster result)
import ferro_ta
sma = ferro_ta.SMA(close, timeperiod=20)
rsi = ferro_ta.RSI(close, timeperiod=14)

🛠️ Development Setup

Requires Rust and Python 3.10–3.13 (PyO3 supports up to 3.13; for Python 3.14+ use a compatible interpreter or set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to attempt a build).

# Create a virtual environment
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# Install build tool and dependencies
pip install maturin numpy pytest pandas

# Compile and install in editable mode
maturin develop --release

# Run tests
pytest tests/unit/ tests/integration/
# or: uv run pytest tests/unit/ tests/integration/

# Run TA-Lib comparison tests (requires ta-lib package)
pip install "ferro-ta[comparison]"   # or: pip install ta-lib
pytest tests/integration/test_vs_talib.py -v

# Build Sphinx documentation (requires sphinx + sphinx-rtd-theme)
pip install "ferro-ta[docs]"
cd docs && make html
# Output: docs/_build/html/index.html

📊 Full TA-Lib Compatibility

ferro-ta covers 100% of TA-Lib's function set (162+ indicators). The table below shows implementation status and numerical accuracy vs TA-Lib.

Legend

Symbol Meaning
✅ Exact Values match TA-Lib to floating-point precision
✅ Close Values match after a short convergence window (EMA-seed difference)
⚠️ Corr Strong correlation (> 0.95) but not numerically identical (Wilder smoothing seed or algorithm variant)
⚠️ Shape Same output shape / NaN structure; values differ due to algorithm variant
Not yet implemented

Overlap Studies

TA-Lib Function ferro-ta Accuracy Notes
BBANDS ✅ Exact Bollinger Bands
DEMA ✅ Close Double EMA; converges after ~20 bars
EMA ✅ Close Exponential Moving Average; converges after ~20 bars
KAMA ✅ Exact Kaufman Adaptive MA (values match after seed bar)
MA ✅ Exact Moving average (generic, type-selectable)
MAMA ⚠️ Corr MESA Adaptive MA
MAVP ✅ Exact MA with variable period
MIDPOINT ✅ Exact Midpoint over period
MIDPRICE ✅ Exact Midpoint price over period
SAR ⚠️ Shape Parabolic SAR (same shape; reversal history diverges)
SAREXT ⚠️ Shape Parabolic SAR Extended
SMA ✅ Exact Simple Moving Average
T3 ✅ Close Triple Exponential MA (T3); converges after ~50 bars
TEMA ✅ Close Triple EMA; converges after ~20 bars
TRIMA ✅ Exact Triangular Moving Average
WMA ✅ Exact Weighted Moving Average

Momentum Indicators

TA-Lib Function ferro-ta Accuracy Notes
ADX ✅ Close Avg Directional Movement Index (TA-Lib Wilder sum-seeding)
ADXR ✅ Close ADX Rating (inherits ADX; TA-Lib seeding)
APO ✅ Close Absolute Price Oscillator (EMA-based)
AROON ✅ Exact Aroon Up/Down
AROONOSC ✅ Exact Aroon Oscillator
BOP ✅ Exact Balance Of Power
CCI ✅ Exact Commodity Channel Index (TA-Lib–compatible MAD formula)
CMO ✅ Close Chande Momentum Oscillator (rolling window, TA-Lib–compatible)
DX ✅ Close Directional Movement Index (TA-Lib Wilder sum-seeding)
MACD ✅ Close MACD (EMA-based; converges after ~30 bars)
MACDEXT ✅ Close MACD with controllable MA type (EMA-based; converges)
MACDFIX ✅ Close MACD Fixed 12/26 (EMA-based; converges)
MFI ✅ Exact Money Flow Index
MINUS_DI ✅ Close Minus Directional Indicator (TA-Lib Wilder sum-seeding)
MINUS_DM ✅ Close Minus Directional Movement (TA-Lib Wilder sum-seeding)
MOM ✅ Exact Momentum
PLUS_DI ✅ Close Plus Directional Indicator (TA-Lib Wilder sum-seeding)
PLUS_DM ✅ Close Plus Directional Movement (TA-Lib Wilder sum-seeding)
PPO ✅ Close Percentage Price Oscillator (EMA-based)
ROC ✅ Exact Rate of Change
ROCP ✅ Exact Rate of Change Percentage
ROCR ✅ Exact Rate of Change Ratio
ROCR100 ✅ Exact Rate of Change Ratio × 100
RSI ✅ Close Relative Strength Index (TA-Lib Wilder seeding; converges after ~1 seed bar)
STOCH ✅ Close Stochastic (TA-Lib–compatible SMA smoothing for slowk and slowd)
STOCHF ✅ Exact Stochastic Fast (%K exact; %D NaN offset ±2)
STOCHRSI ✅ Close Stochastic RSI (TA-Lib–compatible; SMA fastd, Wilder-seeded RSI)
TRIX ✅ Close 1-day ROC of Triple EMA (EMA-based; converges)
ULTOSC ✅ Exact Ultimate Oscillator
WILLR ✅ Exact Williams' %R

Volume Indicators

TA-Lib Function ferro-ta Accuracy Notes
AD ✅ Exact Chaikin A/D Line
ADOSC ✅ Exact Chaikin A/D Oscillator
OBV ✅ Exact On Balance Volume (increments identical; constant offset at bar 0)

Volatility Indicators

TA-Lib Function ferro-ta Accuracy Notes
ATR ✅ Close Average True Range (TA-Lib Wilder seeding; matches from bar timeperiod)
NATR ✅ Close Normalized ATR (TA-Lib Wilder seeding)
TRANGE ✅ Exact True Range (bar 0 differs; all others identical)

Cycle Indicators

TA-Lib Function ferro-ta Accuracy Notes
HT_DCPERIOD ⚠️ Shape Hilbert Transform Dominant Cycle Period (Ehlers algorithm)
HT_DCPHASE ⚠️ Shape Hilbert Transform Dominant Cycle Phase
HT_PHASOR ⚠️ Shape Hilbert Transform Phasor Components (inphase, quadrature)
HT_SINE ⚠️ Shape Hilbert Transform SineWave (sine, leadsine)
HT_TRENDLINE ⚠️ Shape Hilbert Transform Instantaneous Trendline
HT_TRENDMODE ⚠️ Shape Hilbert Transform Trend vs Cycle Mode (1=trend, 0=cycle)

Price Transformations

TA-Lib Function ferro-ta Accuracy Notes
AVGPRICE ✅ Exact Average Price
MEDPRICE ✅ Exact Median Price
TYPPRICE ✅ Exact Typical Price
WCLPRICE ✅ Exact Weighted Close Price

Statistic Functions

TA-Lib Function ferro-ta Accuracy Notes
BETA ✅ Close Beta coefficient (returns-based regression matching TA-Lib)
CORREL ✅ Exact Pearson Correlation Coefficient
LINEARREG ✅ Exact Linear Regression
LINEARREG_ANGLE ✅ Exact Linear Regression Angle
LINEARREG_INTERCEPT ✅ Exact Linear Regression Intercept
LINEARREG_SLOPE ✅ Exact Linear Regression Slope
STDDEV ✅ Exact Standard Deviation
TSF ✅ Exact Time Series Forecast
VAR ✅ Exact Variance

Pattern Recognition

ferro-ta implements all 61 candlestick patterns. All return the same {-100, 0, 100} convention as TA-Lib. Pattern thresholds may differ slightly from the full TA-Lib implementation.

TA-Lib Function ferro-ta Notes
CDL2CROWS Two Crows
CDL3BLACKCROWS Three Black Crows
CDL3INSIDE Three Inside Up/Down
CDL3LINESTRIKE Three-Line Strike
CDL3OUTSIDE Three Outside Up/Down
CDL3STARSINSOUTH Three Stars In The South
CDL3WHITESOLDIERS Three Advancing White Soldiers
CDLABANDONEDBABY Abandoned Baby
CDLADVANCEBLOCK Advance Block
CDLBELTHOLD Belt-hold
CDLBREAKAWAY Breakaway
CDLCLOSINGMARUBOZU Closing Marubozu
CDLCONCEALBABYSWALL Concealing Baby Swallow
CDLCOUNTERATTACK Counterattack
CDLDARKCLOUDCOVER Dark Cloud Cover
CDLDOJI Doji
CDLDOJISTAR Doji Star
CDLDRAGONFLYDOJI Dragonfly Doji
CDLENGULFING Engulfing Pattern
CDLEVENINGDOJISTAR Evening Doji Star
CDLEVENINGSTAR Evening Star
CDLGAPSIDESIDEWHITE Up/Down-gap side-by-side white lines
CDLGRAVESTONEDOJI Gravestone Doji
CDLHAMMER Hammer
CDLHANGINGMAN Hanging Man
CDLHARAMI Harami Pattern
CDLHARAMICROSS Harami Cross Pattern
CDLHIGHWAVE High-Wave Candle
CDLHIKKAKE Hikkake Pattern
CDLHIKKAKEMOD Modified Hikkake Pattern
CDLHOMINGPIGEON Homing Pigeon
CDLIDENTICAL3CROWS Identical Three Crows
CDLINNECK In-Neck Pattern
CDLINVERTEDHAMMER Inverted Hammer
CDLKICKING Kicking
CDLKICKINGBYLENGTH Kicking by the longer Marubozu
CDLLADDERBOTTOM Ladder Bottom
CDLLONGLEGGEDDOJI Long Legged Doji
CDLLONGLINE Long Line Candle
CDLMARUBOZU Marubozu
CDLMATCHINGLOW Matching Low
CDLMATHOLD Mat Hold
CDLMORNINGDOJISTAR Morning Doji Star
CDLMORNINGSTAR Morning Star
CDLONNECK On-Neck Pattern
CDLPIERCING Piercing Pattern
CDLRICKSHAWMAN Rickshaw Man
CDLRISEFALL3METHODS Rising/Falling Three Methods
CDLSEPARATINGLINES Separating Lines
CDLSHOOTINGSTAR Shooting Star
CDLSHORTLINE Short Line Candle
CDLSPINNINGTOP Spinning Top
CDLSTALLEDPATTERN Stalled Pattern
CDLSTICKSANDWICH Stick Sandwich
CDLTAKURI Takuri (Dragonfly Doji with very long lower shadow)
CDLTASUKIGAP Tasuki Gap
CDLTHRUSTING Thrusting Pattern
CDLTRISTAR Tristar Pattern
CDLUNIQUE3RIVER Unique 3 River
CDLUPSIDEGAP2CROWS Upside Gap Two Crows
CDLXSIDEGAP3METHODS Upside/Downside Gap Three Methods

Math Operators / Math Transforms

ferro-ta provides TA-Lib–compatible wrappers for all arithmetic and math-transform functions. Rolling functions (SUM, MAX, MIN) produce NaN for the first timeperiod - 1 bars.

TA-Lib Function ferro-ta Notes
ADD Element-wise addition
SUB Element-wise subtraction
MULT Element-wise multiplication
DIV Element-wise division
SUM Rolling sum over timeperiod
MAX / MAXINDEX Rolling maximum / index
MIN / MININDEX Rolling minimum / index
ACOS / ASIN / ATAN Arc trig transforms
CEIL / FLOOR Round up / down
COS / SIN / TAN Trig transforms
COSH / SINH / TANH Hyperbolic transforms
EXP / LN / LOG10 Exponential / log transforms
SQRT Square root

Pandas API

Contract: All indicators accept pandas.Series (or 1-D DataFrame columns) and return pandas.Series — or a tuple of Series for multi-output functions like MACD, BBANDS — with the original index preserved.

Default OHLCV column names: When using a DataFrame with OHLCV data, the conventional names are open, high, low, close, volume. To use different column names, use the helper :func:ferro_ta.utils.get_ohlcv (or pass Series/arrays extracted from your DataFrame).

Single Series or tuple of Series:

import pandas as pd
from ferro_ta import SMA, BBANDS, MACD, CDLDOJI

close = pd.Series([44.34, 44.09, 44.15, 43.61, 44.33], index=pd.date_range("2024-01-01", 5))

# Single-output: returns Series
sma = SMA(close, timeperiod=3)  # pd.Series with same index

# Multi-output: returns tuple of Series
upper, mid, lower = BBANDS(close, timeperiod=3)  # all pd.Series

DataFrame with OHLCV columns (configurable names):

import pandas as pd
from ferro_ta import ATR, RSI
from ferro_ta.utils import get_ohlcv  # or: from ferro_ta._utils import get_ohlcv

df = pd.DataFrame({
    "Open": [1, 2, 3], "High": [1.1, 2.1, 3.1],
    "Low": [0.9, 1.9, 2.9], "Close": [1.05, 2.05, 3.05],
}, index=pd.date_range("2024-01-01", periods=3, freq="D"))

# Extract with default names (open, high, low, close, volume)
o, h, l, c, v = get_ohlcv(df, open_col="Open", high_col="High", low_col="Low", close_col="Close")
atr = ATR(h, l, c, timeperiod=2)   # index preserved
rsi = RSI(c, timeperiod=2)         # index preserved

Extended Indicators

ferro-ta includes popular indicators that go beyond the TA-Lib standard set. These are available in ferro_ta.extended and importable directly from ferro_ta.

Function ferro-ta Notes
VWAP Volume Weighted Average Price — cumulative (session) or rolling window
SUPERTREND ATR-based trend signal; returns (supertrend_line, direction)
ICHIMOKU Ichimoku Cloud — Tenkan, Kijun, Senkou A/B, Chikou Span
DONCHIAN Donchian Channels — rolling highest high / lowest low
PIVOT_POINTS Pivot points — Classic, Fibonacci, Camarilla methods
KELTNER_CHANNELS EMA ± (ATR × multiplier) bands; returns (upper, middle, lower)
HULL_MA Hull Moving Average — fast, low-lag WMA-based MA
CHANDELIER_EXIT ATR-based trailing stop levels; returns (long_exit, short_exit)
VWMA Volume Weighted Moving Average — rolling sum(close*vol) / sum(vol)
CHOPPINESS_INDEX Market choppiness/trending strength index (0–100)
from ferro_ta import VWAP, SUPERTREND, ICHIMOKU, DONCHIAN, PIVOT_POINTS
from ferro_ta import KELTNER_CHANNELS, HULL_MA, CHANDELIER_EXIT, VWMA, CHOPPINESS_INDEX
import numpy as np

close = np.array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.15])
high  = close + 0.5
low   = close - 0.5
vol   = np.full(len(close), 1_000_000.0)

# Cumulative / rolling VWAP
vwap = VWAP(high, low, close, vol)
rolling_vwap = VWAP(high, low, close, vol, timeperiod=5)

# Supertrend (trend line and direction: 1=up, -1=down)
st_line, direction = SUPERTREND(high, low, close, timeperiod=7, multiplier=3.0)

# Ichimoku Cloud
tenkan, kijun, senkou_a, senkou_b, chikou = ICHIMOKU(high, low, close)

# Donchian Channels
dc_upper, dc_mid, dc_lower = DONCHIAN(high, low, timeperiod=5)

# Pivot Points
pivot, r1, s1, r2, s2 = PIVOT_POINTS(high, low, close, method="classic")
# method options: "classic", "fibonacci", "camarilla"

# Keltner Channels
kc_upper, kc_mid, kc_lower = KELTNER_CHANNELS(high, low, close, timeperiod=20, atr_period=10)

# Hull Moving Average
hull = HULL_MA(close, timeperiod=16)

# Chandelier Exit
long_exit, short_exit = CHANDELIER_EXIT(high, low, close, timeperiod=22, multiplier=3.0)

# Volume Weighted Moving Average
vwma = VWMA(close, vol, timeperiod=20)

# Choppiness Index (100 = choppy, 0 = strong trend)
ci = CHOPPINESS_INDEX(high, low, close, timeperiod=14)

Streaming / Live-Trading API

For real-time / bar-by-bar processing, import classes from ferro_ta.streaming. Each class maintains state internally and returns NaN during the warmup window:

from ferro_ta.streaming import StreamingSMA, StreamingEMA, StreamingRSI, StreamingATR
from ferro_ta.streaming import StreamingBBands, StreamingMACD, StreamingStoch
from ferro_ta.streaming import StreamingVWAP, StreamingSupertrend

sma = StreamingSMA(period=20)
rsi = StreamingRSI(period=14)
atr = StreamingATR(period=14)
bb  = StreamingBBands(period=20, nbdevup=2.0, nbdevdn=2.0)
macd = StreamingMACD(fastperiod=12, slowperiod=26, signalperiod=9)
stoch = StreamingStoch(fastk_period=5, slowk_period=3, slowd_period=3)
vwap = StreamingVWAP()  # reset() at session open
st = StreamingSupertrend(period=7, multiplier=3.0)

for bar in live_data_feed:
    current_sma  = sma.update(bar.close)
    current_rsi  = rsi.update(bar.close)
    current_atr  = atr.update(bar.high, bar.low, bar.close)
    upper, mid, lower = bb.update(bar.close)
    macd_line, signal, histogram = macd.update(bar.close)
    slowk, slowd = stoch.update(bar.high, bar.low, bar.close)
    current_vwap = vwap.update(bar.high, bar.low, bar.close, bar.volume)
    st_line, trend_dir = st.update(bar.high, bar.low, bar.close)  # 1=up, -1=down

📈 Implementation Coverage Summary

Category Implemented Not Implemented
Overlap Studies 19 0
Momentum Indicators 28 0
Volume Indicators 3 0
Volatility Indicators 3 0
Cycle Indicators 6 0
Price Transforms 4 0
Statistic Functions 9 0
Pattern Recognition 61 0
Math Operators / Transforms 24 0
Extended Indicators 10
Streaming Classes 9
Total 162+ 0

🎉 100% of TA-Lib's function set is implemented. NaN values are placed at the beginning of each output array for the warmup period.


🔄 Batch Execution API

Run indicators on multiple price series (symbols) in a single call. Dedicated Rust-backed functions for SMA, EMA, RSI, ATR, STOCH, and ADX; use batch_apply for any other indicator.

import numpy as np
from ferro_ta.batch import batch_sma, batch_ema, batch_rsi, batch_atr, batch_stoch, batch_adx, batch_apply

# 100 bars × 5 symbols
close = np.random.rand(100, 5) + 50.0
high = close + 0.1
low = close - 0.1

sma_out = batch_sma(close, timeperiod=14)       # (100, 5)
ema_out = batch_ema(close, timeperiod=14)       # (100, 5)
rsi_out = batch_rsi(close, timeperiod=14)       # (100, 5)
atr_out = batch_atr(high, low, close, timeperiod=14)
stoch_k, stoch_d = batch_stoch(high, low, close)
adx_out = batch_adx(high, low, close, timeperiod=14)

# Any single-series function via batch_apply
from ferro_ta import BBANDS
def bbands_upper(c, **kw):
    return BBANDS(c, **kw)[0]
upper = batch_apply(close, bbands_upper, timeperiod=20)

🦀 Pure Rust Core Library

ferro-ta is structured as a Cargo workspace with two crates:

Crate Purpose
ferro_ta (root) PyO3 #[pyfunction] wrappers — converts numpy ↔ &[f64]; builds the Python wheel
crates/ferro_ta_core Pure Rust indicators — no PyO3/numpy dependency; usable from any Rust project
# Build and test the core crate directly
cargo build -p ferro_ta_core
cargo test -p ferro_ta_core
use ferro_ta_core::overlap;

let close = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let sma = overlap::sma(&close, 3);

Rust Module Structure

The main ferro_ta crate (src/) uses a consistent directory-based module layout matching the TA-Lib category structure. Every module is a directory with mod.rs declaring sub-modules and a register() function; each indicator (or closely related group) lives in its own .rs file:

src/
├── lib.rs                   # PyModule entry point — calls each module's register()
├── overlap/                 # Overlap Studies (SMA, EMA, BBANDS, MACD, SAR, …)
│   ├── mod.rs
│   ├── sma.rs, ema.rs, wma.rs, dema.rs, tema.rs, trima.rs, kama.rs, t3.rs
│   ├── bbands.rs, macd.rs, macdfix.rs, macdext.rs
│   ├── sar.rs, sarext.rs, mama.rs, midpoint.rs, midprice.rs
│   └── ma_mavp.rs
├── momentum/                # Momentum Indicators (RSI, STOCH, ADX, CCI, …)
│   ├── mod.rs
│   └── rsi.rs, mom.rs, roc.rs, willr.rs, aroon.rs, cci.rs, mfi.rs,
│       bop.rs, stochf.rs, stoch.rs, stochrsi.rs, apo.rs, ppo.rs, cmo.rs,
│       adx.rs, trix.rs, ultosc.rs
├── volatility/              # Volatility Indicators (ATR, NATR, TRANGE)
│   ├── mod.rs
│   ├── common.rs            # shared TR computation
│   ├── trange.rs, atr.rs, natr.rs
├── volume/                  # Volume Indicators (AD, ADOSC, OBV)
│   ├── mod.rs
│   └── ad.rs, adosc.rs, obv.rs
├── statistic/               # Statistic Functions (STDDEV, VAR, LINEARREG*, BETA, CORREL)
│   ├── mod.rs
│   ├── common.rs            # shared linreg() helper
│   └── stddev.rs, var.rs, linearreg.rs, beta.rs, correl.rs
├── price_transform/         # Price Transformations (AVGPRICE, MEDPRICE, TYPPRICE, WCLPRICE)
│   ├── mod.rs
│   └── avgprice.rs, medprice.rs, typprice.rs, wclprice.rs
├── cycle/                   # Cycle Indicators (HT_TRENDLINE, HT_DCPERIOD, …)
│   ├── mod.rs
│   ├── common.rs            # shared HT core pipeline (compute_ht_core)
│   └── ht_trendline.rs, ht_dcperiod.rs, ht_dcphase.rs,
│       ht_phasor.rs, ht_sine.rs, ht_trendmode.rs
└── pattern/                 # Pattern Recognition (CDL2CROWS, CDLDOJI, …)
    ├── mod.rs
    ├── common.rs            # shared candle utilities
    └── cdl*.rs              # one file per pattern (61 patterns)

This layout makes it easy to add, review, or modify individual indicators in isolation — simply edit or add the relevant .rs file and update mod.rs.

Python sub-package layout

The python/ferro_ta/ package is organized into sub-packages by concern. Backward-compat stubs at the old flat paths (e.g. ferro_ta.momentum) re-export from the new locations, so existing code continues to work without changes.

python/ferro_ta/
├── __init__.py        # top-level re-exports and public API
├── core/              # Exceptions, configuration, registry, logging, raw FFI bindings
├── indicators/        # Technical indicators (momentum, overlap, volatility, volume,
│                      #   statistic, cycle, pattern, price_transform, math_ops, extended)
├── data/              # Streaming, batch, chunked, resampling, aggregation, adapters
├── analysis/          # Portfolio, backtest, regime, cross_asset, attribution,
│                      #   signals, features, crypto, options
├── tools/             # Visualisation, alerting, DSL, pipeline, workflow,
│                      #   api_info, GPU support
└── mcp/               # Model Context Protocol server

🌐 Other Languages (WebAssembly / Node.js)

A WebAssembly binding is available in the wasm/ directory, exposing SMA, EMA, BBANDS, RSI, ATR, OBV, and MACD for use in Node.js and browsers.

// Node.js (after `wasm-pack build --target nodejs --out-dir pkg` in wasm/)
const { sma, rsi, macd } = require('./wasm/pkg/ferro_ta_wasm.js');

const close = new Float64Array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10]);
const smaOut = sma(close, 3);       // Float64Array — first 2 values are NaN
const rsiOut = rsi(close, 5);       // Float64Array — first 5 values are NaN

// MACD — returns [macd_line, signal_line, histogram] as a js_sys::Array
const [macdLine, signal, hist] = macd(close, 3, 5, 2);

See wasm/README.md for build instructions, the full list of exposed functions, and browser usage examples.


🔥 GPU Acceleration (Optional)

For very large arrays (millions of bars), an optional GPU-accelerated path is available via PyTorch. Pass a torch.Tensor on CUDA or MPS and get a tensor back; NumPy in → NumPy out (CPU fallback).

pip install "ferro-ta[gpu]"
# or install PyTorch yourself (e.g. with CUDA or MPS support):
# pip install torch
import torch
from ferro_ta.gpu import sma, ema, rsi

# Use CUDA or MPS (Apple Silicon)
close_gpu = torch.tensor(
    [44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.15, 43.61, 44.33],
    device="cuda",  # or device="mps" on Apple Silicon
    dtype=torch.float64,
)

result = sma(close_gpu, timeperiod=5)   # torch.Tensor on same device
result_cpu = result.cpu().numpy()       # back to NumPy if needed

PyTorch tensors in → PyTorch tensors out; NumPy arrays in → NumPy arrays out (CPU). See docs/gpu-backend.md for supported indicators, limitations, and benchmark data.


📉 Backtesting

A minimal vectorized backtester is available at ferro_ta.backtest:

import numpy as np
from ferro_ta.backtest import backtest

np.random.seed(42)
close = np.cumprod(1 + np.random.randn(200) * 0.01) * 100

# Run an RSI 30/70 strategy
result = backtest(close, strategy="rsi_30_70", timeperiod=14)
print(f"Final equity: {result.final_equity:.4f}")
print(f"Number of trades: {result.n_trades}")

# Or use SMA crossover
result2 = backtest(close, strategy="sma_crossover", fast=10, slow=30)
result3 = backtest(close, strategy="macd_crossover", commission_per_trade=0.001, slippage_bps=5)

Note: This is a minimal harness for testing strategies. Optional commission_per_trade and slippage_bps are supported; for margin or full order types consider backtrader, zipline, or vectorbt. For production use consider backtrader, zipline, or vectorbt.


🔗 Indicator Pipeline

Compose multiple indicators into a reusable pipeline:

import numpy as np
from ferro_ta import SMA, EMA, RSI, BBANDS
from ferro_ta.pipeline import Pipeline

close = np.cumprod(1 + np.random.randn(200) * 0.01) * 100

pipe = (
    Pipeline()
    .add("sma_20", SMA, timeperiod=20)
    .add("ema_20", EMA, timeperiod=20)
    .add("rsi_14", RSI, timeperiod=14)
    .add("bb", BBANDS, output_keys=["bb_upper", "bb_mid", "bb_lower"],
         timeperiod=20, nbdevup=2.0, nbdevdn=2.0)
)

results = pipe.run(close)
# {'sma_20': array([...]), 'ema_20': array([...]), ..., 'bb_lower': array([...])}
print(list(results.keys()))

⚙️ Configuration Defaults

Set global parameter defaults to avoid repeating them on every call:

import ferro_ta.config as config

config.set_default("timeperiod", 20)       # applies to all indicators
config.set_default("RSI.timeperiod", 14)   # RSI-specific override

from ferro_ta import RSI, SMA
# RSI(close) uses timeperiod=14; SMA(close) uses timeperiod=20

# Context manager for temporary overrides
with config.Config(timeperiod=5):
    result = SMA(close)   # timeperiod=5 inside this block
# back to timeperiod=20 after the block

config.reset()   # clear all custom defaults

🔌 Plugin Registry

Register and call any indicator (built-in or custom) by name. See the Writing a plugin <docs/plugins.rst>_ doc for the plugin contract and a full example (examples/custom_indicator.py).

import numpy as np
from ferro_ta.registry import register, run, list_indicators

# Call a built-in by name
close = np.array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10])
sma = run("SMA", close, timeperiod=3)

# Register a custom indicator
def DOUBLE_RSI(close, timeperiod=14, smooth=3):
    import ferro_ta
    rsi = ferro_ta.RSI(close, timeperiod=timeperiod)
    return ferro_ta.SMA(rsi, timeperiod=smooth)

register("DOUBLE_RSI", DOUBLE_RSI)
result = run("DOUBLE_RSI", close, timeperiod=5, smooth=2)

# List all registered indicators
print(list_indicators()[:5])  # ['AD', 'ADOSC', 'ADX', 'ADXR', 'APO']

🛡️ Error Handling

ferro-ta provides a typed exception hierarchy with error codes and actionable suggestions:

from ferro_ta import FerroTAError, FerroTAValueError, FerroTAInputError
from ferro_ta.exceptions import check_timeperiod, check_equal_length

# Catch any ferro-ta error
try:
    result = SMA(close, timeperiod=0)
except FerroTAValueError as e:
    print(e.code)        # "FTERR001"
    print(e.suggestion)  # "Set timeperiod=1 or higher."
    print(e)             # "[FTERR001] timeperiod must be >= 1, got 0\n  Suggestion: ..."

# Validate inputs before calling
check_equal_length(open=open_, close=close)   # raises FerroTAInputError (FTERR004) on mismatch
check_timeperiod(timeperiod)                   # raises FerroTAValueError (FTERR001) if < 1

Error code reference:

Code Exception Meaning
FTERR001 FerroTAValueError Invalid parameter value
FTERR002 FerroTAInputError Invalid input array
FTERR003 FerroTAInputError Input array too short
FTERR004 FerroTAInputError Mismatched array lengths
FTERR005 FerroTAInputError Array contains NaN/Inf (strict mode)
FTERR006 FerroTAValueError/InputError Rust-bridge error

🔍 Observability & Logging

ferro-ta ships a lightweight logging module that integrates with Python's standard logging library:

import ferro_ta

# Enable DEBUG-level logging (writes to stderr)
ferro_ta.enable_debug()
result = ferro_ta.SMA(close, timeperiod=20)
# DEBUG [ferro_ta] calling SMA(ndarray(252,) dtype=float64, timeperiod=20)
# DEBUG [ferro_ta] SMA → ndarray(252,)  [0.042 ms]
ferro_ta.disable_debug()

# Context manager: temporary debug output
with ferro_ta.debug_mode():
    ferro_ta.RSI(close, timeperiod=14)

# Call with automatic shape + timing log
result = ferro_ta.log_call(ferro_ta.ATR, high, low, close, timeperiod=14)

# Benchmark: returns {mean_ms, min_ms, max_ms, total_ms, n}
stats = ferro_ta.benchmark(ferro_ta.SMA, close, timeperiod=20, n=500)
print(f"SMA mean: {stats['mean_ms']:.3f} ms")

# Decorator: wrap any function with automatic logging
@ferro_ta.traced
def my_strategy(close):
    sma = ferro_ta.SMA(close, timeperiod=20)
    rsi = ferro_ta.RSI(close, timeperiod=14)
    return sma, rsi

🔎 API Discovery

import ferro_ta

# List all 160+ indicators with metadata
all_indicators = ferro_ta.indicators()
print(len(all_indicators))  # 160+

# Filter by category
overlap = ferro_ta.indicators(category="overlap")
momentum = ferro_ta.indicators(category="momentum")

# Get parameter info for any indicator
d = ferro_ta.info(ferro_ta.SMA)
print(d["signature"])   # (close: ArrayLike, timeperiod: int = 30) -> NDArray[float64]
print(d["params"])      # {"close": {"default": None, ...}, "timeperiod": {"default": 30, ...}}

# By name string
d = ferro_ta.info("MACD")

See PLATFORMS.md for supported OS and Python versions. See CHANGELOG.md and VERSIONING.md for release notes and versioning policy. See RELEASE.md for the step-by-step release playbook. See examples/ for Jupyter notebook examples (quickstart, streaming, backtesting, and more).

🗺️ Multi-Timeframe, Portfolio, and ML Features

OHLCV Resampling and Multi-Timeframe API (ferro_ta.resampling)

from ferro_ta.resampling import resample, volume_bars, multi_timeframe
from ferro_ta import RSI
import pandas as pd

# Resample 1-minute data to 5-minute bars (requires pandas)
df5 = resample(ohlcv_df, '5min')

# Volume bars (every 10,000 units of volume) — Rust backend
vbars = volume_bars(ohlcv_df, volume_threshold=10_000)

# Multi-timeframe RSI in one call
mtf = multi_timeframe(ohlcv_df, ['5min', '15min'], indicator=RSI,
                      indicator_kwargs={'timeperiod': 14})
# mtf = {'5min': array(...), '15min': array(...)}

Tick Aggregation Pipeline (ferro_ta.aggregation)

from ferro_ta.aggregation import aggregate_ticks, TickAggregator

# Tick bars, volume bars, time bars — all Rust-backed
tick_bars   = aggregate_ticks(ticks, rule='tick:100')
volume_bars = aggregate_ticks(ticks, rule='volume:500')
time_bars   = aggregate_ticks(ticks, rule='time:60')

# Class-based API
agg = TickAggregator(rule='tick:100')
bars = agg.aggregate(ticks)  # → pandas DataFrame or dict

Strategy Expression DSL (ferro_ta.dsl)

from ferro_ta.dsl import Strategy, evaluate

# Parse and evaluate expression strings
strat = Strategy("RSI(14) < 30 and close > SMA(20)")
signal = strat.evaluate({"close": close_arr})  # 1/0 integer array

Signal Composition and Screening (ferro_ta.signals)

from ferro_ta.signals import compose, screen, rank_signals

# Weighted combination of signal columns (Rust-backed)
score = compose(signals_df, weights=[0.4, 0.35, 0.25])

# Screening
top2 = screen({'AAPL': 0.8, 'MSFT': 0.9, 'GOOG': 0.5}, top_n=2)
# {'MSFT': 0.9, 'AAPL': 0.8}

Portfolio Analytics (ferro_ta.portfolio)

from ferro_ta.portfolio import correlation_matrix, portfolio_volatility, beta, drawdown

corr   = correlation_matrix(returns_df)               # Pearson corr matrix
vol    = portfolio_volatility(returns_df, weights,     # sqrt(w'Σw)
                              annualise=252)
b      = beta(asset_returns, benchmark_returns)        # OLS beta
rb     = beta(asset_returns, benchmark_returns,        # rolling beta
              window=30)
dd, mx = drawdown(equity_curve)                        # drawdown series + max

Cross-Asset Relative Strength (ferro_ta.cross_asset)

from ferro_ta.cross_asset import relative_strength, spread, ratio, zscore, rolling_beta

rs = relative_strength(asset_rets, bench_rets)  # cumulative return ratio
sp = spread(price_a, price_b, hedge=1.0)        # A - hedge * B
z  = zscore(sp, window=20)                       # rolling Z-score

Feature Matrix for ML (ferro_ta.features)

from ferro_ta.features import feature_matrix

fm = feature_matrix(ohlcv, [
    ('RSI', {'timeperiod': 14}),
    ('SMA', {'timeperiod': 20}),
    ('ATR', {'timeperiod': 14}),
], nan_policy='drop')
# fm is a pandas DataFrame with one column per indicator
# Use with sklearn: clf.fit(fm.values, labels)

Charting and Visualization (ferro_ta.viz)

from ferro_ta.viz import plot
from ferro_ta import RSI, SMA

fig = plot(ohlcv_df, indicators={'RSI(14)': RSI(close), 'SMA(20)': SMA(close)},
           backend='matplotlib', savefig='chart.png')
# Also supports 'plotly' backend for interactive charts

Market Data Adapters (ferro_ta.adapters)

from ferro_ta.adapters import CsvAdapter, InMemoryAdapter, register_adapter, DataAdapter

# Load from CSV
adapter = CsvAdapter('data.csv', index_col='date')
ohlcv = adapter.fetch()

# Custom adapter
class MyAdapter(DataAdapter):
    def fetch(self, **kwargs): return ...

register_adapter('mybroker', MyAdapter)

🤝 Community

GitHub Discussions

  • GitHub Discussions — Ask questions, share strategies, and request features in our Discussions space. Categories: Q&A, Ideas, Show & Tell, Announcements.

  • Contributing: See CONTRIBUTING.md for setup, code style, and PR guidelines.

  • Code of Conduct: All participants are expected to follow the CODE_OF_CONDUCT.md.

  • Governance: Decision-making process and maintainer info in GOVERNANCE.md.

  • Roadmap: Development plan in ROADMAP.md.

  • Security: Responsible disclosure policy in SECURITY.md.

  • Migration from TA-Lib: Step-by-step guide in the documentation.

  • Library Compatibility Guides — drop-in migration instructions and cross-library test results:

  • Cross-Library Benchmarks — accuracy and speed comparison across all 6 libraries:


ferro-ta — Built with ❤️ and Rust. Star ⭐ on GitHub to support the project.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

ferro_ta-1.0.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (1.6 MB view details)

Uploaded CPython 3.14macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

ferro_ta-1.0.1-cp312-cp312-win_amd64.whl (852.6 kB view details)

Uploaded CPython 3.12Windows x86-64

ferro_ta-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (891.2 kB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

Details for the file ferro_ta-1.0.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for ferro_ta-1.0.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 d958d7fb19bfad346bbd8f8278052e6a053e242470647aba932689737510baf5
MD5 9b608563ece663de02a18589eab32bd1
BLAKE2b-256 ff579dfdb439b249f985e4b433ff3211a12f6fef8884c30251713b7f0c9d6e00

See more details on using hashes here.

Provenance

The following attestation bundles were made for ferro_ta-1.0.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: CI.yml on pratikbhadane24/ferro-ta

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ferro_ta-1.0.1-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: ferro_ta-1.0.1-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 852.6 kB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for ferro_ta-1.0.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 d6e10a8a330792f2b2ad2b7aaa59664a74dda2582b0040725a11e5191a92de32
MD5 82d828492d573f1f9854f326814382aa
BLAKE2b-256 aef4e3f134ab61c06a3e0a36064b1ad670718c4e8015b1b5f2333236c1aab728

See more details on using hashes here.

Provenance

The following attestation bundles were made for ferro_ta-1.0.1-cp312-cp312-win_amd64.whl:

Publisher: CI.yml on pratikbhadane24/ferro-ta

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ferro_ta-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for ferro_ta-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 31e5f4f486ee45a00ac3479317dc6a08758efca6e7f7b0e63855302aef28ac89
MD5 20bb77f4d7f3c85ea45a7dc47424c6ec
BLAKE2b-256 f9a40fc3eee2854754520e7edb2afd22babeffb6f5946781a75859ab1123087c

See more details on using hashes here.

Provenance

The following attestation bundles were made for ferro_ta-1.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: CI.yml on pratikbhadane24/ferro-ta

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page