Player-props fetch ignores --gw and mixes two gameweeks' prices #9

Closed
opened 2026-08-27 20:57:39 +02:00 by solvreven · 1 comment
Owner

Found 2026-08-27 while restoring the Matches tab market column.

scripts/fetch_props.py --gw 2 labels its banked run gw2_... but fetches the
whole upcoming slate, not the named gameweek. The 2026-08-27 run returned
20 fixtures — GW2's ten plus GW3's ten reverse fixtures (Ipswich v Liverpool,
Brighton v Leeds, Newcastle v Bournemouth, ...).

Two consequences:

  1. Wrong prices can reach the screen. props_load.load_goalscorer(gw, ...)
    reads every fixture in the run, so a player appearing in both gameweeks is
    priced twice and which one wins is arbitrary. On the live tab this put
    single-book GW3 outliers above Haaland:

    Kroupi.Jr  book=0.625  books=1  conf=LOW
    Wright     book=0.5    books=1  conf=LOW   p_score_uncond=0.0
    Haaland    book=0.680  books=2  conf=LOW
    
  2. It costs double. The run billed 20 credits against an estimate of 10,
    because event_odds is per event. Balance 453 -> 433.

This is the same class as the /api/matches gameweek defect fixed the same day:
an artefact labelled with a gameweek that does not actually constrain its
contents.

Suggested fix. Filter the event list to the requested gameweek before
spending — the FPL fixture list gives the kickoff window, which is how
scripts/fetch_market.py slices the bulk slate. Cost halves and the run's label
becomes true. load_goalscorer should additionally refuse a run whose fixtures
fall outside the gameweek it was asked for, rather than trusting the directory
name.

Related: props coverage is poor regardless — US books only, half the
fixtures at 1-2 books and LOW confidence. Worth re-probing The Odds API uk
region (a 2026-08-20 run showed Betfair, an exchange, covering 5/10) before
building anything on top of the current numbers.

**Found 2026-08-27 while restoring the Matches tab market column.** `scripts/fetch_props.py --gw 2` labels its banked run `gw2_...` but fetches the **whole upcoming slate**, not the named gameweek. The 2026-08-27 run returned **20 fixtures** — GW2's ten plus GW3's ten reverse fixtures (Ipswich v Liverpool, Brighton v Leeds, Newcastle v Bournemouth, ...). Two consequences: 1. **Wrong prices can reach the screen.** `props_load.load_goalscorer(gw, ...)` reads every fixture in the run, so a player appearing in both gameweeks is priced twice and which one wins is arbitrary. On the live tab this put single-book GW3 outliers above Haaland: ``` Kroupi.Jr book=0.625 books=1 conf=LOW Wright book=0.5 books=1 conf=LOW p_score_uncond=0.0 Haaland book=0.680 books=2 conf=LOW ``` 2. **It costs double.** The run billed **20 credits** against an estimate of 10, because `event_odds` is per event. Balance 453 -> 433. This is the same class as the `/api/matches` gameweek defect fixed the same day: an artefact labelled with a gameweek that does not actually constrain its contents. **Suggested fix.** Filter the event list to the requested gameweek before spending — the FPL fixture list gives the kickoff window, which is how `scripts/fetch_market.py` slices the bulk slate. Cost halves and the run's label becomes true. `load_goalscorer` should additionally refuse a run whose fixtures fall outside the gameweek it was asked for, rather than trusting the directory name. **Related:** props coverage is poor regardless — US books only, half the fixtures at 1-2 books and LOW confidence. Worth re-probing The Odds API `uk` region (a 2026-08-20 run showed Betfair, an exchange, covering 5/10) before building anything on top of the current numbers.
Author
Owner

Half of this is fixed. The half that costs credits is not. Recording which,
so the issue is not closed on the strength of the other one.

FIXED — the loader (726c73b). props_load.this_round() filters a banked
run to the requested gameweek on the FPL fixture pair, not on the run's
name:

The run's name says which gameweek it was pulled FOR, which is a fact about
the request; the pair says which round a price is about, which is a fact about
the content.

An empty result refuses rather than serving nothing, since a board that
silently empties is indistinguishable from a book that quoted nobody. So the
wrong-prices-on-screen consequence is closed — the live case that prompted this
(a GW3 Haaland price wearing a GW2 fixture's label) cannot recur.

NOT FIXED — the fetch (scripts/fetch_props.py). --gw N still names the
output directory and nothing else:

chosen = order_events(events, cache)      # every event the vendor is quoting

There is no filter to the named gameweek before spending, so a --gw 2 run
still bills one credit per event across the whole slate. The double cost this
issue measured (20 credits against an estimate of 10) stands.

The fix suggested here still applies: slice events against the FPL fixture
list for the requested gameweek before the spend loop, the way
scripts/fetch_market.py slices the bulk slate. Halves the cost and makes the
run's label true at the point it is created rather than only at the point it is
read.

Leaving open for that.

(Related, from the same week: Norsk Tipping now supplies goalscorer AND assist
prices free and unmetered — scripts/fetch_norsk_tipping.py — which reduces
how often the metered fetch is needed but does not fix its scoping.)

**Half of this is fixed. The half that costs credits is not.** Recording which, so the issue is not closed on the strength of the other one. **FIXED — the loader (`726c73b`).** `props_load.this_round()` filters a banked run to the requested gameweek on the **FPL fixture pair**, not on the run's name: > The run's name says which gameweek it was pulled FOR, which is a fact about > the request; the pair says which round a price is about, which is a fact about > the content. An empty result refuses rather than serving nothing, since a board that silently empties is indistinguishable from a book that quoted nobody. So the wrong-prices-on-screen consequence is closed — the live case that prompted this (a GW3 Haaland price wearing a GW2 fixture's label) cannot recur. **NOT FIXED — the fetch (`scripts/fetch_props.py`).** `--gw N` still names the output directory and nothing else: ```python chosen = order_events(events, cache) # every event the vendor is quoting ``` There is no filter to the named gameweek before spending, so a `--gw 2` run still bills one credit per event across the whole slate. The double cost this issue measured (20 credits against an estimate of 10) stands. The fix suggested here still applies: slice `events` against the FPL fixture list for the requested gameweek before the spend loop, the way `scripts/fetch_market.py` slices the bulk slate. Halves the cost and makes the run's label true at the point it is created rather than only at the point it is read. Leaving open for that. *(Related, from the same week: Norsk Tipping now supplies goalscorer AND assist prices free and unmetered — `scripts/fetch_norsk_tipping.py` — which reduces how often the metered fetch is needed but does not fix its scoping.)*
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
solvreven/FPL#9
No description provided.