Skip to main content

finance_solution/derivatives/
mod.rs

1//! # Derivatives math (options & related)
2//!
3//! Pure **pricing, Greeks, and implied volatility** for engines that also run
4//! [`crate::stocks::ta`] on the underlier. This module does **not** subscribe to
5//! option chains, manage multi-leg books, or know about OSI / exchange symbols.
6//!
7//! ---
8//!
9//! ## How a quant uses these metrics (trading perspective)
10//!
11//! | Metric | Trading question | Desk habit |
12//! |--------|------------------|------------|
13//! | **Price** | Fair value vs mid / edge? | Compare model to NBBO; mark inventory |
14//! | **Δ Delta** | How much underlier exposure per option? | Hedge: sell ≈ Δ shares per long call |
15//! | **Γ Gamma** | How fast does the hedge go wrong? | Scalp gamma; size limits into events |
16//! | **ν Vega** | What if IV moves a point? | Vol trades, earnings, event premium |
17//! | **Θ Theta** | What does the book bleed overnight? | Carry P&L, calendar spreads |
18//! | **ρ Rho** | Rate risk? | Usually second-order for short-dated equity |
19//! | **IV** | What vol is the market implying? | Surfaces, relative value, skew stories |
20//! | **Intrinsic / time value** | How much is “optionality”? | Early exercise intuition (European here) |
21//! | **Parity residual** | Is the quote book consistent? | Sanity / arb alert (within fees) |
22//!
23//! **Typical workflow on a name (e.g. AAPL):**
24//!
25//! 1. Trade the **underlier path** with TA (`StochState`, `EmaState`, …) on 1m/5s bars.  
26//! 2. For each option of interest, maintain **IV from mid** and **Greeks at live spot**.  
27//! 3. Risk: sum Δ/Γ/ν over positions; hedge underlier when net Δ exceeds a band.  
28//! 4. Research: reprice a chain on a vol surface assumption; compare to TA regime (e.g. high RVOL + high IV).
29//!
30//! This crate supplies steps 1–3 **math only**. Order routing, position servers, and
31//! “should I sell the 0.30Δ call?” stay in *your* strategy code.
32//!
33//! ---
34//!
35//! ## How an engineer wires this (engineering perspective)
36//!
37//! ```text
38//! Market data (async / websockets)          finance-solution (sync, pure)
39//! ───────────────────────────────          ─────────────────────────────
40//! 1m bars for underlier          ──push──► StochState / EmaState / …
41//! option quote (bid/ask/mid)     ──IV───► BsmState::set_vol_from_price
42//! underlier tick                 ──spot─► for c in chain { c.set_spot(s); greeks() }
43//! ```
44//!
45//! **Recommended shape (mirrors TA):**
46//!
47//! | Layer | Type | When |
48//! |-------|------|------|
49//! | Config | [`BsmParams`] (`Copy`) | Contract + market inputs |
50//! | Validated | [`ValidatedBsm::new`] | One-shot research / backtest bar |
51//! | Live | [`BsmState`] | Per-contract object in `HashMap` |
52//! | Teaching | [`bsm_solution`] | Formulas + `print_table` |
53//!
54//! **Concurrency:** keep math **sync**. Your runtime may:
55//!
56//! - `rayon::par_iter` over symbols or strikes when recalculating a chain on a spot move  
57//! - `tokio` tasks that only **receive** data then call `set_spot` / `push`  
58//!
59//! Do **not** put `async` inside these functions — there is no I/O to await.
60//!
61//! **Joining TA + options for one underlier** (your types, illustrative):
62//!
63//! ```ignore
64//! struct UnderlierBook {
65//!     ta: StochState,                    // bars
66//!     options: HashMap<StrikeKey, BsmState>, // chain
67//! }
68//! // on_bar  -> ta.push(...); maybe recompute filters
69//! // on_spot -> for opt in options.values_mut() { opt.set_spot(s)?; }
70//! // on_opt_quote -> opt.set_vol_from_price(mid)?;
71//! ```
72//!
73//! ---
74//!
75//! ## Models (phased)
76//!
77//! | Model | Underlier | Status |
78//! |-------|-----------|--------|
79//! | Black–Scholes–Merton | Spot `S`, continuous yield `q` | **available** |
80//! | Black ’76 | Forward / futures `F` | planned |
81//! | Garman–Kohlhagen | FX | planned |
82//!
83//! Equity **single-name** Europeans with continuous yield ≈ BSM.  
84//! **Options on futures** / many index products → Black ’76 (later).  
85//! **Crypto perps** need funding / mark conventions outside this module.
86//!
87//! ## Units (read carefully)
88//!
89//! | Input | Unit |
90//! |-------|------|
91//! | Spot / strike | same money units |
92//! | `time_years` | **years** (`30.0/365.25` for ~30 calendar days) |
93//! | `rate`, `dividend_yield` | continuous, absolute (`0.05` = 5%) |
94//! | `vol` | annualized absolute (`0.20` = 20%) |
95//! | Vega | per **+1.0** in σ (use [`BsmGreeks::vega_per_vol_point`] for per 1%) |
96//! | Theta | per **year** (use [`BsmGreeks::theta_per_calendar_day`] for daily) |
97//!
98//! ## Quick start
99//!
100//! ```
101//! use finance_solution::derivatives::{
102//!     OptionType, BsmParams, ValidatedBsm, bsm_price, bsm_greeks, bsm_implied_vol,
103//! };
104//!
105//! let p = BsmParams::atm_one_year(100.0, 0.05, 0.20);
106//! let model = ValidatedBsm::new(p).unwrap();
107//! let call = model.price(OptionType::Call).unwrap();
108//! let g = model.greeks(OptionType::Call).unwrap();
109//! assert!(call > 0.0 && g.delta > 0.0 && g.delta < 1.0);
110//!
111//! // Market mid → IV
112//! let iv = bsm_implied_vol(p, OptionType::Call, call).unwrap();
113//! assert!((iv - 0.20).abs() < 1e-4);
114//!
115//! let _ = bsm_price(p, OptionType::Put).unwrap();
116//! let _ = bsm_greeks(p, OptionType::Put).unwrap();
117//! ```
118//!
119//! Live underlier ticks: [`BsmState`]. Teaching: [`bsm_solution`].
120
121pub mod black_scholes;
122pub mod implied_vol;
123pub mod norm;
124pub mod state;
125pub mod types;
126
127#[doc(inline)]
128pub use black_scholes::*;
129#[doc(inline)]
130pub use implied_vol::*;
131#[doc(inline)]
132pub use state::*;
133#[doc(inline)]
134pub use types::*;