football-docs 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +34 -21
  2. package/bin/serve.js +6 -1
  3. package/data/docs.db +0 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +25 -5
  6. package/dist/tools.d.ts +12 -1
  7. package/dist/tools.js +309 -18
  8. package/docs/fotmob/identity-surfaces.md +3 -4
  9. package/docs/free-sources/contextual-story-joins.md +110 -0
  10. package/docs/free-sources/overview.md +60 -13
  11. package/docs/free-sources/understat.md +15 -6
  12. package/docs/free-sources/xg-timelines.md +123 -0
  13. package/docs/impect/identity-surfaces.md +3 -4
  14. package/docs/kloppy/event-derived-metrics.md +337 -0
  15. package/docs/kloppy/tracking-rendering.md +420 -0
  16. package/docs/mplsoccer/visualizations.md +363 -6
  17. package/docs/opta/charting-game-state.md +216 -0
  18. package/docs/opta/charting-lineups.md +90 -0
  19. package/docs/opta/charting-passmaps.md +130 -0
  20. package/docs/opta/charting-set-pieces.md +106 -0
  21. package/docs/opta/charting-shot-placement.md +233 -0
  22. package/docs/opta/event-types.md +44 -0
  23. package/docs/opta/identity-surfaces.md +4 -4
  24. package/docs/opta/qualifiers.md +16 -7
  25. package/docs/skillcorner/identity-surfaces.md +4 -4
  26. package/docs/skillcorner/physical-data.md +35 -0
  27. package/docs/socceraction/spadl.md +21 -0
  28. package/docs/soccerdata/data-sources.md +37 -0
  29. package/docs/soccerdonna/identity-surfaces.md +6 -6
  30. package/docs/sportmonks/charting-season-stories.md +125 -0
  31. package/docs/sportmonks/data-model.md +35 -12
  32. package/docs/sportmonks/event-types.md +1 -1
  33. package/docs/sportmonks/identity-surfaces.md +4 -4
  34. package/docs/sportradar/api-access.md +75 -0
  35. package/docs/sportradar/api-endpoints.md +92 -0
  36. package/docs/sportradar/charting-and-stories.md +57 -0
  37. package/docs/sportradar/data-model.md +84 -0
  38. package/docs/sportradar/integration-notes.md +58 -0
  39. package/docs/statsbomb/charting-lineups.md +79 -0
  40. package/docs/statsbomb/identity-surfaces.md +5 -4
  41. package/docs/statsbomb/player-mapping.md +6 -7
  42. package/docs/thesportsdb/api-access.md +68 -0
  43. package/docs/thesportsdb/api-endpoints.md +55 -0
  44. package/docs/thesportsdb/identity-surfaces.md +49 -0
  45. package/docs/thesportsdb/livescore.md +70 -0
  46. package/docs/transfermarkt/identity-surfaces.md +51 -6
  47. package/docs/transferroom/charting-availability.md +74 -0
  48. package/docs/wyscout/charting-analysis-metrics.md +124 -0
  49. package/docs/wyscout/identity-surfaces.md +4 -4
  50. package/package.json +3 -2
  51. package/providers.json +127 -2
@@ -54,9 +54,8 @@ endpoint family, and snapshot date alongside any bridge evidence.
54
54
  - Public endpoints may be undocumented. Treat access and response shape as
55
55
  mutable unless the provider documents the contract.
56
56
 
57
- ## Reep Next Usage
57
+ ## Implementation Notes
58
58
 
59
59
  Use this page as the public provider-fact reference for FotMob identity
60
- surfaces, public URL families, and matching cautions. Reep Next can cite those
61
- facts, but private endpoint handling, scrape internals, rate-limit tactics,
62
- review outcomes, and register decisions belong outside football-docs.
60
+ surfaces, public URL families, and matching cautions. Keep access implementation
61
+ details, review outcomes, and register decisions outside football-docs.
@@ -0,0 +1,110 @@
1
+ # Contextual Story Joins
2
+
3
+ Use free and public sources to enrich football fixture data with context that
4
+ commercial event feeds do not always provide: opponent strength, travel distance,
5
+ weather, venue geography, historical baselines, and independent xG.
6
+
7
+ ## Base fixture authority
8
+
9
+ Choose one provider as the authority for fixture identity, final scores, and match
10
+ status before joining context.
11
+
12
+ | Need | Good base source |
13
+ |---|---|
14
+ | in-season fixture ids and live status | SportMonks, Opta, TheSportsDB, Sportradar |
15
+ | historical result grids | football-data.co.uk, engsoccerdata |
16
+ | shot-level xG enrichment | Understat, StatsBomb Open Data where covered |
17
+ | broad season stats | FBref / soccerdata |
18
+
19
+ Do not let an enrichment source silently override the base fixture score or status.
20
+ If an enrichment feed lags behind the fixture authority, preserve the base fixture
21
+ and mark the enrichment fields unavailable.
22
+
23
+ ## Strength controls with ClubElo
24
+
25
+ ClubElo is useful for match-day team-strength controls and fixture-difficulty
26
+ stories. Join by the fixture date and a maintained team-name map:
27
+
28
+ ```text
29
+ home_elo = ClubElo(home_team, fixture_date)
30
+ away_elo = ClubElo(away_team, fixture_date)
31
+ elo_diff = home_elo - away_elo
32
+ ```
33
+
34
+ ClubElo names are not guaranteed to match provider team labels. Keep the alias
35
+ map in project code, and treat promoted, renamed, and abbreviated clubs as normal
36
+ data-quality cases.
37
+
38
+ ## Weather and climate context
39
+
40
+ Weather can be joined at venue and kickoff time:
41
+
42
+ | Field | Join key |
43
+ |---|---|
44
+ | observed match weather | venue latitude/longitude + kickoff timestamp |
45
+ | climate normal | venue latitude/longitude + month |
46
+ | weather anomaly | observed value minus climate normal |
47
+ | condition bucket | rain, snow, fog, clear, wind, heat, cold |
48
+
49
+ For public weather APIs, store the fetched timestamp, timezone, and units. Weather
50
+ observations are contextual features, not football-provider facts, so label them
51
+ separately from official match data.
52
+
53
+ ## Travel and venue geography
54
+
55
+ For travel-distance stories, maintain a venue table with club, stadium,
56
+ latitude/longitude, city, and country. Calculate away travel with a geodesic
57
+ distance such as haversine:
58
+
59
+ ```text
60
+ away_travel_km = distance(away_venue_lat_lon, home_venue_lat_lon)
61
+ ```
62
+
63
+ Use travel bands for stable charts, for example `<50 km`, `50-100 km`, `100-200 km`,
64
+ `200-300 km`, and `300+ km`. Distances are approximations of geography, not actual
65
+ team travel logistics.
66
+
67
+ ## Independent xG enrichment
68
+
69
+ Understat can add independent xG to result stories for the top European leagues.
70
+ Join by date plus home/away team aliases, not by Understat match id alone. Keep
71
+ fields such as:
72
+
73
+ | Field | Meaning |
74
+ |---|---|
75
+ | `xg` | team's Understat expected goals |
76
+ | `xga` | opponent Understat expected goals |
77
+ | `xg_model` | `understat` |
78
+ | `xg_join_status` | exact, alias, manual, missing |
79
+
80
+ If the base provider also supplies expected goals, do not average the values unless
81
+ the chart explicitly describes a blended model. Expose both model names instead.
82
+
83
+ ## Additive factor stories
84
+
85
+ For home-advantage, away-day, and match-context stories, build factors from
86
+ explicit rules:
87
+
88
+ | Factor | Example rule |
89
+ |---|---|
90
+ | long away trip | `away_travel_km >= 300` |
91
+ | local derby | `away_travel_km < 30` |
92
+ | evening kickoff | kickoff hour `>= 18` |
93
+ | midweek fixture | Tuesday, Wednesday, or Thursday |
94
+ | severe weather | heavy rain, snow, fog, high wind, or extreme heat/cold |
95
+ | strength control | Elo difference at fixture date |
96
+
97
+ Keep the raw fields and the factor flags. This lets users inspect the story logic
98
+ and lets agents reproduce the chart without guessing which thresholds were used.
99
+
100
+ ## Join hygiene
101
+
102
+ For public data-story pipelines:
103
+
104
+ - keep provider ids, team aliases, and venue aliases as explicit mapping tables;
105
+ - record whether each joined field came from exact id, alias, date/team match, or
106
+ manual override;
107
+ - never overwrite official scores with scraped enrichment data;
108
+ - cache public-source responses and respect rate limits;
109
+ - label sample-size limits when slicing by rare conditions such as snow or fog;
110
+ - separate observed facts from modelled controls such as Elo-adjusted residuals.
@@ -9,6 +9,7 @@
9
9
  | Understat | xG, shot-level | Web scrape / soccerdata | Top 5 European leagues | Moderate |
10
10
  | ClubElo | Historical Elo ratings | HTTP API | All top European leagues, 1946-present | Generous |
11
11
  | football-data.co.uk | Match results + odds | CSV download | 25+ leagues, 20+ seasons | None |
12
+ | engsoccerdata | Historical league results | R package / GitHub data | England 1888+, Spain 1928+, other leagues | None |
12
13
  | Transfermarkt | Market values, transfers, injuries | Web scrape | All professional leagues | Strict |
13
14
  | WhoScored | Match ratings, event-level (limited) | Web scrape (headed browser) | Top leagues | Strict, requires JS rendering |
14
15
  | European Football Statistics | Historical results | CSV download | Many European leagues | None |
@@ -97,7 +98,11 @@ Rank,Club,Country,Level,Elo,From,To
97
98
  1,Liverpool,ENG,1,2050,2024-11-30,2024-12-07
98
99
  ```
99
100
 
100
- **Used in**: myTeam's PL Era Champions story page (`scripts/fetch-elo-history.ts`).
101
+ **Project use**: For run-in, fixture-difficulty, and season-story surfaces,
102
+ ClubElo is useful as a lightweight strength prior. Join by a maintained club
103
+ name map rather than assuming site labels match your canonical team labels.
104
+ ClubElo is a rating source, not a fixture/result source, so combine it with
105
+ fixtures from Opta, SportMonks, football-data.co.uk, or another schedule feed.
101
106
 
102
107
  ## football-data.co.uk
103
108
 
@@ -118,6 +123,50 @@ Season format: `2425` for 2024/25. League codes: `E0` (Premier League), `E1` (Ch
118
123
  - `HC`, `AC` (Corners), `HF`, `AF` (Fouls), `HY`, `AY` (Yellows), `HR`, `AR` (Reds)
119
124
  - Betting odds from multiple bookmakers (B365H, B365D, B365A, etc.)
120
125
 
126
+ **Project use**: Excellent for scorigami, scoreline grids, baseline baking,
127
+ result-history charts, odds-history backfills, and simple match-stat trend
128
+ stories. For scorigami baselines, use full-time fields (`FTHG`, `FTAG`, `FTR`)
129
+ and ignore half-time or bookmaker columns unless the chart explicitly needs
130
+ them. Keep a league-code map in project code (`E0`, `E1`, `SP1`, etc.) and a
131
+ team-name map because historical labels and promoted/relegated club names can
132
+ drift from canonical names.
133
+
134
+ ## engsoccerdata
135
+
136
+ **What it provides**: Historical football result datasets packaged for R by
137
+ James P. Curley. The GitHub package includes English league data, FA Cup data,
138
+ playoff data, and several European leagues including Spain, Germany, Italy,
139
+ France, Netherlands, Belgium, Portugal, Turkey, Scotland, Greece, South Africa,
140
+ and MLS.
141
+
142
+ **Useful datasets**:
143
+
144
+ | Dataset | Notes |
145
+ |---|---|
146
+ | `england` | English league results, top four tiers, from 1888/89 in the classic package docs |
147
+ | `englandplayoffs` | English playoff matches |
148
+ | `facup` | FA Cup results |
149
+ | `spain` | Spanish league results from 1928/29 in the classic package docs |
150
+
151
+ **Common fields**:
152
+
153
+ | Field | Meaning |
154
+ |---|---|
155
+ | `Date` | match date |
156
+ | `Season` | season start year |
157
+ | `home` / `visitor` | home and away teams |
158
+ | `FT` | full-time score string |
159
+ | `hgoal` / `vgoal` | home and away full-time goals |
160
+ | `division` | division label |
161
+ | `tier` | football pyramid tier |
162
+ | `result` | `H`, `A`, or `D` |
163
+
164
+ **Project use**: Useful as a deep historical baseline for scorigami and
165
+ marcadorigami-style scoreline grids. Pair with football-data.co.uk for recent
166
+ season gap fills or in-season updates. The package was formerly on CRAN and is
167
+ now best treated as a GitHub/open-data source; cite James P. Curley when using
168
+ it in public analysis.
169
+
121
170
  ## Transfermarkt
122
171
 
123
172
  **What it provides**: Player market values, transfer history, contract details, injury history, squad information, manager history.
@@ -156,8 +205,6 @@ Season format: `2425` for 2024/25. League codes: `E0` (Premier League), `E1` (Ch
156
205
  - Rate limiting is strict; adding delays between requests is essential
157
206
  - Data is Opta-sourced, so event types and qualifier IDs match Opta's system
158
207
 
159
- **Used in**: myTeam's passmap data pipeline (`scripts/fetch-whoscored-events.ts` stores events in Postgres, `scripts/generate-passmap-data.ts` processes them).
160
-
161
208
  ## European Football Statistics
162
209
 
163
210
  **What it provides**: Historical match results for European leagues.
@@ -168,13 +215,13 @@ Season format: `2425` for 2024/25. League codes: `E0` (Premier League), `E1` (Ch
168
215
 
169
216
  ## Comparison Matrix
170
217
 
171
- | Feature | StatsBomb Open | FBref | Understat | ClubElo | football-data.co.uk | Transfermarkt | WhoScored |
172
- |---|---|---|---|---|---|---|---|
173
- | Event-level data | Full | No | Shot-level | No | No | No | Full |
174
- | Aggregated stats | Via events | Yes | Yes | No | Basic | No | Yes |
175
- | xG | Yes | Yes (Opta) | Yes (own model) | No | No | No | No |
176
- | Coordinates | Yes | No | Shot coords | No | No | No | Yes |
177
- | Historical depth | Limited | 2017+ detailed | 2014+ | 1946+ | 1993+ | 2004+ | ~2010+ |
178
- | League coverage | Select | Top 5+ | Top 5 | Europe | 25+ | Global | Top 5+ |
179
- | Commercial use | No | No | Unclear | Yes | Yes | No | No |
180
- | API available | GitHub | No | No | Yes (CSV) | CSV download | No | No |
218
+ | Feature | StatsBomb Open | FBref | Understat | ClubElo | football-data.co.uk | engsoccerdata | Transfermarkt | WhoScored |
219
+ |---|---|---|---|---|---|---|---|---|
220
+ | Event-level data | Full | No | Shot-level | No | No | No | No | Full |
221
+ | Aggregated stats | Via events | Yes | Yes | No | Basic | No | No | Yes |
222
+ | xG | Yes | Yes (Opta) | Yes (own model) | No | No | No | No | No |
223
+ | Coordinates | Yes | No | Shot coords | No | No | No | No | Yes |
224
+ | Historical depth | Limited | 2017+ detailed | 2014+ | 1946+ | 1993+ | 1888+ England | 2004+ | ~2010+ |
225
+ | League coverage | Select | Top 5+ | Top 5 | Europe | 25+ | England + selected global leagues | Global | Top 5+ |
226
+ | Commercial use | No | No | Unclear | Yes | Yes | Non-commercial attribution expected | No | No |
227
+ | API available | GitHub | No | No | Yes (CSV) | CSV download | R/GitHub data | No | No |
@@ -197,12 +197,21 @@ Their xG model is independent from StatsBomb and Opta. Values will differ. The m
197
197
  - xG model methodology is not fully published. It's a black box.
198
198
  - No event data beyond shots. No passes, tackles, etc.
199
199
  - Data updates can lag 1-2 days after matches.
200
- - Match IDs are internal and don't map to other providers. Match by teams + date.
200
+ - Match IDs are source-specific and don't map to other providers. Match by teams + date.
201
201
  - Player names may differ from other sources (transliterations, shortened forms).
202
202
 
203
- ## Used In
203
+ ## Project use
204
204
 
205
- The myTeam project uses Understat data:
206
- - `scripts/fetch-season-story.ts` references Understat xG data
207
- - `src/data/game-state/understat-xg.json` stores cached xG values
208
- - The `--skip-xg` flag on `fetch-season-story.ts` reuses existing Understat xG data
205
+ Understat is useful as an xG enrichment source for match-result, season-story,
206
+ and game-state surfaces when the primary fixture provider does not include
207
+ shot-level or match-level xG. Cache by competition, season, team names, and match
208
+ date rather than by Understat match ID alone, because Understat IDs are not
209
+ portable across providers.
210
+
211
+ When joining to Opta, SportMonks, football-data.co.uk, or another fixture source:
212
+
213
+ - match by date plus home/away team aliases;
214
+ - keep the provider's final score as the fixture authority;
215
+ - store Understat `xG` and `xGA` as an enrichment layer;
216
+ - expose the xG model name so it is not confused with StatsBomb, Opta, or
217
+ provider-supplied expected-goals values.
@@ -0,0 +1,123 @@
1
+ # xG Timeline Recipes
2
+
3
+ Use an xG timeline when the product question is "when did each team create
4
+ danger?" rather than only "what was the final xG total?". The chart should be a
5
+ cumulative step view built from shot-level xG rows plus match-state annotations.
6
+
7
+ ## Required shot fields
8
+
9
+ Normalise provider shots into one row per shot before building the timeline:
10
+
11
+ | Field | Required | Notes |
12
+ |---|---|---|
13
+ | `minute` | yes | Regular match minute; exclude shots without a usable time |
14
+ | `second` | optional | Tie-breaker within a minute when available |
15
+ | `added_time` | optional | Preserve stoppage time for labels such as `45+2` |
16
+ | `period` | optional | Needed for extra-time guides and precise halftime handling |
17
+ | `team_id` | yes | Must resolve to home or away team |
18
+ | `player_name` | optional | Tooltip/label only |
19
+ | `xg` | yes | Provider/model xG value for the shot |
20
+ | `outcome` | optional | Goal, saved, post, missed, blocked, etc. |
21
+ | `is_penalty` | optional | Useful for filters or marker styling |
22
+ | `shot_id` | optional | Stable ordering and deduplication |
23
+ | `xg_model` | yes | `understat`, `statsbomb`, `opta`, or another named model |
24
+
25
+ Do not mix xG models silently. Understat, StatsBomb, Opta, and FBref/Opta values
26
+ come from different models and can disagree for the same shot. If a story blends
27
+ or compares models, expose the model name in the data and chart notes.
28
+
29
+ ## Source-specific inputs
30
+
31
+ | Source | Timeline inputs |
32
+ |---|---|
33
+ | Understat | `minute`, `X`, `Y`, `xG`, `result`, `h_a`, `player`, `situation`, `shotType` |
34
+ | StatsBomb | shot event `minute`/`second`, `team`, `shot.statsbomb_xg`, `shot.outcome`, `shot.type`, `shot.body_part` |
35
+ | Opta / WhoScored-style events | shot event types `13`-`16`, xG qualifier where licensed, body-part/context qualifiers, goal-mouth qualifiers where available |
36
+ | FBref | season/match aggregates only; not enough for a shot-by-shot timeline |
37
+
38
+ For Opta-derived feeds, distinguish shot outcomes from score attribution. Type
39
+ `16` with own-goal qualifier `28` should update the score for the opposing team,
40
+ not the event contestant. Disallowed goals should be marked but should not change
41
+ the running scoreline.
42
+
43
+ ## Cumulative step construction
44
+
45
+ Sort shots by period, minute, second, and a stable shot id if available. For each
46
+ shot, add the current xG value only to the shooting team's cumulative total and
47
+ carry the other team's total forward:
48
+
49
+ ```text
50
+ home_cum_xg_after_shot =
51
+ previous_home_cum_xg + (shot.team_id == home_team_id ? shot.xg : 0)
52
+ away_cum_xg_after_shot =
53
+ previous_away_cum_xg + (shot.team_id == away_team_id ? shot.xg : 0)
54
+ ```
55
+
56
+ Render the lines as steps, not smoothed curves. A shot changes the value at a
57
+ specific match moment; smoothing implies chance quality accrued continuously.
58
+
59
+ For a static chart, emit enough points to draw flat sections:
60
+
61
+ ```text
62
+ (time_before_shot, previous_total)
63
+ (shot_time, previous_total)
64
+ (shot_time, new_total)
65
+ ```
66
+
67
+ This keeps long quiet periods visible and avoids diagonal interpolation between
68
+ unrelated shots.
69
+
70
+ ## Match-time guides
71
+
72
+ Add guide marks from match structure, not from shot distribution:
73
+
74
+ | Guide | Typical position |
75
+ |---|---|
76
+ | halftime | 45 minutes |
77
+ | full time | 90 minutes |
78
+ | extra-time halftime | 105 minutes |
79
+ | extra-time full time | 120 minutes |
80
+
81
+ If stoppage-time placement is flattened into minute values, say so in the output
82
+ or chart notes. Do not invent precise stoppage-time spacing when the source only
83
+ has minute-level timing.
84
+
85
+ ## Running score strip
86
+
87
+ A score strip helps readers connect chance quality to actual goals. Build it from
88
+ goal events, not from xG values:
89
+
90
+ | Case | Score handling |
91
+ |---|---|
92
+ | normal goal | increment scoring team |
93
+ | own goal | increment the opponent of the event contestant when the provider encodes own goals that way |
94
+ | penalty goal | increment scoring team and optionally style the marker |
95
+ | disallowed goal | annotate separately; do not increment score |
96
+ | shootout penalty | keep outside the regular xG timeline unless the chart explicitly covers shootouts |
97
+
98
+ Keep goal markers visually distinct from all-shot dots. A goal is an outcome, not
99
+ a larger xG value.
100
+
101
+ ## Sparse and empty matches
102
+
103
+ The fallback behaviour should be honest:
104
+
105
+ | Input state | Recommended output |
106
+ |---|---|
107
+ | no shots with xG | empty state; do not invent a flat 0-0 narrative |
108
+ | one team's shots only | that team's step line plus a flat zero baseline for the opponent |
109
+ | very sparse match | preserve the stepped geometry and show the low-event nature |
110
+ | missing team assignment | exclude the affected shot and record a data-quality warning |
111
+ | missing xG value | exclude from xG totals or render as an unvalued shot marker, but do not add `0` silently |
112
+
113
+ ## Data contract for agents
114
+
115
+ When generating data for a reusable xG timeline, include:
116
+
117
+ - `match_id`, `home_team_id`, `away_team_id`;
118
+ - a sorted `shots[]` array with raw provider id where available;
119
+ - `xg_model` and provider/source for every xG value;
120
+ - `score_events[]` for running score reconstruction;
121
+ - `time_guides[]` for halftime/full-time/extra-time markers;
122
+ - `data_quality[]` warnings for excluded shots, missing xG, missing teams, or
123
+ source/model mismatches.
@@ -78,9 +78,8 @@ is globally stable until that is confirmed in the contract or repeated exports.
78
78
  - If profile attributes are thin, keep Impect as a corroborator rather than a
79
79
  canonical mint source.
80
80
 
81
- ## Reep Next Usage
81
+ ## Implementation Notes
82
82
 
83
83
  Use this page as the public provider-fact reference for Impect identity field
84
- families and matching cautions. Reep Next can cite those public-safe field
85
- shapes, but customer deliveries, private IDs, raw feed payloads, and register
86
- review outcomes belong outside football-docs.
84
+ families and matching cautions. Keep customer deliveries, non-public IDs, raw feed
85
+ payloads, and register review outcomes outside football-docs.