Analysis

PLACEHOLDER — NOT YET IMPLEMENTED
Section 1 Residual Butterfly Pending

Three-bond butterfly trade using curve-fit residuals rather than raw yields. Demeaning removes slow-moving cheapness/richness bias; DV01-neutral weights make the spread a pure curvature signal.

Signal Construction
-- step 1: demeaned residual per bond -- raw_resid(bond, t) = market_YTM(t) - fitted_YTM(t) -- from ExpSpline adj_resid(bond, t) = raw_resid(bond, t) - rolling_mean(raw_resid, 126d) -- step 2: DV01-neutral fly spread -- w1, w2 = DV01-neutral weights using rolling_126d avg DV01 -- s.t. w1×DV01_w1 + w2×DV01_w2 = DV01_belly fly_spread(t) = adj_resid(belly,t) - w1×adj_resid(wing1,t) - w2×adj_resid(wing2,t) -- step 3: z-score entry signal -- z_fly(t) = (fly_spread(t) - mean(fly_spread, 126d)) / std(fly_spread, 126d) -- entry: |z_fly| > 2, confirmed by individual bond z-scores
PnL & Carry
PnL = -Δfly_spread × DV01_belly × notional carry (v2) = (clean_price - forward_price) × 0.01 × 1e6 -- per £1mm face -- forward_price funded at SONIA (overnight compounded)
Data Flow
gilt_curve_history.parquet fitted_YTM per bond/date residuals z_fly signal
bond_analytics.py DV01 neutral weights w1, w2
UI (planned)

Bond selector: belly + two wings. Fly spread time series + z-score panel. Entry/exit markers. Cumulative PnL chart.

[ Fly spread + z-score chart — not yet built ]
Trades saved to localStorage (key: fly_trades). Schema: { id, belly, wing1, wing2, w1, w2, entry_date, entry_z, notional, status }
parquet ✓ DV01 via bond_analytics ✓ fitted_YTM in cache ✓ residual series — needs API endpoint carry calc — needs SONIA forward rate
Section 2 Forward Curve Trade Pending

Visual trade builder on the instantaneous forward curve. Bonds shown as scatter dots; click to toggle long/short. Weights entered as notionals. Net curve exposure visualised as a shaded area on the forward curve.

Interaction Model

Forward curve (ExpSpline flat-forwards) plotted for the selected date. Each bond in the universe appears as a dot at its maturity with y = market YTM. Click a dot: first click = long (green), second = short (red), third = deselect. Notional input appears inline next to selected bonds.

Exposure & PnL
-- net DV01-weighted forward curve exposure -- exposure(tenor) = Σ sign_i × notional_i × DV01_i(tenor) -- shaded area -- mark-to-market PnL -- PnL(t) = Σ w_i × (P_i(t) - P_i(entry)) × notional_i -- P_i(t): clean price from parquet on date t -- w_i: +1 long / -1 short
Data Flow
/api/curves (flat forwards) Plotly forward curve bond dots overlay
click interactions position vector exposure shading + daily PnL
UI (planned)

Date nav to step forward curve through history. Side panel: open positions, entry prices, daily PnL, cumulative PnL. Export to CSV button.

[ Interactive forward curve + bond dots — not yet built ]
Trades saved to localStorage (key: fwd_trades). Schema: { id, bonds:[{isin, side, notional, entry_px, entry_date}], status }
forward curve in cache ✓ clean prices in parquet ✓ DV01 via bond_analytics ✓ interactive Plotly click handlers — needs build exposure shading layer — needs build
Section 3 Bond / SONIA Spread Pending

Compares each gilt's YTM to the SONIA futures strip implied rate at the bond's mid-life tenor. Gives a credit/term premium spread above risk-free compounded SONIA. Spread-of-spreads and roll-down identify relative value.

SONIA Mid-Rate Interpolation
-- find the two SR3 contracts straddling the bond's mid-tenor -- mid_tenor = (maturity - settle) / 2 -- years T_near, T_far = SR3 contracts with expiry on either side of mid_tenor w = (mid_tenor - T_near) / (T_far - T_near) -- linear weight SONIA_mid(t) = (1-w) × (100 - SR3_near_settle(t)) + w × (100 - SR3_far_settle(t)) -- bond/SONIA spread -- spread(bond, t) = bond_YTM(t) - SONIA_mid(t)
Roll-Down & Trade Signal
-- roll-down: spread change from tenor shortening by 1 day -- roll_down(bond) = spread(bond at mid_tenor) - spread(bond at mid_tenor - 1/365) -- z-score entry signal (126d lookback) -- z_spread(bond, t) = (spread(t) - mean(spread, 126d)) / std(spread, 126d) -- pair trade: wide vs tight spread -- -- long wide-spread bond / short SR3 pair -- short tight-spread bond / long SR3 pair
Data Flow
SR3_settlements.csv SONIA_mid per bond/date spread series z-score signal
yields_by_date + SONIA_mid spread history
UI (planned)

Bond selector dropdown. Spread history chart (multi-line). Spread-of-spreads heatmap. z-score bar chart across all bonds. Roll-down table.

[ Bond/SONIA spread chart — not yet built ]
Trades saved to localStorage (key: sonia_spread_trades). Schema: { id, long_bond, short_bond, sr3_near, sr3_far, entry_date, entry_spread_diff, notional }
SR3_settlements.csv ✓ yields_by_date in cache ✓ mid-tenor SR3 interpolation — needs API endpoint roll-down calc — needs build
Section 4 Implied Forward vs SONIA Futures Pending

Compares the short-end path implied by the fitted ExpSpline forward curve against the path implied by the SR3 futures strip. Divergence between the two is a signal that either the curve-fitting is distorted by supply/demand in the short end, or the futures strip is mispricing the expected SONIA path.

Signal Construction
-- curve-implied path: read instantaneous forward rate at each SR3 expiry -- curve_fwd(T_i) = flat_curve.interpolate(T_i) -- from ExpSpline -- futures-implied path: 100 - settle price -- futures_fwd(T_i) = 100 - SR3_settle(T_i) -- divergence signal per expiry -- divergence(T_i, t)= curve_fwd(T_i, t) - futures_fwd(T_i, t) -- aggregate signal: mean divergence across front 4 contracts -- signal(t) = mean(divergence(T_1..T_4, t)) z_signal(t) = (signal(t) - mean(signal, 63d)) / std(signal, 63d)
Interpretation

curve_fwd > futures_fwd (positive divergence): gilt curve implies higher rates than futures — gilts rich relative to SONIA strip, or futures strip too low (market pricing in more cuts than curve implies).

curve_fwd < futures_fwd (negative divergence): curve implies lower short-end rates — gilts cheap to SONIA, or BOE pricing in futures too hawkish versus bond market.

Data Flow
curves_by_date["flat"] curve_fwd at SR3 expiries divergence series
SR3_settlements.csv futures_fwd at expiries divergence series
divergence history z_signal entry/exit markers
UI (planned)

Dual-panel: top = forward curve overlaid with SR3 dots per date. Bottom = divergence per contract through time (coloured by contract). Aggregate z-score bar. Date navigation linked to top panel.

[ Curve vs futures divergence chart — not yet built ]
Dependency note: requires SR3_settlements.csv to be populated daily via download_futures.py daily (Item 5 — ICE downloader). GitHub Actions workflow futures_download.yml runs weekdays 18:00 UTC. Backfill available via python download_futures.py backfill with ICE credentials.
forward curve in cache ✓ SR3_settlements.csv ✓ ICE daily downloader (Item 5) ✓ divergence API endpoint — needs build dual-panel interactive chart — needs build