Metadata-Version: 2.4
Name: ferro-ta
Version: 1.0.2
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Rust
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Typing :: Typed
Requires-Dist: numpy>=1.20
Requires-Dist: pandas>=1.0 ; extra == 'all'
Requires-Dist: polars>=0.19 ; extra == 'all'
Requires-Dist: pytest>=7.0 ; extra == 'all'
Requires-Dist: pytest-benchmark>=4.0 ; extra == 'all'
Requires-Dist: pytest>=7.0 ; extra == 'benchmark'
Requires-Dist: pytest-benchmark>=4.0 ; extra == 'benchmark'
Requires-Dist: pytest>=7.0 ; extra == 'comparison'
Requires-Dist: ta-lib>=0.4 ; extra == 'comparison'
Requires-Dist: pandas-ta>=0.3 ; python_full_version >= '3.12' and extra == 'comparison'
Requires-Dist: ta>=0.10 ; extra == 'comparison'
Requires-Dist: pandas>=1.0 ; extra == 'comparison'
Requires-Dist: pytest>=7.0 ; extra == 'dev'
Requires-Dist: hypothesis>=6.0 ; extra == 'dev'
Requires-Dist: pandas>=1.0 ; extra == 'dev'
Requires-Dist: polars>=0.19 ; extra == 'dev'
Requires-Dist: ruff>=0.3 ; extra == 'dev'
Requires-Dist: mypy>=1.0 ; extra == 'dev'
Requires-Dist: pyright>=1.1 ; extra == 'dev'
Requires-Dist: maturin>=1.0,<2.0 ; extra == 'dev'
Requires-Dist: pyyaml>=6.0 ; extra == 'dev'
Requires-Dist: matplotlib>=3.5 ; extra == 'dev'
Requires-Dist: fastapi>=0.135.1 ; extra == 'dev'
Requires-Dist: httpx>=0.24 ; extra == 'dev'
Requires-Dist: sphinx>=7.0 ; extra == 'docs'
Requires-Dist: sphinx-rtd-theme>=1.3 ; extra == 'docs'
Requires-Dist: torch>=2.0 ; extra == 'gpu'
Requires-Dist: mcp>=1.0 ; extra == 'mcp'
Requires-Dist: pandas>=1.0 ; extra == 'pandas'
Requires-Dist: polars>=0.19 ; extra == 'polars'
Requires-Dist: pytest>=7.0 ; extra == 'test'
Requires-Dist: hypothesis>=6.0 ; extra == 'test'
Provides-Extra: all
Provides-Extra: benchmark
Provides-Extra: comparison
Provides-Extra: dev
Provides-Extra: docs
Provides-Extra: gpu
Provides-Extra: mcp
Provides-Extra: options
Provides-Extra: pandas
Provides-Extra: polars
Provides-Extra: test
License-File: LICENSE
Summary: A fast Technical Analysis library — TA-Lib alternative powered by Rust and PyO3
Keywords: technical-analysis,trading,finance,rust,pyo3,ta-lib,indicators,candlestick,pandas,numpy
License: MIT
Requires-Python: >=3.10
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Bug Tracker, https://github.com/pratikbhadane24/ferro-ta/issues
Project-URL: Homepage, https://github.com/pratikbhadane24/ferro-ta
Project-URL: Repository, https://github.com/pratikbhadane24/ferro-ta

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

[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pratikbhadane24/ferro-ta/HEAD?labpath=examples%2Fquickstart.ipynb)
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pratikbhadane24/ferro-ta/blob/main/examples/quickstart.ipynb)
[![Documentation](https://img.shields.io/badge/docs-github.io-blue)](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

[![GitHub Discussions](https://img.shields.io/badge/discussions-GitHub-blue?logo=github)](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>

