Analysis Modules

ferro_ta.analysis.options — Rust-backed derivatives analytics for options.

This module preserves the legacy IV-series helpers and expands them with pricing, Greeks, implied-volatility inversion, smile analytics, and strike selection helpers suitable for research and simulation workflows.

class ferro_ta.analysis.options.ExtendedGreeks(vanna, volga, charm, speed, color)[source]

Bases: object

Container for second-order and cross Greeks.

Parameters:
charm: float | ndarray[tuple[Any, ...], dtype[float64]]
color: float | ndarray[tuple[Any, ...], dtype[float64]]
speed: float | ndarray[tuple[Any, ...], dtype[float64]]
to_dict()[source]
Return type:

dict[str, float | ndarray[tuple[Any, …], dtype[float64]]]

vanna: float | ndarray[tuple[Any, ...], dtype[float64]]
volga: float | ndarray[tuple[Any, ...], dtype[float64]]
class ferro_ta.analysis.options.OptionGreeks(delta, gamma, vega, theta, rho)[source]

Bases: object

Container for first-order Greeks.

Parameters:
delta: float | ndarray[tuple[Any, ...], dtype[float64]]
gamma: float | ndarray[tuple[Any, ...], dtype[float64]]
rho: float | ndarray[tuple[Any, ...], dtype[float64]]
theta: float | ndarray[tuple[Any, ...], dtype[float64]]
to_dict()[source]
Return type:

dict[str, float | ndarray[tuple[Any, …], dtype[float64]]]

vega: float | ndarray[tuple[Any, ...], dtype[float64]]
class ferro_ta.analysis.options.SmileMetrics(atm_iv, risk_reversal_25d, butterfly_25d, skew_slope, convexity)[source]

Bases: object

Summary metrics for a single smile slice.

Parameters:
atm_iv: float
butterfly_25d: float
convexity: float
risk_reversal_25d: float
skew_slope: float
to_dict()[source]
Return type:

dict[str, float]

class ferro_ta.analysis.options.VolCone(windows, min, p25, median, p75, max)[source]

Bases: object

Historical realized vol distribution across window lengths.

Parameters:
max: ndarray[tuple[Any, ...], dtype[float64]]
median: ndarray[tuple[Any, ...], dtype[float64]]
min: ndarray[tuple[Any, ...], dtype[float64]]
p25: ndarray[tuple[Any, ...], dtype[float64]]
p75: ndarray[tuple[Any, ...], dtype[float64]]
to_dict()[source]
Return type:

dict[str, ndarray[tuple[Any, …], dtype[float64]]]

windows: ndarray[tuple[Any, ...], dtype[float64]]
ferro_ta.analysis.options.american_option_price(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', carry=0.0)[source]

American option price using the Barone-Adesi-Whaley (1987) approximation.

Accurate to within a few basis points for standard equity/index parameters. O(1) per evaluation — suitable for batch pricing or calibration.

Parameters:
  • underlying (ArrayLike | float) – Current underlying (spot) price.

  • strike (ArrayLike | float) – Option strike price.

  • rate (ArrayLike | float) – Risk-free rate (annualised, decimal).

  • time_to_expiry (ArrayLike | float) – Time to expiry in years.

  • volatility (ArrayLike | float) – Implied volatility (annualised, decimal).

  • option_type (str) – "call" (default) or "put".

  • carry (ArrayLike | float) – Continuous carry / dividend yield (annualised, decimal). Default 0. For calls with carry = 0 (no dividends) early exercise is never optimal and the result equals the European BSM price.

Returns:

American option price ≥ European BSM price.

Return type:

float or NDArray[float64]

Notes

The BAW approximation uses a quadratic equation to find the critical exercise boundary S* via Newton-Raphson iteration, then adds the early exercise premium on top of the European price.

Reference: Barone-Adesi, G. & Whaley, R.E. (1987). “Efficient Analytic Approximation of American Option Values.” Journal of Finance, 42(2), 301–320.

See also

early_exercise_premium

Difference between American and European prices.

ferro_ta.analysis.options.black_76_price(forward, strike, rate, time_to_expiry, volatility, *, option_type='call')[source]

Price options under Black-76.

Parameters:
  • forward (ArrayLike | float)

  • strike (ArrayLike | float)

  • rate (ArrayLike | float)

  • time_to_expiry (ArrayLike | float)

  • volatility (ArrayLike | float)

  • option_type (str)

Return type:

float | ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.black_scholes_price(spot, strike, rate, time_to_expiry, volatility, *, option_type='call', dividend_yield=0.0)[source]

Price options under Black-Scholes-Merton.

Parameters:
  • spot (ArrayLike | float)

  • strike (ArrayLike | float)

  • rate (ArrayLike | float)

  • time_to_expiry (ArrayLike | float)

  • volatility (ArrayLike | float)

  • option_type (str)

  • dividend_yield (ArrayLike | float)

Return type:

float | ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.close_to_close_vol(close, window=20, trading_days_per_year=252.0)[source]

Rolling close-to-close realized volatility (annualised).

Baseline estimator — uses only closing prices. Less efficient than OHLC estimators but requires only daily close data.

Parameters:
  • close (ArrayLike) – Array of closing prices (length ≥ window + 1).

  • window (int) – Rolling look-back period in bars (default 20).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Same length as close. First window values are NaN.

Return type:

NDArray[float64]

Notes

Formula:

σ = √( Σᵢ ln²(Cᵢ/Cᵢ₋₁) / window × trading_days_per_year )

No Bessel correction is applied (population variance, not sample variance).

ferro_ta.analysis.options.digital_option_greeks(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', digital_type='cash_or_nothing', carry=0.0)[source]

Delta, gamma, and vega for a digital option via numerical bumping.

Uses central finite differences (spot bump ε = spot × 10⁻³ for delta/gamma; vol bump ε = 10⁻³ for vega). Theta and rho are set to NaN.

Parameters:
Returns:

Named tuple; only delta, gamma, vega are finite. theta and rho are NaN.

Return type:

OptionGreeks

ferro_ta.analysis.options.digital_option_price(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', digital_type='cash_or_nothing', carry=0.0)[source]

Price a digital (binary) option under BSM.

Parameters:
  • underlying (ArrayLike | float) – Current underlying (spot) price.

  • strike (ArrayLike | float) – Option strike price.

  • rate (ArrayLike | float) – Risk-free rate (annualised, decimal).

  • time_to_expiry (ArrayLike | float) – Time to expiry in years.

  • volatility (ArrayLike | float) – Implied volatility (annualised, decimal).

  • option_type (str) – "call" (default) or "put".

  • digital_type (str) – "cash_or_nothing" (default) — pays 1 unit of cash if ITM at expiry; or "asset_or_nothing" — pays the underlying asset price.

  • carry (ArrayLike | float) – Continuous carry / dividend yield (annualised, decimal). Default 0.

Returns:

Option price. Returns a scalar when all inputs are scalars, or an array when any input is an array.

Return type:

float or NDArray[float64]

Notes

Closed-form BSM formulas:

Cash-or-nothing call: e^{−rT} · N(d₂)
Cash-or-nothing put:  e^{−rT} · N(−d₂)
Asset-or-nothing call: S · e^{−qT} · N(d₁)
Asset-or-nothing put:  S · e^{−qT} · N(−d₁)

Put-call parity for cash-or-nothing: call + put = e^{−rT}. Put-call parity for asset-or-nothing: call + put = S · e^{−qT}.

Invalid inputs (non-positive spot/strike, negative time or vol) return NaN.

ferro_ta.analysis.options.early_exercise_premium(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', carry=0.0)[source]

Early exercise premium: American price − European BSM price.

Represents the additional value an American option holder gains from the right to exercise before expiry. Always ≥ 0.

Parameters:
Returns:

Premium ≥ 0. Typically 0 for calls with no dividends.

Return type:

float or NDArray[float64]

Notes

For equity calls with zero carry (no dividends), early exercise is never optimal so the premium is ≈ 0. For puts (or calls on dividend-paying underlyings), the premium increases with in-the-moneyness, rate, and time to expiry.

ferro_ta.analysis.options.expected_move(spot, iv, days_to_expiry, trading_days_per_year=252.0)[source]

Expected ±1σ move over days_to_expiry calendar days.

Uses the log-normal approximation:

upper_move = spot × e^{+σ√(days/trading_days)} − spot
lower_move = spot × e^{−σ√(days/trading_days)} − spot
Parameters:
  • spot (float) – Current underlying price.

  • iv (float) – Implied volatility (annualised, decimal — e.g. 0.20 for 20 %).

  • days_to_expiry (float) – Number of calendar days until expiry.

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

(lower_move, upper_move) — signed absolute price changes from spot. lower_move < 0, upper_move > 0.

Return type:

tuple[float, float]

Notes

Because of log-normal skew, |upper_move| > |lower_move|.

Examples

>>> from ferro_ta.analysis.options import expected_move
>>> lower, upper = expected_move(100.0, 0.20, 30)
>>> round(upper, 2)
7.14
ferro_ta.analysis.options.extended_greeks(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', model='bsm', carry=0.0)[source]

Return vanna, volga, charm, speed, and color (second-order / cross Greeks).

All Greeks are computed via closed-form BSM formulas. Black-76 is not yet supported and returns NaN for all five values.

Parameters:
  • underlying (ArrayLike | float) – Current underlying (spot) price.

  • strike (ArrayLike | float) – Option strike price.

  • rate (ArrayLike | float) – Risk-free rate (annualised, decimal — e.g. 0.05 for 5 %).

  • time_to_expiry (ArrayLike | float) – Time to expiry in years.

  • volatility (ArrayLike | float) – Implied volatility (annualised, decimal).

  • option_type (str) – "call" (default) or "put".

  • model (str) – "bsm" (default). "black76" returns NaN for all fields.

  • carry (ArrayLike | float) – Continuous carry / dividend yield (annualised, decimal). Default 0.

Returns:

Named tuple with fields:

  • vanna — ∂Δ/∂σ: sensitivity of delta to a change in vol.

  • volga — ∂²V/∂σ² (vomma): sensitivity of vega to a change in vol.

  • charm — ∂Δ/∂t: daily rate of change in delta (theta of delta).

  • speed — ∂Γ/∂S: rate of change in gamma with respect to spot.

  • color — ∂Γ/∂t: daily rate of change in gamma.

Return type:

ExtendedGreeks

Notes

Inputs may be scalars or broadcastable arrays. When arrays are supplied each field of the returned ExtendedGreeks is an NDArray.

Closed-form expressions (BSM, zero-carry):

vanna = -e^{-qT} · φ(d₁) · d₂ / σ
volga = S · e^{-qT} · φ(d₁) · √T · d₁ · d₂ / σ
charm = -e^{-qT} · φ(d₁) · [2(r-q)T - d₂·σ·√T] / (2T·σ·√T)
speed = -Γ/S · (d₁/(σ√T) + 1)
color = -Γ · [r-q + d₁·σ/(2√T) + (2(r-q)T - d₂·σ√T)·d₁/(2T·σ√T)]
ferro_ta.analysis.options.garman_klass_vol(open, high, low, close, window=20, trading_days_per_year=252.0)[source]

Rolling Garman-Klass OHLC realized volatility estimator (annualised).

Extends Parkinson by incorporating the open-close return. ~7.4× more efficient than close-to-close. Does not handle overnight gaps.

Parameters:
  • open (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • high (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • low (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • close (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • window (int) – Rolling look-back period in bars (default 20).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Same length as close. First window - 1 values are NaN.

Return type:

NDArray[float64]

Notes

Per-bar contribution:

GK = 0.5·ln²(H/L) − (2·ln2 − 1)·ln²(C/O)

Reference: Garman, M.B. & Klass, M.J. (1980). “On the Estimation of Security Price Volatilities from Historical Data.” Journal of Business, 53(1).

ferro_ta.analysis.options.greeks(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', model='bsm', carry=0.0)[source]

Return delta, gamma, vega, theta, and rho.

Parameters:
  • underlying (ArrayLike | float)

  • strike (ArrayLike | float)

  • rate (ArrayLike | float)

  • time_to_expiry (ArrayLike | float)

  • volatility (ArrayLike | float)

  • option_type (str)

  • model (str)

  • carry (ArrayLike | float)

Return type:

OptionGreeks

ferro_ta.analysis.options.implied_volatility(price, underlying, strike, rate, time_to_expiry, *, option_type='call', model='bsm', carry=0.0, initial_guess=0.2, tolerance=1e-08, max_iterations=100)[source]

Invert option prices to implied volatility.

Parameters:
  • price (ArrayLike | float)

  • underlying (ArrayLike | float)

  • strike (ArrayLike | float)

  • rate (ArrayLike | float)

  • time_to_expiry (ArrayLike | float)

  • option_type (str)

  • model (str)

  • carry (ArrayLike | float)

  • initial_guess (ArrayLike | float)

  • tolerance (float)

  • max_iterations (int)

Return type:

float | ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.iv_percentile(iv_series, window=252)[source]

Compute rolling IV percentile in Rust while preserving the legacy API.

Parameters:
  • iv_series (ArrayLike)

  • window (int)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.iv_rank(iv_series, window=252)[source]

Compute rolling IV rank in Rust while preserving the legacy API.

Parameters:
  • iv_series (ArrayLike)

  • window (int)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.iv_zscore(iv_series, window=252)[source]

Compute rolling IV z-score in Rust while preserving the legacy API.

Parameters:
  • iv_series (ArrayLike)

  • window (int)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.label_moneyness(strikes, reference_price, *, option_type='call')[source]

Label strikes as ITM, ATM, or OTM.

Parameters:
  • strikes (ArrayLike)

  • reference_price (float)

  • option_type (str)

Return type:

ndarray[tuple[Any, …], dtype[object_]]

ferro_ta.analysis.options.option_price(underlying, strike, rate, time_to_expiry, volatility, *, option_type='call', model='bsm', carry=0.0)[source]

Model-dispatched option price helper.

Parameters:
  • underlying (ArrayLike | float)

  • strike (ArrayLike | float)

  • rate (ArrayLike | float)

  • time_to_expiry (ArrayLike | float)

  • volatility (ArrayLike | float)

  • option_type (str)

  • model (str)

  • carry (ArrayLike | float)

Return type:

float | ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options.parkinson_vol(high, low, window=20, trading_days_per_year=252.0)[source]

Rolling Parkinson high-low realized volatility estimator (annualised).

~5× more efficient than close-to-close for diffusion processes. Does not account for drift or overnight gaps.

Parameters:
  • high (ArrayLike) – Arrays of daily high and low prices (same length, ≥ window).

  • low (ArrayLike) – Arrays of daily high and low prices (same length, ≥ window).

  • window (int) – Rolling look-back period in bars (default 20).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Same length as high. First window - 1 values are NaN.

Return type:

NDArray[float64]

Notes

Formula per window:

σ² = (1 / (4·ln2·window)) · Σ ln²(Hᵢ/Lᵢ) × trading_days_per_year

Reference: Parkinson, M. (1980). “The Extreme Value Method for Estimating the Variance of the Rate of Return.” Journal of Business, 53(1).

ferro_ta.analysis.options.put_call_parity_deviation(call_price, put_price, spot, strike, rate, time_to_expiry, *, carry=0.0)[source]

Put-call parity deviation: C P (S·e^{−q·T} K·e^{−r·T}).

At no-arbitrage the deviation is exactly 0. A non-zero result indicates mispricing, a data error, or a stale quote.

Parameters:
  • call_price (float) – Market or model price of the call option.

  • put_price (float) – Market or model price of the put option.

  • spot (float) – Current underlying price.

  • strike (float) – Common strike price of the call and put.

  • rate (float) – Risk-free rate (annualised, decimal).

  • time_to_expiry (float) – Time to expiry in years.

  • carry (float) – Continuous dividend yield / carry rate (annualised, decimal).

Returns:

Signed deviation. Positive → call is overpriced relative to put; negative → put is overpriced relative to call.

Return type:

float

Examples

>>> from ferro_ta.analysis.options import option_price, put_call_parity_deviation
>>> call = option_price(100, 100, 0.05, 1.0, 0.2, option_type="call")
>>> put  = option_price(100, 100, 0.05, 1.0, 0.2, option_type="put")
>>> put_call_parity_deviation(call, put, 100, 100, 0.05, 1.0)  # ≈ 0.0
ferro_ta.analysis.options.rogers_satchell_vol(open, high, low, close, window=20, trading_days_per_year=252.0)[source]

Rolling Rogers-Satchell OHLC realized volatility estimator (annualised).

Drift-invariant: unbiased for assets with non-zero expected return. Does not handle overnight gaps.

Parameters:
  • open (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • high (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • low (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • close (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window).

  • window (int) – Rolling look-back period in bars (default 20).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Same length as close. First window - 1 values are NaN.

Return type:

NDArray[float64]

Notes

Per-bar contribution (u = ln(H/O), d = ln(L/O), c = ln(C/O)):

RS = u·(u − c) + d·(d − c)

Reference: Rogers, L.C.G. & Satchell, S.E. (1991). “Estimating Variance from High, Low and Closing Prices.” Annals of Applied Probability, 1(4).

ferro_ta.analysis.options.select_strike(strikes, reference_price, *, option_type='call', selector='ATM', delta_target=None, volatilities=None, time_to_expiry=None, model='bsm', rate=0.0, carry=0.0)[source]

Select a strike by ATM/ITM/OTM offset or delta target.

Parameters:
  • strikes (ArrayLike)

  • reference_price (float)

  • option_type (str)

  • selector (str)

  • delta_target (float | None)

  • volatilities (ArrayLike | None)

  • time_to_expiry (float | None)

  • model (str)

  • rate (float)

  • carry (float)

Return type:

float | None

ferro_ta.analysis.options.smile_metrics(strikes, vols, reference_price, time_to_expiry, *, model='bsm', rate=0.0, carry=0.0)[source]

Compute ATM IV, 25-delta RR/BF, skew slope, and convexity.

Parameters:
  • strikes (ArrayLike)

  • vols (ArrayLike)

  • reference_price (float)

  • time_to_expiry (float)

  • model (str)

  • rate (float)

  • carry (float)

Return type:

SmileMetrics

ferro_ta.analysis.options.term_structure_slope(tenors, atm_ivs)[source]

Slope of ATM IV against tenor.

Parameters:
  • tenors (ArrayLike)

  • atm_ivs (ArrayLike)

Return type:

float

ferro_ta.analysis.options.vol_cone(close, *, windows=(21, 42, 63, 126, 252), trading_days_per_year=252.0)[source]

Historical realised vol distribution across window lengths (volatility cone).

For each window, computes the full history of rolling close-to-close realised vol, then returns the min / p25 / median / p75 / max distribution. Contextualises current implied vol: “Is 30 % IV cheap or expensive?”

Parameters:
  • close (ArrayLike) – Array of closing prices (length ≥ max(windows) + 1).

  • windows (tuple[int, ...]) – Tuple of rolling window sizes in bars. Default (21, 42, 63, 126, 252) (approx. 1 month, 2 months, 3 months, 6 months, 1 year).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Dataclass with arrays windows, min, p25, median, p75, max — one value per element of windows.

Return type:

VolCone

Notes

Uses close-to-close vol internally. Overlay the current IV on the cone to see whether it is historically cheap or expensive for each tenor.

Examples

>>> import numpy as np
>>> from ferro_ta.analysis.options import vol_cone
>>> rng = np.random.default_rng(0)
>>> close = 100 * np.cumprod(np.exp(rng.normal(0, 0.01, 500)))
>>> cone = vol_cone(close, windows=(21, 63, 252))
>>> cone.median  # annualised median realised vol per window
ferro_ta.analysis.options.yang_zhang_vol(open, high, low, close, window=20, trading_days_per_year=252.0)[source]

Rolling Yang-Zhang OHLC realized volatility estimator (annualised).

The most efficient standard estimator (~14× vs close-to-close). Handles overnight gaps by combining overnight, intraday open-close, and Rogers-Satchell variance components with an optimal weight k.

Parameters:
  • open (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window + 1).

  • high (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window + 1).

  • low (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window + 1).

  • close (ArrayLike) – Arrays of daily OHLC prices (same length, ≥ window + 1).

  • window (int) – Rolling look-back period in bars (default 20).

  • trading_days_per_year (float) – Annualisation factor (default 252).

Returns:

Same length as close. First window values are NaN.

Return type:

NDArray[float64]

Notes

Mixed estimator:

σ²_YZ = σ²_overnight + k·σ²_open_close + (1−k)·σ²_RS

where k = 0.34 / (1.34 + (window+1)/(window-1)).

Reference: Yang, D. & Zhang, Q. (2000). “Drift-Independent Volatility Estimation Based on High, Low, Open, and Close Prices.” Journal of Business, 73(3).

ferro_ta.analysis.futures — Futures and forward-curve analytics.

class ferro_ta.analysis.futures.CurveSummary(front_basis: 'float', average_basis: 'float', slope: 'float', is_contango: 'bool')[source]

Bases: object

Parameters:
average_basis: float
front_basis: float
is_contango: bool
slope: float
to_dict()[source]
Return type:

dict[str, float | bool]

ferro_ta.analysis.futures.annualized_basis(spot, future, time_to_expiry)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.back_adjusted_continuous_contract(front, next_contract, next_weights)[source]
Parameters:
  • front (ArrayLike)

  • next_contract (ArrayLike)

  • next_weights (ArrayLike)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.futures.basis(spot, future)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.calendar_spreads(futures_prices)[source]
Parameters:

futures_prices (ArrayLike)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.futures.carry_spread(spot, future, rate, time_to_expiry)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.curve_slope(tenors, futures_prices)[source]
Parameters:
  • tenors (ArrayLike)

  • futures_prices (ArrayLike)

Return type:

float

ferro_ta.analysis.futures.curve_summary(spot, tenors, futures_prices)[source]
Parameters:
  • spot (float)

  • tenors (ArrayLike)

  • futures_prices (ArrayLike)

Return type:

CurveSummary

ferro_ta.analysis.futures.implied_carry_rate(spot, future, time_to_expiry)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.parity_gap(call_price, put_price, spot, strike, rate, time_to_expiry, *, carry=0.0)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.ratio_adjusted_continuous_contract(front, next_contract, next_weights)[source]
Parameters:
  • front (ArrayLike)

  • next_contract (ArrayLike)

  • next_weights (ArrayLike)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.futures.roll_yield(front_price, next_price, time_to_expiry)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.synthetic_forward(call_price, put_price, strike, rate, time_to_expiry)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.synthetic_spot(call_price, put_price, strike, rate, time_to_expiry, *, carry=0.0)[source]
Parameters:
Return type:

float

ferro_ta.analysis.futures.weighted_continuous_contract(front, next_contract, next_weights)[source]
Parameters:
  • front (ArrayLike)

  • next_contract (ArrayLike)

  • next_weights (ArrayLike)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.options_strategy — Typed strategy parameter schemas.

class ferro_ta.analysis.options_strategy.DerivativesStrategy(name: 'str', preset: 'LegPreset | str' = <LegPreset.CUSTOM: 'custom'>, legs: 'tuple[StrategyLeg, ...]'=<factory>, risk_controls: 'RiskControl' = <factory>, risk_mode: 'RiskMode | str' = <RiskMode.COMBINED_PNL: 'combined_pnl'>, commission: 'float' = 0.0, slippage: 'float' = 0.0, spread_assumption: 'float' = 0.0, limits: 'SimulationLimits' = <factory>)[source]

Bases: object

Parameters:
commission: float = 0.0
legs: tuple[StrategyLeg, ...]
limits: SimulationLimits
name: str
preset: LegPreset | str = 'custom'
risk_controls: RiskControl
risk_mode: RiskMode | str = 'combined_pnl'
slippage: float = 0.0
spread_assumption: float = 0.0
to_dict()[source]
Return type:

dict[str, Any]

class ferro_ta.analysis.options_strategy.ExpirySelector(kind: 'ExpirySelectorKind | str', explicit_date: 'date | None' = None)[source]

Bases: object

Parameters:
explicit_date: date | None = None
kind: ExpirySelectorKind | str
class ferro_ta.analysis.options_strategy.ExpirySelectorKind(*values)[source]

Bases: str, Enum

CURRENT_MONTH = 'current_month'
CURRENT_WEEK = 'current_week'
EXPLICIT_DATE = 'explicit_date'
NEXT_MONTH = 'next_month'
NEXT_WEEK = 'next_week'
class ferro_ta.analysis.options_strategy.LegPreset(*values)[source]

Bases: str, Enum

BEAR_PUT_SPREAD = 'bear_put_spread'
BULL_CALL_SPREAD = 'bull_call_spread'
CUSTOM = 'custom'
IRON_CONDOR = 'iron_condor'
STRADDLE = 'straddle'
STRANGLE = 'strangle'
class ferro_ta.analysis.options_strategy.RiskControl(stop_loss_type: 'str | None' = None, stop_loss_value: 'float | None' = None, target_type: 'str | None' = None, target_value: 'float | None' = None, trailstop_type: 'str | None' = None, trailstop_value: 'float | None' = None, breakeven_trigger: 'float | None' = None)[source]

Bases: object

Parameters:
  • stop_loss_type (str | None)

  • stop_loss_value (float | None)

  • target_type (str | None)

  • target_value (float | None)

  • trailstop_type (str | None)

  • trailstop_value (float | None)

  • breakeven_trigger (float | None)

breakeven_trigger: float | None = None
stop_loss_type: str | None = None
stop_loss_value: float | None = None
target_type: str | None = None
target_value: float | None = None
trailstop_type: str | None = None
trailstop_value: float | None = None
class ferro_ta.analysis.options_strategy.RiskMode(*values)[source]

Bases: str, Enum

COMBINED_PNL = 'combined_pnl'
PER_LEG = 'per_leg'
class ferro_ta.analysis.options_strategy.SimulationLimits(max_premium_outlay: 'float | None' = None, max_loss_per_trade: 'float | None' = None, daily_max_drawdown: 'float | None' = None, cooldown_bars: 'int' = 0, reentry_allowed: 'bool' = True)[source]

Bases: object

Parameters:
  • max_premium_outlay (float | None)

  • max_loss_per_trade (float | None)

  • daily_max_drawdown (float | None)

  • cooldown_bars (int)

  • reentry_allowed (bool)

cooldown_bars: int = 0
daily_max_drawdown: float | None = None
max_loss_per_trade: float | None = None
max_premium_outlay: float | None = None
reentry_allowed: bool = True
class ferro_ta.analysis.options_strategy.StrategyLeg(underlying: 'str', expiry_selector: 'ExpirySelector | None', strike_selector: 'StrikeSelector | None', option_type: 'str | None', side: 'str' = 'long', quantity: 'int' = 1, instrument: 'str' = 'option', premium_limit: 'float | None' = None)[source]

Bases: object

Parameters:
expiry_selector: ExpirySelector | None
instrument: str = 'option'
option_type: str | None
premium_limit: float | None = None
quantity: int = 1
side: str = 'long'
strike_selector: StrikeSelector | None
underlying: str
class ferro_ta.analysis.options_strategy.StrikeSelector(kind: 'StrikeSelectorKind | str', steps: 'int' = 0, delta: 'float | None' = None, explicit_strike: 'float | None' = None)[source]

Bases: object

Parameters:
delta: float | None = None
explicit_strike: float | None = None
kind: StrikeSelectorKind | str
steps: int = 0
class ferro_ta.analysis.options_strategy.StrikeSelectorKind(*values)[source]

Bases: str, Enum

ATM = 'atm'
DELTA = 'delta'
EXPLICIT = 'explicit'
ITM = 'itm'
OTM = 'otm'
ferro_ta.analysis.options_strategy.build_strategy_preset(preset, *, name, underlying, expiry_selector, base_strike_selector=None, risk_controls=None, risk_mode=RiskMode.COMBINED_PNL, commission=0.0, slippage=0.0, spread_assumption=0.0, limits=None)[source]

Build a common research preset using typed leg definitions.

Parameters:
Return type:

DerivativesStrategy

ferro_ta.analysis.derivatives_payoff — Multi-leg payoff and Greeks aggregation.

class ferro_ta.analysis.derivatives_payoff.PayoffLeg(instrument: 'str', side: 'str', quantity: 'float' = 1.0, option_type: 'str | None' = None, strike: 'float | None' = None, premium: 'float' = 0.0, entry_price: 'float | None' = None, volatility: 'float | None' = None, time_to_expiry: 'float | None' = None, rate: 'float' = 0.0, carry: 'float' = 0.0, multiplier: 'float' = 1.0)[source]

Bases: object

Parameters:
carry: float = 0.0
entry_price: float | None = None
instrument: str
multiplier: float = 1.0
option_type: str | None = None
premium: float = 0.0
quantity: float = 1.0
rate: float = 0.0
side: str
strike: float | None = None
time_to_expiry: float | None = None
volatility: float | None = None
ferro_ta.analysis.derivatives_payoff.aggregate_greeks(spot, *, legs=None, strategy=None)[source]

Aggregate Greeks across option and futures legs.

Parameters:
Return type:

OptionGreeks

ferro_ta.analysis.derivatives_payoff.futures_leg_payoff(spot_grid, *, entry_price, side='long', quantity=1.0, multiplier=1.0)[source]

P/L profile for a futures leg.

Parameters:
  • spot_grid (ArrayLike)

  • entry_price (float)

  • side (str)

  • quantity (float)

  • multiplier (float)

Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.derivatives_payoff.option_leg_payoff(spot_grid, *, strike, premium=0.0, option_type='call', side='long', quantity=1.0, multiplier=1.0)[source]

Expiry payoff for a single option leg.

Parameters:
Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.derivatives_payoff.stock_leg_payoff(spot_grid, *, entry_price, side='long', quantity=1.0, multiplier=1.0)[source]

P/L profile for a single stock (equity) leg over a spot grid.

Payoff is linear:

P/L = sign(side) × quantity × multiplier × (spot − entry_price)

Mathematically equivalent to a futures leg — no optionality. Use this leg type when modelling strategies that hold the underlying equity: Covered Call, Protective Put, Collar, Covered Strangle, etc.

Parameters:
  • spot_grid (ArrayLike) – 1-D array of spot prices at which to evaluate the P/L.

  • entry_price (float) – Purchase (or short-sale) price of the stock.

  • side (str) – "long" (default) or "short".

  • quantity (float) – Number of shares / contracts (default 1).

  • multiplier (float) – Contract multiplier (default 1.0).

Returns:

P/L at each grid point, same shape as spot_grid.

Return type:

NDArray[float64]

ferro_ta.analysis.derivatives_payoff.strategy_payoff(spot_grid, *, legs=None, strategy=None)[source]

Aggregate expiry payoff across option and futures legs.

Parameters:
Return type:

ndarray[tuple[Any, …], dtype[float64]]

ferro_ta.analysis.derivatives_payoff.strategy_value(spot_grid, *, legs, time_to_expiry, volatility, rate=0.0, carry=0.0)[source]

Current BSM mid-price value of a multi-leg strategy over a spot grid.

Unlike strategy_payoff() (which computes intrinsic value at expiry), this uses live BSM pricing for option legs so the result reflects the pre-expiry value including time value.

Parameters:
  • spot_grid (ArrayLike) – Array of spot prices to evaluate.

  • legs (Sequence[PayoffLeg | Mapping[str, Any]]) – Sequence of PayoffLeg (or dicts). Option legs must have strike and premium set; future/stock legs must have entry_price set.

  • time_to_expiry (float) – Shared time-to-expiry (years) applied to all option legs.

  • volatility (float) – Shared implied vol applied to all option legs.

  • rate (float) – Risk-free rate applied to all legs.

  • carry (float) – Carry / dividend yield applied to all option legs.

Return type:

ndarray[tuple[Any, …], dtype[float64]]