overtime-live-trading-utils 4.0.0-rc.1 → 4.0.0-rc.10

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overtime-live-trading-utils",
3
- "version": "4.0.0-rc.1",
3
+ "version": "4.0.0-rc.10",
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.50",
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 { ODDS_THRESHOLD_ANCHORS } from '../../constants/odds';
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
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
- undefined,
127
+
130
128
  ODDS_THRESHOLD_ANCHORS,
131
129
  LeagueMocks.leagueInfoOnlyParentWithSpreadAdded,
132
130
  lastPolledData,
@@ -17,6 +17,12 @@ export type ChildMarket = {
17
17
  type: string;
18
18
  line: number;
19
19
  odds: Array<number>;
20
+ playerProps: {
21
+ playerId: number;
22
+ playerName: string;
23
+ };
24
+ isPlayerPropsMarket: boolean;
25
+ positionNames?: string[];
20
26
  };
21
27
 
22
28
  export type LastPolledArray = { sportsbook: string; timestamp: number }[];
@@ -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 };
@@ -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, MONEYLINE_TYPE_ID, ZERO } from '../constants/common';
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 { adjustSpreadOnOdds, getSpreadData } from './spread';
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
- const spreadData = getSpreadData(sportSpreadData, sportId, MONEYLINE_TYPE_ID, defaultSpreadForLiveMarkets);
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 = {
@@ -308,12 +291,10 @@ export const createChildMarkets: (
308
291
  const maxOdds = leagueInfoByTypeId?.maxOdds; // maximum odds configured for child market (e.g. 0.05 implied probability)
309
292
 
310
293
  if (minOdds && maxOdds) {
311
- let conditionToAddChildMarket = true;
312
- data.odds.forEach((odd: number) => {
313
- if (odd >= minOdds || odd <= maxOdds) {
314
- conditionToAddChildMarket = false;
315
- }
316
- });
294
+ const isTotalMarketType = data.type === 'Total';
295
+ const conditionToAddChildMarket = data.odds.every(
296
+ (odd: number) => (odd < minOdds && odd > maxOdds) || (isTotalMarketType && odd === ZERO)
297
+ );
317
298
  if (conditionToAddChildMarket) {
318
299
  childMarkets.push(childMarket);
319
300
  }
@@ -365,8 +346,8 @@ export const filterOdds = (oddsArray: Odds, leagueInfos: LeagueConfigInfo[], pla
365
346
  .map((leagueInfo) => leagueInfo.marketName.toLowerCase());
366
347
  return oddsArray.reduce((acc: any, odd: any) => {
367
348
  if (allChildMarketsTypes.includes(odd.marketName.toLowerCase())) {
368
- const { points, marketName, selection, selectionLine, sportsBookName, playerId } = odd;
369
- if (playerId && !playersMap.has(playerId)) {
349
+ const { points, marketName, selection, selectionLine, sportsBookName, playerId, isMain } = odd;
350
+ if (playerId && (!playersMap.has(playerId) || !isMain)) {
370
351
  return acc;
371
352
  }
372
353
  const key = `${sportsBookName.toLowerCase()}_${marketName.toLowerCase()}_${points}_${selection}_${selectionLine}`;
@@ -474,17 +455,20 @@ export const groupAndFormatTotalOdds = (oddsArray: any[], commonData: HomeAwayTe
474
455
 
475
456
  // if we have away team in total odds we know the market is team total and we need to increase typeId by one.
476
457
  // if this is false typeId is already mapped correctly
477
- const isAwayTeam = selection === commonData.awayTeam;
478
- if ((value as any).over !== null && (value as any).under !== null) {
479
- acc.push({
480
- line: line as any,
481
- odds: [(value as any).over, (value as any).under],
482
- typeId: !isAwayTeam ? (value as any).typeId : Number((value as any).typeId) + 1,
483
- sportId: (value as any).sportId,
484
- type: (value as any).type,
485
- playerProps: (value as any).playerProps,
486
- });
487
- }
458
+ const shouldIncreaseTypeId = selection === commonData.awayTeam && !(value as any).playerProps;
459
+
460
+ const odds = [(value as any).over, (value as any).under];
461
+ const hasOdds = odds.some((odd) => odd !== null);
462
+
463
+ acc.push({
464
+ line: line as any,
465
+ odds: hasOdds ? odds.map((odd) => odd || ZERO) : [],
466
+ typeId: !shouldIncreaseTypeId ? (value as any).typeId : Number((value as any).typeId) + 1,
467
+ sportId: (value as any).sportId,
468
+ type: (value as any).type,
469
+ playerProps: (value as any).playerProps,
470
+ });
471
+
488
472
  return acc;
489
473
  }, []);
490
474
 
@@ -936,38 +920,18 @@ export const groupAndFormatDoubleChanceOdds = (oddsArray: any[], commonData: Hom
936
920
  };
937
921
 
938
922
  // used for home/away markets
939
- export const adjustSpreadOnChildOdds = (
940
- iterableGroupedOdds: any[],
941
- spreadDataForSport: any,
942
- defaultSpreadForLiveMarkets: any
943
- ) => {
923
+ export const adjustSpreadOnChildOdds = (iterableGroupedOdds: any[]) => {
944
924
  const result: any[] = [];
945
925
  iterableGroupedOdds.forEach((data) => {
946
- const hasDrawOdds = data.odds.length === 3;
947
- const homeTeamOdds = convertOddsToImpl(data.odds[0]) || ZERO;
948
- const awayTeamOdds = convertOddsToImpl(data.odds[1]) || ZERO;
949
- const drawOdds = convertOddsToImpl(data.odds[2]) || ZERO;
950
- const odds = hasDrawOdds ? [homeTeamOdds, awayTeamOdds, drawOdds] : [homeTeamOdds, awayTeamOdds];
951
-
952
- const isZeroOddsChild = homeTeamOdds === ZERO || awayTeamOdds === ZERO || (hasDrawOdds && drawOdds === ZERO);
953
- if (!isZeroOddsChild) {
954
- const spreadData = getSpreadData(
955
- spreadDataForSport,
956
- data.sportId,
957
- data.typeId,
958
- defaultSpreadForLiveMarkets
959
- );
960
-
961
- let adjustedOdds;
962
- if (spreadData !== null) {
963
- adjustedOdds = adjustSpreadOnOdds(odds, spreadData.minSpread, spreadData.targetSpread);
964
- } else {
965
- adjustedOdds = adjustSpreadOnOdds(odds, defaultSpreadForLiveMarkets, 0);
926
+ let odds = data.odds.map((odd: number) => convertOddsToImpl(odd) || ZERO);
927
+ if (data.odds.length > 0) {
928
+ if (data.odds.length > 1) {
929
+ odds = sanityCheckForOdds(odds);
966
930
  }
967
931
 
968
932
  result.push({
969
933
  ...data,
970
- odds: adjustedOdds,
934
+ odds,
971
935
  });
972
936
  }
973
937
  });
@@ -1,6 +1,6 @@
1
1
  import { LeagueConfigInfo } from '../types/sports';
2
2
 
3
- export const adjustSpreadOnOdds = (impliedProbs: number[], minSpread: number, targetSpread: number) => {
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
- // Step 4: Check if targetSpread is zero
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 = (