Sources
Read match statistics and odds from the feeds that publish them.
BaseOddsSchema
Bases: _BaseSchema
Base schema for odds snapshots.
Examples:
>>> from sportsbet.sources import BaseOddsSchema, optional_col, required_col
>>>
>>> class MyOddsSchema(BaseOddsSchema):
... 'The odds of a feed of your own.'
...
... home_team: str = required_col()
... away_team: str = required_col()
... provider: str = required_col()
... home_win: float = optional_col(include=['preplay', 'inplay'], fixed=False)
... away_win: float = optional_col(include=['preplay', 'inplay'], fixed=False)
check_postplay_missing_odds(df)
classmethod
Check that post-match snapshots carry no odds.
Source code in src/sportsbet/sources/_schema.py
158 159 160 161 162 163 164 165 166 167 168 169 | |
list_odds_cols()
classmethod
Return the odds (market) columns.
Source code in src/sportsbet/sources/_schema.py
152 153 154 155 156 | |
BaseOddsSource
Bases: BaseSource
The abstract base class for odds sources.
Examples:
>>> import io
>>> import pandas as pd
>>> from sportsbet.sources import BaseOddsSource, RawItem, RawPayload
>>>
>>> class MyOdds(BaseOddsSource):
... '''Odds from a feed of your own.'''
...
... name = 'my_odds'
... sport = 'soccer'
...
... def list_index_items(self, selection=None):
... return [RawItem(source=self.name, key='seasons', url='https://example.com/seasons.json')]
...
... def read_catalogue(self, payloads):
... return [{'league': 'Ruritania', 'division': 1, 'year': 2025}]
...
... def list_required_items(self, params, schedule=None):
... return [RawItem(source=self.name, key=f'odds_{param["year"]}',
... url=f'https://example.com/odds/{param["year"]}.csv')
... for param in params]
...
... def to_snapshots(self, payloads):
... odds = pd.read_csv(io.BytesIO(payloads[0].content))
... odds['date'] = pd.to_datetime(odds['date'], utc=True)
... return odds.assign(event_status='preplay', event_time=0)
>>>
>>> source = MyOdds()
>>> source.kind
'odds'
>>> csv = b'date,league,division,year,home_team,away_team,provider,home_win,draw,away_win\n'
>>> csv += b'2025-08-16,Ruritania,1,2025,A,B,acme,1.8,3.4,4.2\n'
>>> item = source.list_required_items([{'year': 2025}])[0]
>>> snapshots = source.to_snapshots([RawPayload(item=item, content=csv)])
>>> # The markets are the columns, and the provider is a column too.
>>> snapshots[['provider', 'home_win', 'event_status']].to_dict('records')
[{'provider': 'acme', 'home_win': 1.8, 'event_status': 'preplay'}]
BaseSource
Bases: ABC
The abstract base class for data sources.
A source declares the raw items a selection of parameters needs. It turns the returned payloads into long
snapshots. sport names the one sport it carries. sport is None for a vendor that carries several sports and
takes the sport of the source it is paired with. The list_available_params method returns what the source
publishes.
Examples:
>>> from sportsbet.sources import FootballDataStats, OddsApi
>>> stats = FootballDataStats()
>>> stats.name, stats.kind, stats.sport
('football_data', 'stats', 'soccer')
>>> # A vendor selling every sport carries none of its own, and takes the sport it is paired with.
>>> OddsApi(key_env='ODDS_API_KEY', markets=['h2h']).sport is None
True
>>> # Asking what a source publishes declares the items to read.
>>> items = stats.list_index_items()
>>> items[0].source
'football_data'
list_available_params()
Return the league, division and season combinations the source publishes.
What a source publishes depends on how it is configured.
Returns:
| Name | Type | Description |
|---|---|---|
params |
list[dict]
|
The available |
Source code in src/sportsbet/sources/_base.py
194 195 196 197 198 199 200 201 202 203 | |
list_fixtures_items(params, schedule=None)
Return the raw items the upcoming matches need.
By default it returns the same items training needs. That suits a source whose season file already carries the matches still to be played. It also suits an odds source that prices whatever the schedule lists. A source whose upcoming matches live elsewhere, in a separate fixtures file or the season in progress, overrides this method.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
list[dict]
|
The selected parameter combinations. |
required |
schedule
|
DataFrame | None
|
The upcoming matches, for an odds source that prices by instant. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
items |
list[RawItem]
|
The items whose payloads yield the upcoming matches. |
Source code in src/sportsbet/sources/_base.py
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 | |
list_index_items(selection=None)
abstractmethod
Return the items needed to discover what the source publishes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
selection
|
ParamGrid | None
|
What is being looked for. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
items |
list[RawItem]
|
The items whose payloads describe the catalogue. |
Source code in src/sportsbet/sources/_base.py
168 169 170 171 172 173 174 175 176 177 178 179 | |
list_required_items(params, schedule=None)
abstractmethod
Return the raw items the selected parameters need.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
list[dict]
|
The selected parameter combinations. |
required |
schedule
|
DataFrame | None
|
The matches of the selected parameters, with their kick-off instants. An odds source that addresses its
prices by timestamp needs it. It is |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
items |
list[RawItem]
|
The items to read. Deterministic for the same parameters. |
Source code in src/sportsbet/sources/_base.py
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | |
needs_schedule()
Return whether the source has to be told when the matches are.
A source whose events and odds arrive in the same file carries its own schedule and returns False.
Returns:
| Name | Type | Description |
|---|---|---|
needed |
bool
|
Whether |
Source code in src/sportsbet/sources/_base.py
243 244 245 246 247 248 249 250 251 252 | |
read_catalogue(payloads)
abstractmethod
Return the parameter combinations the index payloads describe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payloads
|
list[RawPayload]
|
The payloads of the index items. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
params |
list[dict]
|
The available |
Source code in src/sportsbet/sources/_base.py
181 182 183 184 185 186 187 188 189 190 191 192 | |
request_url(item)
Return the URL to fetch an item from, with the credential added at the moment of the request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
RawItem
|
The item to fetch. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
url |
str
|
Where to fetch it from. |
Source code in src/sportsbet/sources/_base.py
254 255 256 257 258 259 260 261 262 263 264 265 | |
to_snapshots(payloads)
abstractmethod
Transform the raw payloads into a long snapshot table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
payloads
|
list[RawPayload]
|
The payloads of the required items. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
snapshots |
DataFrame
|
The long snapshots. |
Source code in src/sportsbet/sources/_base.py
267 268 269 270 271 272 273 274 275 276 277 278 | |
BaseStatsSchema
Bases: _BaseSchema
Base schema for statistics snapshots.
Examples:
>>> from sportsbet.sources import BaseStatsSchema, optional_col, required_col
>>>
>>> class MyStatsSchema(BaseStatsSchema):
... 'The statistics of a feed of your own.'
...
... home_team: str = required_col()
... away_team: str = required_col()
... home_goals: float = optional_col(include=['inplay', 'postplay'], fixed=False)
BaseStatsSource
Bases: BaseSource
The abstract base class for statistics sources.
Examples:
>>> import io
>>> import pandas as pd
>>> from sportsbet.sources import BaseStatsSource, RawItem, RawPayload, derive_market_outcomes
>>> IDENTITY = ['date', 'league', 'division', 'year', 'home_team', 'away_team']
>>>
>>> class MyStats(BaseStatsSource):
... '''Statistics from a feed of your own.'''
...
... name = 'my_stats'
... sport = 'soccer'
...
... def list_index_items(self, selection=None):
... return [RawItem(source=self.name, key='seasons', url='https://example.com/seasons.json')]
...
... def read_catalogue(self, payloads):
... return [{'league': 'Ruritania', 'division': 1, 'year': 2025}]
...
... def list_required_items(self, params, schedule=None):
... return [RawItem(source=self.name, key=f'Ruritania_1_{param["year"]}',
... url=f'https://example.com/{param["year"]}.csv')
... for param in params]
...
... def to_snapshots(self, payloads):
... games = pd.read_csv(io.BytesIO(payloads[0].content))
... games['date'] = pd.to_datetime(games['date'], utc=True)
... preplay = games[IDENTITY].assign(event_status='preplay', event_time=0,
... home_form=games['home_form'])
... postplay = games[IDENTITY].assign(event_status='postplay', event_time=0)
... outcomes = derive_market_outcomes(games['home_goals'], games['away_goals'], ['home_win', 'draw',
... 'away_win'])
... postplay = pd.concat([postplay, outcomes], axis=1)
... return pd.concat([preplay, postplay], ignore_index=True)
>>>
>>> source = MyStats()
>>> # It says what it needs, and the dataloader reads it.
>>> source.list_required_items([{'year': 2025}])[0].url
'https://example.com/2025.csv'
>>> csv = b'date,league,division,year,home_team,away_team,home_form,home_goals,away_goals\n'
>>> csv += b'2025-08-16,Ruritania,1,2025,A,B,0.5,2,1\n'
>>> item = source.list_required_items([{'year': 2025}])[0]
>>> snapshots = source.to_snapshots([RawPayload(item=item, content=csv)])
>>> sorted(snapshots['event_status'].unique())
['postplay', 'preplay']
>>> snapshots.loc[snapshots['event_status'].eq('postplay'), 'home_win'].item()
1.0
EuroLeagueStats
Bases: BaseStatsSource
The EuroLeague schedule and final scores from its official API, with home and away markets.
Read more in the user guide.
Examples:
>>> from sportsbet.dataloaders import DataLoader
>>> from sportsbet.sources import EuroLeagueStats, OddsApi
>>> source = EuroLeagueStats()
>>> source.name, source.kind, source.sport
('euroleague', 'stats', 'basketball')
>>> # A whole season arrives in one request, and asking what it publishes costs one more.
>>> len(source.list_index_items())
1
>>> # Pair the statistics with an odds source.
>>> dataloader = DataLoader(
... param_grid={'league': ['Euroleague'], 'division': [1], 'year': [2025]},
... stats=source,
... odds=OddsApi(key_env='ODDS_API_KEY', markets=['h2h']),
... )
>>> dataloader.sport_
'basketball'
list_index_items(selection=None)
Return the seasons the competition publishes.
Source code in src/sportsbet/sources/_stats/_euroleague.py
80 81 82 | |
list_required_items(params, schedule=None)
Return one item per selected season.
Source code in src/sportsbet/sources/_stats/_euroleague.py
94 95 96 97 98 99 100 101 102 103 104 | |
read_catalogue(payloads)
Return the seasons the competition publishes, each named by the year it ends in.
Source code in src/sportsbet/sources/_stats/_euroleague.py
84 85 86 87 88 89 90 91 92 | |
to_snapshots(payloads)
Transform the seasons into the long statistics snapshots.
Source code in src/sportsbet/sources/_stats/_euroleague.py
106 107 108 109 110 111 112 113 114 115 116 | |
FootballDataOdds
Bases: _FootballDataSource, BaseOddsSource
The pre-match closing odds of the market average and market maximum from the football-data.co.uk feed.
Read more in the user guide.
Examples:
>>> from sportsbet.sources import FootballDataOdds
>>> source = FootballDataOdds()
>>> source.name, source.kind, source.sport
('football_data', 'odds', 'soccer')
>>> # It reads the same upstream files as the statistics.
>>> from sportsbet.sources import FootballDataStats
>>> stats_items = FootballDataStats().list_index_items({'league': ['Italy']})
>>> source.list_index_items({'league': ['Italy']}) == stats_items
True
FootballDataStats
Bases: _FootballDataSource, BaseStatsSource
The soccer schedule, results and match statistics from the football-data.co.uk feed.
Read more in the user guide.
Examples:
>>> from sportsbet.dataloaders import DataLoader
>>> from sportsbet.sources import FootballDataOdds, FootballDataStats
>>> source = FootballDataStats()
>>> source.name, source.kind, source.sport
('football_data', 'stats', 'soccer')
>>> # It declares what it would read to learn what it publishes, and reads nothing.
>>> [item.key for item in source.list_index_items({'league': ['Italy']})]
['index_Italy']
>>> # Hand it to a dataloader, together with wherever the odds come from.
>>> dataloader = DataLoader(
... param_grid={'league': ['Italy'], 'division': [1], 'year': [2024]},
... stats=source,
... odds=FootballDataOdds(),
... )
>>> dataloader.sport_
'soccer'
NBAStats
Bases: BaseStatsSource
The NBA schedule and final scores from ESPN, updated through a season, with home and away markets.
Read more in the user guide.
Examples:
>>> from sportsbet.dataloaders import DataLoader
>>> from sportsbet.sources import NBAStats, OddsApi
>>> source = NBAStats()
>>> source.name, source.kind, source.sport
('nba', 'stats', 'basketball')
>>> # A season is named by the year it ends in, so 2026 is the 2025-26 season.
>>> dataloader = DataLoader(
... param_grid={'league': ['NBA'], 'year': [2026]},
... stats=source,
... odds=OddsApi(key_env='ODDS_API_KEY', markets=['h2h']),
... )
>>> dataloader.sport_
'basketball'
>>> # A league is a source. The same sport is the same dataloader.
>>> from sportsbet.sources import EuroLeagueStats
>>> NBAStats().sport == EuroLeagueStats().sport
True
list_index_items(selection=None)
Return the seasons the competition publishes.
Source code in src/sportsbet/sources/_stats/_nba.py
94 95 96 | |
list_required_items(params, schedule=None)
Return one item per month of each selected season.
Source code in src/sportsbet/sources/_stats/_nba.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
read_catalogue(payloads)
Return the seasons the competition publishes, each named by the year it ends in.
Source code in src/sportsbet/sources/_stats/_nba.py
98 99 100 101 102 103 104 105 106 107 | |
to_snapshots(payloads)
Transform the months into the long statistics snapshots.
Source code in src/sportsbet/sources/_stats/_nba.py
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | |
OddsApi(key_env, markets=None, regions=None, moments=None)
Bases: BaseOddsSource
The time-stamped odds of The Odds API, quoting a match at each moment it reaches.
The key is read from the environment variable named by key_env.
Read more in the user guide.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key_env
|
str
|
The name of the environment variable holding your API key. |
required |
markets
|
list[str] | None
|
The markets to price, e.g. |
None
|
regions
|
list[str] | None
|
The bookmaker regions, e.g. |
None
|
moments
|
list[tuple[str, int]] | None
|
The moments of a match to price, as |
None
|
Examples:
>>> import os
>>> from sportsbet.sources import OddsApi, RawItem
>>> os.environ['ODDS_API_KEY'] = 'secret'
>>> source = OddsApi(key_env='ODDS_API_KEY', markets=['h2h'], regions=['eu'])
>>> source.name, source.kind
('odds_api', 'odds')
>>> # It carries no sport of its own and takes the sport it is paired with.
>>> source.sport is None
True
>>> # The key is read from the environment when the request is made.
>>> item = RawItem(source='odds_api', key='snapshot', url='https://api.the-odds-api.com/v4/sports?all=true')
>>> 'secret' in item.url
False
>>> source.request_url(item)
'https://api.the-odds-api.com/v4/sports?all=true&apiKey=secret'
Source code in src/sportsbet/sources/_odds/_odds_api.py
187 188 189 190 191 192 193 194 195 196 197 | |
list_index_items(selection=None)
Return the catalogue of the vendor.
Source code in src/sportsbet/sources/_odds/_odds_api.py
241 242 243 | |
list_required_items(params, schedule=None)
Return one item per snapshot the selected matches need, matches kicking off together sharing one.
Source code in src/sportsbet/sources/_odds/_odds_api.py
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
needs_schedule()
Return True.
Source code in src/sportsbet/sources/_odds/_odds_api.py
237 238 239 | |
read_catalogue(payloads)
Return the combinations the vendor covers, the years being its historical coverage.
Source code in src/sportsbet/sources/_odds/_odds_api.py
245 246 247 248 249 250 251 252 253 254 255 256 257 | |
request_url(item)
Return the URL to fetch an item from, with the key read from the environment and added.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
RawItem
|
The item to fetch. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
url |
str
|
Where to fetch it from. |
Source code in src/sportsbet/sources/_odds/_odds_api.py
223 224 225 226 227 228 229 230 231 232 233 234 235 | |
to_snapshots(payloads)
Transform the vendor's responses into long odds snapshots, keeping the matches each item asked for.
Source code in src/sportsbet/sources/_odds/_odds_api.py
288 289 290 291 292 293 294 295 296 297 | |
RawItem(source, key, url)
dataclass
A raw item to fetch, identified within its source by a key, at a URL or file:// path.
Two items with the same source and key are equal.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The source that declared it. |
required |
key
|
str
|
The item's identity within the source. |
required |
url
|
str
|
The URL, or a |
required |
Examples:
>>> from sportsbet.sources import RawItem
>>> item = RawItem(source='my_stats', key='England_1_2025', url='https://example.com/2025.csv')
>>> item.key
'England_1_2025'
>>> # The same source and key make the same item.
>>> item == RawItem(source='my_stats', key='England_1_2025', url='https://example.com/2025.csv')
True
RawPayload(item, content)
dataclass
A payload a source returned, pairing the fetched item with its raw bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
item
|
RawItem
|
The item that was fetched. |
required |
content
|
bytes
|
The bytes of the response. |
required |
Examples:
>>> from sportsbet.sources import RawItem, RawPayload
>>> item = RawItem(source='my_stats', key='England_1_2025', url='https://example.com/2025.csv')
>>> payload = RawPayload(item=item, content=b'date,home_team,away_team\n2025-08-16,A,B\n')
>>> payload.item.key
'England_1_2025'
>>> # The bytes are exactly what the feed returned.
>>> payload.content.splitlines()[0]
b'date,home_team,away_team'
SampleSoccerOdds
Bases: _SampleSource, BaseOddsSource
The market average and market maximum pre-match odds of the bundled soccer sample season.
Examples:
>>> from sportsbet.dataloaders import DataLoader
>>> from sportsbet.sources import SampleSoccerOdds, SampleSoccerStats
>>> source = SampleSoccerOdds()
>>> source.name, source.kind, source.sport
('sample_soccer', 'odds', 'soccer')
>>> dataloader = DataLoader(stats=SampleSoccerStats(), odds=source)
>>> X, Y, O = dataloader.extract_train_data(odds_type='market_maximum')
>>> # The providers and the markets are read from the data.
>>> dataloader.get_odds_types()
['market_average', 'market_maximum']
>>> list(Y.columns)
['home_win__postplay__0min', 'draw__postplay__0min', 'away_win__postplay__0min', 'over_2.5__postplay__0min', 'under_2.5__postplay__0min']
SampleSoccerStats
Bases: _SampleSource, BaseStatsSource
A frozen real season of the English and Spanish soccer first divisions, bundled with the library.
It carries the identity of every match, each team's form before it, the half-time score and the result. The season is finished.
Examples:
>>> from sportsbet.dataloaders import DataLoader
>>> from sportsbet.sources import SampleSoccerOdds, SampleSoccerStats
>>> source = SampleSoccerStats()
>>> source.name, source.kind, source.sport
('sample_soccer', 'stats', 'soccer')
>>> # The bundled data is known up front.
>>> source.list_available_params()
[{'division': 1, 'league': 'England', 'year': 2024}, {'division': 1, 'league': 'Spain', 'year': 2024}]
>>> dataloader = DataLoader(
... param_grid={'league': ['England']},
... stats=source,
... odds=SampleSoccerOdds(),
... )
>>> X, Y, O = dataloader.extract_train_data(odds_type='market_average')
>>> # A whole season of the Premier League.
>>> len(X)
380
build_roster(data)
Return the clubs of a frame, keyed by their normalized name and valued by how they are written.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
The snapshots whose team columns hold the clubs. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, str]
|
The clubs, keyed by normalized name and valued by their original spelling. |
Source code in src/sportsbet/sources/_resolver.py
122 123 124 125 126 127 128 129 130 131 132 | |
count_common_prefix(one, other)
Return how many characters two strings begin with in common.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
one
|
str
|
The first string. |
required |
other
|
str
|
The second string. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of leading characters the two share. |
Source code in src/sportsbet/sources/_resolver.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
derive_market_outcomes(home_points, away_points, markets)
Derive boolean outcomes for the given markets from home and away points.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
home_points
|
Series | DataFrame
|
The home team points per snapshot. |
required |
away_points
|
Series | DataFrame
|
The away team points per snapshot. |
required |
markets
|
list[str]
|
The markets to derive (e.g. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
A dataframe with one integer 0/1 column per requested market. |
Examples:
>>> import pandas as pd
>>> from sportsbet.sources import derive_market_outcomes
>>> home_points = pd.Series([2, 1, 0])
>>> away_points = pd.Series([1, 1, 3])
>>> derive_market_outcomes(home_points, away_points, ['home_win', 'draw', 'away_win', 'over_2.5'])
home_win draw away_win over_2.5
0 1 0 0 1
1 0 1 0 0
2 0 0 1 1
Source code in src/sportsbet/sources/_utils.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | |
fetch_payloads(items, authorize)
Read each item at the URL authorize gives it and pair the bytes back with the item, in order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
list[RawItem]
|
The items to read. |
required |
authorize
|
Callable[[RawItem], str]
|
A callable turning an item into the URL to fetch it from, adding any credential. |
required |
Returns:
| Type | Description |
|---|---|
list[RawPayload]
|
The payloads, each pairing an item with its bytes, in the order given. |
Source code in src/sportsbet/sources/_base.py
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
measure_names_similarity(one, other)
Return how alike two names are, by the tokens they begin with in common.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
one
|
str
|
The first name. |
required |
other
|
str
|
The second name. |
required |
Returns:
| Type | Description |
|---|---|
float
|
A score in |
Source code in src/sportsbet/sources/_resolver.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | |
normalize_identity(data, mapping=None)
Return the match columns with the team names normalized, and remapped when a mapping is given.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
The snapshots whose match columns are read. |
required |
mapping
|
dict | None
|
The per-league, per-season odds-to-stats name mapping. |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
The match columns with normalized, and optionally remapped, team names. |
Source code in src/sportsbet/sources/_resolver.py
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
normalize_team_name(name)
Return a team name with the parts that carry no meaning removed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The team name to normalize. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The normalized name: lower case, without accents, punctuation or noise words. |
Source code in src/sportsbet/sources/_resolver.py
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | |
optional_col(include, fixed, alias=None)
Define an optional feature or odds column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
include
|
list[str]
|
The event statuses at which the column is meaningful. |
required |
fixed
|
bool
|
Whether the column is time-invariant within a match. |
required |
alias
|
str | None
|
The column name to use when it differs from the field's Python identifier. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A pandera field marking the column as an optional feature or odds column. |
Examples:
>>> from sportsbet.sources import BaseStatsSchema, optional_col, required_col
>>>
>>> class MyStatsSchema(BaseStatsSchema):
... 'The columns a statistics feed of your own may carry.'
...
... home_team: str = required_col()
... away_team: str = required_col()
... home_goals: float = optional_col(include=['inplay', 'postplay'], fixed=False)
... stadium_capacity: float = optional_col(include=['preplay'], fixed=True)
>>>
>>> # There is no score before the match starts.
>>> MyStatsSchema.to_schema().columns['home_goals'].metadata['include']
['inplay', 'postplay']
>>> # A stadium does not change size at half time.
>>> MyStatsSchema.to_schema().columns['stadium_capacity'].metadata['fixed']
True
Source code in src/sportsbet/sources/_schema.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
pair_rosters(normalized_stats_names, normalized_odds_names)
Return the odds names paired to the stats names, and the names left unpaired on each side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
normalized_stats_names
|
set[str]
|
The normalized club names of the statistics source. |
required |
normalized_odds_names
|
set[str]
|
The normalized club names of the odds source. |
required |
Returns:
| Type | Description |
|---|---|
tuple[dict[str, str], set[str], set[str]]
|
The odds-to-stats name pairing, the odds names left unpaired, and the stats names left unpaired. |
Source code in src/sportsbet/sources/_resolver.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
read_csv_content(content)
Return a data frame read from raw CSV content.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
content
|
bytes
|
The raw CSV bytes. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
The parsed data frame. |
Source code in src/sportsbet/sources/_base.py
127 128 129 130 131 132 133 134 135 136 137 138 139 | |
required_col(alias=None)
Define a required snapshot-identity column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alias
|
str | None
|
The column name to use when it differs from the field's Python identifier. |
None
|
Returns:
| Type | Description |
|---|---|
Any
|
A pandera field marking the column as a required snapshot-identity column. |
Examples:
>>> from sportsbet.sources import BaseStatsSchema, required_col
>>>
>>> class MyStatsSchema(BaseStatsSchema):
... 'The columns a statistics feed of your own must always carry.'
...
... home_team: str = required_col()
... away_team: str = required_col()
>>>
>>> # A required column may not be missing.
>>> MyStatsSchema.to_schema().columns['home_team'].nullable
False
Source code in src/sportsbet/sources/_schema.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
resolve_odds(stats, odds, aliases=None)
Return the odds with the identity of the matches they belong to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stats
|
DataFrame
|
The long statistics snapshots, which say which matches exist. |
required |
odds
|
DataFrame
|
The long odds snapshots. |
required |
aliases
|
dict[str, str] | None
|
The team names of the odds source, mapped to the names of the statistics source, for the clubs the pairing leaves over. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
odds |
DataFrame
|
The odds carrying the identity of the statistics. |
Examples:
>>> import pandas as pd
>>> from sportsbet.sources import resolve_odds
>>> identity = {'league': 'England', 'division': 1, 'year': 2025}
>>> moment = {'event_status': 'preplay', 'event_time': pd.Timedelta(0)}
>>> stats = pd.DataFrame([
... {'date': pd.Timestamp('2025-08-16', tz='UTC'), **identity, **moment,
... 'home_team': 'Man United', 'away_team': 'Arsenal'},
... ])
>>> odds = pd.DataFrame([
... {'date': pd.Timestamp('2025-08-16', tz='UTC'), **identity, **moment,
... 'home_team': 'Manchester United', 'away_team': 'Arsenal',
... 'provider': 'acme', 'home_win': 1.8},
... ])
>>> paired = resolve_odds(stats, odds)
>>> # The odds carry the identity of the statistics, so `Manchester United` becomes `Man United`.
>>> paired[['home_team', 'away_team', 'home_win']].to_dict('records')
[{'home_team': 'Man United', 'away_team': 'Arsenal', 'home_win': 1.8}]
>>> # A club the pairing cannot place is named, and can be given as an alias.
>>> paired = resolve_odds(stats, odds.assign(home_team='Utd of Manchester'),
... aliases={'Utd of Manchester': 'Man United'})
>>> len(paired)
1
Source code in src/sportsbet/sources/_resolver.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | |