---
title: "Build: A Risk Engine"
book: "Rates, Credit, XVA and Risk"
subject: quant
language: en
chapter: 29
exercises: 8
source: https://one-course.com/books/quant/6/en/chapter/29-build-a-risk-engine
---

# Chapter 29 — Build: A Risk Engine

At six in the morning the risk report must be on the desk heads’ screens: every trade repriced under every scenario, the P&L summed desk by desk, VaR and [expected shortfall](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-es) at every node, every limit breach flagged. Between the end-of-day market data and that deadline sits a batch that has to fit its window every night; in a 2011 survey of banks by McKinsey, average VaR run times ranged from 2 to 15 hours, and longer under stress. This last chapter builds the firm’s [risk engine](#def-rc-build-a-risk-engine-engine) on top of the pricing library of Book 5 and the measures of [Chapter 21](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#ch-rc-market-risk-measures), and asks the question every such engine faces: how much exactness can the deadline afford?

## 29.1 Architecture: market data, scenarios, revaluation, aggregation

**Definition 29.1 (Risk engine, risk data aggregation).**

A *risk engine* is the system that turns the firm’s positions and market data into its risk measures: it builds scenarios, revalues the positions under them, aggregates the results along the firm’s structure, and reports them against limits. *Risk data aggregation* is, in the Basel Committee’s words, defining, gathering and processing risk data according to the bank’s risk reporting requirements to enable it to measure its performance against its risk tolerance or appetite.

The engine has four stages ([Figure 29.1](#fig-rc-build-a-risk-engine-arch)). A market-data snapshot fixes today’s curves, spots and volatilities. Scenario generators turn history (or stress designs, chapter 22) into shifts of named [risk factors](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-factor). Revaluation turns each position and scenario into a P&L, through the pricing library, so that the [risk engine](#def-rc-build-a-risk-engine-engine) never prices anything itself. Aggregation sums the P&L along the [risk hierarchy](#def-rc-build-a-risk-engine-hierarchy) and computes the measures. The P&L array, scenarios by trades, is the engine’s central object: every report is a function of it.

![The risk engine’s stages. Revaluation calls the pricing library through its fixed interface; everything downstream works on the array of P&L by scenario and trade. Schematic.](https://one-course.com/images/onecourse/chapters/quant-6/rc-build-a-risk-engine/fig-016b99c77d51.svg)

***Figure 29.1.** The [risk engine](#def-rc-build-a-risk-engine-engine)’s stages. Revaluation calls the pricing library through its fixed interface; everything downstream works on the array of P&L by scenario and trade. Schematic.*

Book 5’s library knows market data, instruments, models and engines, and exposes `price`, `sensitivities`, `reprice` and `price_batch` over immutable snapshots bumped by named [risk factors](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-factor). This book adds its rates instruments to it: a [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) zero curve that bumps [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) by [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar), swaps, and swaptions under the normal model of chapter 4, registered with the library’s `register` so that its generic calls price them like any other instrument.

```python
@dataclass(frozen=True)
class SwaptionEngine:
    name: str = "Bachelier"

    def supports(self, inst, model) -> bool:
        return isinstance(inst, Swaption) and isinstance(model, NormalRates)

    def price(self, inst: Swaption, model: NormalRates, md: fp.MarketData) -> float:
        c = inst.curve(md)
        p0 = md.df(c, inst.expiry)
        dfs = [md.df(c, add_years(inst.expiry, i)) for i in range(1, inst.tenor + 1)]
        annuity = sum(dfs)
        fwd = (p0 - dfs[-1]) / annuity
        vol = model.sigma(md, inst.underlying, inst.strike, inst.expiry)
        return inst.notional * annuity * bachelier(fwd, inst.strike, md.t(inst.expiry), vol, inst.payer)


fp.register(IRSwap, NormalRates, SwapEngine())
fp.register(Swaption, NormalRates, SwaptionEngine())
```

***Listing 29.1.** The swaption engine and the registration of the rates instruments with the pricing library. code/firm/riskengine/firm_riskengine.py*

## 29.2 Scenario generation

Each [historical scenario](https://one-course.com/books/quant/6/en/chapter/22-stress-testing-and-scenarios#def-rc-stress-testing-and-scenarios-historical) is a set of bumps on named [risk factors](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-factor): here the Treasury curve’s eight [pillars](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) (par yields treated as zero rates, a simplification) and the euro–dollar spot rate. The engine’s test market is that of 23 September 2026: rates from 4.49% at one year to 5.40% at thirty, EUR/USD at 1.1411. It builds 250 one-day scenarios from the most recent year and 250 overlapping ten-day scenarios; the ten-day moves reach 48 basis points on a [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) and 2.8% on the exchange rate. Filtered or stressed scenarios (chapters 21 and 22) plug in at the same place.

## 29.3 Revaluation: full, grid and sensitivity-based

**Definition 29.2 (Full revaluation, revaluation grid).**

*Full revaluation* reprices every position with its pricing model in every scenario. A *revaluation grid* reprices each position on a small grid of shifts of each [risk factor](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-factor) it depends on, once, and computes each scenario’s P&L by interpolating on the grid and adding the factors’ contributions.

[Full revaluation](#def-rc-build-a-risk-engine-reval) costs one pricing call per trade and scenario; the engine runs it through the library’s `price_batch`, which marks a failing trade instead of stopping the run. The grid costs a fixed number per trade and factor, whatever the number of scenarios; the [delta–gamma approximation](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-dg) of chapter 21 costs two per trade and factor. The grid captures each factor’s nonlinearity over the whole range of moves; like the diagonal [delta–gamma approximation](https://one-course.com/books/quant/6/en/chapter/21-market-risk-measures#def-rc-market-risk-measures-dg), it drops the cross effects between factors.

```python
def grid_revaluation(trades: Sequence[Trade], md: fp.MarketData, scenarios: Sequence[fp.Scenario],
                     counter: Counter, points: int = 7) -> np.ndarray:
    """Each trade repriced on a grid of `points` shifts of each factor it depends on, spanning the
    largest move in the scenarios; scenario P&L = sum over factors of the interpolated grid P&L."""
    x = _moves(scenarios)
    tmpl = scenarios[0].bumps
    out = np.zeros((len(scenarios), len(trades)))
    for j, tr in enumerate(trades):
        base = counter.pv(tr.instrument, md)
        for k in dependencies(tr.instrument, [b.factor for b in tmpl]):
            m = float(np.abs(x[:, k]).max())
            g = np.linspace(-m, m, points)
            pnl = np.array([0.0 if abs(s) < 1e-15 else
                            counter.pv(tr.instrument, md.apply(replace(tmpl[k], size=float(s)))) - base
                            for s in g])
            out[:, j] += tr.quantity * np.interp(x[:, k], g, pnl)
    return out
```

***Listing 29.2.** Grid revaluation: seven points per factor, spanning the largest scenario move. code/firm/riskengine/firm_riskengine.py*

**Example 29.3 (Cost and error on a 1 000-trade book).**

The test book holds 600 swaps, 200 swaptions and 200 EUR/USD options, 89 of them expiring within a month (71 sold). [Full revaluation](#def-rc-build-a-risk-engine-reval) under 250 scenarios takes 251 000 pricing calls, the grid 50 200 and delta–gamma 17 400. With one-day scenarios, the grid’s 99% VaR is within 1.59% of [full revaluation](#def-rc-build-a-risk-engine-reval) at every node and delta–gamma within 0.74%. With ten-day scenarios, delta–gamma overstates the short-dated FX desk’s VaR by 18.45%, and both approximations miss the rate options’ VaR by more than 8.6% ([Figure 29.2](#fig-rc-build-a-risk-engine-errors)).

![Relative error of the ten-day 99% VaR against full revaluation, by node of the hierarchy. Delta–gamma fails on the short-dated options, whose gamma changes over a ten-day move; both approximations fail on the swaptions, whose value depends on the product of moves at different pillars; the plan that revalues the swaptions in full stays within 0.76% everywhere. Data: the chapter’s tutorial, on US Treasury yields and ECB reference rates.](https://one-course.com/images/onecourse/chapters/quant-6/rc-build-a-risk-engine/fig-dcf841c9db27.svg)

***Figure 29.2.** Relative error of the ten-day 99% VaR against [full revaluation](#def-rc-build-a-risk-engine-reval), by node of the hierarchy. Delta–gamma fails on the short-dated options, whose gamma changes over a ten-day move; both approximations fail on the swaptions, whose value depends on the product of moves at different [pillars](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar); the plan that revalues the swaptions in full stays within 0.76% everywhere. Data: the chapter’s tutorial, on US Treasury yields and ECB reference rates.*

The two failures have different causes ([Figure 29.3](#fig-rc-build-a-risk-engine-fx)). The short-dated FX options depend on one factor, and their P&L over a 2.8% move is far from a parabola: the grid, which reprices at the move’s size, follows it; the Taylor expansion does not. The swaptions depend on the forward swap rate, a combination of several [pillars](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar), and their convexity lives in the cross products of [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) moves that no per-factor method sees.

![P&L of the short-dated FX desk (mostly sold options) in each ten-day scenario, against the scenario’s EUR/USD move. The grid sits on the full revaluation; the delta–gamma parabola overstates the losses of large moves. Data: the chapter’s tutorial.](https://one-course.com/images/onecourse/chapters/quant-6/rc-build-a-risk-engine/fig-a81fab077453.svg)

***Figure 29.3.** P&L of the short-dated FX desk (mostly sold options) in each ten-day scenario, against the scenario’s EUR/USD move. The grid sits on the [full revaluation](#def-rc-build-a-risk-engine-reval); the delta–gamma parabola overstates the losses of large moves. Data: the chapter’s tutorial.*

**Method 29.4 (A revaluation plan).**

Measure each approximation’s error against [full revaluation](#def-rc-build-a-risk-engine-reval) node by node, on the scenarios that matter (the longest horizon, the stressed period). Assign each desk the cheapest method within the error budget, [full revaluation](#def-rc-build-a-risk-engine-reval) where none is, and re-run the comparison when the book or the scenarios change.

```python
def planned_revaluation(trades: Sequence[Trade], md: fp.MarketData, scenarios: Sequence[fp.Scenario],
                        counter: Counter, plan: dict[tuple[str, ...], str], default: str = "grid") -> np.ndarray:
    """Each trade revalued by the method its deepest planned node names (a revaluation plan by desk)."""
    def method(t: Trade) -> str:
        hits = [n for n in plan if t.path[:len(n)] == n]
        return plan[max(hits, key=len)] if hits else default
    out = np.zeros((len(scenarios), len(trades)))
    for name, f in METHODS.items():
        idx = [j for j, t in enumerate(trades) if method(t) == name]
        if idx:
            out[:, idx] = f([trades[j] for j in idx], md, scenarios, counter)
    return out
```

***Listing 29.3.** A revaluation plan: each trade revalued by the method of its deepest planned node. code/firm/riskengine/firm_riskengine.py*

## 29.4 Aggregation, hierarchies and limits

**Definition 29.5 (Risk hierarchy, risk limit).**

A *risk hierarchy* is the tree along which positions and their risk are aggregated: trades into books, books into desks, desks into business lines and the firm. A *risk limit* is a ceiling on a risk measure (VaR, a sensitivity, a stress loss) at a node of the hierarchy, whose breach triggers escalation.

VaR and ES are not additive: each node’s measure comes from its own P&L vector, the sum of its trades’ columns of the P&L array. That is why the engine keeps the array, not per-desk numbers. Euler contributions (chapter 20) split a node’s ES among its children so that the parts add up: each child’s average loss in the parent’s worst scenarios.

**Example 29.6 (Ten-day risk by node).**

With [full revaluation](#def-rc-build-a-risk-engine-reval), the firm’s ten-day 99% VaR is USD 53.53 million and its 97.5% ES 50.89 million. The FX business’s own ES is 14.63 million, but its Euler contribution to the firm’s is only 3.56 million, against 47.34 million for rates: the firm’s tail is a rates tail. The short-dated FX desk’s VaR, 14.58 million, breaches its limit of 12 million, and the engine flags it.

```cpp
// (VaR, ES) as positive losses at one level.
inline std::pair<double, double> var_es(const std::vector<double>& pnl, double level) {
    std::vector<double> loss(pnl.size());
    std::transform(pnl.begin(), pnl.end(), loss.begin(), [](double x) { return -x; });
    std::sort(loss.begin(), loss.end(), std::greater<>());
    const auto k = tail_count(loss.size(), level);
    return {loss[k - 1], std::accumulate(loss.begin(), loss.begin() + k, 0.0) / static_cast<double>(k)};
}

// Each child's average loss in the parent's k worst scenarios (stable order on ties).
inline std::map<Path, double> euler_es(const std::vector<double>& parent,
                                       const std::map<Path, std::vector<double>>& children, double level) {
    std::vector<std::size_t> idx(parent.size());
    std::iota(idx.begin(), idx.end(), 0);
    std::stable_sort(idx.begin(), idx.end(), [&](std::size_t a, std::size_t b) { return parent[a] < parent[b]; });
    const auto k = tail_count(parent.size(), level);
    std::map<Path, double> out;
    for (const auto& [c, v] : children) {
        double s = 0.0;
        for (std::size_t i = 0; i < k; ++i) s += v[idx[i]];
        out[c] = -s / static_cast<double>(k);
    }
    return out;
}
```

***Listing 29.4.** The C++20 kernel: VaR, ES and Euler contributions of a node’s P&L vector (the Rust twin is line for line the same). code/firm/riskengine/cpp/firm_riskengine.hpp*

**As of September 2026 — Risk data aggregation.**

The Basel Committee’s principles for effective [risk data aggregation](#def-rc-build-a-risk-engine-engine) and risk reporting (known as BCBS 239, January 2013) set fourteen principles, eleven for banks (governance, data architecture, accuracy, completeness, timeliness, adaptability, and five on reports) and three for supervisors. Global systemically important banks designated in 2011 or 2012 were to comply by January 2016. The Committee’s latest progress report (November 2023) assessed 31 such banks: only two were fully compliant with all the principles, and no principle was fully implemented across all banks; its newsletter of January 2026 named data lineage, governance and cross-border data as continuing challenges. The ECB’s guide of May 2024 recalled that none of 25 significant institutions reviewed in 2016 fully followed the principles, and set seven key areas of supervisory expectations.

## 29.5 Testing a risk engine

A [risk engine](#def-rc-build-a-risk-engine-engine) is tested as a chain. The pricing library carries its own acceptance tests. The rates instruments are tested against closed forms (a swap at the par rate is worth zero, payer minus receiver swaption is the forward swap). The approximations are tested against [full revaluation](#def-rc-build-a-risk-engine-reval) on small moves, where they must agree. The kernel is tested on a fixture shared by its Python, C++20 and Rust versions, and on the identity that Euler contributions add up to the parent’s ES. Counting pricing calls turns the deadline into a number the tests can check.

## 29.6 Tutorial: one night’s run

**Goal.** Run the engine on the 1 000-trade book under one-day and ten-day [historical scenarios](https://one-course.com/books/quant/6/en/chapter/22-stress-testing-and-scenarios#def-rc-stress-testing-and-scenarios-historical); report VaR, ES and Euler contributions by node; compare full, grid and delta–gamma revaluation for cost and error; build a revaluation plan. **End state:** the numbers of Examples [29.3](#ex-rc-build-a-risk-engine-methods) and [29.6](#ex-rc-build-a-risk-engine-report) and the three charts.

1. **Market and book** : `market()` , `book()` .
2. **Scenarios** : `scenarios(1)` , `scenarios(10)` .
3. **Revaluation** : `revaluations(h)` , P&L arrays and pricing calls per method.
4. **Reports** : `reports(h)` , `errors(h)` , `euler()` , `limits()` ; `fig_rc_riskengine.py` .

**What to change next.** Add a parallel-shift grid for the swaptions; use stressed scenarios from 2020 or 2022; run the kernel in C++ on the P&L array written to disk.

## 29.7 Build: the risk engine

**Purpose.** The firm’s overnight risk run: scenarios, revaluation through the pricing library, aggregation along the hierarchy, VaR, ES, Euler contributions and limits.

**Interface.** `PillarCurve`, `IRSwap`, `Swaption`, `NormalRates` (registered with the pricing library); `Trade`; `historical_scenarios`; `full_revaluation`, `grid_revaluation`, `delta_gamma_revaluation`, `planned_revaluation`, `Counter`; `aggregate`, `risk_report`, `euler_es`, `check_limits`; C++20 and Rust kernels.

**Rules.** Prices come only from the pricing library; the P&L array is kept; every approximation is measured against [full revaluation](#def-rc-build-a-risk-engine-reval); one failing trade never stops the run.

**Acceptance tests.** `code/firm/riskengine/tests/`, `cpp/`, `rust/`: closed forms of the rates instruments, [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) bumps, call counts of each method, agreement on small moves, plans, limits, and the shared kernel fixture.

**Stretch.** [Cross-gamma](https://one-course.com/books/quant/6/en/chapter/3-rates-risk#def-rc-rates-risk-crossgamma) or parallel-shift grids; incremental runs for new trades; parallel revaluation; stressed and filtered scenarios; lineage from each number back to its trades and data.

Sources and further reading

- Basel Committee on Banking Supervision, *Principles for effective risk data aggregation and risk reporting* , January 2013.
- Basel Committee on Banking Supervision, *Progress in adopting the Principles for effective risk data aggregation and risk reporting* , November 2023.
- Basel Committee on Banking Supervision, Newsletter No 36 on risk data aggregation, January 2026.
- European Central Bank, *Guide on effective risk data aggregation and risk reporting* , May 2024.
- US Department of the Treasury, Daily Treasury Par Yield Curve Rates; European Central Bank, euro foreign exchange reference rates (data).

## 29.8 Exercises

**Exercise 29.1 ★.**

Count the pricing calls of full, grid (seven points) and delta–gamma revaluation for one swap and one FX option under 250 scenarios, and check the book’s totals.

**Solution of Exercise 29.1.**

Full: 251 calls for either (the base and 250 scenarios). Grid: $1+8\times6=49$ for the swap (eight [pillars](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar), six non-zero points each) and $1+9\times6=55$ for the option (the spot too). Delta–gamma: $1+2\times8=17$ and $1+2\times9=19$. Book: $1\,000\times251=251\,000$; $800\times49+200\times55=50\,200$; $800\times17+200\times19=17\,400$.

**Exercise 29.2 ★.**

Why does the grid’s cost not depend on the number of scenarios?

**Solution of Exercise 29.2.**

The grid prices each trade at a fixed set of shifts; each scenario then costs an interpolation and a sum, not a pricing call. Adding scenarios adds arithmetic only (as long as they stay inside the grid’s range).

**Exercise 29.3 ★.**

Why must the engine keep the P&L array rather than each desk’s VaR?

**Solution of Exercise 29.3.**

VaR is a quantile, not a sum: a desk’s VaR cannot be computed from its books’ VaRs, nor the firm’s from its desks’. Keeping each trade’s P&L per scenario lets the engine compute any node’s measure, and any new grouping, by summing columns first.

**Exercise 29.4 ★★.**

How large is the diversification between rates and FX in the firm’s ten-day ES?

**Solution of Exercise 29.4.**

The stand-alone ES of rates (47.34 million) and FX (14.63 million) add up to USD 61.96 million, from the unrounded values, against 50.89 million for the firm: 11.07 million of diversification. The Euler split puts almost all of the firm’s ES on rates.

**Exercise 29.5 ★★.**

Why does delta–gamma revaluation overstate the short-dated desk’s losses on large moves?

**Solution of Exercise 29.5.**

The desk is short options about a month from expiry. Over a 2.8% move, gamma falls as the options move away from the money, so losses grow more slowly than the parabola that extrapolates today’s gamma.

**Exercise 29.6 ★★.**

Why does no per-factor method capture the swaptions’ ten-day risk, and how would you fix it cheaply?

**Solution of Exercise 29.6.**

The swaption’s value depends on the forward swap rate, a weighted sum of [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) moves, and its convexity is in the square of that sum: the cross products between [pillars](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar). Per-factor methods keep only the squares. Cheap fixes: a grid on the trade’s forward swap rate (one factor that carries the cross terms), a parallel-shift grid plus [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) deltas for the rest, or [full revaluation](#def-rc-build-a-risk-engine-reval) of the few trades concerned.

**Exercise 29.7 ★★★.**

*Coding.* Count the calls of the revaluation plan and check them against the plan’s composition.

**Solution of Exercise 29.7.**

Rate options in full: $200\times251 = 50\,200$; long-dated FX options by delta–gamma: $111\times19 = 2\,109$; swaps by grid: $600\times49 = 29\,400$; short-dated FX options by grid: $89\times55 = 4\,895$. Total 86 604, the count that the engine’s call counter returns for the plan.

**Exercise 29.8 ★★★.**

*Find the flaw.* “The grid passed the error test last year, so it stays.”

**Solution of Exercise 29.8.**

The error depends on the book and the scenarios: new short-dated options, a longer horizon or a stressed period can break an approximation that passed. The test must be re-run whenever either changes, and routinely.

## 29.9 Problem: the six o’clock deadline

**Problem 29.1.**

Weekend problem — the six o’clock deadline

The firm’s real book holds 50 000 trades like the test book’s, the batch may use 16 cores from two to six in the morning, and one pricing call costs 20 milliseconds on average (assumptions). The risk measure is the ten-day 99% VaR under 250 scenarios, and every node’s VaR must be within 2% of [full revaluation](#def-rc-build-a-risk-engine-reval).

**Part I — Cost.**

1. Give the pricing calls of each method on the test book.
2. Convert them into hours for the real book.
3. Which methods fit the window?
4. What does the grid’s cost depend on, and the [full revaluation](#def-rc-build-a-risk-engine-reval) ’s?
5. How many scenarios could [full revaluation](#def-rc-build-a-risk-engine-reval) afford in the window?

**Part II — Error.**

6. Give the worst node error of each approximation at one day.
7. And at ten days.
8. Which desk breaks delta–gamma, and why?
9. Which desk breaks the grid, and why?
10. Why does the error grow with the horizon?

**Part III — The plan.**

11. Build a plan within the budget from the node errors.
12. Give its cost in calls and in hours.
13. Give its worst node error.
14. Could the plan use delta–gamma for the long-dated FX options? Why is that safe?
15. What must be monitored for the plan to stay valid?

**Part IV — Judgement.**

16. Which limit breaches does the report show?
17. What does the Euler split tell the firm about its tail?
18. What would BCBS 239’s timeliness and accuracy principles ask of this run?
19. State the *named result* : the hours and worst VaR error of full, grid, delta–gamma and planned revaluation, and the cheapest method within the 2% budget.
20. Why is the cheapest method within budget not a fixed choice?

**Solution of Problem 29.1.**

**1.** Full 251 000, grid 50 200, delta–gamma 17 400. **2.** Calls $\times\,50\times0.02/16/3\,600$: 4.36, 0.87 and 0.30 hours. **3.** Grid and delta–gamma; [full revaluation](#def-rc-build-a-risk-engine-reval) misses a four-hour window. **4.** The grid on trades, factors and points; [full revaluation](#def-rc-build-a-risk-engine-reval) on trades times scenarios. **5.** $4\times3\,600\times16/(50\,000\times0.02) = 230.4$ calls per trade: 229 scenarios. **6.** 1.59% for the grid, 0.74% for delta–gamma. **7.** 8.64% for the grid, 18.45% for delta–gamma. **8.** The short-dated FX options: their gamma changes over a ten-day move. **9.** The swaptions: their convexity is in cross products of [pillar](https://one-course.com/books/quant/6/en/chapter/1-curve-construction#def-rc-curve-construction-pillar) moves. **10.** Moves grow with the square root of the horizon, and the neglected terms grow faster than the retained ones. **11.** Grid by default, [full revaluation](#def-rc-build-a-risk-engine-reval) for the rate options, delta–gamma for the long-dated FX options. **12.** 86 604 calls, 1.50 hours. **13.** 0.76%, on the short-dated FX desk. **14.** Yes: its delta–gamma error is 0.43%, since options months from expiry have slowly changing gamma; the grid’s error there is larger (2.17%) because seven points interpolate a nearly flat P&L poorly. **15.** The node errors against [full revaluation](#def-rc-build-a-risk-engine-reval), on a schedule and whenever the book or scenarios change. **16.** The short-dated FX desk: ten-day VaR USD 14.58 million against a limit of 12 million. **17.** That its tail is a rates tail: rates contribute 47.34 of the 50.89 million of ES. **18.** A run that finishes in time even in stress, with numbers reconciled and aggregated on a largely automated basis. **19.** Named result: *the six o’clock deadline*: [full revaluation](#def-rc-build-a-risk-engine-reval) 4.36 hours (exact), grid 0.87 hours (8.64% worst error), delta–gamma 0.30 hours (18.45%), plan 1.50 hours (0.76%): at the ten-day horizon the plan is the cheapest method within the 2% budget; at one day, delta–gamma would be. **20.** It depends on the book, the horizon and the scenarios, all of which change.

## 29.10 Interview questions

**Interview question 29.1 ★ risk.**

Describe the architecture of a [risk engine](#def-rc-build-a-risk-engine-engine).

**Solution of Interview question 29.1.**

Trade and market-data sourcing (snapshots, hierarchy), scenario generation, revaluation through the pricing library, the P&L array, aggregation, measures and limits, reporting and lineage; batch orchestration, failures handled per trade, and reconciliation with the books.

*What the interviewer is looking for: the stages and the P&L array as the central object.*

**Interview question 29.2 ★★ developer.**

How would you make an overnight VaR run on 50 000 trades fit a four-hour window?

**Solution of Interview question 29.2.**

Measure where the time goes; parallelise over trades; use grids or sensitivities where their error is small and [full revaluation](#def-rc-build-a-risk-engine-reval) where it is not; reuse unchanged trades’ results; cache market-data builds; fail single trades without stopping the run.

*What the interviewer is looking for: measurement, parallelism and a plan checked against [full revaluation](#def-rc-build-a-risk-engine-reval).*

**Interview question 29.3 ★★ risk, researcher.**

Compare [full revaluation](#def-rc-build-a-risk-engine-reval), grids and sensitivity-based revaluation.

**Solution of Interview question 29.3.**

[Full revaluation](#def-rc-build-a-risk-engine-reval) is exact and costs a call per trade and scenario. Grids cost calls per factor and capture single-factor nonlinearity. Sensitivities are cheapest and fail on large moves and cross effects. The choice is measured, desk by desk.

*What the interviewer is looking for: cost, error and when each fails.*

**Interview question 29.4 ★★ risk.**

Why are VaR numbers not additive across desks, and how do you allocate them?

**Solution of Interview question 29.4.**

VaR is a quantile of a sum, not a sum of quantiles, and it is not subadditive in general. Allocate by Euler contributions (each desk’s expected loss in the firm’s tail scenarios for ES), which add up to the total.

*What the interviewer is looking for: non-additivity and [Euler allocation](https://one-course.com/books/quant/6/en/chapter/20-the-valuation-adjustment-desk#def-rc-the-valuation-adjustment-desk-euler).*

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

How do you test a [risk engine](#def-rc-build-a-risk-engine-engine)?

**Solution of Interview question 29.5.**

Instruments against closed forms and limits; approximations against [full revaluation](#def-rc-build-a-risk-engine-reval) on small moves and on real scenarios; aggregation on fixtures and identities (Euler sums, node totals); cross-language twins on shared fixtures; regression runs on stored outputs; timing tests.

*What the interviewer is looking for: a chain of tests, from pricing to reports.*

**Interview question 29.6 ★★★ bank, risk.**

What does BCBS 239 require, and why do banks struggle to comply?

**Solution of Interview question 29.6.**

Governance, data architecture, accurate, complete, timely and adaptable risk data, and accurate, clear, comprehensive reports. Banks struggle because of legacy systems, fragmented data, underfunded programmes and weak board attention, as the Basel Committee’s 2023 progress report and the ECB’s 2024 guide noted.

*What the interviewer is looking for: the principles and the practical obstacles.*
