Legwork
A structured-product decomposer. It strips a termsheet into its elementary legs, prices each leg with Black-Scholes or seeded Monte Carlo, and then does the part almost nobody shows: the operational calendar, corporate-action adjustments, and SPAN-style margin.
The invariant
Legwork never asks you to trust its decomposition. For every product, the sum of the legs' payoffs must equal the product's own payoff formula, terminal spot by terminal spot. That equality runs as a pytest invariant across 797 spots from 1 to 200, for all three products: 2,391 equalities checked to nine decimal places on every run. If the split were wrong, the suite would fail, not just this page.
The invariant has already earned its keep once. A review pass caught a Reverse Convertible whose matured coupon legs were still being discounted at a negative time to maturity, letting a paid coupon keep contributing value and overpricing the product by up to 3.88% at several grid points. The fix was a one-line guard, landed with two regression tests that now pin a matured leg's contribution to exactly 0.0. Decomposition is proven, not claimed.
Three products
One synthetic underlying, three termsheets pinned in code. Two of the three price fully in closed form; the Autocallable is path-dependent, so it goes to seeded Monte Carlo. The values below are the run's own numbers from data/run_meta.json.
| Term | Capital-Protected Note | Reverse Convertible | Autocallable |
|---|---|---|---|
| Tenor | 5y · matures 2031-06-30 | 1y · matures 2027-06-30 | 3y · matures 2029-06-30 |
| Structure | Zero-coupon bond plus a long call at 70% participation | Deposit plus coupons, minus a short knock-in put | Notional plus memory coupons, minus a short down-and-in put |
| Coupon | None | 8% in 2 installments | 2% per quarter with memory, barrier 70 |
| Barriers | None · strike 100 | Put barrier 70 at expiry only · strike 100 | Trigger 100 · coupon 70 · put 60, 12 quarterly checks |
| Pricing | Black-Scholes, closed form | Closed form: vanilla put plus digital put | Monte Carlo, 100,000 paths |
| Value | EUR 105,903.77 | EUR 105,167.22 | EUR 103,346.76 ± 30.42 |
Payoff & value
Each product's terminal payoff across a 41-point spot axis from 50 to 150, against its current value re-read from the pricing grid. Drag spot, vol and date: the value curve is interpolated bilinearly in spot and vol, and the marker prices the product at your exact spot. Terminal curves from data/payoff_cpn.json, data/payoff_rc.json and data/payoff_autocall.json.
Greeks
Value, delta, gamma, vega and theta on a 41 × 7 × 13 grid: spot 50 to 150, vol 10% to 40%, thirteen dates from valuation to maturity. Closed form where the product allows it, 20,000 seeded paths per node where it does not. The four profiles below re-read the grid as the sliders above move. From data/grid_cpn.json, data/grid_rc.json and data/grid_autocall.json.
Operational calendar
Every date a desk has to act on: fixings, coupon observations and payments, autocall checks, settlement. 46 events across the three products, each with its dependency chain, plus a 3:1 share split walked through with a full audit trail. From data/events_cpn.json, data/events_rc.json and data/events_autocall.json.
Margin & collateral
SPAN-style initial margin from 16 spot and vol shock scenarios, then 60 days of variation margin against one simulated path. The Capital-Protected Note is the only product that breaches its threshold: one margin call of EUR 5,049.61 on 2026-09-21. From data/margin_cpn.json, data/margin_rc.json and data/margin_autocall.json.
Proof
51 tests pin the engine down: payoff parity, Black-Scholes against known values, Monte Carlo convergence, Greeks against the analytic answers, the calendar, the margin arithmetic and the CLI. The suite is fully offline and seeded end to end; the artifacts this page reads come from one run of the real thing.
$ pytest -q ................................................... [100%] 51 passed in 18.59s