feat: add full derivatives analytics layer (options + futures)

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>
This commit is contained in:
Pratik Bhadane
2026-03-24 02:41:50 +05:30
co-authored by Claude Sonnet 4.6
parent 2d5000262f
commit 602d675749
47 changed files with 4538 additions and 280 deletions
+22
View File
@@ -0,0 +1,22 @@
Analysis Modules
================
.. automodule:: ferro_ta.analysis.options
:members:
:undoc-members:
:show-inheritance:
.. automodule:: ferro_ta.analysis.futures
:members:
:undoc-members:
:show-inheritance:
.. automodule:: ferro_ta.analysis.options_strategy
:members:
:undoc-members:
:show-inheritance:
.. automodule:: ferro_ta.analysis.derivatives_payoff
:members:
:undoc-members:
:show-inheritance:
+1
View File
@@ -17,3 +17,4 @@ API Reference
extended
streaming
batch
analysis
+70
View File
@@ -0,0 +1,70 @@
# Derivatives Analytics
`ferro-ta` now includes a Rust-backed derivatives analytics layer focused on
research, simulation, and risk analysis.
## Modules
- `ferro_ta.analysis.options`
- Black-Scholes-Merton and Black-76 pricing
- Delta, gamma, vega, theta, rho
- Implied volatility inversion with guarded Newton + bisection fallback
- IV rank / percentile / z-score
- Smile metrics: ATM IV, 25-delta risk reversal, butterfly, skew slope, convexity
- Chain helpers: moneyness labels and strike selection by offset or delta
- `ferro_ta.analysis.futures`
- Synthetic forwards and parity diagnostics
- Basis, annualized basis, implied carry, carry spread
- Continuous contract stitching: weighted, back-adjusted, ratio-adjusted
- Curve analytics: calendar spreads, slope, contango summary
- `ferro_ta.analysis.options_strategy`
- Typed strategy schemas for expiry selectors, strike selectors, multi-leg presets,
risk controls, cost assumptions, and simulation limits
- `ferro_ta.analysis.derivatives_payoff`
- Multi-leg payoff aggregation
- Portfolio-level Greeks aggregation across option and futures legs
## Model conventions
- `model="bsm"` expects the underlying input to be spot and `carry` to represent
a continuous dividend yield or generic carry term.
- `model="black76"` expects the underlying input to be the forward price.
- Volatility and rates use decimal units:
- `0.20` means 20% annualized volatility
- `0.05` means 5% annualized rate
- `time_to_expiry` is expressed in years.
## Quick examples
```python
from ferro_ta.analysis.options import greeks, implied_volatility, option_price
price = option_price(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
iv = implied_volatility(price, 100.0, 100.0, 0.05, 1.0, option_type="call")
g = greeks(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
print(price, iv, g.delta)
```
```python
from ferro_ta.analysis.futures import basis, curve_summary
print(basis(100.0, 103.0))
print(curve_summary(100.0, [0.1, 0.5, 1.0], [101.0, 102.0, 104.0]))
```
```python
from ferro_ta.analysis.derivatives_payoff import PayoffLeg, strategy_payoff
legs = [
PayoffLeg("option", "long", option_type="call", strike=100.0, premium=5.0),
PayoffLeg("future", "long", entry_price=100.0),
]
grid = [90.0, 100.0, 110.0]
print(strategy_payoff(grid, legs=legs))
```
## Notes
- Existing `iv_rank`, `iv_percentile`, and `iv_zscore` names are preserved.
- The derivatives layer is analytics-only: there is no broker connectivity,
order routing, or execution workflow in this API.
+126
View File
@@ -0,0 +1,126 @@
Derivatives Analytics
=====================
``ferro-ta`` includes a Rust-backed derivatives layer for analytics, research,
and simulation workflows. The implementation is analytics-only: there is no
broker connectivity, order routing, or execution engine in this package.
What Is Included
----------------
Options analytics
~~~~~~~~~~~~~~~~~
- Rolling IV helpers: ``iv_rank``, ``iv_percentile``, ``iv_zscore``
- Black-Scholes-Merton pricing
- Black-76 pricing
- Greeks: delta, gamma, vega, theta, rho
- Implied volatility inversion
- Smile metrics: ATM IV, 25-delta risk reversal, butterfly, skew slope, convexity
- Chain helpers: moneyness labels and strike selection by offset or delta
Futures analytics
~~~~~~~~~~~~~~~~~
- Synthetic forwards and parity diagnostics
- Basis, annualized basis, implied carry, carry spread
- Continuous contract stitching: weighted, back-adjusted, ratio-adjusted
- Curve analytics: calendar spreads, slope, contango/backwardation summary
Strategy and payoff helpers
~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Typed strategy schemas for expiry selectors, strike selectors, leg presets,
risk controls, and simulation limits
- Multi-leg payoff aggregation
- Greeks aggregation across option and futures legs
Conventions
-----------
- ``model="bsm"`` expects spot as the underlying input.
- ``model="black76"`` expects forward as the underlying input.
- Volatility uses decimal annualized units: ``0.20`` means 20%.
- Rates and carry use decimal annualized units: ``0.05`` means 5%.
- ``time_to_expiry`` is expressed in years.
Options Example
---------------
.. code-block:: python
from ferro_ta.analysis.options import greeks, implied_volatility, option_price
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",
)
Futures Example
---------------
.. code-block:: python
from ferro_ta.analysis.futures import basis, curve_summary, synthetic_forward
front_basis = basis(100.0, 103.0)
synthetic = synthetic_forward(8.0, 5.0, 100.0, 0.02, 0.5)
curve = curve_summary(100.0, [0.1, 0.5, 1.0], [101.0, 102.0, 104.0])
Strategy and Payoff Example
---------------------------
.. code-block:: python
from ferro_ta.analysis.derivatives_payoff import PayoffLeg, aggregate_greeks, strategy_payoff
legs = [
PayoffLeg(
instrument="option",
side="long",
option_type="call",
strike=100.0,
premium=5.0,
volatility=0.20,
time_to_expiry=0.5,
),
PayoffLeg(
instrument="future",
side="long",
entry_price=100.0,
),
]
payoff = strategy_payoff([90.0, 100.0, 110.0], legs=legs)
portfolio_greeks = aggregate_greeks(100.0, legs=legs)
Related Modules
---------------
- :mod:`ferro_ta.analysis.options`
- :mod:`ferro_ta.analysis.futures`
- :mod:`ferro_ta.analysis.options_strategy`
- :mod:`ferro_ta.analysis.derivatives_payoff`
+3 -2
View File
@@ -13,6 +13,7 @@ ferro-ta Documentation
streaming
extended
batch
derivatives
benchmarks
plugins
changelog
@@ -35,7 +36,7 @@ Features:
- Math operators and transforms
- Type stubs (.pyi) for IDE auto-completion
- WASM binding for browser/Node.js use
- Options/IV helpers (IV rank, IV percentile, IV z-score) — see `Options/IV Helpers <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/options-volatility.md>`_
- Options/IV helpers and derivatives analytics — see :doc:`derivatives`
- Agentic workflow and LangChain tool wrappers — see `Agentic guide <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/agentic.md>`_
- MCP server for Cursor/Claude integration — see `MCP guide <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/mcp.md>`_
- Sphinx documentation
@@ -71,7 +72,7 @@ Further Reading
- `API Stability <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/stability.md>`_ — stability tiers, versioning, and deprecation policy.
- `Rust-First Policy <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/rust_first.md>`_ — all compute logic belongs in Rust; how to add new indicators.
- `Out-of-Core Execution <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/out-of-core.md>`_ — chunked processing and Dask integration.
- `Options/IV Helpers <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/options-volatility.md>`_ — IV rank, IV percentile, IV z-score.
- :doc:`derivatives`IV helpers, options pricing/Greeks/IV, futures analytics, strategy schemas, and payoff helpers.
- `Agentic Workflow <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/agentic.md>`_ — tools.py, workflow.py, LangChain integration.
- `MCP Server <https://github.com/pratikbhadane24/ferro-ta/blob/main/docs/mcp.md>`_ — run ferro-ta as an MCP server in Cursor/Claude.
+50 -72
View File
@@ -1,101 +1,79 @@
# Options and Implied Volatility
ferro-ta provides optional helpers for implied volatility (IV) analysis
via the `ferro_ta.options` module. This document describes the scope,
data format, dependency strategy, and limitations.
---
`ferro-ta` exposes options analytics from `ferro_ta.analysis.options`.
## Scope
The `ferro_ta.options` module focuses on **IV series analysis**:
The module now covers both classic IV-series helpers and model-based option
analytics:
- **IV rank** — where today's IV sits relative to the min/max over a look-back window.
- **IV percentile** — fraction of observations over a look-back window at or below today's IV.
- **IV z-score** — how many standard deviations today's IV is above the rolling mean.
- `iv_rank`, `iv_percentile`, `iv_zscore`
- Black-Scholes-Merton pricing
- Black-76 pricing
- Delta, gamma, vega, theta, rho
- Implied volatility inversion
- Smile metrics and chain helpers
These functions accept any 1-D IV series (e.g. VIX daily closes, single-name
30-day IV, etc.) and return rolling statistics.
Heavy computation runs in Rust through the `_ferro_ta` extension.
**Out of scope (for now):** Black-Scholes pricing, Greeks, option chain
parsing, synthetic forward construction, dividend adjustment. For full
option-pricing functionality consider `py_vollib`, `mibian`, or similar.
## IV-series helpers
---
## Data format
All functions accept a 1-D NumPy array (or any array-like) of IV values.
IV values are typically in **percentage points** (e.g. VIX = 20 means 20%
annualised volatility), but the helpers are unit-agnostic — they only
compare values within the rolling window.
The original rolling helpers remain available and keep their public names:
```python
import numpy as np
from ferro_ta.options import iv_rank, iv_percentile, iv_zscore
from ferro_ta.analysis.options import iv_rank, iv_percentile, iv_zscore
# VIX-like daily close series
iv = np.array([18.5, 22.3, 19.1, 25.0, 30.2, 27.8, 21.4, 19.0])
rank = iv_rank(iv, window=5) # rolling IV rank in [0, 1]
pct = iv_percentile(iv, window=5) # rolling IV percentile in [0, 1]
z = iv_zscore(iv, window=5) # rolling z-score
rank = iv_rank(iv, window=5)
pct = iv_percentile(iv, window=5)
z = iv_zscore(iv, window=5)
```
---
These helpers accept a 1-D IV series and return rolling statistics with
`NaN` during the warmup period.
## Dependency strategy
## Pricing and Greeks
The `ferro_ta.options` module uses **only NumPy** (already a core dependency).
No additional packages are required for the helpers described here.
```python
from ferro_ta.analysis.options import greeks, implied_volatility, option_price
For advanced option analytics (Black-Scholes, volatility surface
interpolation), install the optional extra:
```bash
pip install "ferro-ta[options]"
price = option_price(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
iv = implied_volatility(price, 100.0, 100.0, 0.05, 1.0, option_type="call")
g = greeks(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
```
This may install additional packages in the future (e.g. `py_vollib`).
Conventions:
---
- Volatility is decimal annualized volatility: `0.20` means 20%.
- Rates are decimal annualized rates: `0.05` means 5%.
- `time_to_expiry` is measured in years.
- `model="bsm"` uses spot as the underlying input.
- `model="black76"` uses forward as the underlying input.
## API reference
## Smile and chain helpers
### `iv_rank(iv_series, window=252)`
```python
from ferro_ta.analysis.options import label_moneyness, select_strike, smile_metrics
Rolling IV rank.
strikes = [80, 90, 100, 110, 120]
vols = [0.30, 0.25, 0.20, 0.22, 0.27]
```
rank_t = (IV_t - min(IV[t-window+1:t+1])) / (max(IV[t-window+1:t+1]) - min(IV[t-window+1:t+1]))
metrics = smile_metrics(strikes, vols, 100.0, 0.5)
labels = label_moneyness(strikes, 100.0, option_type="call")
atm = select_strike(strikes, 100.0, selector="ATM")
delta_strike = select_strike(
strikes,
100.0,
selector="DELTA0.25",
option_type="call",
volatilities=vols,
time_to_expiry=0.5,
)
```
Returns values in [0, 1]. NaN for the first `window - 1` bars.
## Related futures analytics
### `iv_percentile(iv_series, window=252)`
Rolling IV percentile: fraction of the *window* bars whose IV was at or
below the current value.
### `iv_zscore(iv_series, window=252)`
Rolling z-score: `(IV_t - rolling_mean) / rolling_std`.
---
## Limitations
- All functions use **O(n × window)** time complexity (pure Python loops).
For large windows or series consider vectorised alternatives.
- No option chain support; the module assumes IV series as input.
- Streaming (bar-by-bar) versions of these functions are not yet
implemented. For live use, maintain a rolling buffer and call the
functions on the buffer at each bar.
---
## See also
- `ferro_ta.options` — module source.
- `ferro_ta.statistic` — general statistical functions (STDDEV, VAR, CORREL, etc.).
- `ferro_ta.volatility` — price-based volatility indicators (ATR, NATR).
See `ferro_ta.analysis.futures` and
[`docs/derivatives-analytics.md`](./derivatives-analytics.md) for synthetic
forwards, basis, carry, curve, and roll analytics.
+6 -4
View File
@@ -315,10 +315,12 @@ bottlenecks are fixed or deferred.
- No fast path for already 2-D C-contiguous float64 in batch_sma/ema/rsi
(unlike `_to_f64` for 1-D); could avoid a potential copy.
**Options** (`python/ferro_ta/options.py`):
- `iv_rank`, `iv_percentile`, and `iv_zscore` are vectorized now, but
`iv_percentile`/`iv_zscore` still spend meaningful time in NumPy window
materialization on very long series.
**Derivatives analytics** (`python/ferro_ta/analysis/options.py`):
- `iv_rank`, `iv_percentile`, and `iv_zscore` now delegate to Rust.
- The Python layer mostly performs broadcasting and result shaping; the hot
path is in Rust.
- Model-based implied-volatility inversion is much faster now, but still more
expensive than direct pricing or Greeks due to root-finding.
**Features** (`python/ferro_ta/features.py`):
- `nan_policy="fill"` is vectorized now.
+16
View File
@@ -101,3 +101,19 @@ Extended Indicators
# Pivot Points
pivot, r1, s1, r2, s2 = PIVOT_POINTS(high, low, close, method="classic")
Derivatives Analytics
---------------------
.. code-block:: python
from ferro_ta.analysis.options import greeks, option_price
from ferro_ta.analysis.futures import basis
call_price = option_price(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
call_greeks = greeks(100.0, 100.0, 0.05, 1.0, 0.20, option_type="call")
front_basis = basis(100.0, 103.0)
See :doc:`derivatives` for the full analytics surface, including implied
volatility inversion, smile metrics, strike selection, futures curve tools,
strategy schemas, and multi-leg payoff helpers.