hkjc 0.3.19__tar.gz → 0.3.22__tar.gz

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.
@@ -1,3 +1,4 @@
1
1
  .venv/
2
2
  __pycache__/
3
- *.pyc
3
+ *.pyc
4
+ *.parquet
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hkjc
3
- Version: 0.3.19
3
+ Version: 0.3.22
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
@@ -0,0 +1,117 @@
1
+ from flask import Flask, jsonify, render_template, request
2
+
3
+ import polars as pl
4
+ import numpy as np
5
+
6
+ from hkjc.live import live_odds, _fetch_live_races
7
+ from hkjc.harville_model import fit_harville_to_odds
8
+ from hkjc.historical import get_horse_data
9
+ from hkjc.speedpro import speedmap, speedpro_energy
10
+
11
+
12
+ def arr_to_dict(arr: np.ndarray, dtype=float):
13
+ """Convert 0-indexed numpy array into 1-indexed nested dictionary
14
+
15
+ Args:
16
+ arr (np.ndarray): 0-indexed numpy array
17
+ dtype (type, optional): data type. Defaults to float.
18
+
19
+ Returns:
20
+ dict: 1-indexed nested dictionary
21
+ """
22
+ if arr.ndim == 1:
23
+ return {i+1: dtype(v) for i, v in enumerate(arr) if not (np.isnan(v) or np.isinf(v))}
24
+
25
+ return {i+1: arr_to_dict(v) for i, v in enumerate(arr)}
26
+
27
+
28
+ app = Flask(__name__)
29
+
30
+
31
+ @app.route("/")
32
+ def disp_race_info():
33
+ race_info = _fetch_live_races('', '')
34
+
35
+ try:
36
+ df_speedpro = speedpro_energy(race_info['Date'])
37
+ for race_num, race in race_info['Races'].items():
38
+ for i, runner in enumerate(race['Runners']):
39
+ df = (df_speedpro
40
+ .filter(pl.col('RaceNo') == race_num)
41
+ .filter(pl.col('RunnerNumber') == int(runner['No']))
42
+ )
43
+ race_info['Races'][race_num]['Runners'][i]['SPEnergy'] = df['SpeedPRO_Energy_Difference'].item(
44
+ 0)
45
+ race_info['Races'][race_num]['Runners'][i]['Fitness'] = df['FitnessRatings'].item(
46
+ 0)
47
+ except: # fill with dummy value if SpeedPro not available
48
+ for race_num, race in race_info['Races'].items():
49
+ for i, runner in enumerate(race['Runners']):
50
+ race_info['Races'][race_num]['Runners'][i]['SPEnergy'] = 0
51
+ race_info['Races'][race_num]['Runners'][i]['Fitness'] = 0
52
+
53
+ return render_template('index.html',
54
+ race_info=race_info)
55
+
56
+
57
+ turf_going_dict = {'FIRM': 'F',
58
+ 'GOOD TO FIRM': 'GF',
59
+ 'GOOD': 'G',
60
+ 'GOOD TO YIELDING': 'GY',
61
+ 'YIELDING': 'Y',
62
+ 'YIELDING TO SOFT': 'YS',
63
+ 'SOFT': 'S',
64
+ 'HEAVY': 'H'}
65
+ aw_going_dict = {'WET FAST': 'WF',
66
+ 'FAST': 'FT',
67
+ 'GOOD': 'GD',
68
+ 'SLOW': 'SL',
69
+ 'WET SLOW': 'WS',
70
+ 'RAIN AFFECTED': 'RA',
71
+ 'NORMAL WATERING': 'NW'}
72
+ going_dict = {'TURF': turf_going_dict, 'ALL WEATHER TRACK': aw_going_dict}
73
+
74
+
75
+ @app.route("/horse_info/<horse_no>", methods=['GET'])
76
+ def disp_horse_info(horse_no):
77
+ # read optional filters
78
+ dist = request.args.get('dist', type=int)
79
+ track = request.args.get('track')
80
+ going = request.args.get('going')
81
+
82
+ if track not in going_dict.keys():
83
+ track = None
84
+ if (going is not None) and (track is not None) and (going in going_dict[track].keys()):
85
+ going = going_dict[track][going] # translate going to code
86
+ else:
87
+ going = None
88
+
89
+ df = get_horse_data(horse_no)
90
+ if dist is not None:
91
+ df = df.filter(pl.col('Dist') == dist)
92
+ if track and track.upper() == 'TURF':
93
+ df = df.filter(pl.col('Track') == 'Turf')
94
+ elif track and track.upper() == 'ALL WEATHER TRACK':
95
+ df = df.filter(pl.col('Track') == 'AWT')
96
+ if going is not None:
97
+ df = df.filter(pl.col('G') == going)
98
+
99
+ return render_template('horse-info.html', df=df)
100
+
101
+
102
+ @app.route('/live_odds/<race_no>')
103
+ def disp_live_odds(race_no=1):
104
+ odds_dict = live_odds('', '', int(race_no))
105
+ fitted_odds = fit_harville_to_odds(odds_dict)['odds_fit']
106
+ odds_json = {'Raw': {k: arr_to_dict(v) for k, v in odds_dict.items()},
107
+ 'Fit': {k: arr_to_dict(v) for k, v in fitted_odds.items()}
108
+ }
109
+
110
+ return jsonify(odds_json)
111
+
112
+
113
+ @app.route('/speedmap/<race_no>')
114
+ def disp_speedmap(race_no=1):
115
+ return speedmap(int(race_no))
116
+
117
+ # TODO: trades