overtime-live-trading-utils 2.1.44 → 2.1.45-rc.0
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/constants/common.ts +1 -0
- package/src/constants/errors.ts +1 -0
- package/src/tests/mock/MockLeagueMap.ts +30 -0
- package/src/tests/mock/MockOpticSoccer.ts +486 -0
- package/src/tests/unit/bookmakers.test.ts +78 -8
- package/src/tests/unit/markets.test.ts +33 -12
- package/src/tests/unit/odds.test.ts +20 -8
- package/src/tests/unit/spread.test.ts +24 -10
- package/src/tests/utils/helper.ts +10 -0
- package/src/types/bookmakers.ts +7 -0
- package/src/types/odds.ts +19 -0
- package/src/types/sports.ts +4 -1
- package/src/utils/bookmakers.ts +164 -1
- package/src/utils/markets.ts +42 -41
- package/src/utils/odds.ts +73 -38
- package/tsconfig.json +1 -1
package/src/utils/markets.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import * as oddslib from 'oddslib';
|
|
2
|
-
import {
|
|
3
|
-
DIFF_BETWEEN_BOOKMAKERS_MESSAGE,
|
|
4
|
-
ZERO_ODDS_AFTER_SPREAD_ADJUSTMENT,
|
|
5
|
-
ZERO_ODDS_MESSAGE,
|
|
6
|
-
} from '../constants/errors';
|
|
2
|
+
import { ZERO_ODDS_AFTER_SPREAD_ADJUSTMENT } from '../constants/errors';
|
|
7
3
|
import { OddsObject } from '../types/odds';
|
|
4
|
+
import { LastPolledArray } from '../types/sports';
|
|
8
5
|
import { createChildMarkets, getParentOdds } from './odds';
|
|
9
6
|
import { getLeagueInfo } from './sports';
|
|
10
7
|
import { adjustAddedSpread } from './spread';
|
|
@@ -30,8 +27,10 @@ export const processMarket = (
|
|
|
30
27
|
spreadData: any,
|
|
31
28
|
isDrawAvailable: any,
|
|
32
29
|
defaultSpreadForLiveMarkets: any,
|
|
33
|
-
maxPercentageDiffBetwenOdds:
|
|
34
|
-
leagueMap: any
|
|
30
|
+
maxPercentageDiffBetwenOdds: number,
|
|
31
|
+
leagueMap: any,
|
|
32
|
+
lastPolledData: LastPolledArray,
|
|
33
|
+
maxAllowedProviderDataStaleDelay: number
|
|
35
34
|
) => {
|
|
36
35
|
const sportSpreadData = spreadData.filter((data: any) => data.sportId === String(market.leagueId));
|
|
37
36
|
const leagueInfo = getLeagueInfo(market.leagueId, leagueMap);
|
|
@@ -43,7 +42,10 @@ export const processMarket = (
|
|
|
43
42
|
apiResponseWithOdds,
|
|
44
43
|
market.leagueId,
|
|
45
44
|
defaultSpreadForLiveMarkets,
|
|
46
|
-
maxPercentageDiffBetwenOdds
|
|
45
|
+
maxPercentageDiffBetwenOdds,
|
|
46
|
+
leagueInfo,
|
|
47
|
+
lastPolledData,
|
|
48
|
+
maxAllowedProviderDataStaleDelay
|
|
47
49
|
);
|
|
48
50
|
|
|
49
51
|
const oddsAfterSpread = adjustAddedSpread(moneylineOdds.odds, leagueInfo, market.typeId);
|
|
@@ -78,42 +80,41 @@ export const processMarket = (
|
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const packedChildMarkets = childMarkets.map((childMarket: any) => {
|
|
94
|
-
const preparedMarket = { ...market, ...childMarket };
|
|
95
|
-
const oddsAfterSpread = adjustAddedSpread(preparedMarket.odds, leagueInfo, preparedMarket.typeId);
|
|
96
|
-
if (preparedMarket.odds.length > 0) {
|
|
97
|
-
preparedMarket.odds = oddsAfterSpread.map((probability) => {
|
|
98
|
-
if (probability == 0) {
|
|
99
|
-
return {
|
|
100
|
-
american: 0,
|
|
101
|
-
decimal: 0,
|
|
102
|
-
normalizedImplied: 0,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
83
|
+
const childMarkets = createChildMarkets(
|
|
84
|
+
apiResponseWithOdds,
|
|
85
|
+
sportSpreadData,
|
|
86
|
+
market.leagueId,
|
|
87
|
+
liveOddsProviders,
|
|
88
|
+
defaultSpreadForLiveMarkets,
|
|
89
|
+
leagueMap,
|
|
90
|
+
lastPolledData,
|
|
91
|
+
maxAllowedProviderDataStaleDelay,
|
|
92
|
+
maxPercentageDiffBetwenOdds
|
|
93
|
+
);
|
|
105
94
|
|
|
95
|
+
const packedChildMarkets = childMarkets.map((childMarket: any) => {
|
|
96
|
+
const preparedMarket = { ...market, ...childMarket };
|
|
97
|
+
const oddsAfterSpread = adjustAddedSpread(preparedMarket.odds, leagueInfo, preparedMarket.typeId);
|
|
98
|
+
if (preparedMarket.odds.length > 0) {
|
|
99
|
+
preparedMarket.odds = oddsAfterSpread.map((probability) => {
|
|
100
|
+
if (probability == 0) {
|
|
106
101
|
return {
|
|
107
|
-
american:
|
|
108
|
-
decimal:
|
|
109
|
-
normalizedImplied:
|
|
102
|
+
american: 0,
|
|
103
|
+
decimal: 0,
|
|
104
|
+
normalizedImplied: 0,
|
|
110
105
|
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
american: oddslib.from('impliedProbability', probability).to('moneyline'),
|
|
110
|
+
decimal: Number(oddslib.from('impliedProbability', probability).to('decimal').toFixed(10)),
|
|
111
|
+
normalizedImplied: probability,
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return preparedMarket;
|
|
116
|
+
});
|
|
117
|
+
market.childMarkets = packedChildMarkets;
|
|
117
118
|
|
|
118
119
|
return market;
|
|
119
120
|
};
|
package/src/utils/odds.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import * as oddslib from 'oddslib';
|
|
2
2
|
import { MarketType, MarketTypeMap } from 'overtime-utils';
|
|
3
3
|
import { DRAW, MIN_ODDS_FOR_DIFF_CHECKING, MONEYLINE_TYPE_ID, ZERO } from '../constants/common';
|
|
4
|
-
import { NO_MARKETS_FOR_LEAGUE_ID } from '../constants/errors';
|
|
4
|
+
import { LAST_POLLED_TOO_OLD, NO_MARKETS_FOR_LEAGUE_ID } from '../constants/errors';
|
|
5
5
|
import { MoneylineTypes } from '../enums/sports';
|
|
6
6
|
import { HomeAwayTeams, Odds, OddsObject } from '../types/odds';
|
|
7
|
-
import { ChildMarket, LeagueConfigInfo } from '../types/sports';
|
|
8
|
-
import {
|
|
7
|
+
import { ChildMarket, LastPolledArray, LeagueConfigInfo } from '../types/sports';
|
|
8
|
+
import {
|
|
9
|
+
checkOddsFromBookmakers,
|
|
10
|
+
checkOddsFromBookmakersForChildMarkets,
|
|
11
|
+
getPrimaryAndSecondaryBookmakerForTypeId,
|
|
12
|
+
isLastPolledForBookmakersValid,
|
|
13
|
+
} from './bookmakers';
|
|
9
14
|
import { getLeagueInfo } from './sports';
|
|
10
15
|
import { adjustSpreadOnOdds, getSpreadData } from './spread';
|
|
11
16
|
|
|
@@ -123,10 +128,33 @@ export const getParentOdds = (
|
|
|
123
128
|
oddsApiObject: OddsObject,
|
|
124
129
|
sportId: string,
|
|
125
130
|
defaultSpreadForLiveMarkets: number,
|
|
126
|
-
maxPercentageDiffBetwenOdds: number
|
|
131
|
+
maxPercentageDiffBetwenOdds: number,
|
|
132
|
+
leagueInfo: LeagueConfigInfo[],
|
|
133
|
+
lastPolledMap: LastPolledArray,
|
|
134
|
+
maxAllowedProviderDataStaleDelay: number
|
|
127
135
|
) => {
|
|
128
136
|
const commonData = { homeTeam: oddsApiObject.homeTeam, awayTeam: oddsApiObject.awayTeam };
|
|
129
137
|
|
|
138
|
+
const { primaryBookmaker, secondaryBookmaker } = getPrimaryAndSecondaryBookmakerForTypeId(
|
|
139
|
+
liveOddsProviders,
|
|
140
|
+
leagueInfo,
|
|
141
|
+
0 // typeId for moneyline
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const isValidLastPolled = isLastPolledForBookmakersValid(
|
|
145
|
+
lastPolledMap,
|
|
146
|
+
maxAllowedProviderDataStaleDelay,
|
|
147
|
+
primaryBookmaker,
|
|
148
|
+
secondaryBookmaker
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (!isValidLastPolled) {
|
|
152
|
+
return {
|
|
153
|
+
odds: isTwoPositionalSport ? [0, 0] : [0, 0, 0],
|
|
154
|
+
errorMessage: LAST_POLLED_TOO_OLD,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
130
158
|
// EXTRACTING ODDS FROM THE RESPONSE PER MARKET NAME AND BOOKMAKER
|
|
131
159
|
const moneylineOddsMap = filterOddsByMarketNameTeamNameBookmaker(
|
|
132
160
|
oddsApiObject.odds,
|
|
@@ -184,14 +212,20 @@ export const createChildMarkets: (
|
|
|
184
212
|
leagueId: number,
|
|
185
213
|
liveOddsProviders: any,
|
|
186
214
|
defaultSpreadForLiveMarkets: any,
|
|
187
|
-
leagueMap: any
|
|
215
|
+
leagueMap: any,
|
|
216
|
+
lastPolledData: LastPolledArray,
|
|
217
|
+
maxAllowedProviderDataStaleDelay: number,
|
|
218
|
+
maxImpliedPercentageDifference: number
|
|
188
219
|
) => ChildMarket[] = (
|
|
189
220
|
apiResponseWithOdds,
|
|
190
221
|
spreadDataForSport,
|
|
191
222
|
leagueId,
|
|
192
223
|
liveOddsProviders,
|
|
193
224
|
defaultSpreadForLiveMarkets,
|
|
194
|
-
leagueMap
|
|
225
|
+
leagueMap,
|
|
226
|
+
lastPolledData,
|
|
227
|
+
maxAllowedProviderDataStaleDelay,
|
|
228
|
+
maxImpliedPercentageDifference
|
|
195
229
|
) => {
|
|
196
230
|
const [spreadOdds, totalOdds, moneylineOdds, correctScoreOdds, doubleChanceOdds, ggOdds, childMarkets]: any[] = [
|
|
197
231
|
[],
|
|
@@ -209,14 +243,16 @@ export const createChildMarkets: (
|
|
|
209
243
|
};
|
|
210
244
|
|
|
211
245
|
if (leagueInfo.length > 0) {
|
|
212
|
-
|
|
213
|
-
const
|
|
214
|
-
|
|
246
|
+
const allChildOdds = filterOddsByMarketNameBookmaker(apiResponseWithOdds.odds, leagueInfo);
|
|
247
|
+
const checkedChildOdds = checkOddsFromBookmakersForChildMarkets(
|
|
248
|
+
allChildOdds,
|
|
215
249
|
leagueInfo,
|
|
216
|
-
liveOddsProviders
|
|
250
|
+
liveOddsProviders,
|
|
251
|
+
lastPolledData,
|
|
252
|
+
maxAllowedProviderDataStaleDelay,
|
|
253
|
+
maxImpliedPercentageDifference
|
|
217
254
|
);
|
|
218
|
-
|
|
219
|
-
allChildOdds.forEach((odd) => {
|
|
255
|
+
checkedChildOdds.forEach((odd) => {
|
|
220
256
|
if (odd.type === 'Total') {
|
|
221
257
|
if (Math.abs(Number(odd.points) % 1) === 0.5) totalOdds.push(odd);
|
|
222
258
|
} else if (odd.type === 'Spread') {
|
|
@@ -241,6 +277,7 @@ export const createChildMarkets: (
|
|
|
241
277
|
];
|
|
242
278
|
const otherFormattedOdds = [...groupAndFormatCorrectScoreOdds(correctScoreOdds, commonData)];
|
|
243
279
|
|
|
280
|
+
// odds are converted to implied probability inside adjustSpreadOnChildOdds
|
|
244
281
|
const homeAwayOddsWithSpreadAdjusted = adjustSpreadOnChildOdds(
|
|
245
282
|
homeAwayFormattedOdds,
|
|
246
283
|
spreadDataForSport,
|
|
@@ -256,18 +293,20 @@ export const createChildMarkets: (
|
|
|
256
293
|
odds: data.odds,
|
|
257
294
|
};
|
|
258
295
|
const leagueInfoByTypeId = leagueInfo.find((league) => Number(league.typeId) === Number(data.typeId));
|
|
259
|
-
const minOdds = leagueInfoByTypeId?.minOdds;
|
|
260
|
-
const maxOdds = leagueInfoByTypeId?.maxOdds;
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
)
|
|
270
|
-
|
|
296
|
+
const minOdds = leagueInfoByTypeId?.minOdds; // minimum odds configured for child market (e.g. 0.95 implied probability)
|
|
297
|
+
const maxOdds = leagueInfoByTypeId?.maxOdds; // maximum odds configured for child market (e.g. 0.05 implied probability)
|
|
298
|
+
|
|
299
|
+
if (minOdds && maxOdds) {
|
|
300
|
+
let conditionToAddChildMarket = true;
|
|
301
|
+
data.odds.forEach((odd: number) => {
|
|
302
|
+
if (odd >= minOdds || odd <= maxOdds) {
|
|
303
|
+
conditionToAddChildMarket = false;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
if (conditionToAddChildMarket) {
|
|
307
|
+
childMarkets.push(childMarket);
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
271
310
|
childMarkets.push(childMarket);
|
|
272
311
|
}
|
|
273
312
|
});
|
|
@@ -305,11 +344,7 @@ export const createChildMarkets: (
|
|
|
305
344
|
* @param {string} oddsProvider - The main odds provider to filter by.
|
|
306
345
|
* @returns {Array} The filtered odds array.
|
|
307
346
|
*/
|
|
308
|
-
export const filterOddsByMarketNameBookmaker = (
|
|
309
|
-
oddsArray: Odds,
|
|
310
|
-
leagueInfos: LeagueConfigInfo[],
|
|
311
|
-
oddsProvider: string
|
|
312
|
-
): any[] => {
|
|
347
|
+
export const filterOddsByMarketNameBookmaker = (oddsArray: Odds, leagueInfos: LeagueConfigInfo[]): any => {
|
|
313
348
|
const allChildMarketsTypes = leagueInfos
|
|
314
349
|
.filter(
|
|
315
350
|
(leagueInfo) =>
|
|
@@ -317,20 +352,20 @@ export const filterOddsByMarketNameBookmaker = (
|
|
|
317
352
|
leagueInfo.enabled === 'true'
|
|
318
353
|
)
|
|
319
354
|
.map((leagueInfo) => leagueInfo.marketName.toLowerCase());
|
|
320
|
-
return oddsArray
|
|
321
|
-
.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
)
|
|
326
|
-
.map((odd) => {
|
|
327
|
-
return {
|
|
355
|
+
return oddsArray.reduce((acc: any, odd: any) => {
|
|
356
|
+
if (allChildMarketsTypes.includes(odd.marketName.toLowerCase())) {
|
|
357
|
+
const { points, marketName, selection, selectionLine, sportsBookName } = odd;
|
|
358
|
+
const key = `${sportsBookName.toLowerCase()}_${marketName.toLowerCase()}_${points}_${selection}_${selectionLine}`;
|
|
359
|
+
acc[key] = {
|
|
328
360
|
...odd,
|
|
329
361
|
...leagueInfos.find(
|
|
330
362
|
(leagueInfo) => leagueInfo.marketName.toLowerCase() === odd.marketName.toLowerCase()
|
|
331
363
|
), // using .find() for team totals means that we will always assign 10017 as typeID at this point
|
|
332
364
|
};
|
|
333
|
-
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return acc;
|
|
368
|
+
}, {}) as any;
|
|
334
369
|
};
|
|
335
370
|
|
|
336
371
|
/**
|