602d675749
Implements all phases of the derivatives expansion plan: Rust core (crates/ferro_ta_core/src/options/, src/futures/): - BSM and Black-76 pricing (scalar + vectorized batch) - Greeks: delta, gamma, vega, theta, rho - Implied volatility solver (Newton + bisection fallback) - Smile/skew metrics: ATM IV, 25-delta RR/BF, skew slope, convexity - Chain helpers: moneyness labels, strike selection by offset or delta - Synthetic forwards, basis, annualized basis, implied carry, carry spread - Continuous contract stitching: weighted, back-adjusted, ratio-adjusted - Curve analytics: calendar spreads, slope, contango/backwardation summary PyO3 bindings (src/options/, src/futures/): - All Rust functions registered and exposed via _ferro_ta extension Python API (python/ferro_ta/analysis/): - options.py: pricing, greeks, IV, smile, chain, legacy iv_rank/percentile/zscore - futures.py: basis, carry, curve, roll, synthetic, continuous contracts - options_strategy.py: typed strategy schemas (expiry/strike selectors, leg presets, risk controls, simulation limits) - derivatives_payoff.py: multi-leg payoff aggregation and Greeks aggregation Bug fix: wrap _to_f64 calls in iv_rank/iv_percentile/iv_zscore to raise FerroTAInputError (not plain ValueError) for 2D array input. Docs: derivatives.rst, derivatives-analytics.md, options-volatility.md, quickstart.rst, index.rst, api/analysis.rst all updated. Tests: 2053 pass, 12 skipped. All CI checks pass locally. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1114 lines
45 KiB
Markdown
1114 lines
45 KiB
Markdown
<div align="center">
|
||
|
||
# ⚡ 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.**
|
||
|
||
[](https://mybinder.org/v2/gh/pratikbhadane24/ferro-ta/HEAD?labpath=examples%2Fquickstart.ipynb)
|
||
[](https://colab.research.google.com/github/pratikbhadane24/ferro-ta/blob/main/examples/quickstart.ipynb)
|
||
[](https://pratikbhadane24.github.io/ferro-ta/)
|
||
|
||
</div>
|
||
|
||
---
|
||
|
||
> **"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`](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`
|
||
|
||
```bash
|
||
# 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`)
|
||
- **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()` and `ferro_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 `FerroTAError` exception now carries a code (`FTERR001`–`FTERR006`) and an actionable `suggestion` hint
|
||
|
||
---
|
||
|
||
## 📦 Installation
|
||
|
||
```bash
|
||
pip install ferro-ta
|
||
```
|
||
|
||
Optional extras:
|
||
|
||
```bash
|
||
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
|
||
|
||
```python
|
||
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
|
||
|
||
```python
|
||
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:
|
||
|
||
```python
|
||
# 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).
|
||
|
||
```bash
|
||
# 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:**
|
||
|
||
```python
|
||
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):**
|
||
|
||
```python
|
||
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) |
|
||
|
||
```python
|
||
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:
|
||
|
||
```python
|
||
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.
|
||
|
||
```python
|
||
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 |
|
||
|
||
```bash
|
||
# Build and test the core crate directly
|
||
cargo build -p ferro_ta_core
|
||
cargo test -p ferro_ta_core
|
||
```
|
||
|
||
```rust
|
||
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.
|
||
|
||
```javascript
|
||
// 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`](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](https://pytorch.org/). Pass a `torch.Tensor` on CUDA or MPS and get a tensor back;
|
||
NumPy in → NumPy out (CPU fallback).
|
||
|
||
```bash
|
||
pip install "ferro-ta[gpu]"
|
||
# or install PyTorch yourself (e.g. with CUDA or MPS support):
|
||
# pip install torch
|
||
```
|
||
|
||
```python
|
||
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`](docs/gpu-backend.md) for supported indicators, limitations,
|
||
and benchmark data.
|
||
|
||
---
|
||
|
||
## 📉 Backtesting
|
||
|
||
A minimal vectorized backtester is available at `ferro_ta.backtest`:
|
||
|
||
```python
|
||
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:
|
||
|
||
```python
|
||
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:
|
||
|
||
```python
|
||
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``).
|
||
|
||
```python
|
||
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**:
|
||
|
||
```python
|
||
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:
|
||
|
||
```python
|
||
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
|
||
|
||
```python
|
||
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`](PLATFORMS.md) for supported OS and Python versions.
|
||
See [`CHANGELOG.md`](CHANGELOG.md) and [`VERSIONING.md`](VERSIONING.md) for release notes and versioning policy.
|
||
See [`RELEASE.md`](RELEASE.md) for the step-by-step release playbook.
|
||
See [`examples/`](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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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`)
|
||
|
||
```python
|
||
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
|
||
|
||
[](https://github.com/pratikbhadane24/ferro-ta/discussions)
|
||
|
||
- **GitHub Discussions** — Ask questions, share strategies, and request features in our [Discussions](https://github.com/pratikbhadane24/ferro-ta/discussions) space. Categories: **Q&A**, **Ideas**, **Show & Tell**, **Announcements**.
|
||
- **Contributing**: See [`CONTRIBUTING.md`](CONTRIBUTING.md) for setup, code style, and PR guidelines.
|
||
- **Code of Conduct**: All participants are expected to follow the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
|
||
- **Governance**: Decision-making process and maintainer info in [`GOVERNANCE.md`](GOVERNANCE.md).
|
||
- **Roadmap**: Development plan in [`ROADMAP.md`](ROADMAP.md).
|
||
- **Security**: Responsible disclosure policy in [`SECURITY.md`](SECURITY.md).
|
||
- **Migration from TA-Lib**: Step-by-step guide in the [documentation](docs/migration_talib.rst).
|
||
- **Library Compatibility Guides** — drop-in migration instructions and cross-library test results:
|
||
- [TA-Lib compatibility](docs/compatibility/talib.md) — full indicator mapping, API differences, and migration guide
|
||
- [pandas-ta compatibility](docs/compatibility/pandas_ta.md) — indicator mapping, known differences, and comparison tests
|
||
- [ta (Bukosabino) compatibility](docs/compatibility/ta.md) — indicator mapping, known differences, and comparison tests
|
||
- [Tulipy compatibility](docs/compatibility/tulipy.md) — C99 Tulip Indicators: output truncation, memory requirements, signature mapping
|
||
- [finta compatibility](docs/compatibility/finta.md) — pure-Pandas library: DataFrame requirements, speed comparison, migration guide
|
||
|
||
- **Cross-Library Benchmarks** — accuracy and speed comparison across all 6 libraries:
|
||
- [Benchmarks README](benchmarks/README.md) — real timing results (µs), accuracy methodology, and known limitations
|
||
- [Performance Roadmap](PERFORMANCE_ROADMAP.md) — plan to achieve 100x speedup over Tulipy
|
||
|
||
---
|
||
|
||
<div align="center">
|
||
|
||
**ferro-ta** — Built with ❤️ and Rust. [Star ⭐ on GitHub](https://github.com/pratikbhadane24/ferro-ta) to support the project.
|
||
|
||
</div>
|