overtime-live-trading-utils 2.1.34 → 2.1.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overtime-live-trading-utils",
3
- "version": "2.1.34",
3
+ "version": "2.1.35",
4
4
  "description": "",
5
5
  "main": "main.js",
6
6
  "scripts": {
@@ -1,12 +1,15 @@
1
1
  # Live Market Resolution - TypeId-Based Implementation Plan
2
2
 
3
3
  ## Overview
4
+
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.
5
6
 
6
7
  ## Problem Statement
8
+
7
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?"**
8
10
 
9
11
  For live trading, we need to know which specific markets can be resolved based on:
12
+
10
13
  1. Which periods have been completed
11
14
  2. The market type (typeId)
12
15
  3. Whether the game is still live or fully completed
@@ -14,12 +17,14 @@ For live trading, we need to know which specific markets can be resolved based o
14
17
  ## Understanding TypeIds
15
18
 
16
19
  TypeIds identify specific market types:
17
- - `10021` - 1st period/quarter/half markets (various bet types)
18
- - `10022` - 2nd period markets
19
- - `10031` - 1st period totals
20
- - `0`, `10001`, `10002` - Full game markets (should NOT resolve during live games)
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)
21
25
 
22
26
  Example from codebase:
27
+
23
28
  ```typescript
24
29
  {
25
30
  sportId: 9806,
@@ -32,6 +37,7 @@ Example from codebase:
32
37
  ## Solution Design
33
38
 
34
39
  ### 1. Sport-Specific Period-to-TypeId Mappings
40
+
35
41
  Created three separate mappings for different sport period structures:
36
42
 
37
43
  ```typescript
@@ -70,13 +76,15 @@ const FULL_GAME_TYPE_IDS = [0, 10001, 10002, 10003, 10004, 10010, 10011, 10012];
70
76
  ```
71
77
 
72
78
  **Key Insight**: TypeId 10051 (1st half) resolves at different periods depending on sport type:
73
- - Soccer (HALVES): Period 1
74
- - NFL (QUARTERS): Period 2 (after both quarters 1 & 2)
75
- - MLB (INNINGS): Period 5 (after first 5 innings)
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)
76
83
 
77
84
  ### 2. Updated Function Signature
78
85
 
79
86
  **Before:**
87
+
80
88
  ```typescript
81
89
  canResolveMarketForGameIdAndSport(
82
90
  gameId: string,
@@ -86,6 +94,7 @@ canResolveMarketForGameIdAndSport(
86
94
  ```
87
95
 
88
96
  **After:**
97
+
89
98
  ```typescript
90
99
  // Single typeId overload
91
100
  canResolveMarketsForEvent(
@@ -122,7 +131,7 @@ function selectMappingForSportType(sportType: SportPeriodType) {
122
131
  export function canResolveMarketsForEvent(
123
132
  event: OpticOddsEvent,
124
133
  typeIdOrTypeIds: number | number[],
125
- sportType: SportPeriodType, // REQUIRED - must specify sport type
134
+ sportType: SportPeriodType, // REQUIRED - must specify sport type
126
135
  sportName?: string
127
136
  ): boolean | number[] {
128
137
  // Get completed periods
@@ -143,7 +152,7 @@ export function canResolveMarketsForEvent(
143
152
 
144
153
  for (const period of periodData.completedPeriods) {
145
154
  const typeIdsForPeriod = mapping[period] || [];
146
- typeIdsForPeriod.forEach(id => resolvableTypeIds.add(id));
155
+ typeIdsForPeriod.forEach((id) => resolvableTypeIds.add(id));
147
156
  }
148
157
 
149
158
  // Full game typeIds can only be resolved when game is completed
@@ -163,7 +172,7 @@ export function canResolveMarketsForEvent(
163
172
 
164
173
  // Batch typeIds check
165
174
  if (typeIds !== undefined) {
166
- return typeIds.filter(id => {
175
+ return typeIds.filter((id) => {
167
176
  // Exclude full game typeIds during live games
168
177
  if (!isCompleted && FULL_GAME_TYPE_IDS.includes(id)) {
169
178
  return false;
@@ -173,76 +182,80 @@ export function canResolveMarketsForEvent(
173
182
  }
174
183
 
175
184
  return false;
176
- };
185
+ }
177
186
  ```
178
187
 
179
188
  ### 4. Why Sport Parameter is Needed
180
189
 
181
190
  Different sports have different period structures:
182
- - **Soccer**: 2 halves (periods 1-2)
183
- - **NFL/NBA**: 4 quarters (periods 1-4)
184
- - **MLB**: 9+ innings (periods 1-9)
185
- - **NHL**: 3 periods (periods 1-3)
186
- - **Tennis**: Sets (periods 1-5)
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)
187
197
 
188
198
  The sport parameter allows future customization where period-to-typeId mappings could be sport-specific. For example:
189
- - Soccer might map period 1 → "1st Half" markets
190
- - Basketball might map period 1 → "1st Quarter" markets
191
- - Both use period 1, but represent different market types
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
192
203
 
193
204
  ## Implementation Steps
194
205
 
195
206
  ### Phase 1: Add Sport-Specific Mappings ✅ COMPLETED
196
- - [x] Add `SportPeriodType` enum to `src/types/resolution.ts`
197
- - [x] Add `HALVES_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
198
- - [x] Add `QUARTERS_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
199
- - [x] Add `INNINGS_PERIOD_TYPE_ID_MAPPING` to `src/types/resolution.ts`
200
- - [x] Add `FULL_GAME_TYPE_IDS` to `src/types/resolution.ts`
201
- - [x] Export all constants and enum
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
202
214
 
203
215
  ### Phase 2: Update Resolution Function ✅ COMPLETED
204
- - [x] Update `canResolveMarketsForEvent` function signature to accept `sportType` parameter
205
- - [x] Implement sport-type-based mapping selection logic
206
- - [x] Update function overloads for clean API (no undefined placeholders)
207
- - [x] Handle full game typeIds exclusion during live games
208
- - [x] Keep backward compatibility with existing functions
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
209
222
 
210
223
  ### Phase 3: Add Tests ✅ COMPLETED
211
- - [x] Test single typeId resolution
212
- - [x] Test batch typeIds resolution
213
- - [x] Test that full game typeIds are NOT resolved during live games
214
- - [x] Test with real event data (Soccer, NFL, MLB)
215
- - [x] Test edge cases (no completed periods, game in overtime, etc.)
216
- - [x] Add sport-specific tests for typeId 10051 (1st half) with HALVES, QUARTERS, INNINGS
217
- - [x] Add sport-specific tests for typeId 10052 (2nd half)
218
- - [x] Test default behavior (QUARTERS_BASED when no sportType provided)
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)
219
233
 
220
234
  ### Phase 4: Update Exports ✅ COMPLETED
221
- - [x] Export `SportPeriodType` enum from index.ts
222
- - [x] Export `HALVES_PERIOD_TYPE_ID_MAPPING` from index.ts
223
- - [x] Export `QUARTERS_PERIOD_TYPE_ID_MAPPING` from index.ts
224
- - [x] Export `INNINGS_PERIOD_TYPE_ID_MAPPING` from index.ts
225
- - [x] Export `FULL_GAME_TYPE_IDS` from index.ts
226
- - [x] Keep existing exports for backward compatibility
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
227
242
 
228
243
  ### Phase 5: Testing & Validation ✅ COMPLETED
229
- - [x] Run full test suite
230
- - [x] Verify all 85 tests pass
231
- - [x] Test with real OpticOdds API responses (Soccer, NFL, MLB)
232
- - [x] Verify TypeScript compilation succeeds
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
233
249
 
234
250
  ## Example Usage
235
251
 
236
252
  ### Single TypeId Check
253
+
237
254
  ```typescript
238
255
  import { canResolveMarketsForEvent, SportPeriodType } from 'overtime-live-trading-utils';
239
256
 
240
257
  // Check NFL (quarters-based) - Can we resolve "1st Quarter Winner" market (typeId 10021)?
241
- const canResolve = canResolveMarketsForEvent(
242
- nflEvent,
243
- 10021,
244
- SportPeriodType.QUARTERS_BASED
245
- );
258
+ const canResolve = canResolveMarketsForEvent(nflEvent, 10021, SportPeriodType.QUARTERS_BASED);
246
259
 
247
260
  if (canResolve) {
248
261
  // Resolve the 1st quarter market
@@ -251,52 +264,40 @@ if (canResolve) {
251
264
  ```
252
265
 
253
266
  ### Sport-Specific TypeId 10051 (1st Half) Resolution
267
+
254
268
  ```typescript
255
269
  // Soccer (halves-based): Resolves after period 1
256
- const soccerCanResolve = canResolveMarketsForEvent(
257
- soccerEvent,
258
- 10051,
259
- SportPeriodType.HALVES_BASED
260
- ); // true if period 1 complete
270
+ const soccerCanResolve = canResolveMarketsForEvent(soccerEvent, 10051, SportPeriodType.HALVES_BASED); // true if period 1 complete
261
271
 
262
272
  // NFL (quarters-based): Resolves after period 2
263
- const nflCanResolve = canResolveMarketsForEvent(
264
- nflEvent,
265
- 10051,
266
- SportPeriodType.QUARTERS_BASED
267
- ); // true if period 2 complete
273
+ const nflCanResolve = canResolveMarketsForEvent(nflEvent, 10051, SportPeriodType.QUARTERS_BASED); // true if period 2 complete
268
274
 
269
275
  // MLB (innings-based): Resolves after period 5
270
- const mlbCanResolve = canResolveMarketsForEvent(
271
- mlbEvent,
272
- 10051,
273
- SportPeriodType.INNINGS_BASED
274
- ); // true if period 5 complete
276
+ const mlbCanResolve = canResolveMarketsForEvent(mlbEvent, 10051, SportPeriodType.INNINGS_BASED); // true if period 5 complete
275
277
  ```
276
278
 
277
279
  ### Batch TypeIds Check
280
+
278
281
  ```typescript
279
282
  // Which of these markets can we resolve right now for an NFL game?
280
283
  const marketTypeIds = [10021, 10022, 10031, 10001];
281
- const resolvableMarkets = canResolveMarketsForEvent(
282
- nflEvent,
283
- marketTypeIds,
284
- SportPeriodType.QUARTERS_BASED
285
- );
284
+ const resolvableMarkets = canResolveMarketsForEvent(nflEvent, marketTypeIds, SportPeriodType.QUARTERS_BASED);
286
285
 
287
286
  // Returns: [10021, 10031] if only period 1 is complete
288
287
  // Full game typeId 10001 is excluded during live games
289
- resolvableMarkets.forEach(typeId => resolveMarket(typeId));
288
+ resolvableMarkets.forEach((typeId) => resolveMarket(typeId));
290
289
  ```
291
290
 
292
291
  ### Function Overloads (TypeScript)
292
+
293
293
  The function uses TypeScript overloads for clean API:
294
+
294
295
  ```typescript
295
296
  // Single typeId → returns boolean
296
297
  function canResolveMarketsForEvent(
297
298
  event: OpticOddsEvent,
298
299
  typeId: number,
299
- sportType: SportPeriodType, // REQUIRED
300
+ sportType: SportPeriodType, // REQUIRED
300
301
  sportName?: string
301
302
  ): boolean;
302
303
 
@@ -304,7 +305,7 @@ function canResolveMarketsForEvent(
304
305
  function canResolveMarketsForEvent(
305
306
  event: OpticOddsEvent,
306
307
  typeIds: number[],
307
- sportType: SportPeriodType, // REQUIRED
308
+ sportType: SportPeriodType, // REQUIRED
308
309
  sportName?: string
309
310
  ): number[];
310
311
  ```
@@ -312,25 +313,29 @@ function canResolveMarketsForEvent(
312
313
  ## Testing Strategy
313
314
 
314
315
  ### Test Cases
316
+
315
317
  1. **Period 1 Complete (Live Soccer 2nd Half)**
316
- - Input: Soccer event in 2nd half, period 1 complete
317
- - TypeIds: [10021, 10022, 10001]
318
- - Expected: [10021] (only 1st half markets resolvable)
318
+
319
+ - Input: Soccer event in 2nd half, period 1 complete
320
+ - TypeIds: [10021, 10022, 10001]
321
+ - Expected: [10021] (only 1st half markets resolvable)
319
322
 
320
323
  2. **Periods 1-4 Complete (NFL Overtime)**
321
- - Input: NFL event in overtime, all 4 quarters complete
322
- - TypeIds: [10021, 10022, 10023, 10024, 10001]
323
- - Expected: [10021, 10022, 10023, 10024] (all quarter markets, but not full game)
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)
324
328
 
325
329
  3. **Game Completed**
326
- - Input: Completed game with all periods
327
- - TypeIds: [10021, 10001]
328
- - Expected: [10021, 10001] (all markets including full game)
330
+
331
+ - Input: Completed game with all periods
332
+ - TypeIds: [10021, 10001]
333
+ - Expected: [10021, 10001] (all markets including full game)
329
334
 
330
335
  4. **No Periods Complete**
331
- - Input: Live game in 1st period
332
- - TypeIds: [10021, 10022]
333
- - Expected: [] (no markets resolvable yet)
336
+ - Input: Live game in 1st period
337
+ - TypeIds: [10021, 10022]
338
+ - Expected: [] (no markets resolvable yet)
334
339
 
335
340
  ## Benefits
336
341
 
@@ -516,3 +516,147 @@ export const MockNFLCompletedWithOvertime = {
516
516
  clock: null,
517
517
  },
518
518
  };
519
+
520
+ export const MockNBACompletedEvent = {
521
+ sport: {
522
+ id: 'basketball',
523
+ name: 'Basketball',
524
+ numerical_id: 4,
525
+ },
526
+ league: {
527
+ id: 'nba',
528
+ name: 'NBA',
529
+ numerical_id: 355,
530
+ },
531
+ fixture: {
532
+ id: '202511053DEE59D4',
533
+ numerical_id: 402396,
534
+ game_id: '10459-24860-2025-11-04',
535
+ start_date: '2025-11-05T03:00:00Z',
536
+ home_competitors: [
537
+ {
538
+ id: 'DFC9A735A4D7',
539
+ name: 'Golden State Warriors',
540
+ numerical_id: 21801,
541
+ base_id: 14357,
542
+ abbreviation: 'GSW',
543
+ logo: 'https://cdn.opticodds.com/team-logos/basketball/14357.png',
544
+ },
545
+ ],
546
+ away_competitors: [
547
+ {
548
+ id: '9BF9A5FD18B1',
549
+ name: 'Phoenix Suns',
550
+ numerical_id: 21815,
551
+ base_id: 14371,
552
+ abbreviation: 'PHX',
553
+ logo: 'https://cdn.opticodds.com/team-logos/basketball/14371.png',
554
+ },
555
+ ],
556
+ home_team_display: 'Golden State Warriors',
557
+ away_team_display: 'Phoenix Suns',
558
+ status: 'completed',
559
+ is_live: false,
560
+ season_type: 'Regular Season',
561
+ season_year: '2025',
562
+ season_week: '45',
563
+ venue_name: 'Chase Center',
564
+ venue_location: 'San Francisco, CA, USA',
565
+ venue_neutral: false,
566
+ },
567
+ scores: {
568
+ home: {
569
+ total: 118.0,
570
+ periods: {
571
+ period_1: 33.0,
572
+ period_2: 35.0,
573
+ period_3: 24.0,
574
+ period_4: 26.0,
575
+ },
576
+ aggregate: null,
577
+ },
578
+ away: {
579
+ total: 107.0,
580
+ periods: {
581
+ period_1: 19.0,
582
+ period_2: 30.0,
583
+ period_3: 34.0,
584
+ period_4: 24.0,
585
+ },
586
+ aggregate: null,
587
+ },
588
+ },
589
+ in_play: {
590
+ period: '4',
591
+ clock: null,
592
+ },
593
+ };
594
+
595
+ export const MockNBALiveAtHalftime = {
596
+ sport: {
597
+ id: 'basketball',
598
+ name: 'Basketball',
599
+ numerical_id: 4,
600
+ },
601
+ league: {
602
+ id: 'nba',
603
+ name: 'NBA',
604
+ numerical_id: 355,
605
+ },
606
+ fixture: {
607
+ id: '202511053DEE59D4',
608
+ numerical_id: 402396,
609
+ game_id: '10459-24860-2025-11-04',
610
+ start_date: '2025-11-05T03:00:00Z',
611
+ home_competitors: [
612
+ {
613
+ id: 'DFC9A735A4D7',
614
+ name: 'Golden State Warriors',
615
+ numerical_id: 21801,
616
+ base_id: 14357,
617
+ abbreviation: 'GSW',
618
+ logo: 'https://cdn.opticodds.com/team-logos/basketball/14357.png',
619
+ },
620
+ ],
621
+ away_competitors: [
622
+ {
623
+ id: '9BF9A5FD18B1',
624
+ name: 'Phoenix Suns',
625
+ numerical_id: 21815,
626
+ base_id: 14371,
627
+ abbreviation: 'PHX',
628
+ logo: 'https://cdn.opticodds.com/team-logos/basketball/14371.png',
629
+ },
630
+ ],
631
+ home_team_display: 'Golden State Warriors',
632
+ away_team_display: 'Phoenix Suns',
633
+ status: 'half',
634
+ is_live: true,
635
+ season_type: 'Regular Season',
636
+ season_year: '2025',
637
+ season_week: '45',
638
+ venue_name: 'Chase Center',
639
+ venue_location: 'San Francisco, CA, USA',
640
+ venue_neutral: false,
641
+ },
642
+ scores: {
643
+ home: {
644
+ total: 68.0,
645
+ periods: {
646
+ period_1: 33.0,
647
+ period_2: 35.0,
648
+ },
649
+ },
650
+ away: {
651
+ total: 49.0,
652
+ periods: {
653
+ period_1: 19.0,
654
+ period_2: 30.0,
655
+ },
656
+ },
657
+ },
658
+ in_play: {
659
+ period: 'half',
660
+ clock: null,
661
+ },
662
+ };