ATM term structure

The ATM term structure agrees with itself by construction, so I had to work out when its agreement is evidence

The term structure inverts both the call and the put at the at-the-money strike and writes out both. When put-call parity happens to select that same strike, the two legs invert to identical volatilities as an algebraic identity — so their agreement proves nothing, and only the dates where the two strikes differ carry information.

What this stage is, and what it deliberately is not

Everything upstream of this point stops at a single claim: that the implied volatility of one contract is correct. A 5–45 day book is not traded off single contracts. It is traded off the level and slope of the at-the-money curve, and off implied against forecast realised. Neither is queryable until per-contract data is reduced to a term structure, which is what this builds: one row per date, root and expiry, carrying the parity forward, the at-the-money strike, and both leg implied volatilities.

It is not a surface. There is no smile fit and no interpolator, so nothing off-strike or between expiries can be evaluated. That is the largest gap between what runs and what the rest of the architecture assumes, and it is the next thing to build.

Alternatives considered

Build from chain snapshots. The obvious source. Snapshots already carry strike, expiry and open interest, so no symbol parsing is needed. Rejected on history: snapshots exist only from 2026-08-31, because there is no historical snapshot endpoint at any tier. A term structure with two weeks of history cannot be fitted to anything.

Build from option day bars. Chosen. Day bars reach back to 2022-08-31 and are backfillable from the vendor archive at any time. The cost is that they carry no contract terms whatsoever, so strike and expiry must be recovered by parsing the OPRA symbol. That parsing is the price of four years of history instead of two weeks, and it was worth paying.

Go straight to a smile fit. Rejected as sequencing. A parametric fit needs something to be checked against, and the at-the-money point is the one place where an independent value is cheaply available. Fitting first would mean having no baseline for the fit.

The trap: agreement that is a tautology

The forward is recovered from put–call parity, because no index level is entitled on this tier. The procedure takes the strike minimising |C − P| and returns F = K + e^{rT}(C − P). Separately, the at-the-money strike is defined as the strike nearest the forward, and both its legs are inverted and written out.

Those are two different searches — one minimises |C − P|, the other minimises |K − F| — and frequently they land on the same strike. When they do, C − P = (F − K)e^{-rT} holds by construction, because that is the equation the forward was defined by. The call and the put then invert to exactly the same implied volatility, and they would do so regardless of whether the pricing code were correct.

This is the kind of check that looks like validation and is not. Reporting "call and put legs agree at the money on 99% of dates" would be reporting an algebraic identity as though it were an empirical result.

The legs are still both written out rather than averaged, because when the two searches select different strikes the spread between the legs is genuinely informative. That is exactly why the acceptance test for this stage is a residual distribution computed only over the dates where the two strikes differ, together with the count of rejected observations and the reason each was rejected. A diagnostic over all dates would be dominated by the tautological ones and would look far better than the system deserves.

What the term structure answers, and what it cannot

Answers: where at-the-money volatility sits for a given root and expiry; the slope of that curve across expiries; how implied at the money compares against a realised forecast, once a forecast exists. Over the archive back to 2022-08-31, rebuilt end to end in about seven minutes.

Cannot answer: anything requiring a volatility at a strike that was not traded, or at an expiry between two listed ones. That means no skew measure, no risk-reversal or butterfly construction, no model-free variance extraction over a strike continuum, and no Greeks for a structure whose strikes are not all at the money. Every one of those is assumed by a stage further down the pipeline, which is why this stage is marked implemented but not yet validated rather than finished.

Acceptance tests for the stage after this one

Written now, before the fit exists, so they cannot be adjusted to suit a result:

  • Reproduce the baseline. A fitted slice must reproduce the at-the-money term structure at the money to within the pricing layer's own reprice tolerance. A surface that disagrees with the ATM curve is wrong at the one point where an independent value exists.
  • Publish the residuals, not the curve. Input observations, fitted output, residuals, rejected observations, and failure cases. A smooth chart of a fitted smile is not evidence; it is what a fit produces whether or not it is any good.
  • Constraints enforced in the fit, not checked afterwards. Calendar-spread and butterfly no-arbitrage constraints belong in the optimisation, so that downstream Greeks are internally consistent by construction rather than by inspection.
  • Report the failures. The dates and roots where no admissible fit was found, with the reason. A fitter that always succeeds is not being told about enough of the data.

What changed as a result

Both legs are written out rather than the mean alone, so the tautological cases can be identified and excluded rather than silently inflating an agreement statistic. The stage is labelled implemented — under validation rather than operational, because it runs but its diagnostic has not been published. And the public pages, which had claimed a raw SVI fit with arbitrage constraints enforced in the fit running over the archive, now say what is actually there: an at-the-money curve, no smile, no interpolator.

Addendum, 2026-09-08: the smile landed the next day

This entry is left as written, because a dated record that gets edited when it becomes inconvenient is worth nothing. But it is now out of date in a way a reader should not have to discover for themselves.

A raw-SVI (Gatheral) smile fit shipped the following day and is scheduled daily, fifteen minutes after the term structure so that both reductions read the same chain. It fits total implied variance against log-moneyness per date, root and expiry, over own-inverted OTM strikes. SPY is refused: its strikes are American and would invert under the wrong exercise boundary in a European fit path.

Three of the four acceptance tests written above were met by the implementation rather than argued around, which is the outcome I wanted from writing them down first:

  • Constraints enforced in the fit, not checked afterwards — met. The butterfly guard is a hard constraint inside the optimisation, and the calendar guard refits against the previous expiry's total variance on a known union grid.
  • Report the failures — met, and more strictly than specified. A slice that cannot be repaired raises rather than writing an arbitrageable smile, so the failure is loud instead of being a row nobody reads.
  • Reproduce the baseline at the money — met by construction rather than by calibration, which is better. The fit consumes the same chain, the same parity forward and the same inversion as the ATM curve, so agreement between the two at the money is a cross-check between two datasets rather than a target the fit was tuned against. Note that this is the opposite situation to the tautology described above, and for the same underlying reason: what makes an agreement informative is whether the two quantities could have disagreed.
  • Publish the residuals, not the curve — not met. No diagnostic has been published, which is why the stage is marked implemented rather than operational, and why the site says so.

The claim this site carried on 6 September — a raw SVI fit with butterfly and calendar arbitrage enforced in the fit, running over the archive — was false when it was written and is true now. It was still worth correcting. A claim that happens to come true two days later was not evidence on the day it was made, and the difference between those two things is most of what this log is for.

Published . Last revised . This entry follows the pre-committed evaluation protocol; it is versioned in the repository, and any later correction appears as a dated commit rather than a silent edit.