---
title: "Build: A Pricing Library"
book: "Derivatives and Volatility"
subject: quant
language: en
chapter: 28
exercises: 8
source: https://one-course.com/books/quant/5/en/chapter/28-build-a-pricing-library
---

# Chapter 28 — Build: A Pricing Library

A risk manager asks for the book’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to one node of the [volatility surface](https://one-course.com/books/quant/5/en/chapter/7-implied-volatility-and-its-surface#def-dv-implied-volatility-and-its-surface-surface): the one-year, 90% strike. The book holds a vanilla call, an American put, a knock-out, a [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs) and an [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable). Each was priced by the chapter that introduced it, with its own inputs: a volatility here, a rate there, a grid of paths somewhere else. None of them reads the surface as an object that can be bumped at one node. The answer needs every pricer to take the same market snapshot, apply the same bump, and reprice. With a Monte Carlo pricer in the book, it also needs the bumped and unbumped runs to use the same random numbers, or the answer is noise. This chapter builds the library that makes the question a single call: instruments, models and engines behind one interface, market data as an immutable snapshot, [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) and scenarios by [bump-and-reprice](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-bump), and the tests that let other books trust it. On this chapter’s book the node’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) is 2 072 per volatility point. Its Monte Carlo part has a standard deviation of 51 with common random numbers and 217 without.

## 28.1 Instruments, models and engines

A pricing library separates three things that a script mixes. The *instrument* is the contract: its terms and nothing else. The *model* is a set of assumptions about the dynamics, with its parameters. The third is the numerical method.

**Definition 28.1 (Pricing engine).**

A *pricing engine* is the component that computes an instrument’s value under a model from a market snapshot by a given numerical method (closed form, tree, finite differences, Monte Carlo, Fourier); several engines may support the same instrument and model, and a registry chooses the default.

The separation pays three times. A new instrument needs only its terms and one engine. A new method, such as a GPU Monte Carlo, prices every instrument it supports without touching them. And two engines on the same instrument check each other. QuantLib, the open-source library, is built this way: its abstract `Instrument` class takes a [pricing engine](#def-dv-build-a-pricing-library-engine) through `setPricingEngine` and returns its value through `NPV`. The Open Source Risk Engine extends it with further models, instruments and engines and reads trades and market data as XML.

| one-year option (skewed surface) | Analytic | Tree | PDE | Monte Carlo |
| --- | --- | --- | --- | --- |
| call, strike 100 | 8.5437 | 8.5437 | 8.5413 | 8.5415 |
| American put, strike 95 | — | 5.0731 | 5.0702 | — |

The registry returns every engine that supports a pair, and the tests price across them: the analytic and tree engines agree to $2\times10^{-6}$ on the call. The grid’s 200 nodes leave 0.0024, and one Monte Carlo run’s error is of the order of its standard error. For the whole library the engines are the closed forms of chapters 3, 15 and 16, the trees of chapter 2, the finite differences of chapter 22, the Monte Carlo of chapter 23, the Fourier pricer of chapter 24 and the convertible grid of chapter 21 ([Figure 28.1](#fig-dv-build-a-pricing-library-arch)).

![The library’s structure: an instrument and a model select an engine through the registry; every engine reads the same immutable snapshot; the top-level calls produce Greeks, sensitivities and scenarios by repricing on bumped copies of the snapshot.](https://one-course.com/images/onecourse/chapters/quant-5/dv-build-a-pricing-library/fig-1f452d16adbf.svg)

***Figure 28.1.** The library’s structure: an instrument and a model select an engine through the registry; every engine reads the same immutable snapshot; the top-level calls produce [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks), sensitivities and scenarios by repricing on bumped copies of the snapshot.*

## 28.2 Market data as an immutable snapshot

**Definition 28.2 (Market-data snapshot).**

A *market-data snapshot* is the complete, immutable set of market inputs of a valuation at one time (spots, curves, dividends, [volatility surfaces](https://one-course.com/books/quant/5/en/chapter/7-implied-volatility-and-its-surface#def-dv-implied-volatility-and-its-surface-surface), correlations, fixings, exchange rates), addressable by risk-factor identifiers; a bump or a change of date returns a new snapshot and never alters the old one.

QuantLib’s instruments cache their last result and observe their inputs, recalculating when an input changes. That design suits a trading screen that reprices as quotes tick. A risk engine reprices the same book under thousands of shifted markets, often in parallel, and needs the opposite: pricing as a pure function of an instrument and a snapshot, with no state to invalidate. The library’s snapshot has a method `apply(bump)` that returns a new snapshot. Bumps are keyed by the risk-factor identifiers that Book 6’s risk engine shares: `SPOT:ABC`, `VOL:ABC` (parallel), `VOL:ABC:2027-09-24:90` (one node), `CURVE:USD`, `CORR:ABC|XYZ`, `TIME`. A surface that has nodes exposes them as risk factors, and one that does not is first sampled onto a grid.

Immutability is what makes the rest simple. A Greek is two prices on two snapshots, a scenario is one price on one snapshot, and a batch is a matrix of snapshots by trades. Every engine, old or new, gets all three for free, because none of them knows that bumps exist.

## 28.3 Greeks, bumps and scenarios

[Bump-and-reprice](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-bump) is the universal Greek: slower than an engine’s own derivatives, but available for every engine and consistent across them. Its enemy is Monte Carlo noise. Two independent runs, each with a standard error of a few tenths, differ by that much, and a bump of one volatility point divides the difference by 0.01. The library’s binding rule removes it. Every Monte Carlo price of an instrument uses the same seed, derived from the instrument’s identifier by a checksum, and the same time grid. The bumped and base runs share their random numbers, and the noise of the difference is only the noise of the sensitivity itself.

**Example 28.3 (The vega nobody could compute).**

The book, on a surface with 20% at the money and a skew: long 1 000 one-year 100 calls, short 500 one-year 95 American puts, long 800 one-year 100 calls knocked out at 80 (daily monitoring), short a one-year [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs) struck at 21 on a [vega notional](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-veganot) of 50 000, and short 1 000 000 of a three-year [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable). Its value is $-928\,523$. The [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to the (one year, 90) node, per volatility point, is $-88$ from the put, whose strike interpolates halfway to the node, $-90$ from the [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs)’s strip and $+2\,249$ from the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable), which the Monte Carlo engine prices on the total variance at the midpoint of its knock-in (80) and trigger (100): 2 072 in all. The call and the knock-out, struck at 100, have none. Over twenty seeds the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable)’s contribution has a standard deviation of 51 with common random numbers and 217 without, 4.3 times more ([Figure 28.2](#fig-dv-build-a-pricing-library-crn)).

![The autocallable’s vega to the one-year 90 node by bump-and-reprice, from twenty seeds: with the bumped and base runs sharing their random numbers, and with every snapshot drawing afresh. Data: the tutorial.](https://one-course.com/images/onecourse/chapters/quant-5/dv-build-a-pricing-library/fig-fbc190e4bc20.svg)

***Figure 28.2.** The [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable)’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to the one-year 90 node by [bump-and-reprice](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-bump), from twenty seeds: with the bumped and base runs sharing their random numbers, and with every snapshot drawing afresh. Data: the tutorial.*

The node grid gives the whole profile ([Figure 28.3](#fig-dv-build-a-pricing-library-vega)). Summed over its 35 nodes the book’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) is 6 747 per point, against 6 706 for a parallel bump. The difference comes from interpolation, which spreads a node’s bump to its neighbours unevenly.

![The book’s bucketed vega by bump-and-reprice at every node of the surface: by expiry (left) and across the one-year slice (right), where the 90 node carries the autocallable’s exposure. Data: the tutorial.](https://one-course.com/images/onecourse/chapters/quant-5/dv-build-a-pricing-library/fig-454c38662a92.svg)

***Figure 28.3.** The book’s bucketed [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) by [bump-and-reprice](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-bump) at every node of the surface: by expiry (left) and across the one-year slice (right), where the 90 node carries the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable)’s exposure. Data: the tutorial.*

Scenarios are bumps in combination. A scenario is a name and a tuple of bumps, serialisable, so that Book 6’s scenario generators can build them and the library can apply them. The book’s ten-scenario grid ([Figure 28.4](#fig-dv-build-a-pricing-library-scen)) shows it long volatility and long a crash, through the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) it sold, and losing on a rally.

![The book repriced under ten scenarios in one call. Data: the tutorial.](https://one-course.com/images/onecourse/chapters/quant-5/dv-build-a-pricing-library/fig-41d27b3f4859.svg)

***Figure 28.4.** The book repriced under ten scenarios in one call. Data: the tutorial.*

## 28.4 Testing a pricer

A pricer is trusted because of its tests, and a library is only as good as the tests of its weakest engine.

**Definition 28.4 (Reference pricer).**

A *reference pricer* is an independent, slow and accurate implementation of a valuation, kept to test production engines against: a closed form where one exists, otherwise a converged grid or a large simulation, with its own accuracy known.

The library’s tests fall into five kinds. **Reference values**: each engine against a closed form or a [reference pricer](#def-dv-build-a-pricing-library-reference) (Black’s formula, the Gauss–Legendre Lewis integral of chapter 24, the tree at 2 001 steps). **Cross-engine**: every engine that supports an instrument against the others, within their known errors. **Identities**: knock-in plus knock-out equals the vanilla, a digital call plus a digital put pays the discount factor, a one-asset basket equals the vanilla, a TARF with no leverage and no target equals a strip of puts, higher correlation cheapens a worst-of put. **Properties**: snapshots are unchanged by bumps, bucketed [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) sums to the parallel [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks), the seed is the same in every process, JSON round-trips every instrument. **Twins**: the C++20 and Rust cores reproduce the Python engines to $10^{-9}$.

## 28.5 Tutorial: one call for the whole book

**Goal.** Wire the book’s components into one library, price a small book through one call, compute bucketed [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) with common random numbers, and run a ten-scenario grid. **End state:** the four figures, the engine table and the numbers of the weekend problem.

1. **The registry**: engines by instrument and model type, the first that supports being the default: `def register (inst_type: type , model_type: type , engine) -> None : """Add an engine for (instrument type, model type); the first registered that supports is the default.""" _REGISTRY.setdefault((inst_type, model_type), []).append(engine) register(EuropeanOption, BlackScholes, AnalyticEngine()) register(EuropeanOption, BlackScholes, TreeEngine()) def default_engine (inst: Instrument, model: Model): for it in type (inst).__mro__: for mt in type (model).__mro__: for e in _REGISTRY.get((it, mt), []): if e.supports(inst, model): return e raise PricingError(f " no engine for { type (inst).__name__} under { getattr (model, ' name ' , model)} " )` **Listing 28.1.** The engine registry. code/firm/pricing/firm_pricing.py
2. **Common random numbers**: the seed from the identifier, and paths drifted to the snapshot’s forwards: `def _seed (self , inst: Instrument, md: MarketData) -> int : seed = seed_for(inst) + self .seed_offset if not self .crn: import json # noqa: PLC0415 state = json.dumps({" s " : md.spots, " v " : [repr (v) for v in md.vols.values()], " c " : [repr (c) for c in md.curves.values()]}, sort_keys=True , default=str ) seed = zlib.crc32((str (seed) + state).encode()) return seed def paths (self , inst: Instrument, md: MarketData, names: Sequence[str ], dates: Sequence[dt.date], ref_strikes: Sequence[float ], model: BlackScholes) -> np.ndarray: """Spots of `names` at future `dates`: array (n_paths, len(dates), len(names)).""" ts = np.array([md.t(d) for d in dates]) total = np.array([[model.sigma(md, u, k, d) ** 2 * t for u, k in zip (names, ref_strikes, strict=True )] for d, t in zip (dates, ts, strict=True )]) seg = np.maximum(np.diff(np.vstack([np.zeros(len (names)), total]), axis=0 ), 0.0 ) # (dates, names) fwds = np.array([[md.forward(u, d) for u in names] for d in dates]) m = len (names) corr = np.eye(m) for i in range (m): for j in range (i + 1 , m): corr[i, j] = corr[j, i] = md.correlations.get(frozenset ((names[i], names[j])), 0.0 ) low = np.linalg.cholesky(corr) rng = np.random.default_rng(self ._seed(inst, md)) half = self .n_paths // 2 z = rng.standard_normal((half, len (ts), m)) @ low.T z = np.concatenate([z, -z]) x = np.cumsum(z * np.sqrt(seg)[None , :, :] - 0.5 * seg[None , :, :], axis=1 ) return fwds[None , :, :] * np.exp(x)` **Listing 28.2.** Seeds and paths of the Monte Carlo engine. code/firm/pricing/firm_pricing.py
3. **The C++20 twin** of the finite-difference engine: `struct PDEEngine { int m = 200 , n_t = 200 ; double price (const EuropeanOption& o, const MarketData& md) const { const double t = md.t(o.expiry); const double s = md.spots.at(o.underlying); if (t <= 0 ) return t == 0 ? o.notional * intrinsic(o, s) : 0.0 ; const std::string fc = md.funding_curve(o.underlying); const double r = -std::log(md.df(fc, o.expiry)) / t; const double q = r - std::log(md.forward(o.underlying, o.expiry) / s) / t; const double v = fd_vanilla(s, o.strike, t, r, q, md.vols.at(o.underlying), o.call, o.american, m, n_t); return o.notional * v * md.df(o.curve(md), o.expiry) / md.df(fc, o.expiry); } };` **Listing 28.3.** The PDE engine in C++20. code/firm/pricing/cpp/pricing.hpp
4. **Run** `dv_library.book_prices()` , `engine_table()` , `node_vega()` , `crn_noise()` , `bucket_profile()` , `scenario_grid()` and `fig_library.py` .

**What to change next.** Price the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) under Heston by adding a Heston Monte Carlo engine; register a second Monte Carlo engine with a million paths as a [reference pricer](#def-dv-build-a-pricing-library-reference); time `price_batch` on a hundred snapshots.

## 28.6 Build: the library

**Purpose.** The miniature firm’s pricing library: every instrument of Book 5 behind one interface, the one Book 6’s risk engine consumes.

**Interface.** As frozen for Book 6 (`code/firm/INTERFACES.md`, section 1): `MarketData` with `apply`, `rolled`, `risk_factors`; `Instrument` and `instrument_from_dict`; `Model`, `Engine`, `register`; `price`, `greeks`, `sensitivities`, `bucketed_vega`, `reprice`, `price_batch`; `seed_for`. Added here: instruments `DigitalOption`, `BarrierOption`, `AsianOption`, `ForwardStart`, `Cliquet`, `VarianceSwap`, `BasketOption`, `WorstOf`, `Autocallable`, `TARF`, `ConvertibleBond`; models `HestonModel`, `CreditBlackScholes`; engines `ClosedFormEngine`, `MonteCarloEngine(n_paths, seed_offset, crn)`, `PDEEngine` (with `fd_vanilla`), `FourierEngine`, `ConvertibleEngine`; `engines_for(inst, model)`. The C++20 twin `cpp/pricing.hpp` and the Rust crate `rust/` hold the core types with the analytic and finite-difference engines and the desk [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks).

**Rules.** Pricing is a pure function of instrument, model and snapshot; Monte Carlo seeds come from the instrument’s identifier; every engine reads its market only from the snapshot; additions never rename or remove.

**Acceptance tests.** `code/firm/pricing/tests/` (the core’s fourteen and this chapter’s five groups above); `cpp/pricing_test.cpp` and `rust/src/lib.rs` against the Python values.

**Stretch.** Engines’ own [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) (pathwise and adjoint, chapter 23) behind the `greeks` override; local and stochastic volatility Monte Carlo; seasoned path-dependent trades from fixings; a service wrapper that prices batches in parallel processes.

**As of September 2026 — Trade-representation standards.**

FpML, the XML standard for the electronic dealing and processing of derivatives, is at version 5.13 as a Recommendation, with 5.14 a Last Call Working Draft of 24 August 2026. The Common Domain Model, a model of financial products, trades and their lifecycle events published as code in several languages, has been open source at FINOS since February 2023, in partnership with ISDA, ICMA and ISLA. A production library maps its instruments to these representations at its edge, not inside its engines.

Sources and further reading

- QuantLib reference documentation, class `QuantLib::Instrument` (version 1.43), quantlib.org.
- Open Source Risk Engine, repository README, github.com/OpenSourceRisk/Engine.
- FINOS, Common Domain Model, github.com/finos/common-domain-model.
- FpML, fpml.org.

## 28.7 Exercises

**Exercise 28.1 ★.**

Why must the Monte Carlo seed come from a checksum of the identifier and not from Python’s `hash()`?

**Solution of Exercise 28.1.**

Python salts `hash()` of strings per process, so the same instrument would get different seeds in two runs or two worker processes, and a bump computed across processes would compare different random numbers. A checksum (CRC-32) of the identifier is the same everywhere and forever.

**Exercise 28.2 ★.**

Why do the call and the knock-out, both struck at 100, have no [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to the (one year, 90) node?

**Solution of Exercise 28.2.**

Each is priced at the volatility of its own strike, 100 at one year, which is a node. Bumping the 90 node changes the surface only between 80 and 100 (linear interpolation in log-strike), and not at 100 itself. The knock-out’s barrier at 80 would matter only to an engine that reads volatilities along the path.

**Exercise 28.3 ★.**

Name the identity each of these tests checks: knock-in plus knock-out; a digital call plus a digital put; a TARF with no leverage and no target.

**Solution of Exercise 28.3.**

In–out parity (the two barriers together are the vanilla); the digital call and put together pay one for sure, worth the discount factor; with no leverage and an unreachable target the TARF is a strip of long puts, one per fixing.

**Exercise 28.4 ★★.**

The standard deviation of the node [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) is 51 with common random numbers. Where does that remaining noise come from, and how would you reduce it?

**Solution of Exercise 28.4.**

With common random numbers the bumped and base runs differ only through the bump, so what remains is the Monte Carlo error of the sensitivity estimator itself, which falls as $1/\sqrt n$. Reduce it with more paths, a control variate, a smoother payoff near the barriers, or a pathwise or likelihood-ratio [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) (chapter 23) behind the engine’s own `greeks`.

**Exercise 28.5 ★★.**

Why does the sum of the node [vegas](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) (6 747) differ from the parallel [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) (6 706)?

**Solution of Exercise 28.5.**

Between expiries the surface interpolates total variance, $\sigma^2t$, not volatility, and within an expiry it interpolates in log-strike: a parallel shift of every volatility is not exactly the sum of its node shifts. Central differences also leave second-order terms, which differ between one large bump and many small ones.

**Exercise 28.6 ★★.**

What goes wrong if an engine reads a volatility from a global variable instead of from the snapshot?

**Solution of Exercise 28.6.**

The engine’s price no longer follows the snapshot: bumps and scenarios do not reach it, its [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) come out zero, and two threads pricing different snapshots interfere. Parallel batches become wrong without any error being raised.

**Exercise 28.7 ★★★.**

*Coding.* Register a Monte Carlo engine with 400 000 paths and compute the node [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) of the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) over twenty seeds with common random numbers. How does the standard deviation scale?

**Solution of Exercise 28.7.**

With 400 000 paths the standard deviation over twenty seeds is 25.1 (mean 2 231), against 50.8 with 100 000: half, as $1/\sqrt n$ predicts for four times the paths.

**Exercise 28.8 ★★★.**

*Find the flaw.* “Our new engine agrees with the old one to eight decimals on every trade, so it is correct.”

**Solution of Exercise 28.8.**

Agreement with the old engine shows the two share their results, not that either is right: they may share a bug, a convention or a market input. The test must be against an independent reference (a closed form, a converged grid) and an identity, over the whole range of inputs, including near barriers, expiries and deep in and out of the money.

## 28.8 Problem: The Vega Nobody Could Compute

**Problem 28.1.**

Weekend problem — one node, five pricers

The risk manager of the opening wants the book’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to the one-year 90% node, today and every day.

**Part I — The library.**

1. Which engine prices each of the five positions by default, and why that one?
2. What is the book’s value?
3. How close are the engines on the one-year call, and what explains the gaps?
4. What must every engine do for the node bump to reach it?
5. What does the registry do when no engine supports a pair?

**Part II — The node.**

6. Give each position’s [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to the node.
7. Why is the [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) ’s the largest, and why is its sign positive for the firm?
8. How does the put’s strike of 95 give it half its exposure at 90?
9. What does the [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs) ’s strip contribute, and why at every node?
10. What is the book’s total [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) by expiry?

**Part III — The noise.**

11. Give the node [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) ’s standard deviation over twenty seeds with and without common random numbers.
12. Why does sharing random numbers cut it by a factor of 4.3?
13. What would the standard deviation be with four times the paths?
14. Why does the binding rule tie the seed to the instrument and not to the book?
15. What does the grid of ten scenarios say about the book?

**Part IV — Judgement.**

16. Would you report the node [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) to one unit or to a hundred?
17. What would you add before Book 6’s risk engine runs the library every night?
18. Which test would have caught a pricer that reads a stale surface?
19. State the *named result* : the book’s bucketed [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) at the one-year 90% strike, and the Monte Carlo noise in it with and without common random numbers.
20. In one sentence: what makes a Greek computable for every pricer?

**Solution of Problem 28.1.**

**1.** Call: Analytic; American put: Tree; knock-out: ClosedForm (daily monitoring by the Broadie–Glasserman–Kou shift); [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs): ClosedForm (replication on the surface); [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable): MonteCarlo. The registry returns the first registered engine that supports the pair. **2.** $-928\,523$. **3.** Analytic and tree to $2\times10^{-6}$; the grid 0.0024 (200 nodes); Monte Carlo within its standard error. **4.** Read the volatility from the snapshot’s surface at its own strikes and dates, never from a stored number. **5.** It raises a pricing error naming the instrument and the model. **6.** Call 0, American put $-88$, knock-out 0, [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs) $-90$, [autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) $+2\,249$: 2 072. **7.** Its engine reads the total variance at 90, the midpoint of the knock-in and the trigger, and the note is worth less to its holder when volatility rises; the firm is short the note, so long its [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks). **8.** Linear interpolation in log-strike between 90 and 100 puts about half of a 95 strike’s weight on the 90 node. **9.** $-90$: the strip of options that replicates variance spans all strikes, so every node carries part of its [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks). **10.** 2 275 at one year, 1 464 at two and 3 008 at three per point, and nothing before one year. **11.** 51 with common random numbers, 217 without. **12.** The bumped and base prices share their errors, which cancel in the difference; without sharing, the errors of two independent runs add and are divided by the bump. **13.** About half: 25. **14.** So that the price of one trade does not change when other trades are added to or removed from the book, and so that Book 6 can reprice any subset. **15.** The book is long volatility and long a crash ($+161.8$ thousand for $-20\%$ with ten points more volatility), and loses on rallies ($-24.0$ thousand at $+10\%$) and on falling volatility ($-34.9$ thousand). **16.** To about a hundred: the Monte Carlo part alone has a standard deviation of 51. **17.** Reference prices and cross-engine checks run nightly, a record of the engine and seed used for each trade, the snapshot stored with each result, and limits on unexplained changes. **18.** A property test that bumps the snapshot and requires the price to change for every instrument whose value depends on the bumped factor. **19.** 2 072 per volatility point ([autocallable](https://one-course.com/books/quant/5/en/chapter/18-autocallables#def-dv-autocallables-autocallable) $+2\,249$, American put $-88$, [variance swap](https://one-course.com/books/quant/5/en/chapter/14-variance-swaps-and-volatility-derivatives#def-dv-variance-swaps-and-volatility-derivatives-vs) $-90$), with a standard deviation of 51 from Monte Carlo noise with common random numbers and 217 without. **20.** Pricing as a pure function of an immutable snapshot, so that a bumped snapshot reprices anything, with shared random numbers for simulation.

## 28.9 Interview questions

**Interview question 28.1 ★ developer.**

Why separate instruments, models and engines?

**Solution of Interview question 28.1.**

So that each varies independently: a new product needs its terms and one engine; a new method prices every product it supports; two engines check each other; the model’s assumptions are explicit and swappable.

*What the interviewer is looking for: independent variation, cross-checking.*

**Interview question 28.2 ★★ developer, risk.**

Your Monte Carlo [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) are noisy. What design rule fixes most of it?

**Solution of Interview question 28.2.**

Common random numbers: the same seed and grid for every repricing of one instrument, so that bumps difference out the simulation error. Then smoother payoffs and direct [Greeks](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks).

*What the interviewer is looking for: the same seed per instrument.*

**Interview question 28.3 ★★ developer.**

Mutable market objects with observers, or immutable snapshots? Argue for one.

**Solution of Interview question 28.3.**

Immutable snapshots for risk: pricing is a pure function, parallel batches are safe, scenarios are new snapshots, results are reproducible from stored inputs. Observers and caching suit a trading screen that reprices as quotes tick; they complicate thousands of shifted markets and parallel evaluation.

*What the interviewer is looking for: purity and reproducibility against incremental recalculation.*

**Interview question 28.4 ★★ developer, researcher.**

How do you test a new [pricing engine](#def-dv-build-a-pricing-library-engine)?

**Solution of Interview question 28.4.**

Against closed forms and independent [reference pricers](#def-dv-build-a-pricing-library-reference), against other engines, by identities (parity, in–out), by limits (zero volatility, far strikes), by convergence in its own parameters (steps, paths), and by regression tests pinned to certified numbers.

*What the interviewer is looking for: independent references and convergence.*

**Interview question 28.5 ★★ risk.**

How would you compute bucketed [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks) for a book priced by different models and engines?

**Solution of Interview question 28.5.**

Put every engine on the same surface object with node risk factors, bump each node in turn on a copy of the snapshot, reprice every position with its own model and engine, with common random numbers for simulations, and sum by node. Check that the nodes sum to the parallel [vega](https://one-course.com/books/quant/5/en/chapter/4-greeks-and-the-hedging-p-l#def-dv-greeks-and-the-hedging-pnl-greeks).

*What the interviewer is looking for: one surface, node bumps, common random numbers.*

**Interview question 28.6 ★★★ developer.**

You must rewrite the core of the Python library in C++ without changing any number. How do you proceed?

**Solution of Interview question 28.6.**

Freeze reference values from the Python library for a set of cases; port the same arithmetic, operation by operation (grids, boundary values, solve order); test to $10^{-9}$ in both languages in continuous integration; only then optimise, keeping the tests.

*What the interviewer is looking for: reference values and operation-by-operation equivalence.*
