Commit f3ac6cd
authored
feat(greeks): Garman-Kohlhagen FX Greeks (delta/gamma/vega/theta/rho_d/rho_f) (#403)
* feat(greeks): add Garman-Kohlhagen FX Greeks for #401
Closed-form Greeks (delta, gamma, vega, theta, ρ_d, ρ_f) for the
Garman–Kohlhagen (1983) FX option pricing model. Closes #401.
Garman–Kohlhagen prices European options on a foreign-exchange spot rate
`S` quoted as domestic per unit of foreign, with the foreign currency
earning interest at rate `r_f`. Structurally GK ≡ BSM with the continuous
dividend yield `q = r_f`, and FX options carry two rate sensitivities
(domestic and foreign) instead of one.
Public surface (`src/greeks/garman_kohlhagen.rs`):
- `delta_gk`, `gamma_gk`, `vega_gk`, `theta_gk`,
`rho_domestic_gk`, `rho_foreign_gk`
- `GarmanKohlhagenGreeks` trait mirroring the `GarmanKohlhagen` pricing
trait — implementors provide `get_option(&self)` and inherit default
delegations to the free functions above.
FX field mapping: `risk_free_rate → r_d`, `dividend_yield → r_f`,
`underlying_price → S`. Greek units mirror the BSM module: vega per 1 %
vol, theta per calendar day (annual ÷ 365), domestic and foreign rho per
1 % rate. Quantity scales linearly; `Side::Short` flips the delta sign
only (consistent with `greeks::delta`).
The implementation uses the carry-adjusted form of `d1`/`d2` directly
(`b = r_d − r_f`) rather than delegating to the existing BSM Greeks. The
existing BSM Greeks compute `d1` from `risk_free_rate` only and then
multiply by `e^(-qT)` — a mismatch that produces incorrect deltas and
thetas when `dividend_yield ≠ 0`. The pricing kernels are unaffected
(they go through `calculate_d_values`, which does include `−q` in the
drift). Fixing the BSM Greeks is tracked separately; for GK we needed
correct FX numbers, hence the standalone implementation.
Tests (18, all passing):
- Delta range: `Δ_call ∈ (0, e^(-r_f·T))`, `Δ_put ∈ (-e^(-r_f·T), 0)`
- Spot delta-parity: `Δ_call − Δ_put = e^(-r_f·T)` to 1e-9
- `Γ > 0`, `ν > 0` for both call and put across moneyness
- `Γ_call = Γ_put`, `ν_call = ν_put` (Black-Scholes invariant)
- Rho signs: long call → +ρ_d, -ρ_f; long put → -ρ_d, +ρ_f
- BSM equivalence at `q = 0` (the only case where buggy BSM agrees)
- Theta vs numerical price-difference (5e-3 tolerance, 1-day bump)
- FX call-delta reference (S=0.98, K=1.00, r_d=5 %, r_f=4 %, T=4/12,
σ=10 %): Δ ≈ 0.3909 to 1e-3
- Zero volatility on every Greek → error
- American / Bermuda / exotic → `GreeksError::Pricing(UnsupportedOptionType)`
- Trait `GarmanKohlhagenGreeks` round-trips against the free functions
- `Side::Short` negates delta
- Quantity scales linearly
Example: `examples/examples_pricing/src/bin/garman_kohlhagen_greeks.rs`
walks ATM / ITM / OTM calls and puts on a 6-month EUR/USD scenario,
demonstrates the spot delta-parity identity (zero residual) and trait
usage on a wrapper type.
* Simplify Monte Carlo simulator data structure for improved rendering performance.
* address review: handle T=0 + use checked d_add in theta
- Added time_to_expiry helper that mirrors BSM Greeks: Greeks at
expiration return discrete values (delta = ±1/0 by intrinsic state,
gamma/vega/theta/ρ_d/ρ_f = 0) instead of erroring out from d1/d2.
- Replaced raw `+` in theta_gk with checked d_add to keep arithmetic
consistent with the rest of the crate (tracked in src/model/decimal.rs).
- New tests cover T=0 across all six Greeks, including ITM/OTM and short
side. 23 GK Greek tests pass.
Addresses Copilot review on PR #403.1 parent b9623a1 commit f3ac6cd
7 files changed
Lines changed: 991 additions & 6 deletions
File tree
- Draws/Simulation
- examples/examples_pricing
- src/bin
- src
- greeks
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
Lines changed: 118 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
0 commit comments