Skip to content

Commit 18de062

Browse files
authored
Add Black-76 pricing model for futures/forwards (#396) (#398)
* feat(pricing): add Black-76 model for options on futures and forwards Implement Black-76 closed-form pricing for European options on futures, forwards, swaptions, and caps/floors. Core formulas: - d1 = [ln(F/K) + σ²T/2] / (σ√T) - d2 = d1 - σ√T - Call: e^(-rT) * [F*N(d1) - K*N(d2)] - Put: e^(-rT) * [K*N(-d2) - F*N(-d1)] Key differences vs Black-Scholes: - Input is forward price F (not spot S) - No carry term (F already incorporates all carry) - Unified discount factor e^(-rT) on both legs Changes: - src/pricing/black_76.rs: kernel + trait + 20 unit tests - src/greeks/utils.rs: calculate_d_values_black_76 helper (b=0 drift) - src/pricing/mod.rs: module documentation and exports - src/pricing/unified.rs: PricingEngine::ClosedFormBlack76 variant + dispatch - examples/examples_pricing/: new workspace crate with runnable demo - Cargo.toml: workspace members (alphabetical sort) Tests validate: - Hull canonical reference (F=K=20, ATM) - Put-call parity across ATM/ITM/OTM scenarios - Equivalence to BS-Merton with S=F*e^(-rT), q=0 - Monotonicity in forward price - Short side sign negation - Quantity invariance - Unsupported types (American, Bermuda, exotics) All tests pass; clippy/fmt/build clean. * docs(pricing): list Black-76 in lib.rs/pricing module guides Adds Black-76 to the file inventory in `src/lib.rs::Pricing Models`, extends the pricing-models mermaid with a `Forward-Priced` subgraph mapping `black_76 -> {Future, Forward}`, and lists Black-76 in the `pricing/mod.rs` Model Selection Guidelines and Performance Considerations sections. * fix(pricing): add #[non_exhaustive] to PricingEngine enum for semver compatibility The new ClosedFormBlack76 variant was flagged as a semver-breaking change because PricingEngine is a public exhaustive enum. Mark it #[non_exhaustive] to signal that future variants are possible, and add a catch-all _ pattern to price_option dispatch. * release(0.17.0): bump major + address copilot review Bumps the crate version to 0.17.0 because the 0.16.x → main diff introduces two semver-breaking changes: `PricingEngine` is now `#[non_exhaustive]` and `PricingEngine::ClosedFormBlack76` shifts the implicit discriminant of `PricingEngine::MonteCarlo` from 1 to 2 (flagged by `cargo-semver-checks`). Also addresses the three Copilot review comments on PR #398: - pricing/mod.rs: reword the Black-76 module description so it lists "options on" futures / forwards / commodity futures rather than the underlyings themselves. - pricing/unified.rs: extend the `price_option` `# Errors` docstring to cover Black-76 (`MethodError` for zero-vol / non-finite, `UnsupportedOptionType` for non-European inputs). - greeks/utils.rs: compute `expiration_date.get_years()?` once in `calculate_d_values_black_76` and reuse the value for both `d1` and `d2`. Also drops the now-unreachable `_` arm in `price_option` (intra-crate matches see all variants regardless of `#[non_exhaustive]`), which was failing the `lint` job, and updates the version references in `src/lib.rs` and `CHANGELOG.md`.
1 parent c7cfd85 commit 18de062

86 files changed

Lines changed: 1334 additions & 20 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.17.0] - 2026-04-26
11+
12+
Major release adding the Black-76 closed-form pricing model for European
13+
options on futures and forwards. The bump to `0.17.0` is required because
14+
`PricingEngine` is now `#[non_exhaustive]` (a semver-breaking change for
15+
downstream exhaustive matches) and the addition of
16+
`PricingEngine::ClosedFormBlack76` shifts the implicit discriminant of
17+
`PricingEngine::MonteCarlo` from `1` to `2`.
18+
19+
### Added
20+
21+
- `pricing::black_76`: closed-form `black_76(option) -> Result<Decimal, PricingError>`
22+
for European options on futures / forwards. Reuses the existing `d1`
23+
/ `d2` / `big_n` helpers; `Decimal` end-to-end via `d_mul` / `d_sub`;
24+
`tracing::instrument` on the entry point. Only `OptionType::European`
25+
is supported — American, Bermuda and exotics return
26+
`PricingError::UnsupportedOptionType`.
27+
- `pricing::Black76` trait with default `calculate_price_black_76`
28+
(mirrors `BlackScholes`).
29+
- `pricing::PricingEngine::ClosedFormBlack76` variant + dispatch from
30+
`price_option`.
31+
- `greeks::utils::calculate_d_values_black_76` `pub(crate)` helper.
32+
- `examples/examples_pricing/`: new workspace member with binary
33+
`black_76` (Hull canonical example, ITM commodity-futures call,
34+
unified-API dispatch, short-side sign convention).
35+
- `lib.rs` mermaid: `Forward-Priced` subgraph routing
36+
`black_76 -> {Future, Forward}`.
37+
38+
### Changed
39+
40+
- `pricing::PricingEngine` is now `#[non_exhaustive]` so future engine
41+
variants do not require a new major bump.
42+
- `pricing::mod.rs` Core Models / Model Selection Guidelines /
43+
Performance Considerations now include Black-76.
44+
- `financial_types` bumped to `0.2.2` (adds `UnderlyingAssetType::Future`
45+
and `UnderlyingAssetType::Forward`).
46+
1047
## [0.16.5] - 2026-04-20
1148

1249
Documentation-only release. Refresh the crate-level rustdoc and

Cargo.toml

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "optionstratlib"
3-
version = "0.16.5"
3+
version = "0.17.0"
44
edition = "2024"
55
authors = ["Joaquin Bejar <jb@taunais.com>"]
66
description = "OptionStratLib is a comprehensive Rust library for options trading and strategy development across multiple asset classes."
@@ -101,17 +101,18 @@ crate-type = ["cdylib", "rlib"]
101101

102102
[workspace]
103103
members = [
104+
"examples/examples_chain",
105+
"examples/examples_curves",
106+
"examples/examples_exotics",
107+
"examples/examples_metrics",
108+
"examples/examples_pricing",
104109
"examples/examples_simulation",
110+
"examples/examples_strategies",
105111
"examples/examples_strategies_best",
106112
"examples/examples_strategies_delta",
107-
"examples/examples_strategies",
113+
"examples/examples_surfaces",
108114
"examples/examples_visualization",
109-
"examples/examples_chain",
110115
"examples/examples_volatility",
111-
"examples/examples_curves",
112-
"examples/examples_surfaces",
113-
"examples/examples_metrics",
114-
"examples/examples_exotics",
115116
]
116117

117118
[workspace.dependencies]

Draws/Simulation/simulator_test_montecarlo.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.
67.9 KB
Loading
101 KB
Loading
68.4 KB
Loading
88.2 KB
Loading
115 KB
Loading
68 KB
Loading
77.5 KB
Loading

0 commit comments

Comments
 (0)