# Offline odds-comparison eligibility example

This Python standard-library example decides whether two **caller-normalized observations are eligible for a data comparison**. The fixtures describe a fictional soccer match. No real bookmaker data, feed connection, account, wager or external request is used. The code checks declarations; it cannot establish that an adapter mapped an event, rule or timestamp correctly.

The included input has 14 synthetic cases. Exactly one is eligible: its two observations have **different** decimal odds. The other 13 are held with reasons. Eligibility establishes neither equal prices nor current executable prices, availability, an executable opportunity or a recommended action.

Run from this directory:

```sh
python3 odds_compare.py fixtures.json
python3 -m unittest -v test_odds_compare.py
```

The first command prints JSON with `eligible_count: 1` and `held_count: 13`. The second completed 21 test methods successfully; some methods include multiple corrupt-input subcases. Tested with CPython 3.14.7 on Darwin 24.6.0 arm64 on 12 September 2026. Other Python versions were not executed in this run. Tool version: 1.0.0. Save stdout or unittest output locally if you want a record of your own run; all four files needed to reproduce these checks are linked from the article.

## Input contract

The envelope contains `now`, `policy` and `cases`. Each case contains a unique `id`, `left` and `right`. Case IDs use 1–80 lowercase letters, digits, underscores or hyphens. Each observation contains these fields:

| Fields | Required meaning |
| --- | --- |
| `event_namespace`, `event_id` | The same explicitly declared identifier namespace and mapped match ID. Strings are 1–128 characters. Equal raw IDs from unrelated providers do not establish equal events. |
| `period`, `market`, `line` | This narrow example accepts `REGULATION`, `TOTAL_GOALS`, numeric `2.5` only. Both observations must match. |
| `selections` | Exactly `OVER` and `UNDER`, once each. Array order is irrelevant. |
| `settlement_rules` | The allowlisted fictional contract `fixture-soccer-regulation-v1`. See below. A shared unknown label is held. |
| `in_play` | An explicit JSON boolean. Both observations must agree. This does not establish identical score, match clock or next-play state. |
| `status` | `OPEN`, `SUSPENDED`, `CLOSED` or `UNKNOWN`. Only explicit `OPEN` passes. The adapter may declare `OPEN` only when the market and both selections are active and unsuspended. Missing or unrecognized status is unknown. |
| `complete_snapshot` | Literal `true` only after the adapter has a coherent full current state for this market and both selections. |
| `captured_at` | UTC timestamp for capture of this normalized observation. |
| `price_updated_at` | UTC timestamp with documented source price coverage for the odds being compared. Its semantics are supplied by the caller, not verified by this checker. |
| `odds` | Exactly `{"OVER": number, "UNDER": number}`. Decimal odds must be finite numbers greater than 1. Booleans and numeric strings are rejected. |

The fictional settlement contract counts both teams' goals in 90 minutes plus referee stoppage time, excludes extra time and penalty shootouts, and voids canceled or abandoned matches. It exists only for this fixture. Adapting the example requires a reviewed mapping of each provider's event identity, period, line, selections and full applicable settlement rules to an explicit contract. Changing a label to the accepted string does not perform that mapping. Other contracts are intentionally held by this version.

Apply feed deltas to a previously established state before producing these records. Deletion, suspension, recovery and omission semantics belong in that provider's adapter. A raw delta is not a complete snapshot. Do not infer `OPEN` or a new price timestamp from a missing field.

Timestamp names from a provider do not automatically satisfy this contract. A market-level update timestamp is not proof of each outcome's last price change. For a merged state with independently timestamped prices, use a conservative timestamp that covers the oldest constituent price observation; if that coverage is unknown, leave `price_updated_at` missing and hold. A price-bearing snapshot may reconfirm unchanged odds; a connection heartbeat alone cannot do so. This example does not implement or certify any provider adapter.

## Declared policy and result interpretation

The sample policy uses `max_age_seconds: 30` and `max_capture_skew_seconds: 2`. These are **fictional demonstration limits**, not bookmaker guidance or recommended production freshness. Select limits from the actual downstream task and known source update semantics. The supplied `now` is the evaluation instant. Its fixed value, `2026-09-12T12:00:00Z`, is synthetic and separate from the actual execution times.

Both capture age and declared price age must be between zero and 30 seconds inclusive at `now`. The captures must be within two seconds inclusive of each other. A price update later than its capture is inconsistent. Future, missing, naive and non-UTC timestamps are held. Input times must be ISO 8601 datetimes with `T` and UTC `Z` or an equivalent zero offset; this example rejects nonzero offsets rather than silently converting them. Policy values must be finite numbers from 0 through 86,400 seconds, with no silent defaults.

Each case returns `eligible_for_comparison` and `reason_codes`. The prefix identifies `left`, `right` or `pair`. Representative results:

| Synthetic case | Result |
| --- | --- |
| Same normalized contract, odds 1.91/1.93 versus 1.95/1.89 | Eligible; prices differ. |
| Total 2.5 versus 3.5 | `pair:LINE_MISMATCH` and `right:LINE_UNSUPPORTED`. |
| Different rule, period or pre-match/in-play phase | Corresponding `pair:*_MISMATCH`. Unsupported rules/periods also report unknown semantics. |
| Fresh capture, 40-second-old declared price timestamp | `right:PRICE_UPDATED_AT_STALE_BY_POLICY`. |
| Suspended or absent status | `right:STATE_SUSPENDED` or `right:STATUS_UNKNOWN`. |
| Raw delta or missing selection price | `INCOMPLETE_SNAPSHOT` or `ODDS_INCOMPLETE`. |
| Missing, naive or future price time | `PRICE_UPDATED_AT_MISSING`, `_NAIVE` or `_FUTURE`. |

The CLI permits 1–32 cases and accepts inputs of at most 65,536 bytes. It rejects duplicate JSON keys and nonstandard NaN/Infinity literals; malformed input returns a bounded error object on stderr with exit code 2. A well-formed evaluation, including held cases, exits 0. Extra observation fields are held to expose an undeclared input shape. Envelope/case/policy shape errors stop the evaluation.

Tests cover differing-price acceptance, selection order, inclusive policy boundaries, stale capture and price, capture skew, every identity dimension, shared unknown rules, missing/suspended/closed state, incomplete snapshots and odds, invalid decimal values, numeric flags, invalid dates, future times, inconsistent timestamp ordering, malformed inputs, CLI size limits and duplicate keys. They exercise the local contract only. No synthetic result demonstrates real feed accuracy, end-to-end latency or present market availability.
