overtime-live-trading-utils 2.1.44 → 2.1.45
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.
- package/.circleci/config.yml +32 -32
- package/.prettierrc +9 -9
- package/CLAUDE.md +84 -0
- package/codecov.yml +20 -20
- package/index.ts +26 -26
- package/jest.config.ts +16 -16
- package/main.js +1 -1
- package/package.json +30 -30
- package/resolution_live_markets.md +356 -0
- package/src/constants/common.ts +7 -7
- package/src/constants/errors.ts +6 -6
- package/src/constants/sports.ts +78 -78
- package/src/enums/sports.ts +109 -109
- package/src/tests/mock/MockLeagueMap.ts +170 -170
- package/src/tests/mock/MockOpticOddsEvents.ts +662 -662
- package/src/tests/mock/MockOpticSoccer.ts +9378 -9378
- package/src/tests/mock/MockSoccerRedis.ts +2308 -2308
- package/src/tests/unit/bookmakers.test.ts +79 -79
- package/src/tests/unit/markets.test.ts +156 -156
- package/src/tests/unit/odds.test.ts +92 -92
- package/src/tests/unit/resolution.test.ts +1489 -1489
- package/src/tests/unit/sports.test.ts +58 -58
- package/src/tests/unit/spread.test.ts +131 -131
- package/src/types/missing-types.d.ts +2 -2
- package/src/types/odds.ts +61 -61
- package/src/types/resolution.ts +660 -656
- package/src/types/sports.ts +19 -19
- package/src/utils/bookmakers.ts +159 -159
- package/src/utils/constraints.ts +210 -210
- package/src/utils/gameMatching.ts +81 -81
- package/src/utils/markets.ts +119 -119
- package/src/utils/odds.ts +918 -918
- package/src/utils/opticOdds.ts +71 -71
- package/src/utils/resolution.ts +319 -319
- package/src/utils/sportPeriodMapping.ts +36 -36
- package/src/utils/sports.ts +51 -51
- package/src/utils/spread.ts +97 -97
- package/tsconfig.json +16 -16
- package/webpack.config.js +24 -24
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "overtime-live-trading-utils",
|
|
3
|
-
"version": "2.1.
|
|
4
|
-
"description": "",
|
|
5
|
-
"main": "main.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"pack": "webpack --mode production",
|
|
8
|
-
"prepublish": "npm run pack",
|
|
9
|
-
"test": "jest",
|
|
10
|
-
"compile": "tsc"
|
|
11
|
-
},
|
|
12
|
-
"author": "",
|
|
13
|
-
"license": "MIT",
|
|
14
|
-
"dependencies": {
|
|
15
|
-
"@types/node": "^20.8.10",
|
|
16
|
-
"oddslib": "^2.1.1",
|
|
17
|
-
"overtime-utils": "^0.1.44",
|
|
18
|
-
"react": "^17.0.1"
|
|
19
|
-
},
|
|
20
|
-
"devDependencies": {
|
|
21
|
-
"@types/jest": "^29.5.14",
|
|
22
|
-
"jest": "^29.7.0",
|
|
23
|
-
"ts-jest": "^29.2.5",
|
|
24
|
-
"ts-loader": "^9.5.0",
|
|
25
|
-
"ts-node": "^10.9.2",
|
|
26
|
-
"typescript": "^4.5.5",
|
|
27
|
-
"webpack": "^5.89.0",
|
|
28
|
-
"webpack-cli": "^5.1.4"
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "overtime-live-trading-utils",
|
|
3
|
+
"version": "2.1.45",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "main.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"pack": "webpack --mode production",
|
|
8
|
+
"prepublish": "npm run pack",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"compile": "tsc"
|
|
11
|
+
},
|
|
12
|
+
"author": "",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@types/node": "^20.8.10",
|
|
16
|
+
"oddslib": "^2.1.1",
|
|
17
|
+
"overtime-utils": "^0.1.44",
|
|
18
|
+
"react": "^17.0.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/jest": "^29.5.14",
|
|
22
|
+
"jest": "^29.7.0",
|
|
23
|
+
"ts-jest": "^29.2.5",
|
|
24
|
+
"ts-loader": "^9.5.0",
|
|
25
|
+
"ts-node": "^10.9.2",
|
|
26
|
+
"typescript": "^4.5.5",
|
|
27
|
+
"webpack": "^5.89.0",
|
|
28
|
+
"webpack-cli": "^5.1.4"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Live Market Resolution - TypeId-Based Implementation Plan
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Enhance the resolution utilities to support checking if specific market types (identified by `typeId`) can be resolved based on which periods are complete in a live game.
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
The current `canResolveMarketForGameIdAndSport` function only checks if periods are complete, but doesn't answer the key question: **"Can I resolve market type 10021 (e.g., 1st Quarter Winner) right now?"**
|
|
10
|
+
|
|
11
|
+
For live trading, we need to know which specific markets can be resolved based on:
|
|
12
|
+
|
|
13
|
+
1. Which periods have been completed
|
|
14
|
+
2. The market type (typeId)
|
|
15
|
+
3. Whether the game is still live or fully completed
|
|
16
|
+
|
|
17
|
+
## Understanding TypeIds
|
|
18
|
+
|
|
19
|
+
TypeIds identify specific market types:
|
|
20
|
+
|
|
21
|
+
- `10021` - 1st period/quarter/half markets (various bet types)
|
|
22
|
+
- `10022` - 2nd period markets
|
|
23
|
+
- `10031` - 1st period totals
|
|
24
|
+
- `0`, `10001`, `10002` - Full game markets (should NOT resolve during live games)
|
|
25
|
+
|
|
26
|
+
Example from codebase:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
{
|
|
30
|
+
sportId: 9806,
|
|
31
|
+
typeId: 10022,
|
|
32
|
+
marketName: '1st Half Moneyline',
|
|
33
|
+
type: 'Moneyline',
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Solution Design
|
|
38
|
+
|
|
39
|
+
### 1. Sport-Specific Period-to-TypeId Mappings
|
|
40
|
+
|
|
41
|
+
Created three separate mappings for different sport period structures:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// Sport period structure types
|
|
45
|
+
export enum SportPeriodType {
|
|
46
|
+
HALVES_BASED = 'halves_based', // Soccer, NCAAB
|
|
47
|
+
QUARTERS_BASED = 'quarters_based', // NFL, NBA
|
|
48
|
+
INNINGS_BASED = 'innings_based', // MLB, NPB, KBO, College Baseball
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// HALVES-BASED (Soccer, NCAAB): 2 periods
|
|
52
|
+
export const HALVES_PERIOD_TYPE_ID_MAPPING = {
|
|
53
|
+
1: [10021, 10031, 10041, 10051, ...], // Period 1 = 1st half
|
|
54
|
+
2: [10022, 10032, 10042, 10052, ...], // Period 2 = 2nd half
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// QUARTERS-BASED (NFL, NBA): 4 quarters + overtime
|
|
58
|
+
export const QUARTERS_PERIOD_TYPE_ID_MAPPING = {
|
|
59
|
+
1: [10021, 10031, 10041, ...], // 1st quarter
|
|
60
|
+
2: [10022, 10032, 10042, 10051, ...], // 2nd quarter + 1st half (10051)
|
|
61
|
+
3: [10023, 10033, 10043, ...], // 3rd quarter
|
|
62
|
+
4: [10024, 10034, 10044, 10052, ...], // 4th quarter + 2nd half (10052)
|
|
63
|
+
5: [10025, 10035, 10045, ...], // Overtime
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// INNINGS-BASED (MLB, NPB, KBO): 9+ innings
|
|
67
|
+
export const INNINGS_PERIOD_TYPE_ID_MAPPING = {
|
|
68
|
+
1-4: [...], // Innings 1-4
|
|
69
|
+
5: [10025, 10035, 10045, 10051, ...], // 5th inning + 1st half (10051)
|
|
70
|
+
6-8: [...], // Innings 6-8
|
|
71
|
+
9: [10029, 10039, 10049, 10052, ...], // 9th inning + 2nd half (10052)
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Full game type IDs that should be skipped for live period resolution
|
|
75
|
+
const FULL_GAME_TYPE_IDS = [0, 10001, 10002, 10003, 10004, 10010, 10011, 10012];
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Key Insight**: TypeId 10051 (1st half) resolves at different periods depending on sport type:
|
|
79
|
+
|
|
80
|
+
- Soccer (HALVES): Period 1
|
|
81
|
+
- NFL (QUARTERS): Period 2 (after both quarters 1 & 2)
|
|
82
|
+
- MLB (INNINGS): Period 5 (after first 5 innings)
|
|
83
|
+
|
|
84
|
+
### 2. Updated Function Signature
|
|
85
|
+
|
|
86
|
+
**Before:**
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
canResolveMarketForGameIdAndSport(
|
|
90
|
+
gameId: string,
|
|
91
|
+
event: OpticOddsEvent,
|
|
92
|
+
sportName?: string
|
|
93
|
+
): boolean
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**After:**
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Single typeId overload
|
|
100
|
+
canResolveMarketsForEvent(
|
|
101
|
+
event: OpticOddsEvent,
|
|
102
|
+
typeId: number,
|
|
103
|
+
sportType: SportPeriodType, // REQUIRED
|
|
104
|
+
sportName?: string
|
|
105
|
+
): boolean
|
|
106
|
+
|
|
107
|
+
// Batch typeIds overload
|
|
108
|
+
canResolveMarketsForEvent(
|
|
109
|
+
event: OpticOddsEvent,
|
|
110
|
+
typeIds: number[],
|
|
111
|
+
sportType: SportPeriodType, // REQUIRED
|
|
112
|
+
sportName?: string
|
|
113
|
+
): number[]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 3. Implementation Logic
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// Helper function to select appropriate mapping based on sport type
|
|
120
|
+
function selectMappingForSportType(sportType: SportPeriodType) {
|
|
121
|
+
switch (sportType) {
|
|
122
|
+
case SportPeriodType.HALVES_BASED:
|
|
123
|
+
return HALVES_PERIOD_TYPE_ID_MAPPING;
|
|
124
|
+
case SportPeriodType.QUARTERS_BASED:
|
|
125
|
+
return QUARTERS_PERIOD_TYPE_ID_MAPPING;
|
|
126
|
+
case SportPeriodType.INNINGS_BASED:
|
|
127
|
+
return INNINGS_PERIOD_TYPE_ID_MAPPING;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function canResolveMarketsForEvent(
|
|
132
|
+
event: OpticOddsEvent,
|
|
133
|
+
typeIdOrTypeIds: number | number[],
|
|
134
|
+
sportType: SportPeriodType, // REQUIRED - must specify sport type
|
|
135
|
+
sportName?: string
|
|
136
|
+
): boolean | number[] {
|
|
137
|
+
// Get completed periods
|
|
138
|
+
const periodData = detectCompletedPeriods(event, sportName);
|
|
139
|
+
if (!periodData) {
|
|
140
|
+
return Array.isArray(typeIdOrTypeIds) ? [] : false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Check if game is fully completed
|
|
144
|
+
const status = (event.fixture?.status || event.status || '').toLowerCase();
|
|
145
|
+
const isCompleted = status === 'completed' || status === 'complete' || status === 'finished';
|
|
146
|
+
|
|
147
|
+
// Select appropriate mapping based on sport type
|
|
148
|
+
const mapping = selectMappingForSportType(sportType);
|
|
149
|
+
|
|
150
|
+
// Collect all resolvable typeIds based on completed periods
|
|
151
|
+
const resolvableTypeIds = new Set<number>();
|
|
152
|
+
|
|
153
|
+
for (const period of periodData.completedPeriods) {
|
|
154
|
+
const typeIdsForPeriod = mapping[period] || [];
|
|
155
|
+
typeIdsForPeriod.forEach((id) => resolvableTypeIds.add(id));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Full game typeIds can only be resolved when game is completed
|
|
159
|
+
if (isCompleted) {
|
|
160
|
+
// Could add full game typeIds to resolvable set here if needed
|
|
161
|
+
// For now, they're explicitly excluded during live games
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Single typeId check
|
|
165
|
+
if (typeId !== undefined) {
|
|
166
|
+
// Full game typeIds cannot be resolved during live games
|
|
167
|
+
if (!isCompleted && FULL_GAME_TYPE_IDS.includes(typeId)) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
return resolvableTypeIds.has(typeId);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Batch typeIds check
|
|
174
|
+
if (typeIds !== undefined) {
|
|
175
|
+
return typeIds.filter((id) => {
|
|
176
|
+
// Exclude full game typeIds during live games
|
|
177
|
+
if (!isCompleted && FULL_GAME_TYPE_IDS.includes(id)) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return resolvableTypeIds.has(id);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 4. Why Sport Parameter is Needed
|
|
189
|
+
|
|
190
|
+
Different sports have different period structures:
|
|
191
|
+
|
|
192
|
+
- **Soccer**: 2 halves (periods 1-2)
|
|
193
|
+
- **NFL/NBA**: 4 quarters (periods 1-4)
|
|
194
|
+
- **MLB**: 9+ innings (periods 1-9)
|
|
195
|
+
- **NHL**: 3 periods (periods 1-3)
|
|
196
|
+
- **Tennis**: Sets (periods 1-5)
|
|
197
|
+
|
|
198
|
+
The sport parameter allows future customization where period-to-typeId mappings could be sport-specific. For example:
|
|
199
|
+
|
|
200
|
+
- Soccer might map period 1 → "1st Half" markets
|
|
201
|
+
- Basketball might map period 1 → "1st Quarter" markets
|
|
202
|
+
- Both use period 1, but represent different market types
|
|
203
|
+
|
|
204
|
+
## Implementation Steps
|
|
205
|
+
|
|
206
|
+
### Phase 1: Add Sport-Specific Mappings ✅ COMPLETED
|
|
207
|
+
|
|
208
|
+
- [x] Add `SportPeriodType` enum to `src/types/resolution.ts`
|
|
209
|
+
- [x] Add `HALVES_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
|
|
210
|
+
- [x] Add `QUARTERS_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
|
|
211
|
+
- [x] Add `INNINGS_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
|
|
212
|
+
- [x] Add `FULL_GAME_TYPE_IDS` to `src/types/resolution.ts`
|
|
213
|
+
- [x] Export all constants and enum
|
|
214
|
+
|
|
215
|
+
### Phase 2: Update Resolution Function ✅ COMPLETED
|
|
216
|
+
|
|
217
|
+
- [x] Update `canResolveMarketsForEvent` function signature to accept `sportType` parameter
|
|
218
|
+
- [x] Implement sport-type-based mapping selection logic
|
|
219
|
+
- [x] Update function overloads for clean API (no undefined placeholders)
|
|
220
|
+
- [x] Handle full game typeIds exclusion during live games
|
|
221
|
+
- [x] Keep backward compatibility with existing functions
|
|
222
|
+
|
|
223
|
+
### Phase 3: Add Tests ✅ COMPLETED
|
|
224
|
+
|
|
225
|
+
- [x] Test single typeId resolution
|
|
226
|
+
- [x] Test batch typeIds resolution
|
|
227
|
+
- [x] Test that full game typeIds are NOT resolved during live games
|
|
228
|
+
- [x] Test with real event data (Soccer, NFL, MLB)
|
|
229
|
+
- [x] Test edge cases (no completed periods, game in overtime, etc.)
|
|
230
|
+
- [x] Add sport-specific tests for typeId 10051 (1st half) with HALVES, QUARTERS, INNINGS
|
|
231
|
+
- [x] Add sport-specific tests for typeId 10052 (2nd half)
|
|
232
|
+
- [x] Test default behavior (QUARTERS_BASED when no sportType provided)
|
|
233
|
+
|
|
234
|
+
### Phase 4: Update Exports ✅ COMPLETED
|
|
235
|
+
|
|
236
|
+
- [x] Export `SportPeriodType` enum from index.ts
|
|
237
|
+
- [x] Export `HALVES_PERIOD_TYPE_ID_MAPPING` from index.ts
|
|
238
|
+
- [x] Export `QUARTERS_PERIOD_TYPE_ID_MAPPING` from index.ts
|
|
239
|
+
- [x] Export `INNINGS_PERIOD_TYPE_ID_MAPPING` from index.ts
|
|
240
|
+
- [x] Export `FULL_GAME_TYPE_IDS` from index.ts
|
|
241
|
+
- [x] Keep existing exports for backward compatibility
|
|
242
|
+
|
|
243
|
+
### Phase 5: Testing & Validation ✅ COMPLETED
|
|
244
|
+
|
|
245
|
+
- [x] Run full test suite
|
|
246
|
+
- [x] Verify all 85 tests pass
|
|
247
|
+
- [x] Test with real OpticOdds API responses (Soccer, NFL, MLB)
|
|
248
|
+
- [x] Verify TypeScript compilation succeeds
|
|
249
|
+
|
|
250
|
+
## Example Usage
|
|
251
|
+
|
|
252
|
+
### Single TypeId Check
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { canResolveMarketsForEvent, SportPeriodType } from 'overtime-live-trading-utils';
|
|
256
|
+
|
|
257
|
+
// Check NFL (quarters-based) - Can we resolve "1st Quarter Winner" market (typeId 10021)?
|
|
258
|
+
const canResolve = canResolveMarketsForEvent(nflEvent, 10021, SportPeriodType.QUARTERS_BASED);
|
|
259
|
+
|
|
260
|
+
if (canResolve) {
|
|
261
|
+
// Resolve the 1st quarter market
|
|
262
|
+
resolveMarket(10021);
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Sport-Specific TypeId 10051 (1st Half) Resolution
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
// Soccer (halves-based): Resolves after period 1
|
|
270
|
+
const soccerCanResolve = canResolveMarketsForEvent(soccerEvent, 10051, SportPeriodType.HALVES_BASED); // true if period 1 complete
|
|
271
|
+
|
|
272
|
+
// NFL (quarters-based): Resolves after period 2
|
|
273
|
+
const nflCanResolve = canResolveMarketsForEvent(nflEvent, 10051, SportPeriodType.QUARTERS_BASED); // true if period 2 complete
|
|
274
|
+
|
|
275
|
+
// MLB (innings-based): Resolves after period 5
|
|
276
|
+
const mlbCanResolve = canResolveMarketsForEvent(mlbEvent, 10051, SportPeriodType.INNINGS_BASED); // true if period 5 complete
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Batch TypeIds Check
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
// Which of these markets can we resolve right now for an NFL game?
|
|
283
|
+
const marketTypeIds = [10021, 10022, 10031, 10001];
|
|
284
|
+
const resolvableMarkets = canResolveMarketsForEvent(nflEvent, marketTypeIds, SportPeriodType.QUARTERS_BASED);
|
|
285
|
+
|
|
286
|
+
// Returns: [10021, 10031] if only period 1 is complete
|
|
287
|
+
// Full game typeId 10001 is excluded during live games
|
|
288
|
+
resolvableMarkets.forEach((typeId) => resolveMarket(typeId));
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Function Overloads (TypeScript)
|
|
292
|
+
|
|
293
|
+
The function uses TypeScript overloads for clean API:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// Single typeId → returns boolean
|
|
297
|
+
function canResolveMarketsForEvent(
|
|
298
|
+
event: OpticOddsEvent,
|
|
299
|
+
typeId: number,
|
|
300
|
+
sportType: SportPeriodType, // REQUIRED
|
|
301
|
+
sportName?: string
|
|
302
|
+
): boolean;
|
|
303
|
+
|
|
304
|
+
// Batch typeIds → returns number[]
|
|
305
|
+
function canResolveMarketsForEvent(
|
|
306
|
+
event: OpticOddsEvent,
|
|
307
|
+
typeIds: number[],
|
|
308
|
+
sportType: SportPeriodType, // REQUIRED
|
|
309
|
+
sportName?: string
|
|
310
|
+
): number[];
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Testing Strategy
|
|
314
|
+
|
|
315
|
+
### Test Cases
|
|
316
|
+
|
|
317
|
+
1. **Period 1 Complete (Live Soccer 2nd Half)**
|
|
318
|
+
|
|
319
|
+
- Input: Soccer event in 2nd half, period 1 complete
|
|
320
|
+
- TypeIds: [10021, 10022, 10001]
|
|
321
|
+
- Expected: [10021] (only 1st half markets resolvable)
|
|
322
|
+
|
|
323
|
+
2. **Periods 1-4 Complete (NFL Overtime)**
|
|
324
|
+
|
|
325
|
+
- Input: NFL event in overtime, all 4 quarters complete
|
|
326
|
+
- TypeIds: [10021, 10022, 10023, 10024, 10001]
|
|
327
|
+
- Expected: [10021, 10022, 10023, 10024] (all quarter markets, but not full game)
|
|
328
|
+
|
|
329
|
+
3. **Game Completed**
|
|
330
|
+
|
|
331
|
+
- Input: Completed game with all periods
|
|
332
|
+
- TypeIds: [10021, 10001]
|
|
333
|
+
- Expected: [10021, 10001] (all markets including full game)
|
|
334
|
+
|
|
335
|
+
4. **No Periods Complete**
|
|
336
|
+
- Input: Live game in 1st period
|
|
337
|
+
- TypeIds: [10021, 10022]
|
|
338
|
+
- Expected: [] (no markets resolvable yet)
|
|
339
|
+
|
|
340
|
+
## Benefits
|
|
341
|
+
|
|
342
|
+
1. **Centralized Logic**: Single source of truth for market resolution rules
|
|
343
|
+
2. **Sport-Agnostic**: Works across all sports (Soccer, NFL, NBA, MLB, NHL, Tennis, etc.)
|
|
344
|
+
3. **Flexible**: Supports both single and batch typeId checks
|
|
345
|
+
4. **Safe**: Prevents resolving full game markets during live games
|
|
346
|
+
5. **Efficient**: O(1) lookup for period→typeId mapping
|
|
347
|
+
6. **Maintainable**: Easy to add new typeIds or modify mappings
|
|
348
|
+
7. **Testable**: Clear input/output for comprehensive testing
|
|
349
|
+
|
|
350
|
+
## Future Enhancements
|
|
351
|
+
|
|
352
|
+
1. **Sport-Specific Mappings**: Different PERIOD_TYPE_ID_MAPPING per sport if needed
|
|
353
|
+
2. **Dynamic Mapping**: Load period→typeId mappings from database/config
|
|
354
|
+
3. **Market Type Validation**: Verify typeIds exist before checking resolution
|
|
355
|
+
4. **Resolution Metadata**: Return additional info (which period completed, when, scores)
|
|
356
|
+
5. **Caching**: Cache resolvable typeIds per event to avoid recalculation
|
package/src/constants/common.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const ZERO = 0;
|
|
2
|
-
export const DRAW = 'Draw';
|
|
3
|
-
export const MONEYLINE_TYPE_ID = 0;
|
|
4
|
-
export const MIN_ODDS_FOR_DIFF_CHECKING = 0.2;
|
|
5
|
-
export const TAG_CHILD_SPREAD = 10001;
|
|
6
|
-
export const TAG_CHILD_TOTALS = 10002;
|
|
7
|
-
export const MULTIPLIER_100 = 100;
|
|
1
|
+
export const ZERO = 0;
|
|
2
|
+
export const DRAW = 'Draw';
|
|
3
|
+
export const MONEYLINE_TYPE_ID = 0;
|
|
4
|
+
export const MIN_ODDS_FOR_DIFF_CHECKING = 0.2;
|
|
5
|
+
export const TAG_CHILD_SPREAD = 10001;
|
|
6
|
+
export const TAG_CHILD_TOTALS = 10002;
|
|
7
|
+
export const MULTIPLIER_100 = 100;
|
package/src/constants/errors.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export const ZERO_ODDS_MESSAGE = 'Returning zero odds cause bookmakers have 0 or 1 odds';
|
|
2
|
-
export const ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER = 'Returning zero odds cause bookmaker have 0 or 1 odds';
|
|
3
|
-
export const DIFF_BETWEEN_BOOKMAKERS_MESSAGE = 'Returning zero odds due to percentage difference between bookmakers';
|
|
4
|
-
export const NO_MATCHING_BOOKMAKERS_MESSAGE = 'Returning zero odds cause no matching bookmakers have been found';
|
|
5
|
-
export const ZERO_ODDS_AFTER_SPREAD_ADJUSTMENT = 'Bad odds after spread adjustment';
|
|
6
|
-
export const NO_MARKETS_FOR_LEAGUE_ID = 'No child markets for leagueID';
|
|
1
|
+
export const ZERO_ODDS_MESSAGE = 'Returning zero odds cause bookmakers have 0 or 1 odds';
|
|
2
|
+
export const ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER = 'Returning zero odds cause bookmaker have 0 or 1 odds';
|
|
3
|
+
export const DIFF_BETWEEN_BOOKMAKERS_MESSAGE = 'Returning zero odds due to percentage difference between bookmakers';
|
|
4
|
+
export const NO_MATCHING_BOOKMAKERS_MESSAGE = 'Returning zero odds cause no matching bookmakers have been found';
|
|
5
|
+
export const ZERO_ODDS_AFTER_SPREAD_ADJUSTMENT = 'Bad odds after spread adjustment';
|
|
6
|
+
export const NO_MARKETS_FOR_LEAGUE_ID = 'No child markets for leagueID';
|
package/src/constants/sports.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
|
-
import { League } from 'overtime-utils';
|
|
2
|
-
import { TennisSubLeague } from '../enums/sports';
|
|
3
|
-
|
|
4
|
-
export const LEAGUES_NO_FORMAL_HOME_AWAY = [
|
|
5
|
-
League.CSGO,
|
|
6
|
-
League.DOTA2,
|
|
7
|
-
League.LOL,
|
|
8
|
-
League.VALORANT,
|
|
9
|
-
League.ROCKET_LEAGUE,
|
|
10
|
-
League.CALL_OF_DUTY,
|
|
11
|
-
League.STARCRAFT,
|
|
12
|
-
League.TENNIS_GS,
|
|
13
|
-
League.TENNIS_MASTERS,
|
|
14
|
-
League.SUMMER_OLYMPICS_TENNIS,
|
|
15
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_0,
|
|
16
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_1,
|
|
17
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_2,
|
|
18
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_3,
|
|
19
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_4,
|
|
20
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_5,
|
|
21
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_6,
|
|
22
|
-
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_7,
|
|
23
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_0,
|
|
24
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_1,
|
|
25
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_2,
|
|
26
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_3,
|
|
27
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_4,
|
|
28
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_5,
|
|
29
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_6,
|
|
30
|
-
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_7,
|
|
31
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_1,
|
|
32
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_2,
|
|
33
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_3,
|
|
34
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_4,
|
|
35
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_5,
|
|
36
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_6,
|
|
37
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_7,
|
|
38
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_8,
|
|
39
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_9,
|
|
40
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_10,
|
|
41
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_11,
|
|
42
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_12,
|
|
43
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_13,
|
|
44
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_14,
|
|
45
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_15,
|
|
46
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_16,
|
|
47
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_17,
|
|
48
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_18,
|
|
49
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_19,
|
|
50
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_20,
|
|
51
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_21,
|
|
52
|
-
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_22,
|
|
53
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_1,
|
|
54
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_2,
|
|
55
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_3,
|
|
56
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_4,
|
|
57
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_5,
|
|
58
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_6,
|
|
59
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_7,
|
|
60
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_8,
|
|
61
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_9,
|
|
62
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_10,
|
|
63
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_11,
|
|
64
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_12,
|
|
65
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_13,
|
|
66
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_14,
|
|
67
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_15,
|
|
68
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_16,
|
|
69
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_17,
|
|
70
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_18,
|
|
71
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_19,
|
|
72
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_20,
|
|
73
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_21,
|
|
74
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_22,
|
|
75
|
-
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_23,
|
|
76
|
-
];
|
|
77
|
-
|
|
78
|
-
export const UFC_LEAGUE_IDS = [701, 702, 703, 704];
|
|
1
|
+
import { League } from 'overtime-utils';
|
|
2
|
+
import { TennisSubLeague } from '../enums/sports';
|
|
3
|
+
|
|
4
|
+
export const LEAGUES_NO_FORMAL_HOME_AWAY = [
|
|
5
|
+
League.CSGO,
|
|
6
|
+
League.DOTA2,
|
|
7
|
+
League.LOL,
|
|
8
|
+
League.VALORANT,
|
|
9
|
+
League.ROCKET_LEAGUE,
|
|
10
|
+
League.CALL_OF_DUTY,
|
|
11
|
+
League.STARCRAFT,
|
|
12
|
+
League.TENNIS_GS,
|
|
13
|
+
League.TENNIS_MASTERS,
|
|
14
|
+
League.SUMMER_OLYMPICS_TENNIS,
|
|
15
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_0,
|
|
16
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_1,
|
|
17
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_2,
|
|
18
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_3,
|
|
19
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_4,
|
|
20
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_5,
|
|
21
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_6,
|
|
22
|
+
TennisSubLeague.TENNIS_ATP_GRAND_SLAM_LIVE_MAPPING_V2_7,
|
|
23
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_0,
|
|
24
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_1,
|
|
25
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_2,
|
|
26
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_3,
|
|
27
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_4,
|
|
28
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_5,
|
|
29
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_6,
|
|
30
|
+
TennisSubLeague.TENNIS_WTA_GRAND_SLAM_LIVE_MAPPING_V2_7,
|
|
31
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_1,
|
|
32
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_2,
|
|
33
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_3,
|
|
34
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_4,
|
|
35
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_5,
|
|
36
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_6,
|
|
37
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_7,
|
|
38
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_8,
|
|
39
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_9,
|
|
40
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_10,
|
|
41
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_11,
|
|
42
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_12,
|
|
43
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_13,
|
|
44
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_14,
|
|
45
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_15,
|
|
46
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_16,
|
|
47
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_17,
|
|
48
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_18,
|
|
49
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_19,
|
|
50
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_20,
|
|
51
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_21,
|
|
52
|
+
TennisSubLeague.TENNIS_MASTERS_LIVE_MAPPING_V2_22,
|
|
53
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_1,
|
|
54
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_2,
|
|
55
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_3,
|
|
56
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_4,
|
|
57
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_5,
|
|
58
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_6,
|
|
59
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_7,
|
|
60
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_8,
|
|
61
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_9,
|
|
62
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_10,
|
|
63
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_11,
|
|
64
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_12,
|
|
65
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_13,
|
|
66
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_14,
|
|
67
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_15,
|
|
68
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_16,
|
|
69
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_17,
|
|
70
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_18,
|
|
71
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_19,
|
|
72
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_20,
|
|
73
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_21,
|
|
74
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_22,
|
|
75
|
+
TennisSubLeague.TENNIS_WTA_LIVE_MAPPING_V2_23,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
export const UFC_LEAGUE_IDS = [701, 702, 703, 704];
|