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.
@@ -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.set('0C07D14CC5DC', 13234);
14
- playersMap.set('AD91EA260284', 56789);
15
- playersMap.set('674851E026BC', 98765);
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
@@ -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 };
@@ -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) => {