> ## Documentation Index
> Fetch the complete documentation index at: https://docs.odds-api.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 2026 08 31 modelled closing line design

# Modelled closing line for tracked bets

Date: 2026-08-31. Status: design approved in principle; model choices and caps
are set by the backtest in `v3/cmd/closebacktest`.

## Problem

A Terminal-tracked bet gets its CLV from the reference book's closing price for
the exact line it was tracked at. When that line is not on the book's closing
board the bet has no CLV. After the exact-close fixes (oddsapi #442, hub #1028)
the remaining misses are lines the book no longer quoted at close: the main
line moved (basketball median move 4 pts against a stored window of 2.5 pts),
single-line markets (NFL, team totals, tennis totals, most player props), or a
market with no capture for that book. Goal: every tracked bet gets a CLV, with
an honest label and a measured error.

## What the capture gives us

`historical.closing_odds` holds, per book and market, a WINDOW of lines around
the closing main line (basketball 11 rungs, football 7, halves/corners 5,
NFL / team totals / tennis totals / most props 1), each with both sides priced.
The 1X2 / ML is captured too. There is no full board.

## Model

Every lined market is a bet on an integer latent quantity X: total points or
goals (totals), or home margin (spreads, threshold -line). A rung of the
ladder is then (threshold t, fair price for "X above t", fair price for
"X below t"), the fair prices from the shared LOG de-vig (`utils.NoVigFromRow`).

Fair price of a line under a distribution D over the integers, with pushes and
quarter lines handled exactly: split the line into its components (a quarter
line is two half stakes on the adjacent half and whole lines), then
`fair = 1 + sum(P(lose)) / sum(P(win))` over the components. This is the
identity bettors use, not an approximation.

Two distribution families, chosen per sport by the backtest:

* Normal with continuity correction (mean, sd): basketball, American football,
  tennis games, handball, volleyball, esports maps, yards-type props.
* Poisson family: Poisson(lambda) for goal/run/corner/card totals and count
  props; Skellam for goal-margin spreads, with the goal total taken from the
  event's own totals ladder when present, else a sport prior.

Fitting: minimise squared error between model and de-vigged implied
probabilities across the ladder (Nelder-Mead over the family's parameters).
With a single rung only the location is fitted; the dispersion comes from a
prior table per (sport, market family) that the backtest itself calibrates
(median fitted dispersion across multi-rung ladders).

Prediction: evaluate the fitted distribution at the bet's line and side.

## Tiers and labels

1. `exact`: quoted line at the reference book (existing).
2. `derived`: identities on quoted markets (DNB / Double Chance from 1X2;
   quarter line from its two halves when both are quoted). Exact.
3. `modeled`: the fitted distribution evaluated at the bet's line. Stored with
   the anchor (closing main line and its fair price), the distance from the
   nearest quoted rung, and the family used. Shown as "est." in the tracker,
   with the hover text naming the main line it was priced from. Excluded from
   `odds-verified` on public records; filterable in stats.
4. Refused: no reference market rows for the event, or distance beyond the
   per-sport cap set from the backtest.

## Where it lives

Go only, in `v3/closemodel`, called from `resolveSettlementOutcome` after the
exact and derived attempts fail. The hub stores `closingSource` and the anchor
fields and renders them; it does not reimplement the model (live CLV for open
bets keeps refusing until an endpoint exposes the model).

## Backtest (this is what makes "accurate" a measured claim)

`v3/cmd/closebacktest` reads recent Pinnacle (and DraftKings prop) ladders from
`historical.closing_odds` (read replica, read-only) and reports the CLV error
of a predicted rung against its real de-vigged price:

* Interpolation: leave one interior rung out.
* Extrapolation: hold out the top 1..3 rungs of the window, predict from the
  rest. This is the real "line moved past the window" case.
* Single anchor: fit on the main rung only with the prior dispersion, predict
  every other rung. This is the NFL / team total / prop case.

Reported per sport and market family: median and p90 absolute CLV error (%),
signed bias, sample size, by distance in rungs. Caps and family choices come
from these tables; anything without an acceptable error stays refused.
