hkjc 0.3.17__py3-none-any.whl → 0.3.18__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.
hkjc/__init__.py CHANGED
@@ -4,7 +4,7 @@ This module re-exports commonly used symbols from the submodules.
4
4
  """
5
5
  from importlib.metadata import version as _version
6
6
 
7
- __all__ = ["live_odds", "qpbanker",
7
+ __all__ = ["live", "qpbanker",
8
8
  "generate_all_qp_trades", "generate_all_pla_trades", "pareto_filter",
9
9
  "speedpro_energy", "speedmap", "harveille_model",
10
10
  "generate_historical_data"]
@@ -14,8 +14,7 @@ try:
14
14
  except Exception: # pragma: no cover - best-effort version resolution
15
15
  __version__ = "0.0.0"
16
16
 
17
- from .live_odds import live_odds
18
17
  from .processing import generate_all_qp_trades, generate_all_pla_trades, generate_historical_data
19
18
  from .utils import pareto_filter
20
19
  from .speedpro import speedmap, speedpro_energy
21
- from . import harville_model
20
+ from . import harville_model, live
hkjc/historical.py CHANGED
@@ -56,10 +56,9 @@ def _classify_running_style(df: pl.DataFrame, running_pos_col="RunningPosition")
56
56
 
57
57
  df = df.with_columns([
58
58
  (pl.col("StartPosition")-pl.col("FinishPosition")).alias("PositionChange"),
59
- pl.mean_horizontal("StartPosition", "Position2",
60
- "Position3", "FinishPosition").alias("AvgPosition"),
59
+ pl.mean_horizontal("StartPosition", "Position2").alias("AvgStartPosition"),
61
60
  ]).with_columns(pl.when(pl.col("StartPosition").is_null()).then(pl.lit("--"))
62
- .when((pl.col("AvgPosition") <= 3.5) & (pl.col("StartPosition") <= 3)).then(pl.lit("FrontRunner"))
61
+ .when((pl.col("AvgStartPosition") <= 3) & (pl.col("StartPosition") <= 3)).then(pl.lit("FrontRunner"))
63
62
  .when((pl.col("PositionChange") >= 1) & (pl.col("StartPosition") >= 6)).then(pl.lit("Closer"))
64
63
  .otherwise(pl.lit("Pacer")).alias("RunningStyle"))
65
64
 
hkjc/live.py ADDED
@@ -0,0 +1,375 @@
1
+ """Functions to fetch and process data from HKJC
2
+ """
3
+ from __future__ import annotations
4
+ from typing import Tuple, List
5
+
6
+ import requests
7
+ from cachetools.func import ttl_cache
8
+ import numpy as np
9
+
10
+ from .utils import _validate_date, _validate_venue_code
11
+
12
+ HKJC_LIVEODDS_ENDPOINT = "https://info.cld.hkjc.com/graphql/base/"
13
+
14
+ RACEMTG_PAYLOAD = {
15
+ "operationName": "raceMeetings",
16
+ "variables": {"date": None, "venueCode": None},
17
+ "query": """
18
+ fragment raceFragment on Race {
19
+ id
20
+ no
21
+ status
22
+ raceName_en
23
+ raceName_ch
24
+ postTime
25
+ country_en
26
+ country_ch
27
+ distance
28
+ wageringFieldSize
29
+ go_en
30
+ go_ch
31
+ ratingType
32
+ raceTrack {
33
+ description_en
34
+ description_ch
35
+ }
36
+ raceCourse {
37
+ description_en
38
+ description_ch
39
+ displayCode
40
+ }
41
+ claCode
42
+ raceClass_en
43
+ raceClass_ch
44
+ judgeSigns {
45
+ value_en
46
+ }
47
+ }
48
+
49
+ fragment racingBlockFragment on RaceMeeting {
50
+ jpEsts: pmPools(
51
+ oddsTypes: [WIN, PLA, TCE, TRI, FF, QTT, DT, TT, SixUP]
52
+ filters: ["jackpot", "estimatedDividend"]
53
+ ) {
54
+ leg {
55
+ number
56
+ races
57
+ }
58
+ oddsType
59
+ jackpot
60
+ estimatedDividend
61
+ mergedPoolId
62
+ }
63
+ poolInvs: pmPools(
64
+ oddsTypes: [WIN, PLA, QIN, QPL, CWA, CWB, CWC, IWN, FCT, TCE, TRI, FF, QTT, DBL, TBL, DT, TT, SixUP]
65
+ ) {
66
+ id
67
+ leg {
68
+ races
69
+ }
70
+ }
71
+ penetrometerReadings(filters: ["first"]) {
72
+ reading
73
+ readingTime
74
+ }
75
+ hammerReadings(filters: ["first"]) {
76
+ reading
77
+ readingTime
78
+ }
79
+ changeHistories(filters: ["top3"]) {
80
+ type
81
+ time
82
+ raceNo
83
+ runnerNo
84
+ horseName_ch
85
+ horseName_en
86
+ jockeyName_ch
87
+ jockeyName_en
88
+ scratchHorseName_ch
89
+ scratchHorseName_en
90
+ handicapWeight
91
+ scrResvIndicator
92
+ }
93
+ }
94
+
95
+ query raceMeetings($date: String, $venueCode: String) {
96
+ timeOffset {
97
+ rc
98
+ }
99
+ activeMeetings: raceMeetings {
100
+ id
101
+ venueCode
102
+ date
103
+ status
104
+ races {
105
+ no
106
+ postTime
107
+ status
108
+ wageringFieldSize
109
+ }
110
+ }
111
+ raceMeetings(date: $date, venueCode: $venueCode) {
112
+ id
113
+ status
114
+ venueCode
115
+ date
116
+ totalNumberOfRace
117
+ currentNumberOfRace
118
+ dateOfWeek
119
+ meetingType
120
+ totalInvestment
121
+ country {
122
+ code
123
+ namech
124
+ nameen
125
+ seq
126
+ }
127
+ races {
128
+ ...raceFragment
129
+ runners {
130
+ id
131
+ no
132
+ standbyNo
133
+ status
134
+ name_ch
135
+ name_en
136
+ horse {
137
+ id
138
+ code
139
+ }
140
+ color
141
+ barrierDrawNumber
142
+ handicapWeight
143
+ currentWeight
144
+ currentRating
145
+ internationalRating
146
+ gearInfo
147
+ racingColorFileName
148
+ allowance
149
+ trainerPreference
150
+ last6run
151
+ saddleClothNo
152
+ trumpCard
153
+ priority
154
+ finalPosition
155
+ deadHeat
156
+ winOdds
157
+ jockey {
158
+ code
159
+ name_en
160
+ name_ch
161
+ }
162
+ trainer {
163
+ code
164
+ name_en
165
+ name_ch
166
+ }
167
+ }
168
+ }
169
+ obSt: pmPools(oddsTypes: [WIN, PLA]) {
170
+ leg {
171
+ races
172
+ }
173
+ oddsType
174
+ comingleStatus
175
+ }
176
+ poolInvs: pmPools(
177
+ oddsTypes: [WIN, PLA, QIN, QPL, CWA, CWB, CWC, IWN, FCT, TCE, TRI, FF, QTT, DBL, TBL, DT, TT, SixUP]
178
+ ) {
179
+ id
180
+ leg {
181
+ number
182
+ races
183
+ }
184
+ status
185
+ sellStatus
186
+ oddsType
187
+ investment
188
+ mergedPoolId
189
+ lastUpdateTime
190
+ }
191
+ ...racingBlockFragment
192
+ pmPools(oddsTypes: []) {
193
+ id
194
+ }
195
+ jkcInstNo: foPools(oddsTypes: [JKC], filters: ["top"]) {
196
+ instNo
197
+ }
198
+ tncInstNo: foPools(oddsTypes: [TNC], filters: ["top"]) {
199
+ instNo
200
+ }
201
+ }
202
+ }
203
+ """}
204
+
205
+ LIVEODDS_PAYLOAD = {
206
+ "operationName": "racing",
207
+ "variables": {"date": None, "venueCode": None, "raceNo": None, "oddsTypes": None},
208
+ "query": """
209
+ query racing($date: String, $venueCode: String, $oddsTypes: [OddsType], $raceNo: Int) {
210
+ raceMeetings(date: $date, venueCode: $venueCode) {
211
+ pmPools(oddsTypes: $oddsTypes, raceNo: $raceNo) {
212
+ id
213
+ status
214
+ sellStatus
215
+ oddsType
216
+ lastUpdateTime
217
+ guarantee
218
+ minTicketCost
219
+ name_en
220
+ name_ch
221
+ leg {
222
+ number
223
+ races
224
+ }
225
+ cWinSelections {
226
+ composite
227
+ name_ch
228
+ name_en
229
+ starters
230
+ }
231
+ oddsNodes {
232
+ combString
233
+ oddsValue
234
+ hotFavourite
235
+ oddsDropValue
236
+ bankerOdds {
237
+ combString
238
+ oddsValue
239
+ }
240
+ }
241
+ }
242
+ }
243
+ }""",
244
+ }
245
+
246
+
247
+ @ttl_cache(maxsize=12, ttl=1000)
248
+ def _fetch_live_races(date: str, venue_code: str) -> dict:
249
+ """Fetch live race data from HKJC GraphQL endpoint."""
250
+ payload = RACEMTG_PAYLOAD.copy()
251
+ payload["variables"] = payload["variables"].copy()
252
+ payload["variables"]["date"] = date
253
+ payload["variables"]["venueCode"] = venue_code
254
+
255
+ headers = {
256
+ "Origin": "https://bet.hkjc.com",
257
+ "Referer": "https://bet.hkjc.com",
258
+ "Content-Type": "application/json",
259
+ "Accept": "application/json",
260
+ "User-Agent": "python-hkjc-fetch/0.1",
261
+ }
262
+
263
+ r = requests.post(HKJC_LIVEODDS_ENDPOINT, json=payload,
264
+ headers=headers, timeout=10)
265
+ if r.status_code != 200:
266
+ raise RuntimeError(f"Request failed: {r.status_code} - {r.text}")
267
+
268
+ races = r.json()['data']['raceMeetings'][0]['races']
269
+
270
+ race_info = {}
271
+ for race in races:
272
+ race_num = race['no']
273
+ race_name = race['raceName_en']
274
+ race_dist = race['distance']
275
+ race_going = race['go_en']
276
+ race_track = race['raceTrack']['description_en']
277
+ race_class = race['raceClass_en']
278
+ race_course = race['raceCourse']['displayCode']
279
+
280
+ runners = [{'Dr': runner['barrierDrawNumber'],
281
+ 'Rtg' : int(runner['currentRating']),
282
+ 'Wt' : int(runner['currentWeight']),
283
+ 'HorseNo': runner['horse']['code']
284
+ } for runner in race['runners']]
285
+ race_info[race_num]={
286
+ 'No': race_num,
287
+ 'Name': race_name,
288
+ 'Class': race_class,
289
+ 'Course': race_course,
290
+ 'Dist': race_dist,
291
+ 'Going': race_going,
292
+ 'Track': race_track,
293
+ 'Runners': runners
294
+ }
295
+ return race_info
296
+
297
+
298
+ @ttl_cache(maxsize=12, ttl=30)
299
+ def _fetch_live_odds(date: str, venue_code: str, race_number: int, odds_type: Tuple[str] = ('PLA', 'QPL')) -> List[dict]:
300
+ """Fetch live odds data from HKJC GraphQL endpoint."""
301
+ payload = LIVEODDS_PAYLOAD.copy()
302
+ payload["variables"] = payload["variables"].copy()
303
+ payload["variables"]["date"] = date
304
+ payload["variables"]["venueCode"] = venue_code
305
+ payload["variables"]["raceNo"] = race_number
306
+ payload["variables"]["oddsTypes"] = odds_type
307
+
308
+ headers = {
309
+ "Origin": "https://bet.hkjc.com",
310
+ "Referer": "https://bet.hkjc.com",
311
+ "Content-Type": "application/json",
312
+ "Accept": "application/json",
313
+ "User-Agent": "python-hkjc-fetch/0.1",
314
+ }
315
+
316
+ r = requests.post(HKJC_LIVEODDS_ENDPOINT, json=payload,
317
+ headers=headers, timeout=10)
318
+ if r.status_code != 200:
319
+ raise RuntimeError(f"Request failed: {r.status_code} - {r.text}")
320
+
321
+ meetings = r.json().get("data", {}).get("raceMeetings", [])
322
+
323
+ return [
324
+ {"HorseID": node["combString"], "Type": pool.get(
325
+ "oddsType"), "Odds": float(node["oddsValue"])}
326
+ for meeting in meetings
327
+ for pool in meeting.get("pmPools", [])
328
+ for node in pool.get("oddsNodes", [])
329
+ ]
330
+
331
+
332
+ def live_odds(date: str, venue_code: str, race_number: int, odds_type: List[str] = ['PLA', 'QPL']) -> dict:
333
+ """Fetch live odds as numpy arrays.
334
+
335
+ Args:
336
+ date (str): Date in 'YYYY-MM-DD' format.
337
+ venue_code (str): Venue code, e.g., 'ST' for Shatin, 'HV' for Happy Valley.
338
+ race_number (int): Race number.
339
+ odds_type (List[str]): Types of odds to fetch. Default is ['PLA', 'QPL']. Currently the following types are supported:
340
+ - 'WIN': Win odds
341
+ - 'PLA': Place odds
342
+ - 'QIN': Quinella odds
343
+ - 'QPL': Quinella Place odds
344
+ fit_harville (bool): Whether to fit the odds using Harville model. Default is False.
345
+
346
+ Returns:
347
+ dict: Dictionary with keys as odds types and values as numpy arrays containing the odds.
348
+ If odds_type is 'WIN','PLA', returns a 1D array of place odds.
349
+ If odds_type is 'QIN','QPL', returns a 2D array of quinella place odds.
350
+ """
351
+ _validate_date(date)
352
+ _validate_venue_code(venue_code)
353
+
354
+ race_info = _fetch_live_races(date, venue_code)
355
+ N = len(race_info[race_number]['Runners'])
356
+
357
+ data = _fetch_live_odds(date, venue_code, race_number,
358
+ odds_type=tuple(odds_type))
359
+
360
+ odds = {'WIN': np.full(N, np.nan, dtype=float),
361
+ 'PLA': np.full(N, np.nan, dtype=float),
362
+ 'QIN': np.full((N, N), np.nan, dtype=float),
363
+ 'QPL': np.full((N, N), np.nan, dtype=float)}
364
+
365
+ for entry in data:
366
+ if entry["Type"] in ["QIN", "QPL"]:
367
+ horse_ids = list(map(int, entry["HorseID"].split(",")))
368
+ odds[entry["Type"]][horse_ids[0] - 1,
369
+ horse_ids[1] - 1] = entry["Odds"]
370
+ odds[entry["Type"]][horse_ids[1] - 1,
371
+ horse_ids[0] - 1] = entry["Odds"]
372
+ elif entry["Type"] in ["PLA", "WIN"]:
373
+ odds[entry["Type"]][int(entry["HorseID"]) - 1] = entry["Odds"]
374
+
375
+ return {t: odds[t] for t in odds_type}
hkjc/processing.py CHANGED
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
  from typing import Tuple, List, Union
5
5
 
6
- from .live_odds import live_odds
6
+ from .live import live_odds
7
7
  from .strategy import qpbanker, place_only
8
8
  from .harville_model import fit_harville_to_odds
9
9
  from .historical import _extract_horse_data, _extract_race_data, _clean_horse_data
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hkjc
3
- Version: 0.3.17
3
+ Version: 0.3.18
4
4
  Summary: Library for scrapping HKJC data and perform basic analysis
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: beautifulsoup4>=4.14.2
7
7
  Requires-Dist: cachetools>=6.2.0
8
8
  Requires-Dist: fastexcel>=0.16.0
9
- Requires-Dist: joblib>=1.5.2
9
+ Requires-Dist: flask>=3.1.2
10
10
  Requires-Dist: numba>=0.62.1
11
11
  Requires-Dist: numpy>=2.3.3
12
12
  Requires-Dist: polars>=1.33.1
@@ -1,14 +1,14 @@
1
- hkjc/__init__.py,sha256=TI7PVhmoWSvYX-xdTEdaT3jfY99LiYQFRQZaIwBhJd8,785
1
+ hkjc/__init__.py,sha256=5A9MzcITYJDcA2UbIBpkimZBYSqS4pgRuQJhTagOfpE,753
2
2
  hkjc/analysis.py,sha256=0042_NMIkQCl0J6B0P4TFfrBDCnm2B6jsCZKOEO30yI,108
3
3
  hkjc/harville_model.py,sha256=MZjPLS-1nbEhp1d4Syuq13DtraKnd7TlNqBmOOCwxgc,15976
4
- hkjc/historical.py,sha256=yQQAx8vlr2EqcPazpYp1x2ku7dy3imQoDWImHCRv1QA,8330
5
- hkjc/live_odds.py,sha256=G4ELBBp1d2prxye9kKzu2pwtS4vSfRPOmEuT7-Nd-3A,4741
6
- hkjc/processing.py,sha256=xrvEUgu_jz8ZxevOsRsYz0T7pWyNtSCMI6LUYByOLOw,6812
4
+ hkjc/historical.py,sha256=v9k_R47Na5en5ftrocjIHofkNAUthE_lp4CyLaCTsQE,8280
5
+ hkjc/live.py,sha256=GqctH-BVdIL6Vi1g8XHe3p8fZBopCQf5KACLAR0meP0,10249
6
+ hkjc/processing.py,sha256=H0chtW_FBMMhK3IzcjYjrryd3fAPYimanc2fWuGiB0M,6807
7
7
  hkjc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  hkjc/speedpro.py,sha256=Y2Z3GYGeePc4sM-ZnCHXCI1N7L-_j9nrMqS3CC5BBSo,2031
9
9
  hkjc/utils.py,sha256=4CA_FPf_U3GvzoLkqBX0qDPZgrSvKJKvbP7VWqd5FiA,6323
10
10
  hkjc/strategy/place_only.py,sha256=lHPjTSj8PzghxncNBg8FI4T4HJigekB9a3bV7l7VtPA,2079
11
11
  hkjc/strategy/qpbanker.py,sha256=MQxjwsfhllKZroKS8w8Q3bi3HMjGc1DAyBIjNZAp3yQ,4805
12
- hkjc-0.3.17.dist-info/METADATA,sha256=gKSkXKYo_HCg2S4ZeAjnqZniWV0V2kGpRH_g25K9Rmo,481
13
- hkjc-0.3.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- hkjc-0.3.17.dist-info/RECORD,,
12
+ hkjc-0.3.18.dist-info/METADATA,sha256=aoXp6Fvn3EkuXyv6p5LClSbZa5XS_bfcUxMKBJXcNvw,480
13
+ hkjc-0.3.18.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ hkjc-0.3.18.dist-info/RECORD,,
hkjc/live_odds.py DELETED
@@ -1,136 +0,0 @@
1
- """Functions to fetch and process data from HKJC
2
- """
3
- from __future__ import annotations
4
- from typing import Tuple, List
5
-
6
- import requests
7
- from cachetools.func import ttl_cache
8
- import numpy as np
9
-
10
- from .utils import _validate_date, _validate_venue_code
11
-
12
- HKJC_LIVEODDS_ENDPOINT = "https://info.cld.hkjc.com/graphql/base/"
13
-
14
- LIVEODDS_PAYLOAD = {
15
- "operationName": "racing",
16
- "variables": {"date": None, "venueCode": None, "raceNo": None, "oddsTypes": None},
17
- "query": """
18
- query racing($date: String, $venueCode: String, $oddsTypes: [OddsType], $raceNo: Int) {
19
- raceMeetings(date: $date, venueCode: $venueCode) {
20
- pmPools(oddsTypes: $oddsTypes, raceNo: $raceNo) {
21
- id
22
- status
23
- sellStatus
24
- oddsType
25
- lastUpdateTime
26
- guarantee
27
- minTicketCost
28
- name_en
29
- name_ch
30
- leg {
31
- number
32
- races
33
- }
34
- cWinSelections {
35
- composite
36
- name_ch
37
- name_en
38
- starters
39
- }
40
- oddsNodes {
41
- combString
42
- oddsValue
43
- hotFavourite
44
- oddsDropValue
45
- bankerOdds {
46
- combString
47
- oddsValue
48
- }
49
- }
50
- }
51
- }
52
- }""",
53
- }
54
-
55
-
56
- @ttl_cache(maxsize=12, ttl=30)
57
- def _fetch_live_odds(date: str, venue_code: str, race_number: int, odds_type: Tuple[str] = ('PLA', 'QPL')) -> Tuple[dict]:
58
- """Fetch live odds data from HKJC GraphQL endpoint."""
59
- payload = LIVEODDS_PAYLOAD.copy()
60
- payload["variables"] = payload["variables"].copy()
61
- payload["variables"]["date"] = date
62
- payload["variables"]["venueCode"] = venue_code
63
- payload["variables"]["raceNo"] = race_number
64
- payload["variables"]["oddsTypes"] = odds_type
65
-
66
- headers = {
67
- "Origin": "https://bet.hkjc.com",
68
- "Referer": "https://bet.hkjc.com",
69
- "Content-Type": "application/json",
70
- "Accept": "application/json",
71
- "User-Agent": "python-hkjc-fetch/0.1",
72
- }
73
-
74
- r = requests.post(HKJC_LIVEODDS_ENDPOINT, json=payload,
75
- headers=headers, timeout=10)
76
- if r.status_code != 200:
77
- raise RuntimeError(f"Request failed: {r.status_code} - {r.text}")
78
-
79
- meetings = r.json().get("data", {}).get("raceMeetings", [])
80
-
81
- return [
82
- {"HorseID": node["combString"], "Type": pool.get(
83
- "oddsType"), "Odds": float(node["oddsValue"])}
84
- for meeting in meetings
85
- for pool in meeting.get("pmPools", [])
86
- for node in pool.get("oddsNodes", [])
87
- ]
88
-
89
-
90
- def live_odds(date: str, venue_code: str, race_number: int, odds_type: List[str] = ['PLA', 'QPL']) -> dict:
91
- """Fetch live odds as numpy arrays.
92
-
93
- Args:
94
- date (str): Date in 'YYYY-MM-DD' format.
95
- venue_code (str): Venue code, e.g., 'ST' for Shatin, 'HV' for Happy Valley.
96
- race_number (int): Race number.
97
- odds_type (List[str]): Types of odds to fetch. Default is ['PLA', 'QPL']. Currently the following types are supported:
98
- - 'WIN': Win odds
99
- - 'PLA': Place odds
100
- - 'QIN': Quinella odds
101
- - 'QPL': Quinella Place odds
102
- fit_harville (bool): Whether to fit the odds using Harville model. Default is False.
103
-
104
- Returns:
105
- dict: Dictionary with keys as odds types and values as numpy arrays containing the odds.
106
- If odds_type is 'WIN','PLA', returns a 1D array of place odds.
107
- If odds_type is 'QIN','QPL', returns a 2D array of quinella place odds.
108
- """
109
- _validate_date(date)
110
- _validate_venue_code(venue_code)
111
-
112
- mandatory_types = ['PLA']
113
-
114
- data = _fetch_live_odds(date, venue_code, race_number,
115
- odds_type=tuple(set(mandatory_types+odds_type)))
116
-
117
- # use place odds to determine number of horses
118
- pla_data = [entry for entry in data if entry["Type"] == "PLA"]
119
- N = len(pla_data)
120
-
121
- odds = {'WIN': np.full(N, np.nan, dtype=float),
122
- 'PLA': np.full(N, np.nan, dtype=float),
123
- 'QIN': np.full((N, N), np.nan, dtype=float),
124
- 'QPL': np.full((N, N), np.nan, dtype=float)}
125
-
126
- for entry in data:
127
- if entry["Type"] in ["QIN", "QPL"]:
128
- horse_ids = list(map(int, entry["HorseID"].split(",")))
129
- odds[entry["Type"]][horse_ids[0] - 1,
130
- horse_ids[1] - 1] = entry["Odds"]
131
- odds[entry["Type"]][horse_ids[1] - 1,
132
- horse_ids[0] - 1] = entry["Odds"]
133
- elif entry["Type"] in ["PLA", "WIN"]:
134
- odds[entry["Type"]][int(entry["HorseID"]) - 1] = entry["Odds"]
135
-
136
- return {t: odds[t] for t in odds_type}
File without changes