overtime-live-trading-utils 4.0.3 → 4.0.4
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/mock/MockLeagueMap.ts +16 -0
- package/src/tests/mock/OpticOddsMock/MockNBAPlayerPropsAdjustment.ts +476 -0
- package/src/tests/mock/OpticOddsMock/MockRedisNbaPlayerPropsAdjustment.ts +22 -0
- package/src/tests/unit/bookmakers.test.ts +173 -10
- package/src/tests/unit/markets.test.ts +17 -24
- package/src/tests/unit/odds.test.ts +9 -12
- package/src/tests/unit/spread.test.ts +11 -13
- package/src/tests/utils/helper.ts +10 -3
- package/src/utils/bookmakers.ts +31 -1
- package/src/utils/markets.ts +4 -2
- package/src/utils/odds.ts +8 -5
|
@@ -11,10 +11,19 @@ import {
|
|
|
11
11
|
MockOnlyMoneylineWithDifferentSportsbook,
|
|
12
12
|
} from '../mock/MockOpticSoccer';
|
|
13
13
|
import { mockSoccer } from '../mock/MockSoccerRedis';
|
|
14
|
+
import {
|
|
15
|
+
MockPlayerPropsWithLowPoints,
|
|
16
|
+
MockPlayerPropsWithNegativeOnePointDiff,
|
|
17
|
+
MockPlayerPropsWithNoMatchingLine,
|
|
18
|
+
MockPlayerPropsWithOnePointDiff,
|
|
19
|
+
MockPlayerPropsWithTwoPointDiff,
|
|
20
|
+
} from '../mock/OpticOddsMock/MockNBAPlayerPropsAdjustment';
|
|
21
|
+
import { MockRedisNbaPlayerPropsAdjustment } from '../mock/OpticOddsMock/MockRedisNbaPlayerPropsAdjustment';
|
|
14
22
|
import {
|
|
15
23
|
getLastPolledDataForBookmakers,
|
|
16
24
|
getPlayersMap,
|
|
17
25
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
26
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK,
|
|
18
27
|
} from '../utils/helper';
|
|
19
28
|
|
|
20
29
|
const lastPolledData = getLastPolledDataForBookmakers();
|
|
@@ -33,7 +42,8 @@ describe('Bookmakers', () => {
|
|
|
33
42
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
34
43
|
lastPolledData,
|
|
35
44
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
36
|
-
playersMap
|
|
45
|
+
playersMap,
|
|
46
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
37
47
|
);
|
|
38
48
|
|
|
39
49
|
const hasOdds = market.odds.some(
|
|
@@ -57,7 +67,8 @@ describe('Bookmakers', () => {
|
|
|
57
67
|
LeagueMocks.leagueInfoOnlyParent,
|
|
58
68
|
lastPolledData,
|
|
59
69
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
60
|
-
playersMap
|
|
70
|
+
playersMap,
|
|
71
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
61
72
|
);
|
|
62
73
|
|
|
63
74
|
const hasOdds = market.odds.some(
|
|
@@ -81,7 +92,8 @@ describe('Bookmakers', () => {
|
|
|
81
92
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
82
93
|
lastPolledData,
|
|
83
94
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
84
|
-
playersMap
|
|
95
|
+
playersMap,
|
|
96
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
85
97
|
);
|
|
86
98
|
|
|
87
99
|
const hasOdds = market.odds.some(
|
|
@@ -101,12 +113,12 @@ describe('Bookmakers', () => {
|
|
|
101
113
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
102
114
|
['bovada', 'draftkings'],
|
|
103
115
|
true,
|
|
104
|
-
|
|
105
116
|
ODDS_THRESHOLD_ANCHORS,
|
|
106
117
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
107
118
|
lastPolledData,
|
|
108
119
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
109
|
-
playersMap
|
|
120
|
+
playersMap,
|
|
121
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
110
122
|
);
|
|
111
123
|
|
|
112
124
|
expect(market.childMarkets.length).toBe(2);
|
|
@@ -120,12 +132,12 @@ describe('Bookmakers', () => {
|
|
|
120
132
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
121
133
|
['bovada', 'draftkings'],
|
|
122
134
|
true,
|
|
123
|
-
|
|
124
135
|
ODDS_THRESHOLD_ANCHORS,
|
|
125
136
|
LeagueMocks.leaguInfoDifferentPrimaryBookmaker,
|
|
126
137
|
lastPolledData,
|
|
127
138
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
128
|
-
playersMap
|
|
139
|
+
playersMap,
|
|
140
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
129
141
|
);
|
|
130
142
|
|
|
131
143
|
expect(market.childMarkets.length).toBe(3);
|
|
@@ -138,14 +150,13 @@ describe('Bookmakers', () => {
|
|
|
138
150
|
freshMockSoccer,
|
|
139
151
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
140
152
|
['bovada', 'draftkings'],
|
|
141
|
-
|
|
142
153
|
true,
|
|
143
|
-
|
|
144
154
|
ODDS_THRESHOLD_ANCHORS,
|
|
145
155
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
146
156
|
lastPolledData,
|
|
147
157
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
148
|
-
playersMap
|
|
158
|
+
playersMap,
|
|
159
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
149
160
|
);
|
|
150
161
|
|
|
151
162
|
expect(market.childMarkets.length).toBe(1);
|
|
@@ -177,3 +188,155 @@ describe('Bookmakers', () => {
|
|
|
177
188
|
expect(__test__.shouldBlockOdds(OUR_ODDS, OTHER_ODDS, THRESHOLDS)).toBe(true);
|
|
178
189
|
});
|
|
179
190
|
});
|
|
191
|
+
|
|
192
|
+
describe('Bookmakers - Player Props Point Adjustment', () => {
|
|
193
|
+
it('Should find matching line when secondary bookmaker has 1 point higher', () => {
|
|
194
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
195
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithOnePointDiff));
|
|
196
|
+
|
|
197
|
+
const market = processMarket(
|
|
198
|
+
freshMockRedis,
|
|
199
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
200
|
+
['superbet', 'draftkings'],
|
|
201
|
+
true,
|
|
202
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
203
|
+
LeagueMocks.PlayerAssistWithSecondaryBookmaker,
|
|
204
|
+
lastPolledData,
|
|
205
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
206
|
+
playersMap,
|
|
207
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Should have child markets for player props
|
|
211
|
+
expect(market.childMarkets.length).toBeGreaterThan(0);
|
|
212
|
+
// Check that the player props were matched despite the 1 point difference
|
|
213
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId == 54321);
|
|
214
|
+
expect(playerPropsMarket).toBeDefined();
|
|
215
|
+
expect(playerPropsMarket.playerProps.playerName).toContain('James Harden');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('Should find matching line when secondary bookmaker has 1 point lower', () => {
|
|
219
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
220
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithNegativeOnePointDiff));
|
|
221
|
+
|
|
222
|
+
const market = processMarket(
|
|
223
|
+
freshMockRedis,
|
|
224
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
225
|
+
['superbet', 'draftkings'],
|
|
226
|
+
true,
|
|
227
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
228
|
+
LeagueMocks.PlayerAssistWithSecondaryBookmaker,
|
|
229
|
+
lastPolledData,
|
|
230
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
231
|
+
playersMap,
|
|
232
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Should have child markets for player props
|
|
236
|
+
expect(market.childMarkets.length).toBeGreaterThan(0);
|
|
237
|
+
|
|
238
|
+
// Check that the player props were matched despite the -1 point difference
|
|
239
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId === 77889);
|
|
240
|
+
expect(playerPropsMarket).toBeDefined();
|
|
241
|
+
expect(playerPropsMarket.playerProps.playerName).toContain('LeBron James');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('Should find matching line when secondary bookmaker has 2 points higher', () => {
|
|
245
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
246
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithTwoPointDiff));
|
|
247
|
+
|
|
248
|
+
const market = processMarket(
|
|
249
|
+
freshMockRedis,
|
|
250
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
251
|
+
['superbet', 'draftkings'],
|
|
252
|
+
true,
|
|
253
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
254
|
+
LeagueMocks.PlayerAssistWithSecondaryBookmaker,
|
|
255
|
+
lastPolledData,
|
|
256
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
257
|
+
playersMap,
|
|
258
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// Should have child markets for player props
|
|
262
|
+
expect(market.childMarkets.length).toBeGreaterThan(0);
|
|
263
|
+
|
|
264
|
+
// Check that the player props were matched despite the 2 point difference
|
|
265
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId === 99001);
|
|
266
|
+
expect(playerPropsMarket).toBeDefined();
|
|
267
|
+
expect(playerPropsMarket.playerProps.playerName).toContain('Stephen Curry');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('Should NOT find matching line when difference is too large (beyond step range)', () => {
|
|
271
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
272
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithNoMatchingLine));
|
|
273
|
+
|
|
274
|
+
const market = processMarket(
|
|
275
|
+
freshMockRedis,
|
|
276
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
277
|
+
['superbet', 'draftkings'],
|
|
278
|
+
true,
|
|
279
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
280
|
+
LeagueMocks.PlayerAssistWithSecondaryBookmaker,
|
|
281
|
+
lastPolledData,
|
|
282
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
283
|
+
playersMap,
|
|
284
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Check that the player props were NOT matched (5 point difference is too large for 25.5 points)
|
|
288
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId === 44556);
|
|
289
|
+
expect(playerPropsMarket).toBeUndefined();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('Should find matching line with low point values using appropriate step calculation', () => {
|
|
293
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
294
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithLowPoints));
|
|
295
|
+
|
|
296
|
+
const market = processMarket(
|
|
297
|
+
freshMockRedis,
|
|
298
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
299
|
+
['superbet', 'draftkings'],
|
|
300
|
+
true,
|
|
301
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
302
|
+
LeagueMocks.PlayerAssistWithSecondaryBookmaker,
|
|
303
|
+
lastPolledData,
|
|
304
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
305
|
+
playersMap,
|
|
306
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Should have child markets for player props
|
|
310
|
+
expect(market.childMarkets.length).toBeGreaterThan(0);
|
|
311
|
+
|
|
312
|
+
// Check that the player props were matched with low points (10.5 vs 11.5)
|
|
313
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId === 11223);
|
|
314
|
+
expect(playerPropsMarket).toBeDefined();
|
|
315
|
+
expect(playerPropsMarket.playerProps.playerName).toContain('Anthony Davis');
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('Should work correctly with only primary bookmaker (no adjustment needed)', () => {
|
|
319
|
+
const freshMockRedis = JSON.parse(JSON.stringify(MockRedisNbaPlayerPropsAdjustment));
|
|
320
|
+
const freshMockOptic = JSON.parse(JSON.stringify(MockPlayerPropsWithOnePointDiff));
|
|
321
|
+
|
|
322
|
+
const market = processMarket(
|
|
323
|
+
freshMockRedis,
|
|
324
|
+
mapOpticOddsApiFixtureOdds([freshMockOptic])[0],
|
|
325
|
+
['superbet'], // Only primary bookmaker
|
|
326
|
+
true,
|
|
327
|
+
ODDS_THRESHOLD_ANCHORS,
|
|
328
|
+
LeagueMocks.PlayerAssist, // Config without secondary bookmaker
|
|
329
|
+
lastPolledData,
|
|
330
|
+
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
331
|
+
playersMap,
|
|
332
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
// Should still have child markets for player props
|
|
336
|
+
expect(market.childMarkets.length).toBeGreaterThan(0);
|
|
337
|
+
|
|
338
|
+
// Check that the player props were included (no adjustment logic needed)
|
|
339
|
+
const playerPropsMarket = market.childMarkets.find((child: any) => child.playerProps?.playerId === 54321);
|
|
340
|
+
expect(playerPropsMarket).toBeDefined();
|
|
341
|
+
});
|
|
342
|
+
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
getLastPolledDataForBookmakers,
|
|
12
12
|
getPlayersMap,
|
|
13
13
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
14
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK,
|
|
14
15
|
} from '../utils/helper';
|
|
15
16
|
|
|
16
17
|
const lastPolledData = getLastPolledDataForBookmakers();
|
|
@@ -26,14 +27,13 @@ describe('Markets', () => {
|
|
|
26
27
|
freshMockSoccer,
|
|
27
28
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
28
29
|
['draftkings'],
|
|
29
|
-
|
|
30
30
|
true,
|
|
31
|
-
|
|
32
31
|
ODDS_THRESHOLD_ANCHORS,
|
|
33
32
|
LeagueMocks.leagueInfoOnlyParent,
|
|
34
33
|
lastPolledData,
|
|
35
34
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
36
|
-
playersMap
|
|
35
|
+
playersMap,
|
|
36
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
expect(market.childMarkets).toHaveLength(0);
|
|
@@ -46,14 +46,13 @@ describe('Markets', () => {
|
|
|
46
46
|
freshMockSoccer,
|
|
47
47
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
48
48
|
['draftkings'],
|
|
49
|
-
|
|
50
49
|
true,
|
|
51
|
-
|
|
52
50
|
ODDS_THRESHOLD_ANCHORS,
|
|
53
51
|
LeagueMocks.leagueInfoMockDisabledChilds,
|
|
54
52
|
lastPolledData,
|
|
55
53
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
56
|
-
playersMap
|
|
54
|
+
playersMap,
|
|
55
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
57
56
|
);
|
|
58
57
|
|
|
59
58
|
expect(market.childMarkets).toHaveLength(0);
|
|
@@ -66,14 +65,13 @@ describe('Markets', () => {
|
|
|
66
65
|
freshMockSoccer,
|
|
67
66
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
68
67
|
['draftkings'],
|
|
69
|
-
|
|
70
68
|
true,
|
|
71
|
-
|
|
72
69
|
ODDS_THRESHOLD_ANCHORS,
|
|
73
70
|
LeagueMocks.leagueInfoEnabledSpreadDisabledTotals,
|
|
74
71
|
lastPolledData,
|
|
75
72
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
76
|
-
playersMap
|
|
73
|
+
playersMap,
|
|
74
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
77
75
|
);
|
|
78
76
|
|
|
79
77
|
const containsSpread = market.childMarkets.some((child: any) => child.type === 'spread');
|
|
@@ -90,14 +88,13 @@ describe('Markets', () => {
|
|
|
90
88
|
freshMockSoccer,
|
|
91
89
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
92
90
|
['draftkings'],
|
|
93
|
-
|
|
94
91
|
true,
|
|
95
|
-
|
|
96
92
|
ODDS_THRESHOLD_ANCHORS,
|
|
97
93
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
98
94
|
lastPolledData,
|
|
99
95
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
100
|
-
playersMap
|
|
96
|
+
playersMap,
|
|
97
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
101
98
|
);
|
|
102
99
|
|
|
103
100
|
const containsSpread = market.childMarkets.some((child: any) => child.type === 'spread');
|
|
@@ -114,14 +111,13 @@ describe('Markets', () => {
|
|
|
114
111
|
freshMockSoccer,
|
|
115
112
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
116
113
|
['draftkings'],
|
|
117
|
-
|
|
118
114
|
true,
|
|
119
|
-
|
|
120
115
|
ODDS_THRESHOLD_ANCHORS,
|
|
121
116
|
LeagueMocks.leagueInfoEnabledAll,
|
|
122
117
|
lastPolledData,
|
|
123
118
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
124
|
-
playersMap
|
|
119
|
+
playersMap,
|
|
120
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
125
121
|
);
|
|
126
122
|
|
|
127
123
|
const containsSpread = market.childMarkets.some((child: any) => child.type === 'spread');
|
|
@@ -170,14 +166,13 @@ describe('Markets', () => {
|
|
|
170
166
|
freshMockSoccer,
|
|
171
167
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
172
168
|
['draftkings'],
|
|
173
|
-
|
|
174
169
|
true,
|
|
175
|
-
|
|
176
170
|
ODDS_THRESHOLD_ANCHORS,
|
|
177
171
|
LeagueMocks.leagueInfoOnlyParentDiffSportId,
|
|
178
172
|
lastPolledData,
|
|
179
173
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
180
|
-
playersMap
|
|
174
|
+
playersMap,
|
|
175
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
181
176
|
);
|
|
182
177
|
|
|
183
178
|
expect(warnSpy).toHaveBeenCalled();
|
|
@@ -194,14 +189,13 @@ describe('Markets', () => {
|
|
|
194
189
|
freshMockSoccer,
|
|
195
190
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
196
191
|
['bovada', 'draftkings'], // this will be ignored as primaryBookmaker is defined in LeagueMap
|
|
197
|
-
|
|
198
192
|
true,
|
|
199
|
-
|
|
200
193
|
ODDS_THRESHOLD_ANCHORS,
|
|
201
194
|
LeagueMocks.PlayerAssist, // league map with player props configured
|
|
202
195
|
lastPolledData,
|
|
203
196
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
204
|
-
playersMap
|
|
197
|
+
playersMap,
|
|
198
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
205
199
|
);
|
|
206
200
|
|
|
207
201
|
market.childMarkets.forEach((child: any) => {
|
|
@@ -218,14 +212,13 @@ describe('Markets', () => {
|
|
|
218
212
|
freshMockSoccer,
|
|
219
213
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
220
214
|
['bovada', 'draftkings'], // this will be ignored as primaryBookmaker is defined in LeagueMap
|
|
221
|
-
|
|
222
215
|
true,
|
|
223
|
-
|
|
224
216
|
ODDS_THRESHOLD_ANCHORS,
|
|
225
217
|
LeagueMocks.PlayerAssist, // league map with player props configured
|
|
226
218
|
lastPolledData,
|
|
227
219
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
228
|
-
playersMap
|
|
220
|
+
playersMap,
|
|
221
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
229
222
|
);
|
|
230
223
|
|
|
231
224
|
market.childMarkets.forEach((child: any) => {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
getLastPolledDataForBookmakers,
|
|
15
15
|
getPlayersMap,
|
|
16
16
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
17
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK,
|
|
17
18
|
} from '../utils/helper';
|
|
18
19
|
|
|
19
20
|
const lastPolledData = getLastPolledDataForBookmakers();
|
|
@@ -27,14 +28,13 @@ describe('Odds', () => {
|
|
|
27
28
|
freshMockSoccer,
|
|
28
29
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
29
30
|
['draftkings'],
|
|
30
|
-
|
|
31
31
|
true,
|
|
32
|
-
|
|
33
32
|
ODDS_THRESHOLD_ANCHORS,
|
|
34
33
|
LeagueMocks.leagueInfoOnlyParent,
|
|
35
34
|
lastPolledData,
|
|
36
35
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
37
|
-
playersMap
|
|
36
|
+
playersMap,
|
|
37
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
const hasOdds = market.odds.some(
|
|
@@ -51,14 +51,13 @@ describe('Odds', () => {
|
|
|
51
51
|
freshMockSoccer,
|
|
52
52
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
53
53
|
['draftkings'],
|
|
54
|
-
|
|
55
54
|
true,
|
|
56
|
-
|
|
57
55
|
ODDS_THRESHOLD_ANCHORS,
|
|
58
56
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
59
57
|
lastPolledData,
|
|
60
58
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
61
|
-
playersMap
|
|
59
|
+
playersMap,
|
|
60
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
62
61
|
);
|
|
63
62
|
|
|
64
63
|
const hasOdds = market.odds.some(
|
|
@@ -77,14 +76,13 @@ describe('Odds', () => {
|
|
|
77
76
|
freshMockSoccer,
|
|
78
77
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
79
78
|
['draftkings'],
|
|
80
|
-
|
|
81
79
|
true,
|
|
82
|
-
|
|
83
80
|
ODDS_THRESHOLD_ANCHORS,
|
|
84
81
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
85
82
|
lastPolledData,
|
|
86
83
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
87
|
-
playersMap
|
|
84
|
+
playersMap,
|
|
85
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
88
86
|
);
|
|
89
87
|
|
|
90
88
|
const hasChildMarkets = market.childMarkets.length > 0;
|
|
@@ -98,14 +96,13 @@ describe('Odds', () => {
|
|
|
98
96
|
freshMockSoccer,
|
|
99
97
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
100
98
|
['draftkings'],
|
|
101
|
-
|
|
102
99
|
true,
|
|
103
|
-
|
|
104
100
|
ODDS_THRESHOLD_ANCHORS,
|
|
105
101
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
106
102
|
lastPolledData,
|
|
107
103
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
108
|
-
playersMap
|
|
104
|
+
playersMap,
|
|
105
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
109
106
|
);
|
|
110
107
|
|
|
111
108
|
expect(market.childMarkets).toHaveLength(0);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getLastPolledDataForBookmakers,
|
|
10
10
|
getPlayersMap,
|
|
11
11
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
12
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK,
|
|
12
13
|
} from '../utils/helper';
|
|
13
14
|
|
|
14
15
|
const lastPolledData = getLastPolledDataForBookmakers();
|
|
@@ -27,7 +28,8 @@ describe('Spread configuration', () => {
|
|
|
27
28
|
LeagueMocks.leagueInfoEnabledSpeadAndTotals,
|
|
28
29
|
lastPolledData,
|
|
29
30
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
30
|
-
playersMap
|
|
31
|
+
playersMap,
|
|
32
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
31
33
|
);
|
|
32
34
|
|
|
33
35
|
const hasOdds = market.odds.some(
|
|
@@ -48,14 +50,13 @@ describe('Spread configuration', () => {
|
|
|
48
50
|
freshMockSoccer,
|
|
49
51
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
50
52
|
['draftkings'],
|
|
51
|
-
|
|
52
53
|
true,
|
|
53
|
-
|
|
54
54
|
ODDS_THRESHOLD_ANCHORS,
|
|
55
55
|
LeagueMocks.leagueInfoOnlyParent,
|
|
56
56
|
lastPolledData,
|
|
57
57
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
58
|
-
playersMap
|
|
58
|
+
playersMap,
|
|
59
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
59
60
|
)
|
|
60
61
|
)
|
|
61
62
|
);
|
|
@@ -66,14 +67,13 @@ describe('Spread configuration', () => {
|
|
|
66
67
|
freshMockSoccer,
|
|
67
68
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
68
69
|
['draftkings'],
|
|
69
|
-
|
|
70
70
|
true,
|
|
71
|
-
|
|
72
71
|
ODDS_THRESHOLD_ANCHORS,
|
|
73
72
|
LeagueMocks.leagueInfoOnlyParentWithSpreadAdded,
|
|
74
73
|
lastPolledData,
|
|
75
74
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
76
|
-
playersMap
|
|
75
|
+
playersMap,
|
|
76
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
77
77
|
)
|
|
78
78
|
)
|
|
79
79
|
);
|
|
@@ -104,14 +104,13 @@ describe('Spread configuration', () => {
|
|
|
104
104
|
freshMockSoccer,
|
|
105
105
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
106
106
|
['draftkings'],
|
|
107
|
-
|
|
108
107
|
true,
|
|
109
|
-
|
|
110
108
|
ODDS_THRESHOLD_ANCHORS,
|
|
111
109
|
LeagueMocks.leagueInfoOnlyParent,
|
|
112
110
|
lastPolledData,
|
|
113
111
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
114
|
-
playersMap
|
|
112
|
+
playersMap,
|
|
113
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
115
114
|
)
|
|
116
115
|
)
|
|
117
116
|
);
|
|
@@ -122,14 +121,13 @@ describe('Spread configuration', () => {
|
|
|
122
121
|
freshMockSoccer,
|
|
123
122
|
mapOpticOddsApiFixtureOdds([freshMockOpticSoccer])[0],
|
|
124
123
|
['draftkings'],
|
|
125
|
-
|
|
126
124
|
true,
|
|
127
|
-
|
|
128
125
|
ODDS_THRESHOLD_ANCHORS,
|
|
129
126
|
LeagueMocks.leagueInfoOnlyParentWithSpreadAdded,
|
|
130
127
|
lastPolledData,
|
|
131
128
|
MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST,
|
|
132
|
-
playersMap
|
|
129
|
+
playersMap,
|
|
130
|
+
MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK
|
|
133
131
|
)
|
|
134
132
|
)
|
|
135
133
|
);
|
|
@@ -10,10 +10,17 @@ export const getLastPolledDataForBookmakers = () => {
|
|
|
10
10
|
|
|
11
11
|
export const getPlayersMap = () => {
|
|
12
12
|
const playersMap: Map<string, number> = new Map<string, number>();
|
|
13
|
-
playersMap
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
playersMap
|
|
14
|
+
.set('0C07D14CC5DC', 13234)
|
|
15
|
+
.set('AD91EA260284', 56789)
|
|
16
|
+
.set('674851E026BC', 98765)
|
|
17
|
+
.set('CC707B1EADE5', 54321)
|
|
18
|
+
.set('JKL012', 11223)
|
|
19
|
+
.set('GHI789', 44556)
|
|
20
|
+
.set('ABC123', 77889)
|
|
21
|
+
.set('DEF456', 99001);
|
|
16
22
|
return playersMap;
|
|
17
23
|
};
|
|
24
|
+
export const MAX_PERCENTAGE_DIFF_FOR_PP_LINES_MOCK = 10; // 10%, used for tests, the production value is from env variable
|
|
18
25
|
|
|
19
26
|
export const MAX_ALLOWED_PROVIDER_DATA_STALE_DELAY_TEST = 30000; // 30 seconds
|
package/src/utils/bookmakers.ts
CHANGED
|
@@ -156,7 +156,8 @@ export const checkOddsFromBookmakersForChildMarkets = (
|
|
|
156
156
|
oddsProviders: string[],
|
|
157
157
|
lastPolledData: LastPolledArray,
|
|
158
158
|
maxAllowedProviderDataStaleDelay: number,
|
|
159
|
-
anchors: Anchor[]
|
|
159
|
+
anchors: Anchor[],
|
|
160
|
+
percentageDiffForPPLines: number
|
|
160
161
|
): OddsWithLeagueInfo => {
|
|
161
162
|
const formattedOdds = Object.entries(odds as any).reduce((acc: any, [key, value]: [string, any]) => {
|
|
162
163
|
const [sportsBookName, marketName, points, selection, selectionLine] = key.split('_');
|
|
@@ -183,6 +184,7 @@ export const checkOddsFromBookmakersForChildMarkets = (
|
|
|
183
184
|
}
|
|
184
185
|
} else {
|
|
185
186
|
if (sportsBookName.toLowerCase() === primaryBookmaker) {
|
|
187
|
+
if (value.playerId && !value.isMain) return acc; // Skip if not main for player props
|
|
186
188
|
const secondaryBookmakerObject =
|
|
187
189
|
odds[
|
|
188
190
|
`${secondaryBookmaker}_${marketName.toLowerCase()}_${points}_${selection}_${selectionLine}`
|
|
@@ -194,6 +196,24 @@ export const checkOddsFromBookmakersForChildMarkets = (
|
|
|
194
196
|
}
|
|
195
197
|
|
|
196
198
|
acc.push(value);
|
|
199
|
+
} else {
|
|
200
|
+
// if its player props and we didnt find the correct line, try adjusting points by steps defined and search again
|
|
201
|
+
if (value.playerId) {
|
|
202
|
+
const steps = getStepsForPointAdjustment(Number(points), percentageDiffForPPLines);
|
|
203
|
+
for (const step of steps) {
|
|
204
|
+
const adjustedPoints = (Number(points) + step).toString();
|
|
205
|
+
|
|
206
|
+
const secondaryBookmakerObject =
|
|
207
|
+
odds[
|
|
208
|
+
`${secondaryBookmaker}_${marketName.toLowerCase()}_${adjustedPoints}_${selection}_${selectionLine}`
|
|
209
|
+
];
|
|
210
|
+
|
|
211
|
+
if (secondaryBookmakerObject) {
|
|
212
|
+
acc.push(value);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
197
217
|
}
|
|
198
218
|
}
|
|
199
219
|
}
|
|
@@ -296,5 +316,15 @@ const shouldBlockOdds = (ourOdds: number, otherOdds: number, anchors: Anchor[])
|
|
|
296
316
|
return otherOdds < requiredOther;
|
|
297
317
|
};
|
|
298
318
|
|
|
319
|
+
const getStepsForPointAdjustment = (points: number, percentageDiffForPPLines: number): number[] => {
|
|
320
|
+
const stepsDelta = Math.round((points * percentageDiffForPPLines) / 100); // Example logic: 10% of the points value
|
|
321
|
+
const steps: number[] = [];
|
|
322
|
+
for (let index = 1; index <= stepsDelta; index++) {
|
|
323
|
+
steps.push(-index, index);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return steps;
|
|
327
|
+
};
|
|
328
|
+
|
|
299
329
|
// Export only when running tests
|
|
300
330
|
export const __test__ = { getRequiredOtherOdds, shouldBlockOdds };
|
package/src/utils/markets.ts
CHANGED
|
@@ -29,7 +29,8 @@ export const processMarket = (
|
|
|
29
29
|
leagueMap: any,
|
|
30
30
|
lastPolledData: LastPolledArray,
|
|
31
31
|
maxAllowedProviderDataStaleDelay: number,
|
|
32
|
-
playersMap: Map<string, number
|
|
32
|
+
playersMap: Map<string, number>,
|
|
33
|
+
maxPercentageDiffForPPLines: number
|
|
33
34
|
) => {
|
|
34
35
|
const leagueInfo = getLeagueInfo(market.leagueId, leagueMap);
|
|
35
36
|
|
|
@@ -83,7 +84,8 @@ export const processMarket = (
|
|
|
83
84
|
lastPolledData,
|
|
84
85
|
maxAllowedProviderDataStaleDelay,
|
|
85
86
|
anchors,
|
|
86
|
-
playersMap
|
|
87
|
+
playersMap,
|
|
88
|
+
maxPercentageDiffForPPLines
|
|
87
89
|
);
|
|
88
90
|
|
|
89
91
|
const packedChildMarkets = childMarkets.map((childMarket: any) => {
|