The fit is bought from the curve, not the volatilities.
A one-factor Hull-White model, calibrated to a USD at-the-money caplet strip — 118 118 caplets entering the corrected calibration (row 0 has no preceding discount, matching the archived Black loop) export_web_json.py stage 3 caplets, April 2019. Correctly targeted, the model prices the book to 1.4 vol pts 1.4 vol pts mean absolute model-vs-market implied-vol gap at the recalibrated optimum export_web_json.py stage 4 of implied volatility; the accuracy, though, is partly bought by letting a free r(0) tilt the long-end discount curve. Pin r(0) to the forward curve and no parameter pair fits — the miss is structural. Drag the three parameters and price the book yourself.
Market Black implied vols Model at your chosen (a, σ, r(0))
Mean reversion a 0.150, short-rate volatility sigma 0.0145, initial short rate r(0) 0.2%. Price RMSE 12.1 percent; implied-vol error 1.43 vol points; curve tilt at thirty years +16.8%.
| Preset | a | σ | r(0) | Price RMSE | IV MAE |
|---|---|---|---|---|---|
| Recalibrated (free r(0)) | 0.150 | 0.0145 | 0.2% | 12.1% | 1.43 vol pts |
| r(0) pinned to f(0,0) | 1.00e-6 | 0.00331 | 2.5% | 56.5% | 11.57 vol pts |
Black implied volatility against caplet maturity, out to 30.4 years. Market volatilities are drawn as dots; the model's implied-vol curve responds to three parameters you set — mean reversion a, short-rate volatility sigma, and the initial short rate r(0). The recalibrated preset, with r(0) left free, fits the market to 1.43 vol points of implied volatility on average; the market-consistent preset, with r(0) pinned to the forward curve, misses by 11.57. Three read-outs report price RMSE, implied-vol error, and how far the model's discount curve tilts from the market's at thirty years.
Fig. 1 — Market Black implied volatilities (dots) against the Hull-White model's implied-vol curve (line) for the USD ATM caplet strip, as of April 16, 2019. Move a, σ, and r(0), or load a preset; price RMSE, implied-vol error, and the 30-year curve tilt update as you go.
The chart plots Black implied volatility against caplet maturity, out to 30.4y 30.4y longest caplet payment time (30/360) cap.csv T_i column . Market volatilities are drawn as dots; the model's implied-vol curve responds to three parameters the reader sets — mean reversion, short-rate volatility, and the initial short rate. The recalibrated preset, with r(0) left free, fits the market to 1.4 vol pts 1.4 vol pts mean absolute model-vs-market implied-vol gap at the recalibrated optimum export_web_json.py stage 4 of implied volatility on average; the market-consistent preset, with r(0) pinned to the forward curve, misses by 11.6 vol pts 11.6 vol pts IV MAE under the pinned-r0 restriction export_web_json.py stage 4 . Three readouts report price RMSE, implied-vol error, and how far the model's discount curve tilts away from the market's at thirty years.
The instrument
A single USD at-the-money cap
The book is one instrument: a USD at-the-money cap struck at 2.712% 2.712% single ATM cap strike across all caplets cap.csv CapStrike column on $10M $10M cap notional cap.csv Notional column of notional, from a Bloomberg SWPM cashflow table dated April 16, 2019 — 119 119 USD ATM cap cashflow rows, Bloomberg SWPM, as of 2019-04-16 cap.csv, archived repo rows, 118 118 caplets entering the corrected calibration (row 0 has no preceding discount, matching the archived Black loop) export_web_json.py stage 3 priced caplets, payments out to 30.4y 30.4y longest caplet payment time (30/360) cap.csv T_i column . Two curves carry the result. The discount curve fixes f(0,0) = 2.53% 2.53% instantaneous forward rate at t=0 from the discount curve; the market-consistent value of r(0) export_web_json.py stage 1 , the market-consistent value of the model's initial short rate; the implied-volatility term structure rises off a low short end, peaks through the belly, and steps down once near 15.5 years — the shape a constant-σ one-factor model has to hold.
Zero rate (ink), forward f(0,t) (blue), discount factor (grey, right axis). Hover, or focus the handle and use the arrow keys.
| Maturity (y) | Discount factor | Zero rate | Forward f(0,t) |
|---|---|---|---|
| 0.514 | 0.9870 | 2.544% | 2.531% |
| 0.778 | 0.9804 | 2.542% | 2.547% |
| 1.028 | 0.9743 | 2.535% | 2.421% |
| 1.281 | 0.9683 | 2.514% | 2.353% |
| 1.533 | 0.9626 | 2.488% | 2.327% |
| 1.789 | 0.9568 | 2.466% | 2.332% |
| 2.039 | 0.9513 | 2.451% | 2.318% |
| 2.292 | 0.9457 | 2.437% | 2.290% |
| 2.544 | 0.9402 | 2.423% | 2.262% |
| 2.800 | 0.9348 | 2.409% | 2.235% |
| 3.053 | 0.9295 | 2.395% | 2.333% |
| 3.303 | 0.9241 | 2.391% | 2.324% |
| 3.558 | 0.9186 | 2.386% | 2.315% |
| 3.814 | 0.9132 | 2.382% | 2.307% |
| 4.064 | 0.9079 | 2.378% | 2.412% |
| 4.317 | 0.9024 | 2.380% | 2.415% |
| 4.572 | 0.8968 | 2.382% | 2.419% |
| 4.828 | 0.8913 | 2.384% | 2.422% |
| 5.081 | 0.8858 | 2.387% | 2.508% |
| 5.333 | 0.8802 | 2.393% | 2.518% |
| 5.589 | 0.8745 | 2.399% | 2.527% |
| 5.853 | 0.8687 | 2.405% | 2.536% |
| 6.106 | 0.8631 | 2.411% | 2.587% |
| 6.347 | 0.8577 | 2.418% | 2.599% |
| 6.608 | 0.8519 | 2.425% | 2.610% |
| 6.864 | 0.8463 | 2.432% | 2.620% |
| 7.114 | 0.8407 | 2.439% | 2.712% |
| 7.367 | 0.8350 | 2.449% | 2.726% |
| 7.619 | 0.8292 | 2.458% | 2.740% |
| 7.875 | 0.8234 | 2.467% | 2.754% |
| 8.125 | 0.8177 | 2.476% | 2.669% |
| 8.378 | 0.8122 | 2.482% | 2.677% |
| 8.631 | 0.8067 | 2.488% | 2.685% |
| 8.886 | 0.8012 | 2.494% | 2.692% |
| 9.139 | 0.7958 | 2.500% | 2.845% |
| 9.392 | 0.7900 | 2.509% | 2.857% |
| 9.647 | 0.7843 | 2.519% | 2.870% |
| 9.903 | 0.7785 | 2.528% | 2.882% |
| 10.153 | 0.7729 | 2.537% | 2.845% |
| 10.406 | 0.7674 | 2.545% | 2.854% |
| 10.661 | 0.7618 | 2.552% | 2.863% |
| 10.917 | 0.7562 | 2.560% | 2.872% |
| 11.167 | 0.7508 | 2.567% | 2.912% |
| 11.419 | 0.7453 | 2.574% | 2.921% |
| 11.675 | 0.7397 | 2.582% | 2.929% |
| 11.939 | 0.7340 | 2.590% | 2.937% |
| 12.181 | 0.7288 | 2.597% | 2.871% |
| 12.433 | 0.7235 | 2.603% | 2.877% |
| 12.694 | 0.7181 | 2.608% | 2.881% |
| 12.950 | 0.7128 | 2.614% | 2.887% |
| 13.200 | 0.7077 | 2.619% | 2.892% |
| 13.453 | 0.7025 | 2.625% | 2.896% |
| 13.706 | 0.6974 | 2.630% | 2.901% |
| 13.961 | 0.6922 | 2.635% | 2.905% |
| 14.214 | 0.6872 | 2.640% | 2.910% |
| 14.464 | 0.6822 | 2.644% | 2.921% |
| 14.719 | 0.6771 | 2.649% | 2.924% |
| 14.975 | 0.6721 | 2.654% | 2.927% |
| 15.225 | 0.6672 | 2.658% | 2.931% |
| 15.478 | 0.6623 | 2.662% | 2.870% |
| 15.733 | 0.6575 | 2.665% | 2.871% |
| 15.989 | 0.6527 | 2.668% | 2.873% |
| 16.239 | 0.6481 | 2.671% | 2.874% |
| 16.492 | 0.6434 | 2.674% | 2.876% |
| 16.747 | 0.6387 | 2.677% | 2.877% |
| 17.003 | 0.6340 | 2.680% | 2.877% |
| 17.256 | 0.6294 | 2.683% | 2.878% |
| 17.508 | 0.6249 | 2.686% | 2.878% |
| 17.769 | 0.6202 | 2.688% | 2.879% |
| 18.025 | 0.6157 | 2.691% | 2.878% |
| 18.275 | 0.6112 | 2.694% | 2.878% |
| 18.528 | 0.6068 | 2.696% | 2.879% |
| 18.781 | 0.6024 | 2.698% | 2.878% |
| 19.036 | 0.5980 | 2.701% | 2.877% |
| 19.286 | 0.5938 | 2.703% | 2.877% |
| 19.539 | 0.5895 | 2.705% | 2.877% |
| 19.792 | 0.5852 | 2.707% | 2.876% |
| 20.047 | 0.5809 | 2.709% | 2.874% |
| 20.297 | 0.5768 | 2.711% | 2.874% |
| 20.550 | 0.5727 | 2.712% | 2.804% |
| 20.806 | 0.5686 | 2.713% | 2.803% |
| 21.061 | 0.5646 | 2.714% | 2.800% |
| 21.314 | 0.5606 | 2.715% | 2.797% |
| 21.567 | 0.5567 | 2.716% | 2.796% |
| 21.822 | 0.5527 | 2.717% | 2.793% |
| 22.078 | 0.5488 | 2.718% | 2.790% |
| 22.328 | 0.5450 | 2.718% | 2.788% |
| 22.581 | 0.5412 | 2.719% | 2.785% |
| 22.836 | 0.5374 | 2.720% | 2.782% |
| 23.100 | 0.5335 | 2.720% | 2.780% |
| 23.342 | 0.5299 | 2.721% | 2.776% |
| 23.594 | 0.5262 | 2.721% | 2.774% |
| 23.856 | 0.5224 | 2.722% | 2.771% |
| 24.111 | 0.5188 | 2.722% | 2.767% |
| 24.361 | 0.5152 | 2.722% | 2.764% |
| 24.614 | 0.5116 | 2.723% | 2.761% |
| 24.867 | 0.5081 | 2.723% | 2.758% |
| 25.122 | 0.5045 | 2.723% | 2.754% |
| 25.375 | 0.5011 | 2.723% | 2.750% |
| 25.625 | 0.4976 | 2.724% | 2.790% |
| 25.881 | 0.4941 | 2.724% | 2.787% |
| 26.136 | 0.4906 | 2.725% | 2.783% |
| 26.386 | 0.4872 | 2.725% | 2.780% |
| 26.639 | 0.4838 | 2.726% | 2.776% |
| 26.894 | 0.4804 | 2.726% | 2.773% |
| 27.150 | 0.4770 | 2.726% | 2.769% |
| 27.400 | 0.4737 | 2.727% | 2.766% |
| 27.653 | 0.4704 | 2.727% | 2.761% |
| 27.908 | 0.4672 | 2.727% | 2.758% |
| 28.164 | 0.4639 | 2.727% | 2.754% |
| 28.414 | 0.4607 | 2.727% | 2.749% |
| 28.667 | 0.4575 | 2.727% | 2.732% |
| 28.922 | 0.4544 | 2.728% | 2.737% |
| 29.186 | 0.4511 | 2.728% | 2.738% |
| 29.436 | 0.4480 | 2.728% | 2.732% |
| 29.689 | 0.4450 | 2.727% | 2.729% |
| 29.942 | 0.4419 | 2.727% | 2.726% |
| 30.197 | 0.4389 | 2.727% | 2.721% |
| 30.450 | 0.4359 | 2.727% | 1.358% |
| Maturity (y) | Black implied volatility |
|---|---|
| 0.514 | 9.82% |
| 0.778 | 9.52% |
| 1.028 | 9.22% |
| 1.281 | 16.23% |
| 1.533 | 16.72% |
| 1.789 | 16.73% |
| 2.039 | 16.64% |
| 2.292 | 23.20% |
| 2.544 | 23.62% |
| 2.800 | 23.74% |
| 3.053 | 23.87% |
| 3.303 | 25.24% |
| 3.558 | 25.39% |
| 3.814 | 25.44% |
| 4.064 | 25.51% |
| 4.317 | 26.31% |
| 4.572 | 26.35% |
| 4.828 | 26.35% |
| 5.081 | 26.36% |
| 5.333 | 26.11% |
| 5.589 | 26.08% |
| 5.853 | 26.06% |
| 6.106 | 26.04% |
| 6.347 | 25.84% |
| 6.608 | 25.79% |
| 6.864 | 25.77% |
| 7.114 | 25.74% |
| 7.367 | 24.74% |
| 7.619 | 24.65% |
| 7.875 | 24.62% |
| 8.125 | 24.59% |
| 8.378 | 24.87% |
| 8.631 | 24.87% |
| 8.886 | 24.85% |
| 9.139 | 24.84% |
| 9.392 | 24.36% |
| 9.647 | 24.30% |
| 9.903 | 24.28% |
| 10.153 | 24.26% |
| 10.406 | 24.97% |
| 10.661 | 24.96% |
| 10.917 | 24.95% |
| 11.167 | 24.95% |
| 11.419 | 24.81% |
| 11.675 | 24.80% |
| 11.939 | 24.80% |
| 12.181 | 24.81% |
| 12.433 | 25.84% |
| 12.694 | 25.86% |
| 12.950 | 25.86% |
| 13.200 | 25.86% |
| 13.453 | 25.87% |
| 13.706 | 25.87% |
| 13.961 | 25.88% |
| 14.214 | 25.89% |
| 14.464 | 25.89% |
| 14.719 | 25.90% |
| 14.975 | 25.91% |
| 15.225 | 25.90% |
| 15.478 | 23.33% |
| 15.733 | 23.21% |
| 15.989 | 23.22% |
| 16.239 | 23.23% |
| 16.492 | 23.24% |
| 16.747 | 23.25% |
| 17.003 | 23.26% |
| 17.256 | 23.28% |
| 17.508 | 23.29% |
| 17.769 | 23.30% |
| 18.025 | 23.32% |
| 18.275 | 23.33% |
| 18.528 | 23.35% |
| 18.781 | 23.36% |
| 19.036 | 23.38% |
| 19.286 | 23.40% |
| 19.539 | 23.42% |
| 19.792 | 23.44% |
| 20.047 | 23.46% |
| 20.297 | 23.48% |
| 20.550 | 23.80% |
| 20.806 | 23.84% |
| 21.061 | 23.86% |
| 21.314 | 23.89% |
| 21.567 | 23.92% |
| 21.822 | 23.94% |
| 22.078 | 23.97% |
| 22.328 | 24.00% |
| 22.581 | 24.03% |
| 22.836 | 24.06% |
| 23.100 | 24.08% |
| 23.342 | 24.12% |
| 23.594 | 24.15% |
| 23.856 | 24.18% |
| 24.111 | 24.21% |
| 24.361 | 24.24% |
| 24.614 | 24.27% |
| 24.867 | 24.31% |
| 25.122 | 24.34% |
| 25.375 | 24.37% |
| 25.625 | 24.21% |
| 25.881 | 24.23% |
| 26.136 | 24.26% |
| 26.386 | 24.30% |
| 26.639 | 24.33% |
| 26.894 | 24.36% |
| 27.150 | 24.40% |
| 27.400 | 24.44% |
| 27.653 | 24.47% |
| 27.908 | 24.51% |
| 28.164 | 24.54% |
| 28.414 | 24.58% |
| 28.667 | 24.62% |
| 28.922 | 24.66% |
| 29.186 | 24.70% |
| 29.436 | 24.74% |
| 29.689 | 24.78% |
| 29.942 | 24.82% |
| 30.197 | 24.86% |
| 30.450 | 24.90% |
The market inputs for a USD at-the-money cap as of April 16, 2019, over 119 caplet maturities out to about 30 years. The curves view shows the discount factor falling from near 1.0 to below 0.5, the zero rate holding in a narrow band, and the instantaneous forward f(0,t); the t=0 forward, f(0,0) = 2.53%, is the market-consistent value of the model short rate. The implied-vol view shows the Black volatility term structure rising off a low short end, peaking through the belly, and stepping down once near 15.5 years. Nothing here is fitted.
Fig. 2 — The market inputs. Left: the discount curve, its zero rates, and the instantaneous forward f(0,t), with f(0,0) = 2.53% 2.53% instantaneous forward rate at t=0 from the discount curve; the market-consistent value of r(0) export_web_json.py stage 1 marked. Right: the Black implied-volatility term structure, with its step-down near 15.5 years. Both are reproduced from the course-provided quotes, unaltered.
Two panels share the maturity axis. The first draws the discount curve and its zero rates alongside the instantaneous forward curve, with the t=0 forward, f(0,0) = 2.53% 2.53% instantaneous forward rate at t=0 from the discount curve; the market-consistent value of r(0) export_web_json.py stage 1 , marked as the market-consistent initial short rate. The second draws the Black implied-volatility term structure for the 118 118 caplets entering the corrected calibration (row 0 has no preceding discount, matching the archived Black loop) export_web_json.py stage 3 priced caplets: it begins low at the short end, peaks through the middle maturities, and steps down once near 15.5 years. Nothing here is fitted — these are the inputs the calibration is scored against.
The target
The archived target held no volatility information
The archived pipeline — graduate coursework, 2024; the committed notebook reproduces bit-for-bit — built its calibration target with a units slip: strike and volatility, already decimals, were divided by 100 again. Priced at a strike near zero, the target collapses to pure forward value — double every input volatility and it moves by <0.0001% <0.0001% largest move in the archived calibration target when every input vol is DOUBLED -- the target carried effectively no volatility information export_web_json.py stage 2 . A separate floating-point truncation in the accrual grid halved the accrual on 49 49 caps whose arange sub-division silently dropped half the accrual period -- the sawtooth in the archived price plot export_web_json.py stage 1 of the 119 119 USD ATM cap cashflow rows, Bloomberg SWPM, as of 2019-04-16 cap.csv, archived repo caplets: the sawtooth in the figure below, drawn hatched.
That explains the archived numbers. The report's σ = 1.75% 1.75% parameters printed in the archived course report archived course report (2024), optimization section at an SSE of 8.97% 8.97% SSE printed in the archived course report; this pipeline evaluates the report parameters at 0.0890 on the archived target with tau/8 subdivision -- same problem, better optimum than the committed notebook run archived course report (2024), optimization section and the notebook's a = 0.812 0.812 archived notebook differential-evolution run (2024) notebook cell 65, commit 025f598 , σ = 9.13% 9.13% archived notebook run; implausibly large for a Gaussian short rate -- a symptom of the corrupted target notebook cell 65, commit 025f598 — implausibly large for a Gaussian short rate — are both genuine optima of the same corrupted target, whose flattened objective holds a long trough of near-equivalent minima. And the 22.2% 22.2% archived IV-error metric; equals the gap between the inverter's own starting guess and the mean market vol, exactly -- the inversion never left its starting point because the double-divided strike flattened the objective notebook cell 70 + stage 2 implied-vol error it reports is not a fit statistic: with the objective flat, the price inverter never leaves its starting guess, and the figure equals that guess measured against the market mean, exactly.
| Finding | Value |
|---|---|
| Largest move in the archived target when every input volatility is doubled | <0.0001% |
| Archived implied-vol error, decoded (the inverter's own starting guess vs the market mean) | 22.2% |
| Caplets whose accrual grid was silently truncated to half a period | 49 of 119 |
| Maturity (y) | Archived target price | Accrual |
|---|---|---|
| 0.514 | $32,643 | halved (artifact) |
| 0.778 | $66,596 | full |
| 1.028 | $63,474 | full |
| 1.281 | $61,654 | full |
| 1.533 | $59,511 | full |
| 1.789 | $59,078 | full |
| 2.039 | $28,806 | halved (artifact) |
| 2.292 | $57,522 | full |
| 2.544 | $28,274 | halved (artifact) |
| 2.800 | $56,069 | full |
| 3.053 | $54,496 | full |
| 3.303 | $57,002 | full |
| 3.558 | $28,313 | halved (artifact) |
| 3.814 | $56,044 | full |
| 4.064 | $53,191 | full |
| 4.317 | $57,013 | full |
| 4.572 | $28,731 | halved (artifact) |
| 4.828 | $27,991 | halved (artifact) |
| 5.081 | $55,113 | full |
| 5.333 | $28,337 | halved (artifact) |
| 5.589 | $28,608 | halved (artifact) |
| 5.853 | $58,885 | full |
| 6.106 | $57,506 | full |
| 6.347 | $28,479 | halved (artifact) |
| 6.608 | $29,392 | halved (artifact) |
| 6.864 | $58,642 | full |
| 7.114 | $28,639 | halved (artifact) |
| 7.367 | $59,444 | full |
| 7.619 | $29,715 | halved (artifact) |
| 7.875 | $59,950 | full |
| 8.125 | $29,293 | halved (artifact) |
| 8.378 | $57,045 | full |
| 8.631 | $56,754 | full |
| 8.886 | $57,133 | full |
| 9.139 | $56,304 | full |
| 9.392 | $60,173 | full |
| 9.647 | $29,785 | halved (artifact) |
| 9.903 | $58,150 | full |
| 10.153 | $56,742 | full |
| 10.406 | $56,216 | full |
| 10.661 | $28,292 | halved (artifact) |
| 10.917 | $28,176 | halved (artifact) |
| 11.167 | $54,919 | full |
| 11.419 | $27,927 | halved (artifact) |
| 11.675 | $28,116 | halved (artifact) |
| 11.939 | $57,760 | full |
| 12.181 | $53,943 | full |
| 12.433 | $26,762 | halved (artifact) |
| 12.694 | $27,453 | halved (artifact) |
| 12.950 | $54,592 | full |
| 13.200 | $26,588 | halved (artifact) |
| 13.453 | $53,439 | full |
| 13.706 | $53,121 | full |
| 13.961 | $53,376 | full |
| 14.214 | $52,502 | full |
| 14.464 | $52,752 | full |
| 14.719 | $26,215 | halved (artifact) |
| 14.975 | $52,087 | full |
| 15.225 | $49,602 | full |
| 15.478 | $49,832 | full |
| 15.733 | $24,991 | halved (artifact) |
| 15.989 | $24,302 | halved (artifact) |
| 16.239 | $47,234 | full |
| 16.492 | $47,425 | full |
| 16.747 | $23,801 | halved (artifact) |
| 17.003 | $23,634 | halved (artifact) |
| 17.256 | $46,433 | full |
| 17.508 | $23,053 | halved (artifact) |
| 17.769 | $23,624 | halved (artifact) |
| 18.025 | $46,891 | full |
| 18.275 | $22,801 | halved (artifact) |
| 18.528 | $45,743 | full |
| 18.781 | $45,402 | full |
| 19.036 | $45,526 | full |
| 19.286 | $22,133 | halved (artifact) |
| 19.539 | $44,398 | full |
| 19.792 | $44,057 | full |
| 20.047 | $22,093 | halved (artifact) |
| 20.297 | $21,471 | halved (artifact) |
| 20.550 | $42,073 | full |
| 20.806 | $42,144 | full |
| 21.061 | $41,804 | full |
| 21.314 | $20,086 | halved (artifact) |
| 21.567 | $39,861 | full |
| 21.822 | $19,985 | halved (artifact) |
| 22.078 | $19,823 | halved (artifact) |
| 22.328 | $38,493 | full |
| 22.581 | $38,595 | full |
| 22.836 | $19,351 | halved (artifact) |
| 23.100 | $19,808 | halved (artifact) |
| 23.342 | $36,855 | full |
| 23.594 | $18,687 | halved (artifact) |
| 23.856 | $38,263 | full |
| 24.111 | $37,944 | full |
| 24.361 | $18,431 | halved (artifact) |
| 24.614 | $36,946 | full |
| 24.867 | $36,646 | full |
| 25.122 | $18,367 | halved (artifact) |
| 25.375 | $36,036 | full |
| 25.625 | $36,657 | full |
| 25.881 | $36,385 | full |
| 26.136 | $36,085 | full |
| 26.386 | $17,148 | halved (artifact) |
| 26.639 | $35,118 | full |
| 26.894 | $17,603 | halved (artifact) |
| 27.150 | $17,084 | halved (artifact) |
| 27.400 | $33,165 | full |
| 27.653 | $33,238 | full |
| 27.908 | $16,663 | halved (artifact) |
| 28.164 | $16,526 | halved (artifact) |
| 28.414 | $32,060 | full |
| 28.667 | $32,153 | full |
| 28.922 | $16,111 | halved (artifact) |
| 29.186 | $16,484 | halved (artifact) |
| 29.436 | $15,838 | halved (artifact) |
| 29.689 | $31,741 | full |
| 29.942 | $31,483 | full |
| 30.197 | $15,776 | halved (artifact) |
| 30.450 | $30,944 | full |
The archived calibration target price plotted against caplet maturity, over 119 caplets out to about 30 years. Most caplets price cleanly on the upper curve; 49 of them sit at roughly half that level because a floating-point truncation in the accrual grid silently halved their accrual period. Those half-accrual caplets are drawn with a hatch texture to flag them as mechanical artifacts, and they are the source of the sawtooth. A quiet companion line shows the model at the archived notebook parameters tracking the same sawtooth down into every trough.
Fig. 3 — Archived target price by caplet maturity. Solid marks price cleanly; hatched marks are the 49 49 caps whose arange sub-division silently dropped half the accrual period -- the sawtooth in the archived price plot export_web_json.py stage 1 caplets whose accrual grid lost half its period to a floating-point truncation. The sawtooth is a mechanical artifact of the pipeline, not a feature of the market.
The chart plots the archived target price against caplet maturity. Most marks lie on a smooth curve; 49 49 caps whose arange sub-division silently dropped half the accrual period -- the sawtooth in the archived price plot export_web_json.py stage 1 of the 119 119 USD ATM cap cashflow rows, Bloomberg SWPM, as of 2019-04-16 cap.csv, archived repo caplets sit visibly below it, drawn with a hatch texture to flag them as mechanical artifacts. Those are the caplets whose accrual grid was silently truncated to half a period, and they are the source of the sawtooth.
The recalibration
The miss is the message
On a corrected target — strike restored, accrual grid repaired — the model calibrates, with r(0) free, to a = 0.150 0.150 mean-reversion speed, corrected-target recalibration, free r(0) export_web_json.py stage 3 , σ = 145 bp 145 bp Gaussian short-rate volatility (absolute, per year), corrected-target recalibration, free r(0) export_web_json.py stage 3 , r(0) = 0.17% 0.17% free r(0) as the archived setup treats it; compare fm00 -- the gap is the curve tilt the optimiser buys itself export_web_json.py stage 3 , and prices the caplet book to 1.4 vol pts 1.4 vol pts mean absolute model-vs-market implied-vol gap at the recalibrated optimum export_web_json.py stage 4 of implied volatility at a 12.1% 12.1% RMS relative cap-pricing error at the recalibrated optimum export_web_json.py stage 4 price RMSE. The accuracy has one legible source: r(0) sits well below the market-consistent f(0,0) = 2.53% 2.53% instantaneous forward rate at t=0 from the discount curve; the market-consistent value of r(0) export_web_json.py stage 1 , and the model's discount factor scales with exp(B(0,T)·(f(0,0) − r(0))) — a low r(0) lifts the model's long-maturity discounts above the market's, as the playground's “curve tilt at 30y” readout reports. Part of the fit re-prices the curve rather than describing the volatilities.
Pin r(0) to f(0,0) and the same model class collapses: a runs to its lower bound (0.000 0.000 mean-reversion speed with r(0) pinned to f(0,0) export_web_json.py stage 3 ), σ falls to 33 bp 33 bp sigma when r(0) is pinned to f(0,0) (market-consistent HW) export_web_json.py stage 3 , and the fit widens to 11.6 vol pts 11.6 vol pts IV MAE under the pinned-r0 restriction export_web_json.py stage 4 at a 56.5% 56.5% RMS relative pricing error under the pinned-r0 restriction export_web_json.py stage 4 price RMSE, almost every model volatility below the market's. The reason is structural. Market caplet prices imply a normal-volatility term structure that rises with maturity; a constant-σ one-factor model with a ≥ 0 can only produce one that is flat or falling. No parameter search fixes the wrong shape — the miss prices the next model class, a time-dependent σ(t) or a second factor as in G2++.
Profiled surface · r(0) free
- a
- 0.158
- σ
- 0.0146
- loss
- 0.877
- r(0)*
- 0.14%
r(0)* minimises this cell’s loss over r(0).
Hover or focus the surface; arrow keys move the probe.
Note — on the profiled surface each cell’s loss is minimised over r(0) separately, and the r(0) that achieves it is the r(0)* read by the probe. The pinned surface fixes r(0) = f(0,0). Both surfaces are shaded on one shared log10-loss scale, so the pinned minimum reads visibly worse than the profiled basin.
Profiled surface · r(0) free. a 0.158, sigma 0.0146, loss 0.877, profiled r-zero 0.14%.
| Optimum | a | σ | r(0) | loss |
|---|---|---|---|---|
| Recalibrated (r(0) free) | 0.15 | 0.0145 | 0.17% | 0.864 |
| Pinned (r(0) = f(0,0)) | 0.000001 | 0.00331 | 2.53% | 18.8 |
Fig. 4 — Calibration loss over (a, σ), on a log–log grid. The filled marker is the recalibrated optimum with r(0) free; the hollow marker is the pinned-r(0) restriction, driven onto the lower bound of a. Toggle between the profiled and pinned surfaces to see the fit the pin costs.
The chart is a loss surface over mean reversion a and volatility σ, each on a log scale, shaded from low loss to high. With r(0) profiled, the minimum is a broad, well-defined basin at a = 0.150 0.150 mean-reversion speed, corrected-target recalibration, free r(0) export_web_json.py stage 3 , σ = 145 bp 145 bp Gaussian short-rate volatility (absolute, per year), corrected-target recalibration, free r(0) export_web_json.py stage 3 . With r(0) pinned to f(0,0), the same surface has no interior minimum — the optimum slides to the lower bound of a, σ collapses to 33 bp 33 bp sigma when r(0) is pinned to f(0,0) (market-consistent HW) export_web_json.py stage 3 , and the best attainable fit is 11.6 vol pts 11.6 vol pts IV MAE under the pinned-r0 restriction export_web_json.py stage 4 of implied vol against 1.4 vol pts 1.4 vol pts mean absolute model-vs-market implied-vol gap at the recalibrated optimum export_web_json.py stage 4 when r(0) is free. The surface shows the pin removing a degree of freedom the fit was quietly relying on.
The cross-check
The cross-check had its own bias
The closed forms are verified against Monte Carlo, and the estimators themselves are part of the result. The archived T-measure estimator for zero-coupon-bond puts omits the MT drift — the T-forward change-of-measure term — and sits +0.08% +0.08% mean MC-vs-closed-form gap of the archived T-measure estimator (missing M^T drift); small but systematically positive across every maturity export_web_json.py stage 6 above the closed-form price: small, but positive at every maturity, a signature rather than sampling noise. With the drift restored it closes to within 2.6 SE 2.6 SE worst |MC - closed form| of the drift-corrected estimator, in MC standard errors export_web_json.py stage 6 of Monte-Carlo error. A second estimator, defined in the notebook but never invoked, draws the short rate independently at every step of its discount integral and so loses the convexity term: off by -14.8% -14.8% worst ZCB gap of the archived estimator (independent marginal draws kill the convexity term), at the committed parameters, 30y export_web_json.py stage 6 at the long end, where exact-transition path simulation stays within +0.47% +0.47% worst ZCB gap of the exact-transition path estimator (within MC error) export_web_json.py stage 6 .
Note — the two tiers share the maturity axis but not the vertical scale: the archived tier's vertical scale is finer by more than an order of magnitude, so its small, systematic bias is legible against its own ±2 SE envelope. The tick labels carry the true scale.
Maturity 15.5 y
- Archived T-measure
- +0.087%+12.5 SE above zero
- Drift-corrected
- +0.24%+0.2 SE · within ±2 SE band
Archived bias +0.08% at every maturity; corrected within 2.6 SE.
Drag, or focus the handle and use the arrow keys (Shift = 5)
Both series are like-for-like reproductions of the archived estimators at the committed parameters — not the archived figure. The visible gap in the archived notebook's own Monte-Carlo plot traces to a separate discount-pairing slip in its closed-form call; the independent-draw ZCB estimator, the one that would have been worst, never ran in the committed notebook at all.
| Maturity (y) | Archived gap (%) | Archived SE multiple | Corrected gap (%) | Corrected SE multiple |
|---|---|---|---|---|
| 0.3 | +0.006% | +1.5 SE | −0.61% | −0.7 SE |
| 1.3 | +0.037% | +5.7 SE | +0.32% | +0.3 SE |
| 2.3 | +0.062% | +8.9 SE | −0.29% | −0.3 SE |
| 3.3 | +0.072% | +10.2 SE | −1.29% | −1.3 SE |
| 4.3 | +0.083% | +11.8 SE | −0.39% | −0.4 SE |
| 5.3 | +0.078% | +11.3 SE | +0.93% | +0.9 SE |
| 6.3 | +0.077% | +10.8 SE | −0.47% | −0.5 SE |
| 7.4 | +0.093% | +13.3 SE | +1.45% | +1.4 SE |
| 8.4 | +0.086% | +12.4 SE | −0.87% | −0.9 SE |
| 9.4 | +0.093% | +13.2 SE | +2.69% | +2.6 SE |
| 10.4 | +0.080% | +11.6 SE | −1.38% | −1.4 SE |
| 11.4 | +0.085% | +12.3 SE | +0.74% | +0.7 SE |
| 12.4 | +0.083% | +11.7 SE | −0.08% | −0.1 SE |
| 13.4 | +0.098% | +14.1 SE | +1.07% | +1.0 SE |
| 14.5 | +0.093% | +13.3 SE | −1.22% | −1.2 SE |
| 15.5 | +0.087% | +12.5 SE | +0.24% | +0.2 SE |
| 16.5 | +0.091% | +13.3 SE | −0.86% | −0.8 SE |
| 17.5 | +0.071% | +10.2 SE | +1.16% | +1.1 SE |
| 18.5 | +0.086% | +12.5 SE | −0.20% | −0.2 SE |
| 19.5 | +0.090% | +12.9 SE | −0.00% | −0.0 SE |
| 20.5 | +0.098% | +14.1 SE | +0.94% | +0.9 SE |
| 21.6 | +0.096% | +13.9 SE | +0.15% | +0.1 SE |
| 22.6 | +0.083% | +12.1 SE | +0.35% | +0.3 SE |
| 23.6 | +0.088% | +12.7 SE | +0.23% | +0.2 SE |
| 24.6 | +0.091% | +13.2 SE | +0.26% | +0.3 SE |
| 25.6 | +0.079% | +11.2 SE | +0.72% | +0.7 SE |
| 26.6 | +0.094% | +13.4 SE | −0.37% | −0.4 SE |
| 27.6 | +0.091% | +13.3 SE | +0.88% | +0.9 SE |
| 28.7 | +0.096% | +14.0 SE | −1.76% | −1.7 SE |
| 29.7 | +0.075% | +10.8 SE | −0.35% | −0.3 SE |
| Paths n | Running mean | Gap from closed form (%) | SE multiple |
|---|---|---|---|
| 50 | 0.004931 | +11.07% | +0.5 SE |
| 58 | 0.004787 | +7.84% | +0.4 SE |
| 68 | 0.004564 | +2.82% | +0.2 SE |
| 79 | 0.004670 | +5.19% | +0.3 SE |
| 92 | 0.004675 | +5.31% | +0.3 SE |
| 108 | 0.004368 | −1.60% | −0.1 SE |
| 126 | 0.004595 | +3.51% | +0.3 SE |
| 147 | 0.004618 | +4.03% | +0.3 SE |
| 171 | 0.004328 | −2.51% | −0.2 SE |
| 199 | 0.004330 | −2.47% | −0.2 SE |
| 232 | 0.004261 | −4.02% | −0.4 SE |
| 271 | 0.004306 | −3.00% | −0.3 SE |
| 316 | 0.004396 | −0.98% | −0.1 SE |
| 368 | 0.004364 | −1.70% | −0.2 SE |
| 430 | 0.004366 | −1.65% | −0.2 SE |
| 501 | 0.004288 | −3.40% | −0.5 SE |
| 584 | 0.004280 | −3.59% | −0.6 SE |
| 681 | 0.004368 | −1.61% | −0.3 SE |
| 794 | 0.004269 | −3.84% | −0.8 SE |
| 926 | 0.004200 | −5.38% | −1.2 SE |
| 1080 | 0.004278 | −3.63% | −0.8 SE |
| 1259 | 0.004325 | −2.56% | −0.6 SE |
| 1468 | 0.004355 | −1.90% | −0.5 SE |
| 1712 | 0.004402 | −0.83% | −0.2 SE |
| 1996 | 0.004375 | −1.45% | −0.5 SE |
| 2328 | 0.004487 | +1.08% | +0.4 SE |
| 2714 | 0.004472 | +0.73% | +0.3 SE |
| 3165 | 0.004466 | +0.60% | +0.2 SE |
| 3691 | 0.004362 | −1.74% | −0.7 SE |
| 4304 | 0.004356 | −1.89% | −0.9 SE |
| 5018 | 0.004338 | −2.28% | −1.1 SE |
| 5852 | 0.004384 | −1.25% | −0.7 SE |
| 6823 | 0.004453 | +0.32% | +0.2 SE |
| 7956 | 0.004461 | +0.48% | +0.3 SE |
| 9278 | 0.004486 | +1.04% | +0.7 SE |
| 10818 | 0.004496 | +1.28% | +0.9 SE |
| 12615 | 0.004519 | +1.79% | +1.4 SE |
| 14709 | 0.004480 | +0.91% | +0.8 SE |
| 17152 | 0.004464 | +0.56% | +0.5 SE |
| 20000 | 0.004439 | −0.01% | −0.0 SE |
Monte-Carlo minus closed-form pricing gap, as a share of the closed-form price, across caplet maturity. The archived T-measure estimator omits the change-of-measure drift and sits systematically above zero at every maturity (mean +0.08%, around 12 standard errors out); the drift-corrected estimator scatters inside its plus or minus 2 standard-error band around zero, within 2.6 SE of Monte-Carlo error. A separate zero-coupon-bond check: an independent-draw estimator that discards convexity would miss by as much as -14.8% at 30 years, where the exact-transition path simulation stays within +0.47%.
Fig. 5 — Monte-Carlo minus closed-form, as a share of the closed-form price, across maturity. The archived T-measure estimator (upper marks) runs systematically positive; the drift-corrected estimator scatters inside its ±2 standard-error band around zero. Companion panel: the corrected estimator's running mean converging to the closed-form level as paths accumulate.
The chart plots the relative gap between Monte-Carlo and closed-form prices against maturity. The archived estimator's marks lie above the zero line at every maturity, averaging +0.08% +0.08% mean MC-vs-closed-form gap of the archived T-measure estimator (missing M^T drift); small but systematically positive across every maturity export_web_json.py stage 6 — the fingerprint of a missing change-of-measure drift, not random error. The drift-corrected estimator's marks straddle zero and stay within 2.6 SE 2.6 SE worst |MC - closed form| of the drift-corrected estimator, in MC standard errors export_web_json.py stage 6 of Monte-Carlo error. A note records the separately worst case: an independent-draw estimator that discards convexity would miss by -14.8% -14.8% worst ZCB gap of the archived estimator (independent marginal draws kill the convexity term), at the committed parameters, 30y export_web_json.py stage 6 at the long end against +0.47% +0.47% worst ZCB gap of the exact-transition path estimator (within MC error) export_web_json.py stage 6 for the path simulation, but it never ran in the committed notebook.
Provenance & takeaways
Audit the target before the optimizer.
A search is only as honest as the objective it minimizes; here the objective could not feel the volatilities it was scored on, and the optimizer did exactly what it was asked.
A parameter that can only tilt the curve is a symptom, not a fit.
When r(0) buys accuracy by re-pricing the discount curve, the number to trust is the tilt it leaves at thirty years, not the fit it reports at the front.
A metric that never moves is not a metric.
The headline implied-vol error equaled the inverter's own starting guess; a statistic that returns its input has measured nothing.
Every number on this page regenerates from a single export script. Archived coursework figures reproduce exactly and are quoted only as archived values, never merged with the recalibration. One-factor Hull-White, USD ATM caplet strip; graduate fixed-income coursework (2024), recalibrated 2026. The archived repository — notebook, data, committed outputs — is public on GitHub.