RustyQLib 0.0.3

RustyQLib is a lightweight yet robust quantitative finance library designed to price derivatives and perform risk analysis
Documentation
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
[![Build and Tests](https://github.com/siddharthqs/RustyQLib/actions/workflows/rust.yml/badge.svg)](https://github.com/siddharthqs/RustyQLib/actions/workflows/rust.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
![Crates.io](https://img.shields.io/crates/dr/rustyqlib)
![Crates.io](https://img.shields.io/crates/v/rustyqlib)
[![codecov](https://codecov.io/gh/siddharthqs/RustyQLib/graph/badge.svg?token=879K6LTTR4)](https://codecov.io/gh/siddharthqs/RustyQLib)

# RustyQLib — Pricing Options using JSON or XML

RustyQLib is a lightweight quantitative finance library written entirely in Rust.
It prices equity derivatives through JSON or XML contracts (a stateless pricing service
in a single binary) or as a Rust library, with an emphasis on numerically validated
implementations: every pricer is cross-checked against independent oracles,
put-call parity, replication identities and cross-engine agreement in the test suite.

## Highlights

- **Six pricing engines** — analytic closed forms, two analytic American
  approximations (Barone-Adesi-Whaley and Bjerksund-Stensland 2002), binomial
  tree, finite difference (log-spot Crank-Nicolson with Rannacher smoothing),
  and parallel Monte Carlo — behind one dispatch, so the same contract prices
  on any suitable engine.
- **Four volatility frameworks** — Black-Scholes, **Dupire local volatility**
  (calibrated non-parametrically from an implied vol surface), **Heston
  stochastic volatility** (semi-analytic characteristic-function pricing +
  Monte Carlo) with **Bates jump-diffusion extensions** (Heston + lognormal
  Merton jumps, and Heston + Kou double-exponential jumps, both priced
  semi-analytically through the shared characteristic-function machinery and
  calibrated by the same Levenberg-Marquardt transform-space pattern),
  **stochastic local volatility** (Heston-style variance times a leverage
  function calibrated to the Dupire surface by the particle / binning
  method, so vanillas reprice while forward smiles stay stochastic),
  and **SVI / SSVI parametric implied surfaces** (Gatheral,
  Gatheral-Jacquier) — Levenberg-Marquardt smile and surface calibration,
  Gatheral-Jacquier butterfly (`g(k)`) and calendar no-arbitrage checks, and
  sampling into the pricing vol surface.
- **JSON and XML** contracts and results, over a single shared schema.
- **Market-standard infrastructure** — discount curves with discount factors as the
  source of truth (flat / zero rates / discount factors / forward rates in, any
  compounding), volatility surfaces (flat, strike x expiry, moneyness, FX-style
  delta quotes), robust implied vol, day counts, term-structure-consistent PDE
  discounting, and **holiday calendars with business-day conventions**
  (weekends-only, TARGET, NYSE, UK bank holidays, custom lists — rule-based,
  any year): date adjustment (following / modified following / preceding),
  settlement lags (`add_business_days`), and periodic **schedule generation**
  (forward/backward with stubs). Autocallable observation dates can be
  calendar-generated (`.autocall_schedule(3, Calendar::UsNyse)`) or supplied
  explicitly (`autocall_observation_dates` in JSON), so observations never
  land on weekends or holidays.
- **Options on futures**: European vanillas priced with Black-76, both
  standard (discounted premium) and futures-style (margined, undiscounted).
- **Payoffs**: European & American vanillas, cash- and asset-or-nothing binaries,
  all eight barrier types (knock-in/out, up/down) with **rebates** (at hit or
  at expiry) and **double-barrier corridors** (Ikeda-Kunitomo), Asian options (arithmetic /
  geometric, fixed / floating strike), forward-start options, autocallable
  notes with coupons and knock-in protection (incl. **Phoenix certificates**
  with conditional and memory coupons), **lookback options** (floating and
  fixed strike, Goldman-Sosin-Gatto / Conze-Viswanathan closed forms),
  **accumulators / decumulators** (daily geared accrual with knock-out,
  priced as a strip of barrier-option pairs or by Monte Carlo),
  **variance, gamma and corridor variance swaps** (model-free replication
  over any smile: DDKZ log contract, spot-weighted `S ln S` contract with
  exact carry adjustment, Carr-Lewis corridor truncation with exact corridor
  additivity; seasoned MtM with accrued realized variance and the exact GBM
  volatility-swap strike), **cliquets / ratchets, reverse
  cliquets and Napoleons** (capped-floored return strips, coupon-minus-losses
  and coupon-plus-worst-month structures; closed forms under Black-Scholes
  where the payoff permits, Monte Carlo under GBM or Heston), and **multi-asset rainbow
  options** (best-of, worst-of, spread, basket, exchange) on n correlated
  assets. Carry handles dividend yield, discrete cash dividends and stock
  borrow cost.

## Products and engines

| Payoff | Analytic | Binomial | Finite difference | Monte Carlo |
|---|---|---|---|---|
| Vanilla European | Black-Scholes / Heston CF | yes | yes (grid Greeks) | yes (+ stderr) |
| Vanilla on a future | Black-76 (discounted / margined) ||||
| Vanilla American | Barone-Adesi-Whaley / Bjerksund-Stensland 2002 (approx.) | yes | Brennan-Schwartz | two-pass Longstaff-Schwartz |
| Vanilla Bermudan (discrete exercise dates) || yes | Brennan-Schwartz on exercise dates | LSMC restricted to exercise dates |
| Perpetual American | Merton closed form (exact) ||||
| Binary (cash / asset) | closed form / Heston CF | yes | yes (Rannacher + cell averaging) | yes |
| Barrier (8 types, + rebates at hit / at expiry) | Reiner-Rubinstein + E/F rebate terms || absorbing boundary / parity | Brownian-bridge corrected (rebate at expiry) |
| Double barrier (knock-in / knock-out corridor) | Ikeda-Kunitomo image series ||| discrete corridor monitoring |
| Asian (arith / geo, fixed / floating) | Turnbull-Wakeman (fixed + Henderson-Wojakowski average-strike) / exact geometric (fixed + average-strike) ||| geometric control variate |
| Forward-start | Rubinstein (BS) ||| yes (incl. Heston forward smile) |
| Autocallable / Phoenix (conditional + memory coupons) |||| multi-date discounting; GBM / local vol / Heston |
| Rainbow (best/worst-of, spread, basket, exchange) | Margrabe / Kirk / moment matching ||| correlated terminal GBM |
| Accumulator / decumulator (geared, knock-out) | strip of Reiner-Rubinstein knock-out pairs ||| discrete daily knockout |
| Lookback (floating / fixed strike) | Goldman-Sosin-Gatto / Conze-Viswanathan (continuous) ||| discrete monitoring |
| Variance / gamma / corridor variance swap (+ GBM vol-swap strike) | model-free replication: DDKZ 1/K^2 kernel, S ln S contract with carry adjustment, Carr-Lewis corridor truncation ||||
| Cliquet / ratchet / reverse cliquet / Napoleon | forward-start call/put spreads (no global clamp; Napoleon MC-only) ||| per-period GBM or Heston paths |

Model availability: local vol runs on the FD and MC engines; Heston runs on the
analytic (vanilla + binary) and MC engines (all payoffs above except American
and rainbow). Rainbow options are a separate product type
(`"product_type": "rainbow_option"`) with per-asset spots/vols/dividends and a
correlation matrix; outputs include per-asset `deltas` and `vegas`.

### Engine details

- **Binomial lattice**: six parameterizations behind one enum —
  **Leisen-Reimer** (the default: strike-aware Peizer-Pratt, second-order
  smooth convergence, at ~100 steps it matches CRR at 1000 — ~90x
  faster), Cox-Ross-Rubinstein, Jarrow-Rudd, Tian, Trigeorgis and the
  additive equal-probability tree — selected per contract (`tree_type`,
  `tree_steps`). The engine lives in `core::lattice`, asset-class
  agnostic (payoffs and early exercise enter as closures), with two
  implementations: an optimized rolling-array engine (O(n) memory) and a
  diagnostic engine that keeps the full spot/value trees, the
  early-exercise boundary per layer, tree Greeks and wall-clock timing,
  plus a `convergence_study` helper for step-ladder analysis. A
  **trinomial lattice** rounds out `core::lattice` for fixed income:
  per-node branching (Hull-White edge-switching included), per-node
  discounting (the short rate lives on the node), and Arrow-Debreu state
  prices by forward induction — validated by repricing the Vasicek
  zero-coupon bond closed form on a mean-reverting clamped tree. A
  **term-structure lattice** (`tree_term_structure` in JSON,
  `.tree_term_structure()` on the builder) applies time-dependent
  parameters directly on the tree: a variance-equal time grid keeps the
  tree recombining under a vol term structure, while per-step
  probabilities and discount factors take each step's forward rate and
  carry from the curve — so rate timing (not just the zero rate to
  maturity) prices into early exercise.
- **Finite difference**: theta-scheme in log-spot with per-node, per-step
  coefficients (local vol ready), forward rates from the discount curve per time
  step, cell-averaged terminal conditions for digitals, barrier-aligned absorbing
  boundaries, and delta/gamma/theta read directly off the grid. Grid sizes are
  configurable per contract. The numerical kernels live in a reusable
  `core::fd_solvers` toolkit covering 1-D to 3-D problems: Thomas tridiagonal,
  Brennan-Schwartz and PSOR obstacle solvers (one- and two-sided), tensor-grid
  axis operators, and Douglas / Hundsdorfer-Verwer ADI time steppers with
  explicit mixed-derivative (correlation) terms — the machinery a 2-D Heston or
  hybrid three-factor PDE needs.
- **Monte Carlo**: deterministic per-path RNG streams (bit-reproducible under
  rayon parallelism), low-discrepancy sampling through a Brownian bridge,
  exact/Euler/Milstein stepping, antithetic + moment matching, geometric control
  variates for Asians, Brownian-bridge barrier monitoring, and standard errors
  reported with every price. Greeks via common-random-number bumps.
- **Analytic American approximations**: Barone-Adesi-Whaley (`pricer: "BAW"`,
  quadratic approximation with a Newton solve for the exercise boundary) and
  Bjerksund-Stensland 2002 (`pricer: "BS2002"`, two-step flat exercise
  boundary priced in closed form via the cumulative bivariate normal — a lower
  bound on the true price, generally the tighter of the two). Both price in
  under a microsecond, within a few cents of a fine tree, and return true
  American Greeks (unlike the tree, whose Greeks fall back to the European
  closed form). Use them when speed matters more than the last basis point.
  **Perpetual American** calls and puts have exact Merton closed forms
  (`equity::perpetual`), verified against the stationary pricing ODE, smooth
  pasting and the finite-maturity limit.
- **Calibration workflow**: quoted option prices -> robust implied vols
  (safeguarded Newton with arbitrage bounds) -> implied surface -> Dupire local
  vol -> reprice anything, including barriers under smile dynamics. **Heston
  calibration** fits all five parameters to vanilla quotes by
  Levenberg-Marquardt in an unconstrained transform space (log/atanh).
  The Heston/Bates calibration objectives price through the **COS method**
  (Fang-Oosterlee Fourier-cosine expansion): one characteristic-function
  sweep per expiry prices the whole strike strip, making a 20-strike smile
  ~90x faster than per-strike integration (`heston::cos_smile`). The
  truncation range comes from numerically estimated cumulants (including
  the kurtosis term, so Feller-violated fat tails stay covered), deep
  ITM prices recover via put-call parity from the CF-implied forward, and
  the legacy P1/P2 integration is kept as the independent cross-check
  oracle — the two methods agree to ~1e-6 across the test grid.
- **Risk analytics** (`risk`): Value-at-Risk and Expected Shortfall in the
  standard flavors — historical, parametric normal, Cornish-Fisher
  higher-moment corrected, and delta-normal multi-asset VaR with the exact
  Euler component / marginal decomposition — plus scenario **VaR/ES for an
  options book** (delta-gamma-vega-theta from aggregated Greeks and full
  revaluation through the portfolio repricer, on shared scenarios so the
  difference isolates the Taylor error), EWMA / realized volatility,
  max drawdown, Sharpe / Sortino, the Kupiec VaR backtest, and
  **TOML-configured stress MtM**: named shock scenarios (relative / absolute
  bumps on spot, vol, rates and time, with per-underlying filters and
  key-rate `tenors` restricting a rate shock to part of the curve) prepared
  into bumped market data, checked against a configurable no-arbitrage
  guard (`allow` / `warn` / `reject` on the minimum implied forward) and
  fully revalued, reported per trade and aggregated per scenario.
- **Adjoint Algorithmic Differentiation** (`core::aad`): a tape-based
  reverse-mode differentiator with operator overloading — write a pricer over
  `Var` and one backward sweep returns every input sensitivity at a fixed
  small multiple of pricing cost. Ships with the Black-Scholes closed form
  on tape (all six first-order Greeks from one sweep, validated to the
  library's closed forms) and pathwise Monte Carlo Greeks (delta / vega /
  rho differentiated straight through the simulation).
- **Numerical toolkits** in `core`: 1-D root finding (`solvers`: bisection,
  Newton-Raphson, secant, Halley, safeguarded Newton — pluggable by enum),
  multi-dimensional optimization (`optimization`: Levenberg-Marquardt, BFGS,
  conjugate gradient, steepest descent, Nelder-Mead, differential evolution —
  the fitting layer for Heston today, SABR / Nelson-Siegel tomorrow), FD
  linear solvers (`fd_solvers`, 1-D to 3-D), asset-agnostic Monte Carlo
  machinery (`montecarlo`: reproducible per-path RNG streams, Sobol with
  digital-shift scrambling, Halton with Cranley-Patterson rotation, Brownian
  bridge, stratified / Latin-hypercube sampling, control variates, moment
  matching, Welford statistics), an interpolation toolkit (`interpolation`:
  linear, cubic splines with natural / clamped / not-a-knot boundaries,
  shape-preserving PCHIP and Akima, bilinear grids and thin-plate splines for
  2-D vol surfaces), and linear algebra (`linalg`: Cholesky / QR / SVD /
  symmetric-eigen decompositions with least-squares and pseudo-inverse
  solves, plus correlation repair — PSD-tolerant Cholesky and Higham's
  nearest-correlation projection, auto-applied to non-PSD rainbow
  correlation inputs).

## Feature flags

The default build is the lean pricing library — no CLI, no XML, ~40% fewer
transitive dependencies. Opt in to what you need:

| Feature | Adds | Extra deps |
|---|---|---|
| *(default)* | pricing, calibration, risk, JSON contracts ||
| `xml` | XML contract input/output | `quick-xml` |
| `stress-config` | TOML stress-scenario files | `toml` |
| `cli` | the `rustyqlib` binary (implies `xml`, `stress-config`) | `clap`, `csv` |

```bash
cargo add rustyqlib                    # library only
cargo install rustyqlib --features cli # the command-line tool
```

## Benchmarks

`cargo bench` runs a criterion suite over the public API (one benchmark per
engine, implied vol, and a 1,000-option batch), with fixed seeds and grids
so runs are comparable; criterion flags statistically significant
regressions against the previous run. Indicative single-threaded numbers:
a fully-loaded Black-Scholes `npv()` (discount curve and vol surface
lookups included) runs in ~0.7 µs — about 1.5M prices/sec; a 20-strike
Heston smile prices in ~1.6 ms via the COS method vs ~137 ms by
per-strike integration (~90x, the ratio the calibration loop inherits);
an American put on a Leisen-Reimer 101-step tree prices in ~26 µs vs
~2.3 ms on the CRR-1000 tree at comparable accuracy.

## Running the CLI

```bash
cargo build --release --features cli
# price a single JSON file of contracts
rustyqlib file --input contracts.json --output results.json
# price every JSON file in a directory (parallel)
rustyqlib dir --input contracts/ --output results/
# build an implied vol surface from quoted options
rustyqlib build --input quotes.json --output out/
# guided pricing in the terminal
rustyqlib interactive
```

### Contract examples

Vanilla European call priced analytically (a flat rate builds a flat curve):

```json
{
  "asset": "EQ",
  "contracts": [{
    "action": "PV", "asset": "EQ",
    "product_type": {
      "product_type": "option", "symbol": "ABC",
      "underlying_price": 100.0, "put_or_call": "C", "payoff_type": "vanilla",
      "strike_price": 100.0, "volatility": 0.3, "maturity": "2027-07-17",
      "risk_free_rate": 0.05, "dividend": 0.0, "pricer": "Analytical"
    }
  }]
}
```

The same contract can carry richer market data and model choices:

```json
{
  "discount_curve": { "type": "zero_rates", "tenors": [0.25, 1.0, "2029-07-17"],
                      "rates": [0.045, 0.05, 0.055], "compounding": "continuous" },
  "vol_surface":    { "type": "strike_expiry", "expiries": [0.5, 1.0],
                      "strikes": [90.0, 100.0, 110.0],
                      "vols": [[0.32, 0.30, 0.28], [0.33, 0.31, 0.30]] },
  "mc_model": "heston",
  "heston": { "v0": 0.09, "kappa": 2.0, "theta": 0.09, "vol_of_vol": 0.4, "rho": -0.7 },
  "pricer": "MC", "simulation": 100000
}
```

Selected fields (all optional unless noted):

| Field | Meaning |
|---|---|
| `valuation_date` | pricing as-of date `YYYY-MM-DD`; defaults to today — set it for reproducible pricing and historical re-marking |
| `pricer` | `Analytical`, `Binomial`, `FD`, `MC`, `BAW`, `BS2002` (analytic American) |
| `payoff_type` | `vanilla`, `binary`, `barrier`, `asian`, `forward_start`, `autocallable` |
| `exercise_style` | `European` (default), `American`, `Bermudan` (needs `exercise_dates`) |
| `exercise_dates` | Bermudan exercise dates `["YYYY-MM-DD", ...]`, strictly increasing; expiry is always exercisable |
| `binary_type`, `cash_amount` | `cash` / `asset`, cash paid when ITM |
| `barrier_type`, `barrier_level` | `up_in`, `up_out`, `down_in`, `down_out` |
| `averaging_type`, `asian_strike_type` | `arithmetic`/`geometric`, `fixed`/`floating` |
| `rainbow_type`, `assets`, `correlations`, `weights` | rainbow options: `best_of`, `worst_of`, `spread`, `basket`, `exchange` |
| `forward_start_date`, `strike_fraction` | forward-start options |
| `autocall_barrier`, `protection_barrier`, `autocall_coupon`, `autocall_observations`, `notional` | autocallable notes |
| `borrow_cost` | continuous stock borrow (repo) cost, part of the carry |
| `futures_settlement` | option on a future (Black-76): `discounted` (standard) or `margined` (futures-style); `underlying_price` is then the futures price |
| `cash_dividends` | discrete dividends `[{"date", "amount"}]`; escrowed model on analytic/tree/terminal-MC, jumps on path-MC and FD |
| `discount_curve` | `flat`, `zero_rates`, `discount_factors`, `forward_rates` |
| `vol_surface` | `flat`, `strike_expiry`, `moneyness_expiry`, `delta_expiry` |
| `mc_model` | `gbm` (default), `local_vol`, `heston` (needs `heston` params) |
| `simulation`, `mc_time_steps`, `mc_scheme`, `mc_sampler`, `mc_seed` | Monte Carlo controls |
| `fd_spot_steps`, `fd_time_steps` | finite difference grid |
| `tree_type`, `tree_steps` | binomial lattice: `LeisenReimer` (default), `CRR`, `JarrowRudd`, `Tian`, `Trigeorgis`, `EQP`; steps default 1000 |
| `tree_term_structure` | apply the discount curve's forward rates and the vol term structure per tree step (variance-equal grid) |

Working examples for every product live in [`src/examples/EQ/`](src/examples/EQ/).
Monte Carlo outputs include the standard error (`std_err`) alongside price and Greeks.

### XML contracts

Every contract can equally be written in XML — the input format is detected from the
content and the output format from the output file extension, so `-o results.xml`
writes XML and `-o results.json` writes JSON regardless of the input:

```xml
<contracts>
  <asset>EQ</asset>
  <contracts>
    <item>
      <action>PV</action>
      <asset>EQ</asset>
      <product_type product_type="option">
        <symbol>ABC</symbol>
        <underlying_price>100.0</underlying_price>
        <put_or_call>C</put_or_call>
        <payoff_type>vanilla</payoff_type>
        <strike_price>100.0</strike_price>
        <volatility>0.3</volatility>
        <maturity>2027-07-17</maturity>
        <risk_free_rate>0.05</risk_free_rate>
        <pricer>Analytical</pricer>
      </product_type>
    </item>
  </contracts>
</contracts>
```

Conventions: elements are object fields; **attributes are fields too** (convenient for
enum tags such as `type="flat"`); `<item>` children make an array, including
single-element ones; scalars are inferred, so numbers become numbers while
`2027-07-17` and `C` stay strings. See
[`src/examples/EQ/equity_option.xml`](src/examples/EQ/equity_option.xml), and convert
between formats with `cargo run --features xml --example convert_format -- in.json out.xml`.

XML is a *syntax* over the same data model — documents are transcoded to
`serde_json::Value` and deserialized with the same derives, so both formats share one
schema, one set of defaults and one set of validation rules, and every new product
supports both automatically.

## Runnable examples

One file per product under [`examples/`](examples/), each pricing across every
applicable engine and model with identity checks:

```bash
cargo run --release --example vanilla_option     # all four engines, European + American
cargo run --release --example barrier_option     # eight barrier types, in-out parity
cargo run --release --example heston_option      # char. function vs MC, smile shape
cargo run --release --example local_vol_calibration  # quotes -> surface -> Dupire -> reprice
```

See [`examples/README.md`](examples/README.md) for the full list.

## Using it as a library

Build contracts with the fluent builder:

```rust
use rustyqlib::equity::builder::EquityOptionBuilder;
use rustyqlib::equity::utils::Engine;
use rustyqlib::core::trade::PutOrCall;
use rustyqlib::Instrument;

// build() validates every input and the engine/payoff combination:
// an option that builds is guaranteed to price
let option = EquityOptionBuilder::new()
    .spot(100.0)
    .strike(100.0)
    .flat_vol(0.30)
    .flat_rate(0.05)
    .dividend_yield(0.02)
    .years_to_maturity(1.0)
    .vanilla(PutOrCall::Call)
    .engine(Engine::FiniteDifference)
    .build()?;

// one call returns value, all Greeks and (on MC engines) the std error
let result = option.price()?;
println!(
    "pv {:.6}  delta {:.4}  vanna {:.4}  charm {:.4}",
    result.pv, result.greeks.delta, result.greeks.vanna, result.greeks.charm
);

// ...or use the per-Greek accessors individually
println!("npv {:.6}  gamma {:.4}", option.npv(), option.gamma());
```

...or deserialize the same JSON the CLI consumes:

```rust
use rustyqlib::equity::vanilla_option::EquityOption;
use rustyqlib::core::data_models::EquityOptionData;

let contract: EquityOptionData = serde_json::from_str(json)?;
let option = EquityOption::from_json(&contract);
```

Lower-level building blocks are exported directly: `YieldCurve`, `VolSurface`,
`DayCountConvention`, the `Payoff` trait, Dupire `LocalVol`, `HestonParams`, and
the engine modules (`blackscholes`, `binomial`, `finite_difference`, `montecarlo`).

## Design principles

- **Contracts and market state are separable** — instruments embed the
  market they were built with (the stateless-service shape), and a
  `MarketData` snapshot provides the desk shape: capture a book's market
  once, `bump()` it with named shocks (relative/absolute spot, vol, rate
  and time, per-underlying filters), and reprice the whole book under the
  bumped snapshot (`book.npv_under(&market)`), each position revaluing
  fully on its own engine. The TOML stress runner is a consumer of these
  primitives.
- **Discount factors are state, rates are views** — curves store pillar dfs;
  zero/forward rates in any compounding are derived on demand. Vol surfaces
  canonicalize every quoting style into per-expiry smiles with total-variance
  time interpolation.
- **One payoff trait, every engine** — payoffs implement `payoff(spot, strike)`
  and (for path dependence) `path_payoff(path, strike)`; adding a payoff makes it
  price on every compatible engine without engine changes.
- **Validated numerics** — golden values against independently coded oracles,
  parity and replication identities at 1e-10, cross-engine agreement tests, and
  bit-reproducible Monte Carlo. Engines refuse unsupported combinations with a
  clear error instead of silently mispricing.
- **Typed errors, no panics at the boundary** — every fallible operation
  returns `RustyQLibError` (`InvalidInput` naming the offending field,
  `UnsupportedEngine`, `CalibrationFailed` with iterations and residual,
  `NumericalError`, `ParseError`). Pricing is fallible via
  `Instrument::try_npv()` / `try_from_json()`; the infallible `npv()` /
  `from_json()` remain as conveniences for validated instruments.
  `Instrument::price()` returns a structured `PricingResult { pv, greeks,
  std_err }` — value, all nine Greeks and the Monte Carlo standard error
  from a single call. `EquityOptionBuilder::build()` validates every field
  and the engine/payoff combination up front, so an option that builds is
  guaranteed to price — and setter order never matters. Batch
  pricing reports failures per contract in the output's `error` field and
  keeps going instead of aborting the file.

## Roadmap

- Andersen QE scheme and American exercise (LSMC) under Heston; 2-D ADI finite
  difference for stochastic vol
- Barrier rebates, double/window barriers, seasoned Asians
- Rates: curve bootstrapping from deposits/FRAs/swaps onto the core curve type,
  swaps and swaptions; FX (Garman-Kohlhagen)
- Stulz closed forms for two-asset best-of/worst-of; per-asset smiles and
  path-dependent multi-asset payoffs; SVI smile parameterization with
  no-arbitrage checks; pathwise / likelihood-ratio Greeks

## License

MIT — see [License](License).