overtime-live-trading-utils 2.1.16 → 2.1.18

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.
Files changed (38) hide show
  1. package/.circleci/config.yml +32 -32
  2. package/.prettierrc +9 -9
  3. package/CLAUDE.md +77 -0
  4. package/codecov.yml +20 -20
  5. package/index.ts +26 -16
  6. package/jest.config.ts +16 -16
  7. package/main.js +1 -1
  8. package/package.json +30 -30
  9. package/resolution_live_markets.md +351 -0
  10. package/src/constants/common.ts +7 -7
  11. package/src/constants/errors.ts +6 -6
  12. package/src/constants/sports.ts +78 -78
  13. package/src/enums/sports.ts +109 -109
  14. package/src/tests/mock/MockLeagueMap.ts +170 -159
  15. package/src/tests/mock/MockOpticOddsEvents.ts +518 -0
  16. package/src/tests/mock/MockOpticSoccer.ts +9378 -9342
  17. package/src/tests/mock/MockSoccerRedis.ts +2308 -2308
  18. package/src/tests/unit/bookmakers.test.ts +79 -79
  19. package/src/tests/unit/markets.test.ts +156 -152
  20. package/src/tests/unit/odds.test.ts +92 -92
  21. package/src/tests/unit/resolution.test.ts +1043 -0
  22. package/src/tests/unit/sports.test.ts +58 -58
  23. package/src/tests/unit/spread.test.ts +131 -131
  24. package/src/types/missing-types.d.ts +2 -2
  25. package/src/types/odds.ts +61 -61
  26. package/src/types/resolution.ts +78 -0
  27. package/src/types/sports.ts +19 -19
  28. package/src/utils/bookmakers.ts +159 -159
  29. package/src/utils/constraints.ts +210 -210
  30. package/src/utils/gameMatching.ts +81 -81
  31. package/src/utils/markets.ts +119 -119
  32. package/src/utils/odds.ts +674 -674
  33. package/src/utils/opticOdds.ts +71 -71
  34. package/src/utils/resolution.ts +221 -0
  35. package/src/utils/sports.ts +51 -51
  36. package/src/utils/spread.ts +97 -97
  37. package/tsconfig.json +16 -16
  38. package/webpack.config.js +24 -24
@@ -1,159 +1,159 @@
1
- import * as oddslib from 'oddslib';
2
- import {
3
- DIFF_BETWEEN_BOOKMAKERS_MESSAGE,
4
- NO_MATCHING_BOOKMAKERS_MESSAGE,
5
- ZERO_ODDS_MESSAGE,
6
- ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER,
7
- } from '../constants/errors';
8
-
9
- export const getBookmakersArray = (bookmakersData: any[], sportId: any, backupLiveOddsProviders: string[]) => {
10
- const sportBookmakersData = bookmakersData.find((data) => Number(data.sportId) === Number(sportId));
11
- if (sportBookmakersData) {
12
- if (sportBookmakersData.primaryBookmaker == '') {
13
- return backupLiveOddsProviders;
14
- }
15
- const bookmakersArray: string[] = [];
16
-
17
- sportBookmakersData.primaryBookmaker ? bookmakersArray.push(sportBookmakersData.primaryBookmaker) : '';
18
- sportBookmakersData.secondaryBookmaker ? bookmakersArray.push(sportBookmakersData.secondaryBookmaker) : '';
19
- sportBookmakersData.tertiaryBookmaker ? bookmakersArray.push(sportBookmakersData.tertiaryBookmaker) : '';
20
-
21
- return bookmakersArray;
22
- }
23
- return backupLiveOddsProviders;
24
- };
25
-
26
- export const checkOddsFromBookmakers = (
27
- oddsMap: Map<string, any>,
28
- arrayOfBookmakers: string[],
29
- isTwoPositionalSport: boolean,
30
- maxImpliedPercentageDifference: number,
31
- minOddsForDiffChecking: number
32
- ) => {
33
- // Main bookmaker odds
34
- const firstBookmakerOdds = oddsMap.get(arrayOfBookmakers[0].toLowerCase());
35
-
36
- if (!firstBookmakerOdds) {
37
- // If no matching bookmakers are found, return zero odds
38
- return {
39
- homeOdds: 0,
40
- awayOdds: 0,
41
- drawOdds: 0,
42
- errorMessage: NO_MATCHING_BOOKMAKERS_MESSAGE,
43
- };
44
- }
45
-
46
- const homeOdd = firstBookmakerOdds.homeOdds;
47
- const awayOdd = firstBookmakerOdds.awayOdds;
48
- const drawOdd = isTwoPositionalSport ? 0 : firstBookmakerOdds.drawOdds;
49
-
50
- // Check if any bookmaker has odds of 0 or 0.0001
51
- const hasZeroOrOne = arrayOfBookmakers.some((bookmakerId) => {
52
- const line = oddsMap.get(bookmakerId);
53
- if (line) {
54
- return (
55
- line.homeOdds === 0 ||
56
- line.awayOdds === 0 ||
57
- (!isTwoPositionalSport && line.drawOdds === 0) ||
58
- line.homeOdds === 1 ||
59
- line.awayOdds === 1 ||
60
- (!isTwoPositionalSport && line.drawOdds === 1)
61
- );
62
- }
63
- return false; // fix for es-lint
64
- });
65
-
66
- if (hasZeroOrOne) {
67
- // If any bookmaker has zero odds, return zero odds
68
- return {
69
- homeOdds: 0,
70
- awayOdds: 0,
71
- drawOdds: 0,
72
- errorMessage: arrayOfBookmakers.length === 1 ? ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER : ZERO_ODDS_MESSAGE,
73
- // TODO: Return sportsbook name with zero odds
74
- };
75
- }
76
-
77
- if (arrayOfBookmakers.length == 1) {
78
- return {
79
- homeOdds: homeOdd,
80
- awayOdds: awayOdd,
81
- drawOdds: isTwoPositionalSport ? 0 : drawOdd,
82
- };
83
- }
84
-
85
- // If none of the bookmakers have zero odds, check implied odds percentage difference
86
- const hasLargeImpliedPercentageDifference = arrayOfBookmakers.slice(1).some((bookmakerId) => {
87
- const line = oddsMap.get(bookmakerId);
88
- if (line) {
89
- const otherHomeOdd = line.homeOdds;
90
- const otherAwayOdd = line.awayOdds;
91
- const otherDrawOdd = line.drawOdds;
92
-
93
- const homeOddsImplied = oddslib.from('decimal', homeOdd).to('impliedProbability');
94
-
95
- const awayOddsImplied = oddslib.from('decimal', awayOdd).to('impliedProbability');
96
-
97
- // Calculate implied odds for the "draw" if it's not a two-positions sport
98
- const drawOddsImplied = isTwoPositionalSport
99
- ? 0
100
- : oddslib.from('decimal', drawOdd).to('impliedProbability');
101
-
102
- const otherHomeOddImplied = oddslib.from('decimal', otherHomeOdd).to('impliedProbability');
103
-
104
- const otherAwayOddImplied = oddslib.from('decimal', otherAwayOdd).to('impliedProbability');
105
-
106
- // Calculate implied odds for the "draw" if it's not a two-positions sport
107
- const otherDrawOddImplied = isTwoPositionalSport
108
- ? 0
109
- : oddslib.from('decimal', otherDrawOdd).to('impliedProbability');
110
-
111
- // Calculate the percentage difference for implied odds
112
- const homeOddsDifference = calculateImpliedOddsDifference(homeOddsImplied, otherHomeOddImplied);
113
-
114
- const awayOddsDifference = calculateImpliedOddsDifference(awayOddsImplied, otherAwayOddImplied);
115
-
116
- // Check implied odds difference for the "draw" only if it's not a two-positions sport
117
- const drawOddsDifference = isTwoPositionalSport
118
- ? 0
119
- : calculateImpliedOddsDifference(drawOddsImplied, otherDrawOddImplied);
120
-
121
- // Check if the percentage difference exceeds the threshold
122
- if (
123
- (homeOddsDifference > maxImpliedPercentageDifference &&
124
- homeOddsImplied > minOddsForDiffChecking &&
125
- otherHomeOddImplied > minOddsForDiffChecking) ||
126
- (awayOddsDifference > maxImpliedPercentageDifference &&
127
- awayOddsImplied > minOddsForDiffChecking &&
128
- otherAwayOddImplied > minOddsForDiffChecking) ||
129
- (!isTwoPositionalSport &&
130
- drawOddsDifference > maxImpliedPercentageDifference &&
131
- drawOddsImplied > minOddsForDiffChecking &&
132
- otherDrawOddImplied > minOddsForDiffChecking)
133
- ) {
134
- return true;
135
- }
136
- }
137
- return false;
138
- });
139
-
140
- if (hasLargeImpliedPercentageDifference) {
141
- return {
142
- homeOdds: 0,
143
- awayOdds: 0,
144
- drawOdds: 0,
145
- errorMessage: DIFF_BETWEEN_BOOKMAKERS_MESSAGE,
146
- };
147
- }
148
-
149
- return {
150
- homeOdds: homeOdd,
151
- awayOdds: awayOdd,
152
- drawOdds: isTwoPositionalSport ? 0 : drawOdd,
153
- };
154
- };
155
-
156
- export const calculateImpliedOddsDifference = (impliedOddsA: number, impliedOddsB: number): number => {
157
- const percentageDifference = (Math.abs(impliedOddsA - impliedOddsB) / impliedOddsA) * 100;
158
- return percentageDifference;
159
- };
1
+ import * as oddslib from 'oddslib';
2
+ import {
3
+ DIFF_BETWEEN_BOOKMAKERS_MESSAGE,
4
+ NO_MATCHING_BOOKMAKERS_MESSAGE,
5
+ ZERO_ODDS_MESSAGE,
6
+ ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER,
7
+ } from '../constants/errors';
8
+
9
+ export const getBookmakersArray = (bookmakersData: any[], sportId: any, backupLiveOddsProviders: string[]) => {
10
+ const sportBookmakersData = bookmakersData.find((data) => Number(data.sportId) === Number(sportId));
11
+ if (sportBookmakersData) {
12
+ if (sportBookmakersData.primaryBookmaker == '') {
13
+ return backupLiveOddsProviders;
14
+ }
15
+ const bookmakersArray: string[] = [];
16
+
17
+ sportBookmakersData.primaryBookmaker ? bookmakersArray.push(sportBookmakersData.primaryBookmaker) : '';
18
+ sportBookmakersData.secondaryBookmaker ? bookmakersArray.push(sportBookmakersData.secondaryBookmaker) : '';
19
+ sportBookmakersData.tertiaryBookmaker ? bookmakersArray.push(sportBookmakersData.tertiaryBookmaker) : '';
20
+
21
+ return bookmakersArray;
22
+ }
23
+ return backupLiveOddsProviders;
24
+ };
25
+
26
+ export const checkOddsFromBookmakers = (
27
+ oddsMap: Map<string, any>,
28
+ arrayOfBookmakers: string[],
29
+ isTwoPositionalSport: boolean,
30
+ maxImpliedPercentageDifference: number,
31
+ minOddsForDiffChecking: number
32
+ ) => {
33
+ // Main bookmaker odds
34
+ const firstBookmakerOdds = oddsMap.get(arrayOfBookmakers[0].toLowerCase());
35
+
36
+ if (!firstBookmakerOdds) {
37
+ // If no matching bookmakers are found, return zero odds
38
+ return {
39
+ homeOdds: 0,
40
+ awayOdds: 0,
41
+ drawOdds: 0,
42
+ errorMessage: NO_MATCHING_BOOKMAKERS_MESSAGE,
43
+ };
44
+ }
45
+
46
+ const homeOdd = firstBookmakerOdds.homeOdds;
47
+ const awayOdd = firstBookmakerOdds.awayOdds;
48
+ const drawOdd = isTwoPositionalSport ? 0 : firstBookmakerOdds.drawOdds;
49
+
50
+ // Check if any bookmaker has odds of 0 or 0.0001
51
+ const hasZeroOrOne = arrayOfBookmakers.some((bookmakerId) => {
52
+ const line = oddsMap.get(bookmakerId);
53
+ if (line) {
54
+ return (
55
+ line.homeOdds === 0 ||
56
+ line.awayOdds === 0 ||
57
+ (!isTwoPositionalSport && line.drawOdds === 0) ||
58
+ line.homeOdds === 1 ||
59
+ line.awayOdds === 1 ||
60
+ (!isTwoPositionalSport && line.drawOdds === 1)
61
+ );
62
+ }
63
+ return false; // fix for es-lint
64
+ });
65
+
66
+ if (hasZeroOrOne) {
67
+ // If any bookmaker has zero odds, return zero odds
68
+ return {
69
+ homeOdds: 0,
70
+ awayOdds: 0,
71
+ drawOdds: 0,
72
+ errorMessage: arrayOfBookmakers.length === 1 ? ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER : ZERO_ODDS_MESSAGE,
73
+ // TODO: Return sportsbook name with zero odds
74
+ };
75
+ }
76
+
77
+ if (arrayOfBookmakers.length == 1) {
78
+ return {
79
+ homeOdds: homeOdd,
80
+ awayOdds: awayOdd,
81
+ drawOdds: isTwoPositionalSport ? 0 : drawOdd,
82
+ };
83
+ }
84
+
85
+ // If none of the bookmakers have zero odds, check implied odds percentage difference
86
+ const hasLargeImpliedPercentageDifference = arrayOfBookmakers.slice(1).some((bookmakerId) => {
87
+ const line = oddsMap.get(bookmakerId);
88
+ if (line) {
89
+ const otherHomeOdd = line.homeOdds;
90
+ const otherAwayOdd = line.awayOdds;
91
+ const otherDrawOdd = line.drawOdds;
92
+
93
+ const homeOddsImplied = oddslib.from('decimal', homeOdd).to('impliedProbability');
94
+
95
+ const awayOddsImplied = oddslib.from('decimal', awayOdd).to('impliedProbability');
96
+
97
+ // Calculate implied odds for the "draw" if it's not a two-positions sport
98
+ const drawOddsImplied = isTwoPositionalSport
99
+ ? 0
100
+ : oddslib.from('decimal', drawOdd).to('impliedProbability');
101
+
102
+ const otherHomeOddImplied = oddslib.from('decimal', otherHomeOdd).to('impliedProbability');
103
+
104
+ const otherAwayOddImplied = oddslib.from('decimal', otherAwayOdd).to('impliedProbability');
105
+
106
+ // Calculate implied odds for the "draw" if it's not a two-positions sport
107
+ const otherDrawOddImplied = isTwoPositionalSport
108
+ ? 0
109
+ : oddslib.from('decimal', otherDrawOdd).to('impliedProbability');
110
+
111
+ // Calculate the percentage difference for implied odds
112
+ const homeOddsDifference = calculateImpliedOddsDifference(homeOddsImplied, otherHomeOddImplied);
113
+
114
+ const awayOddsDifference = calculateImpliedOddsDifference(awayOddsImplied, otherAwayOddImplied);
115
+
116
+ // Check implied odds difference for the "draw" only if it's not a two-positions sport
117
+ const drawOddsDifference = isTwoPositionalSport
118
+ ? 0
119
+ : calculateImpliedOddsDifference(drawOddsImplied, otherDrawOddImplied);
120
+
121
+ // Check if the percentage difference exceeds the threshold
122
+ if (
123
+ (homeOddsDifference > maxImpliedPercentageDifference &&
124
+ homeOddsImplied > minOddsForDiffChecking &&
125
+ otherHomeOddImplied > minOddsForDiffChecking) ||
126
+ (awayOddsDifference > maxImpliedPercentageDifference &&
127
+ awayOddsImplied > minOddsForDiffChecking &&
128
+ otherAwayOddImplied > minOddsForDiffChecking) ||
129
+ (!isTwoPositionalSport &&
130
+ drawOddsDifference > maxImpliedPercentageDifference &&
131
+ drawOddsImplied > minOddsForDiffChecking &&
132
+ otherDrawOddImplied > minOddsForDiffChecking)
133
+ ) {
134
+ return true;
135
+ }
136
+ }
137
+ return false;
138
+ });
139
+
140
+ if (hasLargeImpliedPercentageDifference) {
141
+ return {
142
+ homeOdds: 0,
143
+ awayOdds: 0,
144
+ drawOdds: 0,
145
+ errorMessage: DIFF_BETWEEN_BOOKMAKERS_MESSAGE,
146
+ };
147
+ }
148
+
149
+ return {
150
+ homeOdds: homeOdd,
151
+ awayOdds: awayOdd,
152
+ drawOdds: isTwoPositionalSport ? 0 : drawOdd,
153
+ };
154
+ };
155
+
156
+ export const calculateImpliedOddsDifference = (impliedOddsA: number, impliedOddsB: number): number => {
157
+ const percentageDifference = (Math.abs(impliedOddsA - impliedOddsB) / impliedOddsA) * 100;
158
+ return percentageDifference;
159
+ };