overtime-live-trading-utils 2.1.22 → 2.1.24
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/main.js +1 -1
- package/package.json +1 -1
- package/src/tests/unit/resolution.test.ts +71 -179
- package/src/utils/resolution.ts +112 -50
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectCompletedPeriods,
|
|
3
|
-
canResolveMarketForGameIdAndSport,
|
|
4
|
-
canResolveMarketViaOpticOddsApi,
|
|
5
3
|
canResolveMarketsForEvent,
|
|
4
|
+
canResolveMultipleTypeIdsForEvent,
|
|
5
|
+
filterMarketsThatCanBeResolved,
|
|
6
6
|
} from '../../utils/resolution';
|
|
7
7
|
import { SportPeriodType } from '../../types/resolution';
|
|
8
8
|
import {
|
|
@@ -570,173 +570,6 @@ describe('Resolution Utils', () => {
|
|
|
570
570
|
});
|
|
571
571
|
});
|
|
572
572
|
|
|
573
|
-
describe('canResolveMarketForGameIdAndSport', () => {
|
|
574
|
-
it('Should return true when game can be resolved', () => {
|
|
575
|
-
const event = {
|
|
576
|
-
sport: {
|
|
577
|
-
name: 'Soccer',
|
|
578
|
-
},
|
|
579
|
-
fixture: {
|
|
580
|
-
id: 'match-123',
|
|
581
|
-
status: 'live',
|
|
582
|
-
is_live: true,
|
|
583
|
-
},
|
|
584
|
-
scores: {
|
|
585
|
-
home: {
|
|
586
|
-
total: 2.0,
|
|
587
|
-
periods: {
|
|
588
|
-
period_1: 1.0,
|
|
589
|
-
},
|
|
590
|
-
},
|
|
591
|
-
away: {
|
|
592
|
-
total: 1.0,
|
|
593
|
-
periods: {
|
|
594
|
-
period_1: 1.0,
|
|
595
|
-
},
|
|
596
|
-
},
|
|
597
|
-
},
|
|
598
|
-
in_play: {
|
|
599
|
-
period: '2',
|
|
600
|
-
},
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
const result = canResolveMarketForGameIdAndSport('match-123', event);
|
|
604
|
-
|
|
605
|
-
expect(result).toBe(true);
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it('Should return false when gameId does not match', () => {
|
|
609
|
-
const event = {
|
|
610
|
-
sport: {
|
|
611
|
-
name: 'Soccer',
|
|
612
|
-
},
|
|
613
|
-
fixture: {
|
|
614
|
-
id: 'match-456',
|
|
615
|
-
status: 'live',
|
|
616
|
-
is_live: true,
|
|
617
|
-
},
|
|
618
|
-
scores: {
|
|
619
|
-
home: {
|
|
620
|
-
total: 1.0,
|
|
621
|
-
periods: {
|
|
622
|
-
period_1: 1.0,
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
away: {
|
|
626
|
-
total: 0.0,
|
|
627
|
-
periods: {
|
|
628
|
-
period_1: 0.0,
|
|
629
|
-
},
|
|
630
|
-
},
|
|
631
|
-
},
|
|
632
|
-
in_play: {
|
|
633
|
-
period: '2',
|
|
634
|
-
},
|
|
635
|
-
};
|
|
636
|
-
|
|
637
|
-
const result = canResolveMarketForGameIdAndSport('match-999', event);
|
|
638
|
-
|
|
639
|
-
expect(result).toBe(false);
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
it('Should return false when no periods are completed', () => {
|
|
643
|
-
const event = {
|
|
644
|
-
sport: {
|
|
645
|
-
name: 'Basketball',
|
|
646
|
-
},
|
|
647
|
-
fixture: {
|
|
648
|
-
id: 'match-789',
|
|
649
|
-
status: 'live',
|
|
650
|
-
is_live: true,
|
|
651
|
-
},
|
|
652
|
-
scores: {
|
|
653
|
-
home: {
|
|
654
|
-
total: 15.0,
|
|
655
|
-
periods: {},
|
|
656
|
-
},
|
|
657
|
-
away: {
|
|
658
|
-
total: 12.0,
|
|
659
|
-
periods: {},
|
|
660
|
-
},
|
|
661
|
-
},
|
|
662
|
-
in_play: {
|
|
663
|
-
period: '1',
|
|
664
|
-
},
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
const result = canResolveMarketForGameIdAndSport('match-789', event);
|
|
668
|
-
|
|
669
|
-
expect(result).toBe(false);
|
|
670
|
-
});
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
describe('canResolveMarketViaOpticOddsApi', () => {
|
|
674
|
-
it('Should return period resolution data when available', () => {
|
|
675
|
-
const event = {
|
|
676
|
-
sport: {
|
|
677
|
-
name: 'Tennis',
|
|
678
|
-
},
|
|
679
|
-
fixture: {
|
|
680
|
-
id: 'api-test-1',
|
|
681
|
-
status: 'live',
|
|
682
|
-
is_live: true,
|
|
683
|
-
},
|
|
684
|
-
scores: {
|
|
685
|
-
home: {
|
|
686
|
-
total: 1.0,
|
|
687
|
-
periods: {
|
|
688
|
-
period_1: 7.0,
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
away: {
|
|
692
|
-
total: 0.0,
|
|
693
|
-
periods: {
|
|
694
|
-
period_1: 5.0,
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
in_play: {
|
|
699
|
-
period: '2',
|
|
700
|
-
},
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
const result = canResolveMarketViaOpticOddsApi(event);
|
|
704
|
-
|
|
705
|
-
expect(result).not.toBeNull();
|
|
706
|
-
expect(result?.completedPeriods).toEqual([1]);
|
|
707
|
-
expect(result?.readyForResolution).toBe(true);
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
it('Should return null when no periods completed', () => {
|
|
711
|
-
const event = {
|
|
712
|
-
sport: {
|
|
713
|
-
name: 'Soccer',
|
|
714
|
-
},
|
|
715
|
-
fixture: {
|
|
716
|
-
id: 'api-test-2',
|
|
717
|
-
status: 'live',
|
|
718
|
-
is_live: true,
|
|
719
|
-
},
|
|
720
|
-
scores: {
|
|
721
|
-
home: {
|
|
722
|
-
total: 0.0,
|
|
723
|
-
periods: {},
|
|
724
|
-
},
|
|
725
|
-
away: {
|
|
726
|
-
total: 0.0,
|
|
727
|
-
periods: {},
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
|
-
in_play: {
|
|
731
|
-
period: '1',
|
|
732
|
-
},
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
const result = canResolveMarketViaOpticOddsApi(event);
|
|
736
|
-
|
|
737
|
-
expect(result).toBeNull();
|
|
738
|
-
});
|
|
739
|
-
});
|
|
740
573
|
|
|
741
574
|
describe('canResolveMarketsForEvent', () => {
|
|
742
575
|
describe('Single typeId checks', () => {
|
|
@@ -796,35 +629,35 @@ describe('Resolution Utils', () => {
|
|
|
796
629
|
describe('Batch typeIds checks', () => {
|
|
797
630
|
it('Should return only resolvable typeIds for live soccer in 2nd half', () => {
|
|
798
631
|
const typeIds = [10021, 10022, 10031, 10001];
|
|
799
|
-
const result =
|
|
632
|
+
const result = filterMarketsThatCanBeResolved(MockSoccerLiveSecondHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
800
633
|
|
|
801
634
|
expect(result).toEqual([10021, 10031]); // Only period 1 typeIds
|
|
802
635
|
});
|
|
803
636
|
|
|
804
637
|
it('Should exclude full game typeIds during live game', () => {
|
|
805
638
|
const typeIds = [10021, 10001, 10002, 10003];
|
|
806
|
-
const result =
|
|
639
|
+
const result = filterMarketsThatCanBeResolved(MockNFLLiveThirdQuarter, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
807
640
|
|
|
808
641
|
expect(result).toEqual([10021]); // Full game typeIds excluded
|
|
809
642
|
});
|
|
810
643
|
|
|
811
644
|
it('Should include full game typeIds when game is completed', () => {
|
|
812
645
|
const typeIds = [10021, 10022, 10001, 10002];
|
|
813
|
-
const result =
|
|
646
|
+
const result = filterMarketsThatCanBeResolved(MockSoccerCompletedEvent, typeIds, SportPeriodType.HALVES_BASED);
|
|
814
647
|
|
|
815
648
|
expect(result).toEqual([10021, 10022, 10001, 10002]);
|
|
816
649
|
});
|
|
817
650
|
|
|
818
651
|
it('Should return empty array when no typeIds are resolvable', () => {
|
|
819
652
|
const typeIds = [10022, 10023, 10024];
|
|
820
|
-
const result =
|
|
653
|
+
const result = filterMarketsThatCanBeResolved(MockSoccerLiveSecondHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
821
654
|
|
|
822
655
|
expect(result).toEqual([]);
|
|
823
656
|
});
|
|
824
657
|
|
|
825
658
|
it('Should return multiple period typeIds for NFL game in 3rd quarter', () => {
|
|
826
659
|
const typeIds = [10021, 10022, 10023, 10024, 10031, 10032, 10051];
|
|
827
|
-
const result =
|
|
660
|
+
const result = filterMarketsThatCanBeResolved(MockNFLLiveThirdQuarter, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
828
661
|
|
|
829
662
|
// Periods 1 and 2 are complete (period 2 also completes 1st half = 10051)
|
|
830
663
|
expect(result).toEqual([10021, 10022, 10031, 10032, 10051]);
|
|
@@ -832,19 +665,78 @@ describe('Resolution Utils', () => {
|
|
|
832
665
|
|
|
833
666
|
it('Should handle all 9 periods for completed MLB game', () => {
|
|
834
667
|
const typeIds = [10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029];
|
|
835
|
-
const result =
|
|
668
|
+
const result = filterMarketsThatCanBeResolved(MockMLBCompletedEvent, typeIds, SportPeriodType.INNINGS_BASED);
|
|
836
669
|
|
|
837
670
|
expect(result).toEqual(typeIds); // All 9 innings complete
|
|
838
671
|
});
|
|
839
672
|
|
|
840
673
|
it('Should return empty array when no periods complete', () => {
|
|
841
674
|
const typeIds = [10021, 10022, 10031];
|
|
842
|
-
const result =
|
|
675
|
+
const result = filterMarketsThatCanBeResolved(MockSoccerLiveFirstHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
843
676
|
|
|
844
677
|
expect(result).toEqual([]);
|
|
845
678
|
});
|
|
846
679
|
});
|
|
847
680
|
|
|
681
|
+
describe('Multiple typeIds boolean array checks', () => {
|
|
682
|
+
it('Should return boolean array for live soccer in 2nd half', () => {
|
|
683
|
+
const typeIds = [10021, 10022, 10031, 10001];
|
|
684
|
+
const result = canResolveMultipleTypeIdsForEvent(MockSoccerLiveSecondHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
685
|
+
|
|
686
|
+
expect(result).toEqual([true, false, true, false]); // Period 1 typeIds are true, period 2 and full game are false
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('Should return false for full game typeIds during live game', () => {
|
|
690
|
+
const typeIds = [10021, 10001, 10002, 10003];
|
|
691
|
+
const result = canResolveMultipleTypeIdsForEvent(MockNFLLiveThirdQuarter, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
692
|
+
|
|
693
|
+
expect(result).toEqual([true, false, false, false]); // Only period 1 is true
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it('Should return all true for completed game', () => {
|
|
697
|
+
const typeIds = [10021, 10022, 10001, 10002];
|
|
698
|
+
const result = canResolveMultipleTypeIdsForEvent(MockSoccerCompletedEvent, typeIds, SportPeriodType.HALVES_BASED);
|
|
699
|
+
|
|
700
|
+
expect(result).toEqual([true, true, true, true]); // All complete
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
it('Should return all false when no typeIds are resolvable', () => {
|
|
704
|
+
const typeIds = [10022, 10023, 10024];
|
|
705
|
+
const result = canResolveMultipleTypeIdsForEvent(MockSoccerLiveSecondHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
706
|
+
|
|
707
|
+
expect(result).toEqual([false, false, false]);
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it('Should return mixed booleans for NFL game in 3rd quarter', () => {
|
|
711
|
+
const typeIds = [10021, 10022, 10023, 10024, 10031, 10032, 10051];
|
|
712
|
+
const result = canResolveMultipleTypeIdsForEvent(MockNFLLiveThirdQuarter, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
713
|
+
|
|
714
|
+
// Periods 1 and 2 are complete (period 2 also completes 1st half = 10051)
|
|
715
|
+
expect(result).toEqual([true, true, false, false, true, true, true]);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('Should handle all 9 periods for completed MLB game', () => {
|
|
719
|
+
const typeIds = [10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029];
|
|
720
|
+
const result = canResolveMultipleTypeIdsForEvent(MockMLBCompletedEvent, typeIds, SportPeriodType.INNINGS_BASED);
|
|
721
|
+
|
|
722
|
+
expect(result).toEqual([true, true, true, true, true, true, true, true, true]); // All 9 innings complete
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
it('Should return all false when no periods complete', () => {
|
|
726
|
+
const typeIds = [10021, 10022, 10031];
|
|
727
|
+
const result = canResolveMultipleTypeIdsForEvent(MockSoccerLiveFirstHalf, typeIds, SportPeriodType.HALVES_BASED);
|
|
728
|
+
|
|
729
|
+
expect(result).toEqual([false, false, false]);
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('Should work with numeric sport type parameter', () => {
|
|
733
|
+
const typeIds = [10021, 10022];
|
|
734
|
+
const result = canResolveMultipleTypeIdsForEvent(MockSoccerLiveSecondHalf, typeIds, 0); // 0 = HALVES_BASED
|
|
735
|
+
|
|
736
|
+
expect(result).toEqual([true, false]);
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
|
|
848
740
|
describe('Edge cases', () => {
|
|
849
741
|
it('Should handle event with no completed periods', () => {
|
|
850
742
|
const result = canResolveMarketsForEvent(MockSoccerLiveFirstHalfInProgress, 10021, SportPeriodType.HALVES_BASED);
|
|
@@ -867,7 +759,7 @@ describe('Resolution Utils', () => {
|
|
|
867
759
|
});
|
|
868
760
|
|
|
869
761
|
it('Should work with sport type parameter for batch typeIds', () => {
|
|
870
|
-
const result =
|
|
762
|
+
const result = filterMarketsThatCanBeResolved(MockSoccerLiveSecondHalf, [10021, 10022], SportPeriodType.HALVES_BASED);
|
|
871
763
|
expect(result).toEqual([10021]);
|
|
872
764
|
});
|
|
873
765
|
});
|
|
@@ -901,7 +793,7 @@ describe('Resolution Utils', () => {
|
|
|
901
793
|
|
|
902
794
|
it('Should return all resolvable typeIds including overtime in batch check', () => {
|
|
903
795
|
const typeIds = [10021, 10022, 10023, 10024, 10025, 10001];
|
|
904
|
-
const result =
|
|
796
|
+
const result = filterMarketsThatCanBeResolved(MockNFLCompletedWithOvertime, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
905
797
|
|
|
906
798
|
expect(result).toEqual(typeIds); // All should be resolvable (game completed with overtime)
|
|
907
799
|
});
|
|
@@ -918,7 +810,7 @@ describe('Resolution Utils', () => {
|
|
|
918
810
|
|
|
919
811
|
it('Should not include non-existent periods in batch check', () => {
|
|
920
812
|
const typeIds = [10021, 10022, 10025, 10028, 10029];
|
|
921
|
-
const result =
|
|
813
|
+
const result = filterMarketsThatCanBeResolved(MockNFLCompletedWithOvertime, typeIds, SportPeriodType.QUARTERS_BASED);
|
|
922
814
|
|
|
923
815
|
// Only periods 1, 2, and 5 occurred, so only their typeIds should be returned
|
|
924
816
|
expect(result).toEqual([10021, 10022, 10025]);
|
package/src/utils/resolution.ts
CHANGED
|
@@ -92,37 +92,6 @@ export const detectCompletedPeriods = (
|
|
|
92
92
|
}
|
|
93
93
|
: null;
|
|
94
94
|
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Checks if a market can be resolved based on game and sport
|
|
98
|
-
* @param gameId - The game/fixture ID
|
|
99
|
-
* @param event - Event object from OpticOdds API
|
|
100
|
-
* @returns boolean indicating if market can be resolved
|
|
101
|
-
*/
|
|
102
|
-
export const canResolveMarketForGameIdAndSport = (
|
|
103
|
-
gameId: string,
|
|
104
|
-
event: OpticOddsEvent
|
|
105
|
-
): boolean => {
|
|
106
|
-
const eventId = event.fixture?.id || event.id || '';
|
|
107
|
-
if (eventId !== gameId) {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const periodData = detectCompletedPeriods(event);
|
|
112
|
-
return periodData !== null && periodData.readyForResolution;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Convenience function to check resolution status via OpticOdds API event
|
|
117
|
-
* @param event - Event object from OpticOdds API
|
|
118
|
-
* @returns PeriodResolutionData if periods are complete, null otherwise
|
|
119
|
-
*/
|
|
120
|
-
export const canResolveMarketViaOpticOddsApi = (
|
|
121
|
-
event: OpticOddsEvent
|
|
122
|
-
): PeriodResolutionData | null => {
|
|
123
|
-
return detectCompletedPeriods(event);
|
|
124
|
-
};
|
|
125
|
-
|
|
126
95
|
/**
|
|
127
96
|
* Maps a numeric value to a SportPeriodType enum
|
|
128
97
|
* @param sportTypeNum - Numeric representation of sport type (0 = halves, 1 = quarters, 2 = innings, 3 = period)
|
|
@@ -163,32 +132,87 @@ function selectMappingForSportType(sportType: SportPeriodType): { [period: numbe
|
|
|
163
132
|
}
|
|
164
133
|
|
|
165
134
|
/**
|
|
166
|
-
*
|
|
135
|
+
* Checks if a specific market type can be resolved based on completed periods (Enum version)
|
|
167
136
|
*
|
|
168
137
|
* @example
|
|
169
138
|
* // Check single typeId for NFL (quarters-based)
|
|
170
139
|
* const canResolve = canResolveMarketsForEvent(event, 10021, SportPeriodType.QUARTERS_BASED);
|
|
171
|
-
* // Or using number
|
|
172
|
-
* const canResolve = canResolveMarketsForEvent(event, 10021, 1);
|
|
173
|
-
*
|
|
174
|
-
* // Check batch of typeIds for MLB (innings-based)
|
|
175
|
-
* const resolvable = canResolveMarketsForEvent(event, [10021, 10051], SportPeriodType.INNINGS_BASED);
|
|
176
|
-
* // Returns: [10021] if only period 1-4 complete, [10021, 10051] if period 5 complete
|
|
177
140
|
*
|
|
178
141
|
* // Check with period-based (no halves/secondary moneyline)
|
|
179
142
|
* const canResolve = canResolveMarketsForEvent(event, 10021, SportPeriodType.PERIOD_BASED);
|
|
180
|
-
*
|
|
181
|
-
*
|
|
143
|
+
*
|
|
144
|
+
* @param event - OpticOdds event object containing fixture data
|
|
145
|
+
* @param typeId - The market type identifier
|
|
146
|
+
* @param sportType - The sport period type enum
|
|
147
|
+
* @returns true if the market can be resolved based on completed periods
|
|
182
148
|
*/
|
|
183
149
|
export function canResolveMarketsForEvent(
|
|
184
150
|
event: OpticOddsEvent,
|
|
185
|
-
|
|
151
|
+
typeId: number,
|
|
152
|
+
sportType: SportPeriodType
|
|
153
|
+
): boolean {
|
|
154
|
+
const periodData = detectCompletedPeriods(event);
|
|
155
|
+
if (!periodData) return false;
|
|
156
|
+
|
|
157
|
+
const status = (event.fixture?.status || event.status || '').toLowerCase();
|
|
158
|
+
const isCompleted = status === 'completed' || status === 'complete' || status === 'finished';
|
|
159
|
+
|
|
160
|
+
// Full game typeIds can only be resolved when game is completed
|
|
161
|
+
if (FULL_GAME_TYPE_IDS.includes(typeId)) {
|
|
162
|
+
return isCompleted;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Select appropriate mapping based on sport type
|
|
166
|
+
const mapping = selectMappingForSportType(sportType);
|
|
167
|
+
|
|
168
|
+
const resolvableTypeIds = new Set<number>();
|
|
169
|
+
for (const period of periodData.completedPeriods) {
|
|
170
|
+
const typeIdsForPeriod = mapping[period] || [];
|
|
171
|
+
typeIdsForPeriod.forEach((id) => resolvableTypeIds.add(id));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return resolvableTypeIds.has(typeId);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Checks if a specific market type can be resolved based on completed periods (Number version)
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* // Check single typeId for NFL (quarters-based)
|
|
182
|
+
* const canResolve = canResolveMarketsForEventNumber(event, 10021, 1);
|
|
183
|
+
*
|
|
184
|
+
* @param event - OpticOdds event object containing fixture data
|
|
185
|
+
* @param typeId - The market type identifier
|
|
186
|
+
* @param sportTypeNumber - Numeric value representing the sport period type
|
|
187
|
+
* @returns true if the market can be resolved based on completed periods
|
|
188
|
+
*/
|
|
189
|
+
export function canResolveMarketsForEventNumber(
|
|
190
|
+
event: OpticOddsEvent,
|
|
191
|
+
typeId: number,
|
|
192
|
+
sportTypeNumber: number
|
|
193
|
+
): boolean {
|
|
194
|
+
const sportTypeEnum = mapNumberToSportPeriodType(sportTypeNumber);
|
|
195
|
+
return canResolveMarketsForEvent(event, typeId, sportTypeEnum);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Checks if multiple market types can be resolved, returning a boolean for each
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* // Check multiple typeIds for MLB (innings-based)
|
|
204
|
+
* const canResolve = canResolveMultipleTypeIdsForEvent(event, [10021, 10051], SportPeriodType.INNINGS_BASED);
|
|
205
|
+
* // Returns: [true, false] if only 10021 can be resolved
|
|
206
|
+
*/
|
|
207
|
+
export function canResolveMultipleTypeIdsForEvent(
|
|
208
|
+
event: OpticOddsEvent,
|
|
209
|
+
typeIds: number[],
|
|
186
210
|
sportType: SportPeriodType | number
|
|
187
|
-
): boolean
|
|
211
|
+
): boolean[] {
|
|
188
212
|
// Get completed periods
|
|
189
213
|
const periodData = detectCompletedPeriods(event);
|
|
190
214
|
if (!periodData) {
|
|
191
|
-
return
|
|
215
|
+
return typeIds.map(() => false);
|
|
192
216
|
}
|
|
193
217
|
|
|
194
218
|
// Check if game is fully completed
|
|
@@ -209,21 +233,59 @@ export function canResolveMarketsForEvent(
|
|
|
209
233
|
typeIdsForPeriod.forEach((id) => resolvableTypeIds.add(id));
|
|
210
234
|
}
|
|
211
235
|
|
|
212
|
-
//
|
|
213
|
-
|
|
236
|
+
// Map each typeId to a boolean
|
|
237
|
+
return typeIds.map((id) => {
|
|
214
238
|
// Full game typeIds can only be resolved when game is completed
|
|
215
|
-
if (FULL_GAME_TYPE_IDS.includes(
|
|
239
|
+
if (FULL_GAME_TYPE_IDS.includes(id)) {
|
|
216
240
|
return isCompleted;
|
|
217
241
|
}
|
|
218
|
-
return resolvableTypeIds.has(
|
|
242
|
+
return resolvableTypeIds.has(id);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Filters a list of market types to only those that can be resolved
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* // Filter typeIds for MLB (innings-based)
|
|
251
|
+
* const resolvable = filterMarketsThatCanBeResolved(event, [10021, 10051, 10061], SportPeriodType.INNINGS_BASED);
|
|
252
|
+
* // Returns: [10021] if only period 1-4 complete, [10021, 10051] if period 5 complete
|
|
253
|
+
*/
|
|
254
|
+
export function filterMarketsThatCanBeResolved(
|
|
255
|
+
event: OpticOddsEvent,
|
|
256
|
+
typeIds: number[],
|
|
257
|
+
sportType: SportPeriodType | number
|
|
258
|
+
): number[] {
|
|
259
|
+
// Get completed periods
|
|
260
|
+
const periodData = detectCompletedPeriods(event);
|
|
261
|
+
if (!periodData) {
|
|
262
|
+
return [];
|
|
219
263
|
}
|
|
220
264
|
|
|
221
|
-
//
|
|
222
|
-
|
|
265
|
+
// Check if game is fully completed
|
|
266
|
+
const status = (event.fixture?.status || event.status || '').toLowerCase();
|
|
267
|
+
const isCompleted = status === 'completed' || status === 'complete' || status === 'finished';
|
|
268
|
+
|
|
269
|
+
// Convert number to SportPeriodType if needed
|
|
270
|
+
const sportTypeEnum = typeof sportType === 'number' ? mapNumberToSportPeriodType(sportType) : sportType;
|
|
271
|
+
|
|
272
|
+
// Select appropriate mapping based on sport type
|
|
273
|
+
const mapping = selectMappingForSportType(sportTypeEnum);
|
|
274
|
+
|
|
275
|
+
// Collect all resolvable typeIds based on completed periods
|
|
276
|
+
const resolvableTypeIds = new Set<number>();
|
|
277
|
+
|
|
278
|
+
for (const period of periodData.completedPeriods) {
|
|
279
|
+
const typeIdsForPeriod = mapping[period] || [];
|
|
280
|
+
typeIdsForPeriod.forEach((id) => resolvableTypeIds.add(id));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Filter typeIds to only those that can be resolved
|
|
284
|
+
return typeIds.filter((id) => {
|
|
223
285
|
// Full game typeIds can only be resolved when game is completed
|
|
224
286
|
if (FULL_GAME_TYPE_IDS.includes(id)) {
|
|
225
287
|
return isCompleted;
|
|
226
288
|
}
|
|
227
289
|
return resolvableTypeIds.has(id);
|
|
228
290
|
});
|
|
229
|
-
}
|
|
291
|
+
}
|