bench_simd.py re-invokes `maturin develop` at runtime to compare portable vs SIMD builds, which requires an active virtualenv. CI installs into system Python so maturin develop fails with "Couldn't find a virtualenv". --skip-simd avoids this; the SIMD comparison is optional profiling, not a correctness gate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
⚡ 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.
"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 ofif/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/DataFrameand returnsSerieswith original index preserved - Polars integration — transparently accepts
polars.Seriesand returnspolars.Series; install withpip 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/rsiand get a tensor back (CUDA or MPS); install withpip install "ferro-ta[gpu]" - Type stubs (
.pyi) +py.typed(PEP 561) for IDE auto-completion andmypy/pyrightsupport - 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 inexamples/ - 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) - Derivatives analytics — IV rank/percentile/z-score, options pricing/Greeks/IV, futures basis/curve/roll, strategy schemas, and multi-leg payoff helpers (
ferro_ta.analysis.*) - 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 / Logging —
ferro_ta.enable_debug(),ferro_ta.log_call(),ferro_ta.benchmark()andferro_ta.traced()decorator for instrumentation - API discovery —
ferro_ta.indicators(category=None)lists all 160+ indicators with metadata;ferro_ta.info(func)returns full parameter docs - Structured error codes — every
FerroTAErrorexception now carries a code (FTERR001–FTERR006) and an actionablesuggestionhint
📦 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]" # Derivatives analytics helpers
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)
Δ Derivatives Analytics
from ferro_ta.analysis.options import greeks, implied_volatility, option_price
from ferro_ta.analysis.futures import basis, curve_summary
price = option_price(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call", model="bsm")
iv = implied_volatility(price, 100.0, 100.0, 0.05, 1.0, option_type="call", model="bsm")
g = greeks(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call", model="bsm")
front_basis = basis(100.0, 103.0)
curve = curve_summary(100.0, [0.1, 0.5, 1.0], [101.0, 102.0, 104.0])
The derivatives layer is analytics-only. It includes:
- options pricing under Black-Scholes-Merton and Black-76
- delta, gamma, vega, theta, and rho
- implied volatility inversion and smile metrics
- futures basis, carry, curve, and continuous-roll helpers
- typed strategy schemas and multi-leg payoff/Greeks aggregation
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, futures,
│ # options_strategy, derivatives_payoff
├── 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_tradeandslippage_bpsare supported; for margin or full order types considerbacktrader,zipline, orvectorbt. For production use considerbacktrader,zipline, orvectorbt.
🔗 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 — Ask questions, share strategies, and request features in our Discussions space. Categories: Q&A, Ideas, Show & Tell, Announcements.
-
Contributing: See
CONTRIBUTING.mdfor 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:
- TA-Lib compatibility — full indicator mapping, API differences, and migration guide
- pandas-ta compatibility — indicator mapping, known differences, and comparison tests
- ta (Bukosabino) compatibility — indicator mapping, known differences, and comparison tests
- Tulipy compatibility — C99 Tulip Indicators: output truncation, memory requirements, signature mapping
- finta compatibility — pure-Pandas library: DataFrame requirements, speed comparison, migration guide
-
Cross-Library Benchmarks — accuracy and speed comparison across all 6 libraries:
- Benchmarks README — real timing results (µs), accuracy methodology, and known limitations
- Performance Roadmap — plan to achieve 100x speedup over Tulipy
ferro-ta — Built with ❤️ and Rust. Star ⭐ on GitHub to support the project.