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:
co-authored by
Claude Sonnet 4.6
parent
2d5000262f
commit
602d675749
@@ -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:
|
||||
@@ -17,3 +17,4 @@ API Reference
|
||||
extended
|
||||
streaming
|
||||
batch
|
||||
analysis
|
||||
|
||||
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user