overtime-live-trading-utils 4.0.0-rc.6 → 4.0.0-rc.8
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 +2 -2
- package/src/tests/mock/OpticOddsMock/MockNBA.ts +5 -0
- package/src/tests/unit/bookmakers.test.ts +32 -13
- package/src/tests/unit/markets.test.ts +40 -16
- package/src/tests/unit/odds.test.ts +9 -9
- package/src/tests/unit/spread.test.ts +9 -11
- package/src/utils/bookmakers.ts +3 -0
- package/src/utils/markets.ts +0 -10
- package/src/utils/odds.ts +13 -45
- package/src/utils/spread.ts +2 -39
- /package/src/{constants/odds.ts → tests/mock/MockAnchors.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtime-live-trading-utils",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.8",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@types/node": "^20.8.10",
|
|
16
16
|
"oddslib": "^2.1.1",
|
|
17
|
-
"overtime-utils": "^0.1.
|
|
17
|
+
"overtime-utils": "^0.1.59",
|
|
18
18
|
"react": "^17.0.1"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
@@ -17267,3 +17267,8 @@ export const MockNbaData = {
|
|
|
17267
17267
|
...MockBaseNBAEvent,
|
|
17268
17268
|
odds: MockOdds,
|
|
17269
17269
|
};
|
|
17270
|
+
|
|
17271
|
+
export const MockOddsOnlyOver = {
|
|
17272
|
+
...MockBaseNBAEvent,
|
|
17273
|
+
odds: MockOdds.filter((odd) => odd.selection_line === 'over'),
|
|
17274
|
+
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DIFF_BETWEEN_BOOKMAKERS_MESSAGE, ZERO_ODDS_MESSAGE } from '../../constants/errors';
|
|
2
|
-
import {
|
|
2
|
+
import { __test__ } from '../../utils/bookmakers';
|
|
3
3
|
import { processMarket } from '../../utils/markets';
|
|
4
4
|
import { mapOpticOddsApiFixtureOdds } from '../../utils/opticOdds';
|
|
5
|
+
import { ODDS_THRESHOLD_ANCHORS } from '../mock/MockAnchors';
|
|
5
6
|
import { LeagueMocks } from '../mock/MockLeagueMap';
|
|
6
7
|
import {
|
|
7
8
|
MockOddsChildMarketsDifferentBookmakers,
|
|
@@ -27,9 +28,7 @@ describe('Bookmakers', () => {
|
|
|
27
28
|
freshMockSoccer,
|
|
28
29
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
29
30
|
['draftkings', 'bovada'],
|
|
30
|
-
[],
|
|
31
31
|
true,
|
|
32
|
-
undefined,
|
|
33
32
|
ODDS_THRESHOLD_ANCHORS,
|
|
34
33
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
35
34
|
lastPolledData,
|
|
@@ -53,9 +52,7 @@ describe('Bookmakers', () => {
|
|
|
53
52
|
freshMockSoccer,
|
|
54
53
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
55
54
|
['draftkings', 'bovada'],
|
|
56
|
-
[],
|
|
57
55
|
true,
|
|
58
|
-
undefined,
|
|
59
56
|
ODDS_THRESHOLD_ANCHORS,
|
|
60
57
|
LeagueMocks.leagueInfoOnlyParent,
|
|
61
58
|
lastPolledData,
|
|
@@ -79,9 +76,7 @@ describe('Bookmakers', () => {
|
|
|
79
76
|
freshMockSoccer,
|
|
80
77
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
81
78
|
['bovada', 'draftkings'],
|
|
82
|
-
[],
|
|
83
79
|
true,
|
|
84
|
-
undefined,
|
|
85
80
|
ODDS_THRESHOLD_ANCHORS,
|
|
86
81
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
87
82
|
lastPolledData,
|
|
@@ -105,9 +100,8 @@ describe('Bookmakers', () => {
|
|
|
105
100
|
freshMockSoccer,
|
|
106
101
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
107
102
|
['bovada', 'draftkings'],
|
|
108
|
-
[],
|
|
109
103
|
true,
|
|
110
|
-
|
|
104
|
+
|
|
111
105
|
ODDS_THRESHOLD_ANCHORS,
|
|
112
106
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
113
107
|
lastPolledData,
|
|
@@ -125,9 +119,8 @@ describe('Bookmakers', () => {
|
|
|
125
119
|
freshMockSoccer,
|
|
126
120
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
127
121
|
['bovada', 'draftkings'],
|
|
128
|
-
[],
|
|
129
122
|
true,
|
|
130
|
-
|
|
123
|
+
|
|
131
124
|
ODDS_THRESHOLD_ANCHORS,
|
|
132
125
|
LeagueMocks.leaguInfoDifferentPrimaryBookmaker,
|
|
133
126
|
lastPolledData,
|
|
@@ -145,9 +138,9 @@ describe('Bookmakers', () => {
|
|
|
145
138
|
freshMockSoccer,
|
|
146
139
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
147
140
|
['bovada', 'draftkings'],
|
|
148
|
-
|
|
141
|
+
|
|
149
142
|
true,
|
|
150
|
-
|
|
143
|
+
|
|
151
144
|
ODDS_THRESHOLD_ANCHORS,
|
|
152
145
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
153
146
|
lastPolledData,
|
|
@@ -158,3 +151,29 @@ describe('Bookmakers', () => {
|
|
|
158
151
|
expect(market.childMarkets.length).toBe(1);
|
|
159
152
|
});
|
|
160
153
|
});
|
|
154
|
+
|
|
155
|
+
describe('Bookmakers', () => {
|
|
156
|
+
it('Should export test only functions', () => {
|
|
157
|
+
const OUR_ODDS = 1.943;
|
|
158
|
+
const OTHER_ODDS = 1.847;
|
|
159
|
+
|
|
160
|
+
const THRESHOLDS = [
|
|
161
|
+
{ our: 1.02, otherMin: 1.01 },
|
|
162
|
+
{ our: 1.05, otherMin: 1.04 },
|
|
163
|
+
{ our: 1.1, otherMin: 1.09 },
|
|
164
|
+
{ our: 1.2, otherMin: 1.19 },
|
|
165
|
+
{ our: 1.3, otherMin: 1.29 },
|
|
166
|
+
{ our: 1.4, otherMin: 1.39 },
|
|
167
|
+
{ our: 1.5, otherMin: 1.48 },
|
|
168
|
+
{ our: 2.0, otherMin: 1.95 },
|
|
169
|
+
{ our: 2.5, otherMin: 2.25 },
|
|
170
|
+
{ our: 3.0, otherMin: 2.5 },
|
|
171
|
+
{ our: 4.0, otherMin: 3.5 },
|
|
172
|
+
{ our: 8.0, otherMin: 6.5 },
|
|
173
|
+
{ our: 10.0, otherMin: 8.0 },
|
|
174
|
+
{ our: 100.0, otherMin: 70.0 },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
expect(__test__.shouldBlockOdds(OUR_ODDS, OTHER_ODDS, THRESHOLDS)).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { NO_MARKETS_FOR_LEAGUE_ID } from '../../constants/errors';
|
|
2
|
-
import { ODDS_THRESHOLD_ANCHORS } from '../../constants/odds';
|
|
3
2
|
import { processMarket } from '../../utils/markets';
|
|
4
3
|
import { mapOpticOddsApiFixtureOdds } from '../../utils/opticOdds';
|
|
4
|
+
import { ODDS_THRESHOLD_ANCHORS } from '../mock/MockAnchors';
|
|
5
5
|
import { LeagueMocks } from '../mock/MockLeagueMap';
|
|
6
6
|
import { MockOnlyMoneyline, MockOpticSoccer } from '../mock/MockOpticSoccer';
|
|
7
7
|
import { mockSoccer } from '../mock/MockSoccerRedis';
|
|
8
|
-
import { MockNbaData } from '../mock/OpticOddsMock/MockNBA';
|
|
8
|
+
import { MockNbaData, MockOddsOnlyOver } from '../mock/OpticOddsMock/MockNBA';
|
|
9
9
|
import { MockRedisNba } from '../mock/OpticOddsMock/MockRedisNba';
|
|
10
10
|
import {
|
|
11
11
|
getLastPolledDataForBookmakers,
|
|
@@ -26,9 +26,9 @@ describe('Markets', () => {
|
|
|
26
26
|
freshMockSoccer,
|
|
27
27
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
28
28
|
['draftkings'],
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
true,
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
ODDS_THRESHOLD_ANCHORS,
|
|
33
33
|
LeagueMocks.leagueInfoOnlyParent,
|
|
34
34
|
lastPolledData,
|
|
@@ -46,9 +46,9 @@ describe('Markets', () => {
|
|
|
46
46
|
freshMockSoccer,
|
|
47
47
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
48
48
|
['draftkings'],
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
true,
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
ODDS_THRESHOLD_ANCHORS,
|
|
53
53
|
LeagueMocks.leagueInfoMockDisabledChilds,
|
|
54
54
|
lastPolledData,
|
|
@@ -66,9 +66,9 @@ describe('Markets', () => {
|
|
|
66
66
|
freshMockSoccer,
|
|
67
67
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
68
68
|
['draftkings'],
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
true,
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
ODDS_THRESHOLD_ANCHORS,
|
|
73
73
|
LeagueMocks.leagueInfoEnabledSpreadDisabledTotals,
|
|
74
74
|
lastPolledData,
|
|
@@ -90,9 +90,9 @@ describe('Markets', () => {
|
|
|
90
90
|
freshMockSoccer,
|
|
91
91
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
92
92
|
['draftkings'],
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
true,
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
ODDS_THRESHOLD_ANCHORS,
|
|
97
97
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
98
98
|
lastPolledData,
|
|
@@ -114,9 +114,9 @@ describe('Markets', () => {
|
|
|
114
114
|
freshMockSoccer,
|
|
115
115
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
116
116
|
['draftkings'],
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
true,
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
ODDS_THRESHOLD_ANCHORS,
|
|
121
121
|
LeagueMocks.leagueInfoEnabledAll,
|
|
122
122
|
lastPolledData,
|
|
@@ -170,9 +170,9 @@ describe('Markets', () => {
|
|
|
170
170
|
freshMockSoccer,
|
|
171
171
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
172
172
|
['draftkings'],
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
true,
|
|
175
|
-
|
|
175
|
+
|
|
176
176
|
ODDS_THRESHOLD_ANCHORS,
|
|
177
177
|
LeagueMocks.leagueInfoOnlyParentDiffSportId,
|
|
178
178
|
lastPolledData,
|
|
@@ -194,9 +194,33 @@ describe('Markets', () => {
|
|
|
194
194
|
freshMockSoccer,
|
|
195
195
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
196
196
|
['bovada', 'draftkings'], // this will be ignored as primaryBookmaker is defined in LeagueMap
|
|
197
|
-
|
|
197
|
+
|
|
198
|
+
true,
|
|
199
|
+
|
|
200
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
201
|
+
LeagueMocks.PlayerAssist, // league map with player props configured
|
|
202
|
+
lastPolledData,
|
|
203
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
204
|
+
playersMap
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
market.childMarkets.forEach((child: any) => {
|
|
208
|
+
expect(child.playerProps).toBeDefined();
|
|
209
|
+
expect(child.playerProps.playerId).toBeDefined();
|
|
210
|
+
expect(child.playerProps.playerName).toBeDefined();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('Should return child markets with player props that have some over odds', () => {
|
|
215
|
+
const freshMockSoccer = JSON.parse(JSON.stringify(MockRedisNba));
|
|
216
|
+
const freshMockOpticSoccer = JSON.parse(JSON.stringify(MockOddsOnlyOver));
|
|
217
|
+
const market = processMarket(
|
|
218
|
+
freshMockSoccer,
|
|
219
|
+
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
220
|
+
['bovada', 'draftkings'], // this will be ignored as primaryBookmaker is defined in LeagueMap
|
|
221
|
+
|
|
198
222
|
true,
|
|
199
|
-
|
|
223
|
+
|
|
200
224
|
ODDS_THRESHOLD_ANCHORS,
|
|
201
225
|
LeagueMocks.PlayerAssist, // league map with player props configured
|
|
202
226
|
lastPolledData,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ZERO_ODDS_MESSAGE_SINGLE_BOOKMAKER } from '../../constants/errors';
|
|
2
|
-
import { ODDS_THRESHOLD_ANCHORS } from '../../constants/odds';
|
|
3
2
|
import { processMarket } from '../../utils/markets';
|
|
4
3
|
import { mapOpticOddsApiFixtureOdds } from '../../utils/opticOdds';
|
|
4
|
+
import { ODDS_THRESHOLD_ANCHORS } from '../mock/MockAnchors';
|
|
5
5
|
import { LeagueMocks } from '../mock/MockLeagueMap';
|
|
6
6
|
import {
|
|
7
7
|
MockOddsChildMarketsGoodOdds,
|
|
@@ -27,9 +27,9 @@ describe('Odds', () => {
|
|
|
27
27
|
freshMockSoccer,
|
|
28
28
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
29
29
|
['draftkings'],
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
true,
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
ODDS_THRESHOLD_ANCHORS,
|
|
34
34
|
LeagueMocks.leagueInfoOnlyParent,
|
|
35
35
|
lastPolledData,
|
|
@@ -51,9 +51,9 @@ describe('Odds', () => {
|
|
|
51
51
|
freshMockSoccer,
|
|
52
52
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
53
53
|
['draftkings'],
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
true,
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
ODDS_THRESHOLD_ANCHORS,
|
|
58
58
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
59
59
|
lastPolledData,
|
|
@@ -77,9 +77,9 @@ describe('Odds', () => {
|
|
|
77
77
|
freshMockSoccer,
|
|
78
78
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
79
79
|
['draftkings'],
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
true,
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
ODDS_THRESHOLD_ANCHORS,
|
|
84
84
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
85
85
|
lastPolledData,
|
|
@@ -98,9 +98,9 @@ describe('Odds', () => {
|
|
|
98
98
|
freshMockSoccer,
|
|
99
99
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
100
100
|
['draftkings'],
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
true,
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
ODDS_THRESHOLD_ANCHORS,
|
|
105
105
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
106
106
|
lastPolledData,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ZERO_ODDS_AFTER_SPREAD_ADJUSTMENT } from '../../constants/errors';
|
|
2
|
-
import { ODDS_THRESHOLD_ANCHORS } from '../../constants/odds';
|
|
3
2
|
import { processMarket } from '../../utils/markets';
|
|
4
3
|
import { mapOpticOddsApiFixtureOdds } from '../../utils/opticOdds';
|
|
4
|
+
import { ODDS_THRESHOLD_ANCHORS } from '../mock/MockAnchors';
|
|
5
5
|
import { LeagueMocks } from '../mock/MockLeagueMap';
|
|
6
6
|
import { MockAfterSpreadZeroOdds1, MockOnlyMoneylineFavorite, MockOpticSoccer } from '../mock/MockOpticSoccer';
|
|
7
7
|
import { mockSoccer } from '../mock/MockSoccerRedis';
|
|
@@ -22,9 +22,7 @@ describe('Spread configuration', () => {
|
|
|
22
22
|
freshMockSoccer,
|
|
23
23
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
24
24
|
['draftkings'],
|
|
25
|
-
[],
|
|
26
25
|
false,
|
|
27
|
-
undefined,
|
|
28
26
|
ODDS_THRESHOLD_ANCHORS,
|
|
29
27
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
30
28
|
lastPolledData,
|
|
@@ -50,9 +48,9 @@ describe('Spread configuration', () => {
|
|
|
50
48
|
freshMockSoccer,
|
|
51
49
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
52
50
|
['draftkings'],
|
|
53
|
-
|
|
51
|
+
|
|
54
52
|
true,
|
|
55
|
-
|
|
53
|
+
|
|
56
54
|
ODDS_THRESHOLD_ANCHORS,
|
|
57
55
|
LeagueMocks.leagueInfoOnlyParent,
|
|
58
56
|
lastPolledData,
|
|
@@ -68,9 +66,9 @@ describe('Spread configuration', () => {
|
|
|
68
66
|
freshMockSoccer,
|
|
69
67
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
70
68
|
['draftkings'],
|
|
71
|
-
|
|
69
|
+
|
|
72
70
|
true,
|
|
73
|
-
|
|
71
|
+
|
|
74
72
|
ODDS_THRESHOLD_ANCHORS,
|
|
75
73
|
LeagueMocks.leagueInfoOnlyParentWithSpreadAdded,
|
|
76
74
|
lastPolledData,
|
|
@@ -106,9 +104,9 @@ describe('Spread configuration', () => {
|
|
|
106
104
|
freshMockSoccer,
|
|
107
105
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
108
106
|
['draftkings'],
|
|
109
|
-
|
|
107
|
+
|
|
110
108
|
true,
|
|
111
|
-
|
|
109
|
+
|
|
112
110
|
ODDS_THRESHOLD_ANCHORS,
|
|
113
111
|
LeagueMocks.leagueInfoOnlyParent,
|
|
114
112
|
lastPolledData,
|
|
@@ -124,9 +122,9 @@ describe('Spread configuration', () => {
|
|
|
124
122
|
freshMockSoccer,
|
|
125
123
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
126
124
|
['draftkings'],
|
|
127
|
-
|
|
125
|
+
|
|
128
126
|
true,
|
|
129
|
-
|
|
127
|
+
|
|
130
128
|
ODDS_THRESHOLD_ANCHORS,
|
|
131
129
|
LeagueMocks.leagueInfoOnlyParentWithSpreadAdded,
|
|
132
130
|
lastPolledData,
|
package/src/utils/bookmakers.ts
CHANGED
|
@@ -295,3 +295,6 @@ const shouldBlockOdds = (ourOdds: number, otherOdds: number, anchors: Anchor[])
|
|
|
295
295
|
// Block if the other book is below the required threshold
|
|
296
296
|
return otherOdds < requiredOther;
|
|
297
297
|
};
|
|
298
|
+
|
|
299
|
+
// Export only when running tests
|
|
300
|
+
export const __test__ = { getRequiredOtherOdds, shouldBlockOdds };
|
package/src/utils/markets.ts
CHANGED
|
@@ -13,9 +13,7 @@ import { adjustAddedSpread } from './spread';
|
|
|
13
13
|
* @param {Object} market - The market API object to process
|
|
14
14
|
* @param {Object} apiResponseWithOdds - Provider's API object to process
|
|
15
15
|
* @param {Array} liveOddsProviders - Odds providers for live odds
|
|
16
|
-
* @param {Array} spreadData - Spread data for odds.
|
|
17
16
|
* @param {Boolean} isDrawAvailable - Is it two or three-positional sport
|
|
18
|
-
* @param {Number} defaultSpreadForLiveMarkets - Default spread for live markets
|
|
19
17
|
* @param {Object} leagueMap - League map for additional league information
|
|
20
18
|
* @param {LastPolledArray} lastPolledData - Array containing last polled timestamps for bookmakers
|
|
21
19
|
* @param {Number} maxAllowedProviderDataStaleDelay - Maximum allowed delay for provider data to be considered fresh
|
|
@@ -26,25 +24,19 @@ export const processMarket = (
|
|
|
26
24
|
market: any,
|
|
27
25
|
apiResponseWithOdds: OddsObject,
|
|
28
26
|
liveOddsProviders: any,
|
|
29
|
-
spreadData: any,
|
|
30
27
|
isDrawAvailable: any,
|
|
31
|
-
defaultSpreadForLiveMarkets: any,
|
|
32
28
|
anchors: Anchor[],
|
|
33
29
|
leagueMap: any,
|
|
34
30
|
lastPolledData: LastPolledArray,
|
|
35
31
|
maxAllowedProviderDataStaleDelay: number,
|
|
36
32
|
playersMap: Map<string, number>
|
|
37
33
|
) => {
|
|
38
|
-
const sportSpreadData = spreadData.filter((data: any) => data.sportId === String(market.leagueId));
|
|
39
34
|
const leagueInfo = getLeagueInfo(market.leagueId, leagueMap);
|
|
40
35
|
|
|
41
36
|
const moneylineOdds = getParentOdds(
|
|
42
37
|
!isDrawAvailable,
|
|
43
|
-
sportSpreadData,
|
|
44
38
|
liveOddsProviders,
|
|
45
39
|
apiResponseWithOdds,
|
|
46
|
-
market.leagueId,
|
|
47
|
-
defaultSpreadForLiveMarkets,
|
|
48
40
|
anchors,
|
|
49
41
|
leagueInfo,
|
|
50
42
|
lastPolledData,
|
|
@@ -85,10 +77,8 @@ export const processMarket = (
|
|
|
85
77
|
|
|
86
78
|
const childMarkets = createChildMarkets(
|
|
87
79
|
apiResponseWithOdds,
|
|
88
|
-
sportSpreadData,
|
|
89
80
|
market.leagueId,
|
|
90
81
|
liveOddsProviders,
|
|
91
|
-
defaultSpreadForLiveMarkets,
|
|
92
82
|
leagueMap,
|
|
93
83
|
lastPolledData,
|
|
94
84
|
maxAllowedProviderDataStaleDelay,
|
package/src/utils/odds.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as oddslib from 'oddslib';
|
|
2
2
|
import { MarketType, MarketTypeMap } from 'overtime-utils';
|
|
3
|
-
import { DRAW,
|
|
3
|
+
import { DRAW, ZERO } from '../constants/common';
|
|
4
4
|
import { LAST_POLLED_TOO_OLD, NO_MARKETS_FOR_LEAGUE_ID } from '../constants/errors';
|
|
5
5
|
import { MoneylineTypes } from '../enums/sports';
|
|
6
6
|
import { Anchor, HomeAwayTeams, Odds, OddsObject } from '../types/odds';
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isLastPolledForBookmakersValid,
|
|
13
13
|
} from './bookmakers';
|
|
14
14
|
import { getLeagueInfo } from './sports';
|
|
15
|
-
import {
|
|
15
|
+
import { sanityCheckForOdds } from './spread';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Converts a given odds value from one format to another.
|
|
@@ -123,11 +123,8 @@ export const filterOddsByMarketNameTeamNameBookmaker = (
|
|
|
123
123
|
*/
|
|
124
124
|
export const getParentOdds = (
|
|
125
125
|
isTwoPositionalSport: boolean,
|
|
126
|
-
sportSpreadData: any[],
|
|
127
126
|
liveOddsProviders: any[],
|
|
128
127
|
oddsApiObject: OddsObject,
|
|
129
|
-
sportId: string,
|
|
130
|
-
defaultSpreadForLiveMarkets: number,
|
|
131
128
|
anchors: Anchor[],
|
|
132
129
|
leagueInfo: LeagueConfigInfo[],
|
|
133
130
|
lastPolledData: LastPolledArray,
|
|
@@ -177,14 +174,8 @@ export const getParentOdds = (
|
|
|
177
174
|
: [oddsObject.homeOdds, oddsObject.awayOdds, oddsObject.drawOdds];
|
|
178
175
|
|
|
179
176
|
let parentOdds = primaryBookmakerOdds.map((odd) => convertOddsToImpl(odd));
|
|
180
|
-
|
|
177
|
+
parentOdds = sanityCheckForOdds(parentOdds);
|
|
181
178
|
|
|
182
|
-
if (spreadData !== null) {
|
|
183
|
-
parentOdds = adjustSpreadOnOdds(parentOdds, spreadData.minSpread, spreadData.targetSpread);
|
|
184
|
-
} else {
|
|
185
|
-
// Use min spread by sport if available, otherwise use default min spread
|
|
186
|
-
parentOdds = adjustSpreadOnOdds(parentOdds, defaultSpreadForLiveMarkets, 0);
|
|
187
|
-
}
|
|
188
179
|
return { odds: parentOdds };
|
|
189
180
|
};
|
|
190
181
|
|
|
@@ -201,10 +192,8 @@ export const getParentOdds = (
|
|
|
201
192
|
*/
|
|
202
193
|
export const createChildMarkets: (
|
|
203
194
|
apiResponseWithOdds: OddsObject,
|
|
204
|
-
spreadDataForSport: any,
|
|
205
195
|
leagueId: number,
|
|
206
196
|
liveOddsProviders: any,
|
|
207
|
-
defaultSpreadForLiveMarkets: any,
|
|
208
197
|
leagueMap: any,
|
|
209
198
|
lastPolledData: LastPolledArray,
|
|
210
199
|
maxAllowedProviderDataStaleDelay: number,
|
|
@@ -212,10 +201,8 @@ export const createChildMarkets: (
|
|
|
212
201
|
playersMap: Map<string, number>
|
|
213
202
|
) => ChildMarket[] = (
|
|
214
203
|
apiResponseWithOdds,
|
|
215
|
-
spreadDataForSport,
|
|
216
204
|
leagueId,
|
|
217
205
|
liveOddsProviders,
|
|
218
|
-
defaultSpreadForLiveMarkets,
|
|
219
206
|
leagueMap,
|
|
220
207
|
lastPolledData,
|
|
221
208
|
maxAllowedProviderDataStaleDelay,
|
|
@@ -273,11 +260,7 @@ export const createChildMarkets: (
|
|
|
273
260
|
const otherFormattedOdds = [...groupAndFormatCorrectScoreOdds(correctScoreOdds, commonData)];
|
|
274
261
|
|
|
275
262
|
// odds are converted to implied probability inside adjustSpreadOnChildOdds
|
|
276
|
-
const homeAwayOddsWithSpreadAdjusted = adjustSpreadOnChildOdds(
|
|
277
|
-
homeAwayFormattedOdds,
|
|
278
|
-
spreadDataForSport,
|
|
279
|
-
defaultSpreadForLiveMarkets
|
|
280
|
-
);
|
|
263
|
+
const homeAwayOddsWithSpreadAdjusted = adjustSpreadOnChildOdds(homeAwayFormattedOdds);
|
|
281
264
|
|
|
282
265
|
homeAwayOddsWithSpreadAdjusted.forEach((data) => {
|
|
283
266
|
let childMarket = {
|
|
@@ -365,8 +348,8 @@ export const filterOdds = (oddsArray: Odds, leagueInfos: LeagueConfigInfo[], pla
|
|
|
365
348
|
.map((leagueInfo) => leagueInfo.marketName.toLowerCase());
|
|
366
349
|
return oddsArray.reduce((acc: any, odd: any) => {
|
|
367
350
|
if (allChildMarketsTypes.includes(odd.marketName.toLowerCase())) {
|
|
368
|
-
const { points, marketName, selection, selectionLine, sportsBookName, playerId } = odd;
|
|
369
|
-
if (playerId && !playersMap.has(playerId)) {
|
|
351
|
+
const { points, marketName, selection, selectionLine, sportsBookName, playerId, isMain } = odd;
|
|
352
|
+
if (playerId && (!playersMap.has(playerId) || !isMain)) {
|
|
370
353
|
return acc;
|
|
371
354
|
}
|
|
372
355
|
const key = `${sportsBookName.toLowerCase()}_${marketName.toLowerCase()}_${points}_${selection}_${selectionLine}`;
|
|
@@ -478,7 +461,7 @@ export const groupAndFormatTotalOdds = (oddsArray: any[], commonData: HomeAwayTe
|
|
|
478
461
|
|
|
479
462
|
acc.push({
|
|
480
463
|
line: line as any,
|
|
481
|
-
odds: [(value as any).over, (value as any).under].
|
|
464
|
+
odds: [(value as any).over, (value as any).under].map((odd) => odd || ZERO),
|
|
482
465
|
typeId: !shouldIncreaseTypeId ? (value as any).typeId : Number((value as any).typeId) + 1,
|
|
483
466
|
sportId: (value as any).sportId,
|
|
484
467
|
type: (value as any).type,
|
|
@@ -936,33 +919,18 @@ export const groupAndFormatDoubleChanceOdds = (oddsArray: any[], commonData: Hom
|
|
|
936
919
|
};
|
|
937
920
|
|
|
938
921
|
// used for home/away markets
|
|
939
|
-
export const adjustSpreadOnChildOdds = (
|
|
940
|
-
iterableGroupedOdds: any[],
|
|
941
|
-
spreadDataForSport: any,
|
|
942
|
-
defaultSpreadForLiveMarkets: any
|
|
943
|
-
) => {
|
|
922
|
+
export const adjustSpreadOnChildOdds = (iterableGroupedOdds: any[]) => {
|
|
944
923
|
const result: any[] = [];
|
|
945
924
|
iterableGroupedOdds.forEach((data) => {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
spreadDataForSport,
|
|
951
|
-
data.sportId,
|
|
952
|
-
data.typeId,
|
|
953
|
-
defaultSpreadForLiveMarkets
|
|
954
|
-
);
|
|
955
|
-
|
|
956
|
-
let adjustedOdds;
|
|
957
|
-
if (spreadData !== null) {
|
|
958
|
-
adjustedOdds = adjustSpreadOnOdds(odds, spreadData.minSpread, spreadData.targetSpread);
|
|
959
|
-
} else {
|
|
960
|
-
adjustedOdds = adjustSpreadOnOdds(odds, defaultSpreadForLiveMarkets, 0);
|
|
925
|
+
let odds = data.odds.map((odd: number) => convertOddsToImpl(odd) || ZERO);
|
|
926
|
+
if (data.odds.length > 0) {
|
|
927
|
+
if (data.odds.length > 1) {
|
|
928
|
+
odds = sanityCheckForOdds(odds);
|
|
961
929
|
}
|
|
962
930
|
|
|
963
931
|
result.push({
|
|
964
932
|
...data,
|
|
965
|
-
odds
|
|
933
|
+
odds,
|
|
966
934
|
});
|
|
967
935
|
}
|
|
968
936
|
});
|
package/src/utils/spread.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LeagueConfigInfo } from '../types/sports';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const sanityCheckForOdds = (impliedProbs: number[]) => {
|
|
4
4
|
// Step 1: Check if any implied probability is zero
|
|
5
5
|
if (impliedProbs.some((prob) => prob === 0)) {
|
|
6
6
|
return impliedProbs;
|
|
@@ -14,44 +14,7 @@ export const adjustSpreadOnOdds = (impliedProbs: number[], minSpread: number, ta
|
|
|
14
14
|
return Array(impliedProbs.length).fill(0);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
if (targetSpread === 0) {
|
|
19
|
-
const currentSpread = (totalImpliedProbs - 1) * 100;
|
|
20
|
-
// If minSpread is set and greater than current spread, use minSpread
|
|
21
|
-
if (minSpread > currentSpread) {
|
|
22
|
-
targetSpread = minSpread;
|
|
23
|
-
} else {
|
|
24
|
-
// If minSpread is less than current spread, return odds as they are
|
|
25
|
-
return impliedProbs;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Step 5: Calculate the target total implied probabilities
|
|
30
|
-
const targetTotalImpliedProbs = 1 + targetSpread / 100;
|
|
31
|
-
|
|
32
|
-
// Step 6: Calculate the adjustment factor
|
|
33
|
-
const adjustmentFactor = targetTotalImpliedProbs / totalImpliedProbs;
|
|
34
|
-
|
|
35
|
-
// Step 7: Adjust the probabilities to reflect the target spread
|
|
36
|
-
let adjustedImpliedProbs = impliedProbs.map((prob) => prob * adjustmentFactor);
|
|
37
|
-
|
|
38
|
-
// Step 8: Check if any adjusted probability equals or exceeds 1
|
|
39
|
-
if (adjustedImpliedProbs.some((prob) => prob >= 1)) {
|
|
40
|
-
return Array(impliedProbs.length).fill(0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Step 9: Ensure the sum of the adjusted probabilities equals the target total implied probabilities
|
|
44
|
-
const sumAdjustedProbs = adjustedImpliedProbs.reduce((sum, prob) => sum + prob, 0);
|
|
45
|
-
|
|
46
|
-
// Step 10: If the sum of the adjusted probabilities is less than 1, return zeros
|
|
47
|
-
if (sumAdjustedProbs < 1) {
|
|
48
|
-
return Array(impliedProbs.length).fill(0);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const normalizationFactor = targetTotalImpliedProbs / sumAdjustedProbs;
|
|
52
|
-
adjustedImpliedProbs = adjustedImpliedProbs.map((prob) => prob * normalizationFactor);
|
|
53
|
-
|
|
54
|
-
return adjustedImpliedProbs;
|
|
17
|
+
return impliedProbs;
|
|
55
18
|
};
|
|
56
19
|
|
|
57
20
|
export const getSpreadData = (
|
|
File without changes
|