---
title: Classify alt win-total ladder markets at the two books that fold them into the main type
type: task
status: completed
description: >-
  Move the alt win-total rungs BETMGM and BETRIVERS fold into SEASON_TEAM_REGULAR_SEASON_WINS onto
  SEASON_TEAM_ALT_REGULAR_SEASON_WINS, so the main type is one line per team at every book and the
  team-season win-total column can offer all of them.
base_uri: user:task/league/data-views/classify-alt-win-total-ladder-markets.md
created_at: '2026-10-17T18:13:08.851Z'
entity_id: 9953a695-83ac-4262-bbab-5ebf82ea42fd
finished_at: '2026-10-17T19:19:46.435Z'
observations:
  - ████████████████████████████████████████████████
owner_identity_uri: █████████████████████████
priority: High
public_read: true
tags:
  - user:tag/league-xo-football.md
updated_at: '2026-11-06T00:03:01.249Z'
visibility_analyzed_at: '2026-11-06T00:17:45.041Z'
---

The two books whose main win-total market folds an alt ladder into it are not offered by the new team-season win-total column, because their rows are several per team and the season path has no dedup and no line axis to give the rungs somewhere to go.

## Overview

Classify each book's alt win-total rungs under `SEASON_TEAM_ALT_REGULAR_SEASON_WINS` at its importer, so the main market type carries one line per team at every book, then offer those books in `team_season_prop_line_from_betting_markets`'s bookmaker picker.

## Background

`██████████████████████████████████████████████████████████████` shipped `team_season_prop_line_from_betting_markets` (league `2b3b4657e`, `023748581`) and backfilled the market's team subject in production (3,188 pids). Its default book is PINNACLE, the only book whose main win-total market is one line per team in both 2025 and 2026 (32 markets / 32 teams, measured).

Measured across the ingested books, the main market type `SEASON_TEAM_REGULAR_SEASON_WINS` is not one-market-per-team everywhere:

| Book | Main market type in 2026 | Rows per team |
| --- | --- | --- |
| PINNACLE | 32 markets | one |
| CAESARS | one per team (2023, 2024, 2026) | one |
| DRAFTKINGS | one per team (2025) | one |
| BETMGM | 333 markets | several — the alt rungs are inside the main type |
| BETRIVERS | 89 markets | several — same shape |

`SEASON_TEAM_ALT_REGULAR_SEASON_WINS` already exists as a market type; the two books above simply do not use it for the ladder. The win-totals column deliberately did not dedup on the season path, because a `DISTINCT ON` keyed on `selection_pid` alone would pick the lowest rung (0.5 wins) — a silently wrong number, worse than a visible fanout. The classification fix at the importer is the prerequisite the column's code comment names.

## Plan

- [x] **Measure both books' main-market ladders.** Both books have 2026 only. BETMGM: 333 main-type markets for 32 teams, one market per rung, 8-10 rungs per team. BETRIVERS: 89 markets for 32 teams, 1-5 rungs. No field marks the main line. BETMGM's `isMain` reads false on all 333, and BETRIVERS' `tags` and `lineTypes` are empty where present. `(source_event_id, source_market_name)` is one team at both books (32 keys for 32 teams).
- [x] **Classify the ladder at each importer.** `libs-server/classify-win-total-ladder.mjs`, called by `scripts/import-betrivers-odds.mjs` and the private BETMGM importer. It keeps the rung with the most balanced implied probability as main, moves the rest to alt, and after insert demotes stored main rungs the ladder no longer picks. The demotion is needed because season markets are never delisted (the run-level sweep is scoped by `esbid`). Spec: `test/libs-server.classify-win-total-ladder.spec.mjs`, 7 cases, shown red by three mutants.
- [x] **Backfill the historical rows.** `scripts/backfill-win-total-ladder-classification.mjs`, dry run by default. Written to production 2026-10-17 after the deploy: 358 of 358 types. BETMGM 2026 went from 333 main markets to 32 for 32 teams, with 301 alt. BETRIVERS went from 89 to 32 for 32 teams, with 57 alt. No ladder was left without a main rung. Every book and season now holds at most one main market per team.
- [x] **Offer the two books in the column's picker.** Both added to `create_team_season_prop_column_params`. The alt type is dropped from the market picker because it is several rungs per team by definition. The column comment now points at the classifier.
- [x] **Fixture and commit.** `test/data-view-queries/team-season-prop-line-ladder-book-one-line-per-team-result-equivalence.json` renders one line per team from a seeded BETMGM ladder. Shown red as three BUF rows when the alt rungs are typed main. League `8ebd98bcf`, private `a65710a`.

## State

Completed 2026-10-17.

- The balanced-rung rule matches the market. Against CAESARS on 2026-10-17, BETMGM's main line is exact for 28 of 32 teams and BETRIVERS' for 27, with every team within one win. Picking over all stored rungs instead of the latest run alone was off by up to 3 wins, because stale rungs keep old prices.
- Deployed 2026-10-17 at league `527c27c2c`: the main host and the SPA. Both importers run on ████████████ from `/home/user/league`, which is refreshed before each run, so the classifier is live there. The worker host was not reached, because ████████████ holds no key for it. Nothing in this change runs there.
- Browser check on xo.football: BETMGM, BETRIVERS, PINNACLE and CAESARS 2026 each render 32 rows, one per team. The BETMGM and BETRIVERS lines match the stored main rungs for 32 of 32 teams.
- Full isolated suite with this change: 16,347 passing, 3 failing, none in touched code.
