ps3838api 1.1.0__py3-none-any.whl
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.
- ps3838api/__init__.py +7 -0
- ps3838api/api/__init__.py +45 -0
- ps3838api/api/client.py +516 -0
- ps3838api/api/default_client.py +231 -0
- ps3838api/api/v4client.py +107 -0
- ps3838api/matching.py +141 -0
- ps3838api/models/__init__.py +0 -0
- ps3838api/models/bets.py +250 -0
- ps3838api/models/client.py +48 -0
- ps3838api/models/errors.py +43 -0
- ps3838api/models/event.py +61 -0
- ps3838api/models/fixtures.py +53 -0
- ps3838api/models/lines.py +27 -0
- ps3838api/models/odds.py +221 -0
- ps3838api/models/sports.py +256 -0
- ps3838api/models/tank.py +6 -0
- ps3838api/py.typed +0 -0
- ps3838api/tank.py +93 -0
- ps3838api/totals.py +62 -0
- ps3838api/utils/match_leagues.py +90 -0
- ps3838api/utils/ops.py +146 -0
- ps3838api-1.1.0.dist-info/METADATA +176 -0
- ps3838api-1.1.0.dist-info/RECORD +26 -0
- ps3838api-1.1.0.dist-info/WHEEL +5 -0
- ps3838api-1.1.0.dist-info/licenses/LICENSE +8 -0
- ps3838api-1.1.0.dist-info/top_level.txt +1 -0
ps3838api/models/bets.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from typing import Literal, NotRequired, TypedDict
|
|
2
|
+
|
|
3
|
+
type OddsFormat = Literal["AMERICAN", "DECIMAL", "HONGKONG", "INDONESIAN", "MALAY"]
|
|
4
|
+
|
|
5
|
+
type FillType = Literal["NORMAL", "FILLANDKILL", "FILLMAXLIMIT"]
|
|
6
|
+
"""
|
|
7
|
+
### NORMAL
|
|
8
|
+
bet will be placed on specified stake.
|
|
9
|
+
|
|
10
|
+
### FILLANDKILL
|
|
11
|
+
|
|
12
|
+
If the stake is over the max limit, bet will be placed on max limit,
|
|
13
|
+
otherwise it will be placed on specified stake.
|
|
14
|
+
|
|
15
|
+
### FILLMAXLIMIT⚠️
|
|
16
|
+
|
|
17
|
+
bet will be places on max limit⚠️, stake amount will be ignored.
|
|
18
|
+
Please note that maximum limits can change at any moment, which may result in
|
|
19
|
+
risking more than anticipated. This option is replacement of isMaxStakeBet from
|
|
20
|
+
v1/bets/place'
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
type Team = Literal["TEAM1", "TEAM2", "DRAW"]
|
|
24
|
+
type Side = Literal["OVER", "UNDER"]
|
|
25
|
+
|
|
26
|
+
type BetList = Literal["SETTLED", "RUNNING", "ALL"]
|
|
27
|
+
|
|
28
|
+
type SortDir = Literal["ASC", "DESC"]
|
|
29
|
+
|
|
30
|
+
type BetStatus = Literal[
|
|
31
|
+
"ACCEPTED",
|
|
32
|
+
"CANCELLED",
|
|
33
|
+
"LOSE",
|
|
34
|
+
"PENDING_ACCEPTANCE",
|
|
35
|
+
"REFUNDED",
|
|
36
|
+
"NOT_ACCEPTED",
|
|
37
|
+
"WON",
|
|
38
|
+
"REJECTED",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
type BetStatus2 = Literal[
|
|
42
|
+
"ACCEPTED",
|
|
43
|
+
"CANCELLED",
|
|
44
|
+
"LOST",
|
|
45
|
+
"PENDING_ACCEPTANCE",
|
|
46
|
+
"REFUNDED",
|
|
47
|
+
"NOT_ACCEPTED",
|
|
48
|
+
"WON",
|
|
49
|
+
"REJECTED",
|
|
50
|
+
"HALF_WON_HALF_PUSHED",
|
|
51
|
+
"HALF_LOST_HALF_PUSHED",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
type BetType = Literal["MONEYLINE", "TEAM_TOTAL_POINTS", "SPREAD", "TOTAL_POINTS"]
|
|
55
|
+
|
|
56
|
+
type BetTypeFull = Literal[
|
|
57
|
+
"MONEYLINE",
|
|
58
|
+
"TEAM_TOTAL_POINTS",
|
|
59
|
+
"SPREAD",
|
|
60
|
+
"TOTAL_POINTS",
|
|
61
|
+
"SPECIAL",
|
|
62
|
+
"PARLAY",
|
|
63
|
+
"TEASER",
|
|
64
|
+
"MANUAL",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PlaceStraightBetRequest(TypedDict):
|
|
69
|
+
oddsFormat: OddsFormat
|
|
70
|
+
uniqueRequestId: str
|
|
71
|
+
acceptBetterLine: bool
|
|
72
|
+
stake: float
|
|
73
|
+
winRiskStake: Literal["WIN", "RISK"]
|
|
74
|
+
lineId: int
|
|
75
|
+
altLineId: NotRequired[int]
|
|
76
|
+
pitcher1MustStart: bool
|
|
77
|
+
pitcher2MustStart: bool
|
|
78
|
+
fillType: Literal["NORMAL", "FILLANDKILL", "FILLMAXLIMIT"]
|
|
79
|
+
sportId: int
|
|
80
|
+
eventId: int
|
|
81
|
+
periodNumber: int
|
|
82
|
+
betType: BetTypeFull
|
|
83
|
+
team: Literal["TEAM1", "TEAM2", "DRAW"]
|
|
84
|
+
side: NotRequired[Literal["OVER", "UNDER"]]
|
|
85
|
+
handicap: NotRequired[float]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class CancellationDetails(TypedDict):
|
|
89
|
+
key: str
|
|
90
|
+
value: str
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class CancellationReason(TypedDict):
|
|
94
|
+
code: str
|
|
95
|
+
details: list[CancellationDetails]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class StraightBet(TypedDict):
|
|
99
|
+
betId: int
|
|
100
|
+
wagerNumber: int
|
|
101
|
+
placedAt: str
|
|
102
|
+
betStatus: Literal[
|
|
103
|
+
"ACCEPTED",
|
|
104
|
+
"CANCELLED",
|
|
105
|
+
"LOSE",
|
|
106
|
+
"PENDING_ACCEPTANCE",
|
|
107
|
+
"REFUNDED",
|
|
108
|
+
"NOT_ACCEPTED",
|
|
109
|
+
"WON",
|
|
110
|
+
]
|
|
111
|
+
betType: BetTypeFull
|
|
112
|
+
win: float
|
|
113
|
+
risk: float
|
|
114
|
+
oddsFormat: OddsFormat
|
|
115
|
+
updateSequence: int
|
|
116
|
+
price: float
|
|
117
|
+
winLoss: NotRequired[float]
|
|
118
|
+
customerCommission: NotRequired[float]
|
|
119
|
+
cancellationReason: NotRequired[CancellationReason]
|
|
120
|
+
handicap: NotRequired[float]
|
|
121
|
+
side: NotRequired[Literal["OVER", "UNDER"]]
|
|
122
|
+
pitcher1: NotRequired[str]
|
|
123
|
+
pitcher2: NotRequired[str]
|
|
124
|
+
pitcher1MustStart: NotRequired[str]
|
|
125
|
+
pitcher2MustStart: NotRequired[str]
|
|
126
|
+
teamName: NotRequired[str]
|
|
127
|
+
team1: NotRequired[str]
|
|
128
|
+
team2: NotRequired[str]
|
|
129
|
+
periodNumber: NotRequired[int]
|
|
130
|
+
team1Score: NotRequired[float]
|
|
131
|
+
team2Score: NotRequired[float]
|
|
132
|
+
ftTeam1Score: NotRequired[float]
|
|
133
|
+
ftTeam2Score: NotRequired[float]
|
|
134
|
+
pTeam1Score: NotRequired[float]
|
|
135
|
+
pTeam2Score: NotRequired[float]
|
|
136
|
+
isLive: Literal["true", "false"]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class PlaceStraightBetResponse(TypedDict):
|
|
140
|
+
status: Literal["ACCEPTED", "PENDING_ACCEPTANCE", "PROCESSED_WITH_ERROR"]
|
|
141
|
+
uniqueRequestId: str
|
|
142
|
+
errorCode: NotRequired[
|
|
143
|
+
Literal[
|
|
144
|
+
"ALL_BETTING_CLOSED",
|
|
145
|
+
"ALL_LIVE_BETTING_CLOSED",
|
|
146
|
+
"ABOVE_EVENT_MAX",
|
|
147
|
+
"ABOVE_MAX_BET_AMOUNT",
|
|
148
|
+
"BELOW_MIN_BET_AMOUNT",
|
|
149
|
+
"BLOCKED_BETTING",
|
|
150
|
+
"BLOCKED_CLIENT",
|
|
151
|
+
"INSUFFICIENT_FUNDS",
|
|
152
|
+
"INVALID_COUNTRY",
|
|
153
|
+
"INVALID_EVENT",
|
|
154
|
+
"INVALID_ODDS_FORMAT",
|
|
155
|
+
"LINE_CHANGED",
|
|
156
|
+
"LISTED_PITCHERS_SELECTION_ERROR",
|
|
157
|
+
"OFFLINE_EVENT",
|
|
158
|
+
"PAST_CUTOFFTIME",
|
|
159
|
+
"RED_CARDS_CHANGED",
|
|
160
|
+
"SCORE_CHANGED",
|
|
161
|
+
"DUPLICATE_UNIQUE_REQUEST_ID",
|
|
162
|
+
"INCOMPLETE_CUSTOMER_BETTING_PROFILE",
|
|
163
|
+
"INVALID_CUSTOMER_PROFILE",
|
|
164
|
+
"LIMITS_CONFIGURATION_ISSUE",
|
|
165
|
+
"RESPONSIBLE_BETTING_LOSS_LIMIT_EXCEEDED",
|
|
166
|
+
"RESPONSIBLE_BETTING_RISK_LIMIT_EXCEEDED",
|
|
167
|
+
"RESUBMIT_REQUEST",
|
|
168
|
+
"SYSTEM_ERROR_3",
|
|
169
|
+
"LICENCE_RESTRICTION_LIVE_BETTING_BLOCKED",
|
|
170
|
+
"INVALID_HANDICAP",
|
|
171
|
+
"BETTING_SUSPENDED",
|
|
172
|
+
]
|
|
173
|
+
]
|
|
174
|
+
straightBet: NotRequired[StraightBet]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class CancellationDetailsV3(TypedDict):
|
|
178
|
+
key: str
|
|
179
|
+
value: str
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class CancellationReasonV3(TypedDict):
|
|
183
|
+
code: str
|
|
184
|
+
details: list[CancellationDetailsV3]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class StraightBetV3(TypedDict):
|
|
188
|
+
betId: int
|
|
189
|
+
wagerNumber: int
|
|
190
|
+
placedAt: str
|
|
191
|
+
betStatus: BetStatus
|
|
192
|
+
betStatus2: BetStatus2
|
|
193
|
+
betType: BetTypeFull
|
|
194
|
+
win: float
|
|
195
|
+
risk: float
|
|
196
|
+
oddsFormat: OddsFormat
|
|
197
|
+
updateSequence: int
|
|
198
|
+
price: float
|
|
199
|
+
isLive: bool
|
|
200
|
+
eventStartTime: str
|
|
201
|
+
|
|
202
|
+
# Optional fields (use NotRequired for fields that may be absent)
|
|
203
|
+
winLoss: NotRequired[float | None]
|
|
204
|
+
customerCommission: NotRequired[float | None]
|
|
205
|
+
cancellationReason: NotRequired[CancellationReasonV3]
|
|
206
|
+
sportId: NotRequired[int]
|
|
207
|
+
leagueId: NotRequired[int]
|
|
208
|
+
eventId: NotRequired[int]
|
|
209
|
+
handicap: NotRequired[float | None]
|
|
210
|
+
teamName: NotRequired[str]
|
|
211
|
+
side: NotRequired[Literal["OVER", "UNDER"] | None]
|
|
212
|
+
pitcher1: NotRequired[str | None]
|
|
213
|
+
pitcher2: NotRequired[str | None]
|
|
214
|
+
pitcher1MustStart: NotRequired[bool | None]
|
|
215
|
+
pitcher2MustStart: NotRequired[bool | None]
|
|
216
|
+
team1: NotRequired[str]
|
|
217
|
+
team2: NotRequired[str]
|
|
218
|
+
periodNumber: NotRequired[int]
|
|
219
|
+
team1Score: NotRequired[float | None]
|
|
220
|
+
team2Score: NotRequired[float | None]
|
|
221
|
+
ftTeam1Score: NotRequired[float | None]
|
|
222
|
+
ftTeam2Score: NotRequired[float | None]
|
|
223
|
+
pTeam1Score: NotRequired[float | None]
|
|
224
|
+
pTeam2Score: NotRequired[float | None]
|
|
225
|
+
resultingUnit: NotRequired[str]
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# Not implemented placeholders:
|
|
229
|
+
class ParlayBetV2(TypedDict): ...
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class TeaserBet(TypedDict): ...
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class SpecialBetV3(TypedDict): ...
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class ManualBet(TypedDict): ...
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class BetsResponse(TypedDict):
|
|
242
|
+
moreAvailable: bool
|
|
243
|
+
pageSize: int
|
|
244
|
+
fromRecord: int
|
|
245
|
+
toRecord: int
|
|
246
|
+
straightBets: list[StraightBetV3]
|
|
247
|
+
parlayBets: list[ParlayBetV2]
|
|
248
|
+
teaserBets: list[TeaserBet]
|
|
249
|
+
specialBets: list[SpecialBetV3]
|
|
250
|
+
manualBets: list[ManualBet]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Literal, NotRequired, TypedDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BalanceData(TypedDict):
|
|
5
|
+
availableBalance: float
|
|
6
|
+
outstandingTransactions: float
|
|
7
|
+
givenCredit: float
|
|
8
|
+
currency: str
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PeriodData(TypedDict, total=False):
|
|
12
|
+
number: NotRequired[int]
|
|
13
|
+
description: NotRequired[str]
|
|
14
|
+
shortDescription: NotRequired[str]
|
|
15
|
+
spreadDescription: NotRequired[str]
|
|
16
|
+
moneylineDescription: NotRequired[str]
|
|
17
|
+
totalDescription: NotRequired[str]
|
|
18
|
+
team1TotalDescription: NotRequired[str]
|
|
19
|
+
team2TotalDescription: NotRequired[str]
|
|
20
|
+
spreadShortDescription: NotRequired[str]
|
|
21
|
+
moneylineShortDescription: NotRequired[str]
|
|
22
|
+
totalShortDescription: NotRequired[str]
|
|
23
|
+
team1TotalShortDescription: NotRequired[str]
|
|
24
|
+
team2TotalShortDescription: NotRequired[str]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LeagueV3(TypedDict):
|
|
28
|
+
id: int
|
|
29
|
+
name: str
|
|
30
|
+
homeTeamType: NotRequired[str]
|
|
31
|
+
hasOfferings: NotRequired[bool]
|
|
32
|
+
container: NotRequired[str]
|
|
33
|
+
allowRoundRobins: NotRequired[bool]
|
|
34
|
+
leagueSpecialsCount: NotRequired[int]
|
|
35
|
+
eventSpecialsCount: NotRequired[int]
|
|
36
|
+
eventCount: NotRequired[int]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BettingStatusResponse(TypedDict):
|
|
40
|
+
status: Literal["ALL_BETTING_ENABLED", "ALL_LIVE_BETTING_CLOSED", "ALL_BETTING_CLOSED"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"BalanceData",
|
|
45
|
+
"PeriodData",
|
|
46
|
+
"LeagueV3",
|
|
47
|
+
"BettingStatusResponse",
|
|
48
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BasePS3838Error(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ResponseError(BasePS3838Error):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AccessBlockedError(ResponseError):
|
|
13
|
+
"""
|
|
14
|
+
Raised when the API returns an empty response, likely due to access restrictions.
|
|
15
|
+
|
|
16
|
+
This may not be a strict rate limit. In many cases, it indicates that the account
|
|
17
|
+
needs to meet certain behavioral criteria — such as placing $30–$40 in manual bets per day —
|
|
18
|
+
before automated requests are allowed again.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class WrongEndpoint(ResponseError):
|
|
25
|
+
"""405 HTTP Error"""
|
|
26
|
+
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class PS3838APIError(ResponseError):
|
|
32
|
+
code: str | None
|
|
33
|
+
message: str | None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LogicError(BasePS3838Error):
|
|
37
|
+
"""Raised when there is a violation of client-side logic or input invariant."""
|
|
38
|
+
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseballOnlyArgumentError(LogicError):
|
|
43
|
+
pass
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import TypedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MatchedLeague(TypedDict):
|
|
6
|
+
betsapi_league: str
|
|
7
|
+
ps3838_league: str | None
|
|
8
|
+
ps3838_id: int | None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
#######################################
|
|
12
|
+
# Return Types For the Matching Tanks #
|
|
13
|
+
#######################################
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class NoSuchLeague:
|
|
18
|
+
league: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class NoSuchLeagueMatching(NoSuchLeague):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class NoSuchLeagueFixtures(NoSuchLeague):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class WrongLeague(NoSuchLeague):
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class NoSuchEvent:
|
|
38
|
+
league: str
|
|
39
|
+
home: str
|
|
40
|
+
away: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class EventTooFarInFuture(NoSuchEvent):
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
type Failure = NoSuchLeague | NoSuchEvent
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
#######################################
|
|
52
|
+
# Return Types For the Odds Tank #
|
|
53
|
+
#######################################
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class NoSuchOddsAvailable:
|
|
58
|
+
event_id: int
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
type NoResult = NoSuchLeague | NoSuchEvent | NoSuchOddsAvailable
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# models/fixtures.py
|
|
2
|
+
from typing import Required, TypedDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class FixtureV3(TypedDict, total=False):
|
|
6
|
+
"""
|
|
7
|
+
Represents a single fixture within the API response.
|
|
8
|
+
|
|
9
|
+
- liveStatus: 0=no live, 1=live event, 2=will be offered live
|
|
10
|
+
- status: Deprecated; check period status in /odds
|
|
11
|
+
- betAcceptanceType: 0=none, 1=danger zone, 2=live delay, 3=both
|
|
12
|
+
- parlayRestriction: 0=full parlay allowed, 1=not allowed, 2=partial
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
id: Required[int]
|
|
16
|
+
parentId: int
|
|
17
|
+
starts: str # date-time in UTC
|
|
18
|
+
home: Required[str]
|
|
19
|
+
away: Required[str]
|
|
20
|
+
rotNum: str # Will be removed in future; see docs
|
|
21
|
+
liveStatus: Required[int]
|
|
22
|
+
homePitcher: str # Baseball only
|
|
23
|
+
awayPitcher: str # Baseball only
|
|
24
|
+
status: str # "O", "H", or "I" (deprecated)
|
|
25
|
+
betAcceptanceType: int
|
|
26
|
+
parlayRestriction: int
|
|
27
|
+
altTeaser: bool
|
|
28
|
+
resultingUnit: Required[str] # e.g. "corners", "bookings"
|
|
29
|
+
version: int # fixture version changes with any update
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class FixturesLeagueV3(TypedDict):
|
|
33
|
+
"""
|
|
34
|
+
Container for leagues in the Get Fixtures response.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
id: int
|
|
38
|
+
name: str
|
|
39
|
+
events: list[FixtureV3]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FixturesResponse(TypedDict):
|
|
43
|
+
"""
|
|
44
|
+
Full response for GET /v3/fixtures
|
|
45
|
+
|
|
46
|
+
- sportId: same as requested ID
|
|
47
|
+
- last: for delta updates (use as 'since' in next request)
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
sportId: int
|
|
51
|
+
last: int
|
|
52
|
+
league: list[FixturesLeagueV3]
|
|
53
|
+
"""list of leagues"""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Literal, Required, TypedDict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LineResponse(TypedDict, total=False):
|
|
5
|
+
status: Required[Literal["SUCCESS", "NOT_EXISTS"]]
|
|
6
|
+
|
|
7
|
+
# The following fields are present only when status == "SUCCESS"
|
|
8
|
+
price: float
|
|
9
|
+
lineId: int
|
|
10
|
+
altLineId: int
|
|
11
|
+
|
|
12
|
+
team1Score: int
|
|
13
|
+
team2Score: int
|
|
14
|
+
team1RedCards: int
|
|
15
|
+
team2RedCards: int
|
|
16
|
+
|
|
17
|
+
maxRiskStake: float
|
|
18
|
+
minRiskStake: float
|
|
19
|
+
maxWinStake: float
|
|
20
|
+
minWinStake: float
|
|
21
|
+
|
|
22
|
+
effectiveAsOf: str
|
|
23
|
+
|
|
24
|
+
periodTeam1Score: int
|
|
25
|
+
periodTeam2Score: int
|
|
26
|
+
periodTeam1RedCards: int
|
|
27
|
+
periodTeam2RedCards: int
|
ps3838api/models/odds.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import NotRequired, Required, TypedDict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# ─────────────────────────────────────────
|
|
7
|
+
# Top-level structure of the response (V3)
|
|
8
|
+
# ─────────────────────────────────────────
|
|
9
|
+
class OddsResponse(TypedDict):
|
|
10
|
+
sportId: int
|
|
11
|
+
last: int # Used for `since` in future incremental updates
|
|
12
|
+
leagues: list[OddsLeagueV3]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ─────────────────────────────────────────
|
|
16
|
+
# League level
|
|
17
|
+
# ─────────────────────────────────────────
|
|
18
|
+
class OddsLeagueV3(TypedDict):
|
|
19
|
+
id: int
|
|
20
|
+
events: list[OddsEventV3]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ─────────────────────────────────────────
|
|
24
|
+
# Event level
|
|
25
|
+
# ─────────────────────────────────────────
|
|
26
|
+
class OddsEventV3(TypedDict, total=False):
|
|
27
|
+
id: Required[int]
|
|
28
|
+
awayScore: float
|
|
29
|
+
homeScore: float
|
|
30
|
+
awayRedCards: int
|
|
31
|
+
homeRedCards: int
|
|
32
|
+
periods: list[OddsPeriodV3]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ─────────────────────────────────────────
|
|
36
|
+
# Period level (e.g., full match, 1st half, etc.)
|
|
37
|
+
# ─────────────────────────────────────────
|
|
38
|
+
class OddsPeriodV3(TypedDict, total=False):
|
|
39
|
+
lineId: int
|
|
40
|
+
number: int # 0 = full match, 1 = 1st half, etc.
|
|
41
|
+
cutoff: str # ISO datetime string (UTC)
|
|
42
|
+
status: int # 1 = online, 2 = offline
|
|
43
|
+
|
|
44
|
+
maxSpread: float
|
|
45
|
+
maxMoneyline: float
|
|
46
|
+
maxTotal: float
|
|
47
|
+
maxTeamTotal: float
|
|
48
|
+
|
|
49
|
+
moneylineUpdatedAt: str
|
|
50
|
+
spreadUpdatedAt: str
|
|
51
|
+
totalUpdatedAt: str
|
|
52
|
+
teamTotalUpdatedAt: str
|
|
53
|
+
|
|
54
|
+
spreads: list[OddsSpreadV3]
|
|
55
|
+
moneyline: OddsMoneylineV3
|
|
56
|
+
totals: list[OddsTotalV3]
|
|
57
|
+
teamTotal: OddsTeamTotalsV3
|
|
58
|
+
|
|
59
|
+
# Live stats at period level (Match and Extra Time only)
|
|
60
|
+
awayScore: float
|
|
61
|
+
homeScore: float
|
|
62
|
+
awayRedCards: int
|
|
63
|
+
homeRedCards: int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ─────────────────────────────────────────
|
|
67
|
+
# Spread line data (handicap)
|
|
68
|
+
# ─────────────────────────────────────────
|
|
69
|
+
class OddsSpreadV3(TypedDict, total=False):
|
|
70
|
+
altLineId: int # Present only for alternative lines
|
|
71
|
+
hdp: float # Handicap
|
|
72
|
+
home: float # Decimal odds for home team
|
|
73
|
+
away: float # Decimal odds for away team
|
|
74
|
+
max: float # Overrides `maxSpread` if present
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ─────────────────────────────────────────
|
|
78
|
+
# Moneyline data (1X2 market)
|
|
79
|
+
# ─────────────────────────────────────────
|
|
80
|
+
class OddsMoneylineV3(TypedDict, total=False):
|
|
81
|
+
home: float
|
|
82
|
+
away: float
|
|
83
|
+
draw: float # Optional, only for sports/events with a draw
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ─────────────────────────────────────────
|
|
87
|
+
# Total Points line (Over/Under market)
|
|
88
|
+
# ─────────────────────────────────────────
|
|
89
|
+
class OddsTotalV3(TypedDict):
|
|
90
|
+
altLineId: NotRequired[int] # Optional alternative line
|
|
91
|
+
points: float # Total goals/points line
|
|
92
|
+
over: float # Decimal odds for over
|
|
93
|
+
under: float # Decimal odds for under
|
|
94
|
+
max: NotRequired[float] # Overrides `maxTotal` if present
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
# ─────────────────────────────────────────
|
|
98
|
+
# Team Total Points (each team separately)
|
|
99
|
+
# ─────────────────────────────────────────
|
|
100
|
+
class OddsTeamTotalsV3(TypedDict, total=False):
|
|
101
|
+
home: OddsTeamTotalV3
|
|
102
|
+
away: OddsTeamTotalV3
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class OddsTeamTotalV3(TypedDict, total=False):
|
|
106
|
+
points: float # Team-specific total line
|
|
107
|
+
over: float
|
|
108
|
+
under: float
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
112
|
+
# V4 Odds Models
|
|
113
|
+
# ═════════════════════════════════════════════════════════════════════════════
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# ─────────────────────────────────────────
|
|
117
|
+
# V4 Top-level response structure
|
|
118
|
+
# ─────────────────────────────────────────
|
|
119
|
+
class OddsResponseV4(TypedDict):
|
|
120
|
+
sportId: int
|
|
121
|
+
last: int # Used for `since` in future incremental updates
|
|
122
|
+
leagues: list[OddsLeagueV4]
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ─────────────────────────────────────────
|
|
126
|
+
# V4 League level
|
|
127
|
+
# ─────────────────────────────────────────
|
|
128
|
+
class OddsLeagueV4(TypedDict):
|
|
129
|
+
id: int
|
|
130
|
+
events: list[OddsEventV4]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ─────────────────────────────────────────
|
|
134
|
+
# V4 Event level
|
|
135
|
+
# ─────────────────────────────────────────
|
|
136
|
+
class OddsEventV4(TypedDict, total=False):
|
|
137
|
+
id: Required[int]
|
|
138
|
+
awayScore: float
|
|
139
|
+
homeScore: float
|
|
140
|
+
awayRedCards: int
|
|
141
|
+
homeRedCards: int
|
|
142
|
+
periods: list[OddsPeriodV4]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# ─────────────────────────────────────────
|
|
146
|
+
# V4 Period level
|
|
147
|
+
# ─────────────────────────────────────────
|
|
148
|
+
class OddsPeriodV4(TypedDict, total=False):
|
|
149
|
+
lineId: int
|
|
150
|
+
number: int # 0 = full match, 1 = 1st half, etc.
|
|
151
|
+
cutoff: str # ISO datetime string (UTC)
|
|
152
|
+
status: int # 1 = online, 2 = offline
|
|
153
|
+
|
|
154
|
+
maxSpread: float
|
|
155
|
+
maxMoneyline: float
|
|
156
|
+
maxTotal: float
|
|
157
|
+
maxTeamTotal: float
|
|
158
|
+
|
|
159
|
+
moneylineUpdatedAt: str
|
|
160
|
+
spreadUpdatedAt: str
|
|
161
|
+
totalUpdatedAt: str
|
|
162
|
+
teamTotalUpdatedAt: str
|
|
163
|
+
|
|
164
|
+
spreads: list[OddsSpreadV4]
|
|
165
|
+
moneyline: OddsMoneylineV4
|
|
166
|
+
totals: list[OddsTotalV4]
|
|
167
|
+
teamTotal: OddsTeamTotalsV4
|
|
168
|
+
|
|
169
|
+
# Live stats at period level (Match and Extra Time only)
|
|
170
|
+
awayScore: float
|
|
171
|
+
homeScore: float
|
|
172
|
+
awayRedCards: int
|
|
173
|
+
homeRedCards: int
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ─────────────────────────────────────────
|
|
177
|
+
# V4 Spread line data (handicap)
|
|
178
|
+
# ─────────────────────────────────────────
|
|
179
|
+
class OddsSpreadV4(TypedDict, total=False):
|
|
180
|
+
altLineId: int # Present only for alternative lines
|
|
181
|
+
hdp: float # Handicap
|
|
182
|
+
home: float # Decimal odds for home team
|
|
183
|
+
away: float # Decimal odds for away team
|
|
184
|
+
max: float # Overrides `maxSpread` if present
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ─────────────────────────────────────────
|
|
188
|
+
# V4 Moneyline data (1X2 market)
|
|
189
|
+
# ─────────────────────────────────────────
|
|
190
|
+
class OddsMoneylineV4(TypedDict, total=False):
|
|
191
|
+
home: float
|
|
192
|
+
away: float
|
|
193
|
+
draw: float # Optional, only for sports/events with a draw
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# ─────────────────────────────────────────
|
|
197
|
+
# V4 Total Points line (Over/Under market)
|
|
198
|
+
# ─────────────────────────────────────────
|
|
199
|
+
class OddsTotalV4(TypedDict, total=False):
|
|
200
|
+
altLineId: int # Optional alternative line
|
|
201
|
+
points: Required[float] # Total goals/points line
|
|
202
|
+
over: Required[float] # Decimal odds for over
|
|
203
|
+
under: Required[float] # Decimal odds for under
|
|
204
|
+
max: float # Overrides `maxTotal` if present
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ─────────────────────────────────────────
|
|
208
|
+
# V4 Team Total Points (each team separately)
|
|
209
|
+
# Key difference from V3: arrays instead of single objects
|
|
210
|
+
# ─────────────────────────────────────────
|
|
211
|
+
class OddsTeamTotalsV4(TypedDict, total=False):
|
|
212
|
+
home: list[OddsTeamTotalV4]
|
|
213
|
+
away: list[OddsTeamTotalV4]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class OddsTeamTotalV4(TypedDict, total=False):
|
|
217
|
+
points: Required[float] # Team-specific total line
|
|
218
|
+
over: Required[float]
|
|
219
|
+
under: Required[float]
|
|
220
|
+
altLineId: int # Present only for alternative lines
|
|
221
|
+
max: float # Maximum bet volume for alternative lines
|