overtime-live-trading-utils 2.0.17 → 2.0.18
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 +39 -1
- package/src/tests/unit/markets.test.ts +4 -0
- package/src/tests/unit/sports.test.ts +13 -2
- package/src/utils/odds.ts +153 -7
package/package.json
CHANGED
|
@@ -30,6 +30,26 @@ const totalMock: LeagueConfigInfo = {
|
|
|
30
30
|
minOdds: 0.75,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
const doubleChanceMock: LeagueConfigInfo = {
|
|
34
|
+
sportId: 9806,
|
|
35
|
+
enabled: 'true',
|
|
36
|
+
marketName: 'Double Chance',
|
|
37
|
+
typeId: 10003,
|
|
38
|
+
type: 'Double Chance',
|
|
39
|
+
maxOdds: 0.01,
|
|
40
|
+
minOdds: 0.99,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const correctScoreMock: LeagueConfigInfo = {
|
|
44
|
+
sportId: 9806,
|
|
45
|
+
enabled: 'true',
|
|
46
|
+
marketName: 'Correct Score',
|
|
47
|
+
typeId: 10100,
|
|
48
|
+
type: 'Correct Score',
|
|
49
|
+
maxOdds: 0.25,
|
|
50
|
+
minOdds: 0.75,
|
|
51
|
+
};
|
|
52
|
+
|
|
33
53
|
const childMoneylineMock: LeagueConfigInfo = {
|
|
34
54
|
sportId: 9806,
|
|
35
55
|
enabled: 'true',
|
|
@@ -54,6 +74,8 @@ const leagueInfoMockDisabledChilds: LeagueConfigInfo[] = [
|
|
|
54
74
|
baseLeagueInfo,
|
|
55
75
|
{ ...spreadMock, enabled: 'false' },
|
|
56
76
|
{ ...totalMock, enabled: 'false' },
|
|
77
|
+
{ ...doubleChanceMock, enabled: 'false' },
|
|
78
|
+
{ ...correctScoreMock, enabled: 'false' },
|
|
57
79
|
];
|
|
58
80
|
|
|
59
81
|
const leagueInfoEnabledSpreadDisabledTotals: LeagueConfigInfo[] = [
|
|
@@ -62,8 +84,23 @@ const leagueInfoEnabledSpreadDisabledTotals: LeagueConfigInfo[] = [
|
|
|
62
84
|
{ ...totalMock, enabled: 'false' },
|
|
63
85
|
];
|
|
64
86
|
|
|
87
|
+
const leagueInfoDisabledCorrectScoreAndDoubleChance: LeagueConfigInfo[] = [
|
|
88
|
+
baseLeagueInfo,
|
|
89
|
+
spreadMock,
|
|
90
|
+
totalMock,
|
|
91
|
+
{ ...doubleChanceMock, enabled: 'false' },
|
|
92
|
+
{ ...correctScoreMock, enabled: 'false' },
|
|
93
|
+
];
|
|
94
|
+
|
|
65
95
|
const leagueInfoEnabledSpeadAndTotals: LeagueConfigInfo[] = [baseLeagueInfo, spreadMock, totalMock];
|
|
66
|
-
const leagueInfoEnabledAll: LeagueConfigInfo[] = [
|
|
96
|
+
const leagueInfoEnabledAll: LeagueConfigInfo[] = [
|
|
97
|
+
baseLeagueInfo,
|
|
98
|
+
spreadMock,
|
|
99
|
+
totalMock,
|
|
100
|
+
childMoneylineMock,
|
|
101
|
+
doubleChanceMock,
|
|
102
|
+
correctScoreMock,
|
|
103
|
+
];
|
|
67
104
|
|
|
68
105
|
// Grouped Exports
|
|
69
106
|
export const LeagueMocks = {
|
|
@@ -74,4 +111,5 @@ export const LeagueMocks = {
|
|
|
74
111
|
leagueInfoEnabledSpreadDisabledTotals,
|
|
75
112
|
leagueInfoEnabledSpeadAndTotals,
|
|
76
113
|
leagueInfoEnabledAll,
|
|
114
|
+
leagueInfoDisabledCorrectScoreAndDoubleChance,
|
|
77
115
|
};
|
|
@@ -101,10 +101,14 @@ describe('Markets', () => {
|
|
|
101
101
|
const containsSpread = market.childMarkets.some((child: any) => child.type === 'spread');
|
|
102
102
|
const containsTotal = market.childMarkets.some((child: any) => child.type === 'total');
|
|
103
103
|
const containsChildMoneyline = market.childMarkets.some((child: any) => child.type === 'moneyline');
|
|
104
|
+
const containsChildCorrectScore = market.childMarkets.some((child: any) => child.type === 'correct score');
|
|
105
|
+
const containsChildDoubleChance = market.childMarkets.some((child: any) => child.type === 'double chance');
|
|
104
106
|
|
|
105
107
|
expect(containsSpread).toBe(true);
|
|
106
108
|
expect(containsTotal).toBe(true);
|
|
107
109
|
expect(containsChildMoneyline).toBe(true);
|
|
110
|
+
expect(containsChildCorrectScore).toBe(true);
|
|
111
|
+
expect(containsChildDoubleChance).toBe(true);
|
|
108
112
|
});
|
|
109
113
|
|
|
110
114
|
it('Should return warning message that there are is no configuration available in league map csv', () => {
|
|
@@ -14,14 +14,16 @@ describe('Sports', () => {
|
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
it('Should return all enabled bet types for league', () => {
|
|
17
|
-
const betTypes = getBetTypesForLeague(9806, LeagueMocks.
|
|
17
|
+
const betTypes = getBetTypesForLeague(9806, LeagueMocks.leagueInfoEnabledAll);
|
|
18
18
|
|
|
19
19
|
expect(betTypes).toContain('Moneyline');
|
|
20
20
|
expect(betTypes).toContain('Goal Spread');
|
|
21
21
|
expect(betTypes).toContain('Total Goals');
|
|
22
|
+
expect(betTypes).toContain('Double Chance');
|
|
23
|
+
expect(betTypes).toContain('Correct Score');
|
|
22
24
|
});
|
|
23
25
|
|
|
24
|
-
it('Should return all enabled bet types for league, and not contain disabled ones', () => {
|
|
26
|
+
it('Should return all enabled bet types for league, and not contain disabled ones (Totals)', () => {
|
|
25
27
|
const betTypes = getBetTypesForLeague(9806, LeagueMocks.leagueInfoEnabledSpreadDisabledTotals);
|
|
26
28
|
|
|
27
29
|
expect(betTypes).toContain('Moneyline');
|
|
@@ -29,6 +31,15 @@ describe('Sports', () => {
|
|
|
29
31
|
expect(betTypes).not.toContain('Total Goals');
|
|
30
32
|
});
|
|
31
33
|
|
|
34
|
+
it('Should return all enabled bet types for league, and not contain disabled ones (Double Chance and Correct Score)', () => {
|
|
35
|
+
const betTypes = getBetTypesForLeague(9806, LeagueMocks.leagueInfoDisabledCorrectScoreAndDoubleChance);
|
|
36
|
+
|
|
37
|
+
expect(betTypes).toContain('Moneyline');
|
|
38
|
+
expect(betTypes).toContain('Goal Spread');
|
|
39
|
+
expect(betTypes).not.toContain('Double Chance');
|
|
40
|
+
expect(betTypes).not.toContain('Correct Score');
|
|
41
|
+
});
|
|
42
|
+
|
|
32
43
|
it('Should return all enabled spread bet types for league', () => {
|
|
33
44
|
const betTypes = getLeagueSpreadTypes(9806, LeagueMocks.leagueInfoEnabledSpeadAndTotals);
|
|
34
45
|
|
package/src/utils/odds.ts
CHANGED
|
@@ -191,12 +191,20 @@ export const createChildMarkets: (
|
|
|
191
191
|
defaultSpreadForLiveMarkets,
|
|
192
192
|
leagueMap
|
|
193
193
|
) => {
|
|
194
|
-
const [spreadOdds, totalOdds, moneylineOdds, childMarkets]: any[] = [
|
|
194
|
+
const [spreadOdds, totalOdds, moneylineOdds, correctScoreOdds, doubleChanceOdds, childMarkets]: any[] = [
|
|
195
|
+
[],
|
|
196
|
+
[],
|
|
197
|
+
[],
|
|
198
|
+
[],
|
|
199
|
+
[],
|
|
200
|
+
[],
|
|
201
|
+
];
|
|
195
202
|
const leagueInfo = getLeagueInfo(leagueId, leagueMap);
|
|
196
203
|
const commonData = {
|
|
197
204
|
homeTeam: apiResponseWithOdds.homeTeam,
|
|
198
205
|
awayTeam: apiResponseWithOdds.awayTeam,
|
|
199
206
|
};
|
|
207
|
+
|
|
200
208
|
if (leagueInfo.length > 0) {
|
|
201
209
|
// TODO ADD ODDS COMPARISON BETWEEN BOOKMAKERS
|
|
202
210
|
const allChildOdds = filterOddsByMarketNameBookmaker(
|
|
@@ -212,22 +220,28 @@ export const createChildMarkets: (
|
|
|
212
220
|
if (Math.abs(Number(odd.points) % 1) === 0.5) spreadOdds.push(odd);
|
|
213
221
|
} else if (odd.type === 'Moneyline') {
|
|
214
222
|
moneylineOdds.push(odd);
|
|
223
|
+
} else if (odd.type === 'Correct Score') {
|
|
224
|
+
correctScoreOdds.push(odd);
|
|
225
|
+
} else if (odd.type === 'Double Chance') {
|
|
226
|
+
doubleChanceOdds.push(odd);
|
|
215
227
|
}
|
|
216
228
|
});
|
|
217
229
|
|
|
218
|
-
const
|
|
230
|
+
const homeAwayFormattedOdds = [
|
|
219
231
|
...groupAndFormatSpreadOdds(spreadOdds, commonData),
|
|
220
232
|
...groupAndFormatTotalOdds(totalOdds, commonData),
|
|
221
233
|
...groupAndFormatMoneylineOdds(moneylineOdds, commonData),
|
|
234
|
+
...groupAndFormatDoubleChanceOdds(doubleChanceOdds, commonData),
|
|
222
235
|
];
|
|
236
|
+
const otherFormattedOdds = [...groupAndFormatCorrectScoreOdds(correctScoreOdds, commonData)];
|
|
223
237
|
|
|
224
|
-
const
|
|
225
|
-
|
|
238
|
+
const homeAwayOddsWithSpreadAdjusted = adjustSpreadOnChildOdds(
|
|
239
|
+
homeAwayFormattedOdds,
|
|
226
240
|
spreadDataForSport,
|
|
227
241
|
defaultSpreadForLiveMarkets
|
|
228
242
|
);
|
|
229
243
|
|
|
230
|
-
|
|
244
|
+
homeAwayOddsWithSpreadAdjusted.forEach((data) => {
|
|
231
245
|
const childMarket = {
|
|
232
246
|
leagueId: Number(data.sportId),
|
|
233
247
|
typeId: Number(data.typeId),
|
|
@@ -251,9 +265,29 @@ export const createChildMarkets: (
|
|
|
251
265
|
childMarkets.push(childMarket);
|
|
252
266
|
}
|
|
253
267
|
});
|
|
268
|
+
|
|
269
|
+
otherFormattedOdds.forEach((data) => {
|
|
270
|
+
const leagueInfoByTypeId = leagueInfo.find((league) => Number(league.typeId) === Number(data.typeId));
|
|
271
|
+
const minOdds = leagueInfoByTypeId?.minOdds;
|
|
272
|
+
const maxOdds = leagueInfoByTypeId?.maxOdds;
|
|
273
|
+
|
|
274
|
+
const childMarket = {
|
|
275
|
+
leagueId: Number(data.sportId),
|
|
276
|
+
typeId: Number(data.typeId),
|
|
277
|
+
type: data.type.toLowerCase(),
|
|
278
|
+
line: Number(data.line || 0),
|
|
279
|
+
odds: data.odds.map((odd: any) => {
|
|
280
|
+
const impliedOdds = convertOddsToImpl(odd) || ZERO;
|
|
281
|
+
return !minOdds || !maxOdds || impliedOdds >= minOdds || impliedOdds <= maxOdds ? 0 : impliedOdds;
|
|
282
|
+
}),
|
|
283
|
+
positionNames: data.positionNames,
|
|
284
|
+
};
|
|
285
|
+
childMarkets.push(childMarket);
|
|
286
|
+
});
|
|
254
287
|
} else {
|
|
255
288
|
console.warn(`No child markets for leagueID: ${Number(leagueId)}`);
|
|
256
289
|
}
|
|
290
|
+
|
|
257
291
|
return childMarkets;
|
|
258
292
|
};
|
|
259
293
|
|
|
@@ -395,11 +429,11 @@ export const groupAndFormatTotalOdds = (oddsArray: any[], commonData: HomeAwayTe
|
|
|
395
429
|
};
|
|
396
430
|
|
|
397
431
|
/**
|
|
398
|
-
* Groups
|
|
432
|
+
* Groups moneyline odds by their lines and formats the result.
|
|
399
433
|
*
|
|
400
434
|
* @param {Array} oddsArray - The input array of odds objects.
|
|
401
435
|
* @param {Object} commonData - The common data object containing homeTeam information.
|
|
402
|
-
* @returns {Array} The grouped and formatted
|
|
436
|
+
* @returns {Array} The grouped and formatted moneyline odds.
|
|
403
437
|
*/
|
|
404
438
|
export const groupAndFormatMoneylineOdds = (oddsArray: any[], commonData: HomeAwayTeams) => {
|
|
405
439
|
// Group odds by their selection points and selection
|
|
@@ -439,6 +473,118 @@ export const groupAndFormatMoneylineOdds = (oddsArray: any[], commonData: HomeAw
|
|
|
439
473
|
return formattedOdds;
|
|
440
474
|
};
|
|
441
475
|
|
|
476
|
+
/**
|
|
477
|
+
* Groups correct score odds by their lines and formats the result.
|
|
478
|
+
*
|
|
479
|
+
* @param {Array} oddsArray - The input array of odds objects.
|
|
480
|
+
* @param {Object} commonData - The common data object containing homeTeam and awayTeam information.
|
|
481
|
+
* @returns {Array} The grouped and formatted correct score odds.
|
|
482
|
+
*/
|
|
483
|
+
export const groupAndFormatCorrectScoreOdds = (oddsArray: any[], commonData: HomeAwayTeams): any[] => {
|
|
484
|
+
const homeTeamKey = commonData.homeTeam.toLowerCase().replace(/\s+/g, '_');
|
|
485
|
+
const awayTeamKey = commonData.awayTeam.toLowerCase().replace(/\s+/g, '_');
|
|
486
|
+
|
|
487
|
+
const oddsMap = {
|
|
488
|
+
draw_0_0: 0,
|
|
489
|
+
draw_1_1: 0,
|
|
490
|
+
draw_2_2: 0,
|
|
491
|
+
draw_3_3: 0,
|
|
492
|
+
draw_4_4: 0,
|
|
493
|
+
[`${homeTeamKey}_1_0`]: 0,
|
|
494
|
+
[`${homeTeamKey}_2_0`]: 0,
|
|
495
|
+
[`${homeTeamKey}_2_1`]: 0,
|
|
496
|
+
[`${homeTeamKey}_3_0`]: 0,
|
|
497
|
+
[`${homeTeamKey}_3_1`]: 0,
|
|
498
|
+
[`${homeTeamKey}_3_2`]: 0,
|
|
499
|
+
[`${homeTeamKey}_4_0`]: 0,
|
|
500
|
+
[`${homeTeamKey}_4_1`]: 0,
|
|
501
|
+
[`${homeTeamKey}_4_2`]: 0,
|
|
502
|
+
[`${homeTeamKey}_4_3`]: 0,
|
|
503
|
+
[`${awayTeamKey}_1_0`]: 0,
|
|
504
|
+
[`${awayTeamKey}_2_0`]: 0,
|
|
505
|
+
[`${awayTeamKey}_2_1`]: 0,
|
|
506
|
+
[`${awayTeamKey}_3_0`]: 0,
|
|
507
|
+
[`${awayTeamKey}_3_1`]: 0,
|
|
508
|
+
[`${awayTeamKey}_3_2`]: 0,
|
|
509
|
+
[`${awayTeamKey}_4_0`]: 0,
|
|
510
|
+
[`${awayTeamKey}_4_1`]: 0,
|
|
511
|
+
[`${awayTeamKey}_4_2`]: 0,
|
|
512
|
+
[`${awayTeamKey}_4_3`]: 0,
|
|
513
|
+
other: 0,
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// Populate the oddsMap with the odds from the provided array
|
|
517
|
+
oddsArray.forEach((odd) => {
|
|
518
|
+
const normalizedSelection = `${odd.selection.toLowerCase().replace(/\s+/g, '_')}_${odd.selectionLine.replace(
|
|
519
|
+
':',
|
|
520
|
+
'_'
|
|
521
|
+
)}`;
|
|
522
|
+
if (oddsMap.hasOwnProperty(normalizedSelection)) {
|
|
523
|
+
oddsMap[normalizedSelection] = odd.price;
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
const allOddsAreZero = Object.values(oddsMap).every((odd) => odd === ZERO);
|
|
528
|
+
|
|
529
|
+
if (allOddsAreZero) {
|
|
530
|
+
return [];
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const positionNames = Object.keys(oddsMap);
|
|
534
|
+
|
|
535
|
+
// Create the market object
|
|
536
|
+
const marketObject = {
|
|
537
|
+
homeTeam: commonData.homeTeam,
|
|
538
|
+
awayTeam: commonData.awayTeam,
|
|
539
|
+
line: 0,
|
|
540
|
+
positionNames: positionNames,
|
|
541
|
+
odds: Object.values(oddsMap),
|
|
542
|
+
type: 'Correct Score',
|
|
543
|
+
typeId: 10100,
|
|
544
|
+
};
|
|
545
|
+
return [marketObject];
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Groups double chance odds by their lines and formats the result.
|
|
550
|
+
*
|
|
551
|
+
* @param {Array} oddsArray - The input array of odds objects.
|
|
552
|
+
* @param {Object} commonData - The common data object containing homeTeam and awayTeam information.
|
|
553
|
+
* @returns {Array} The grouped and formatted correct score odds.
|
|
554
|
+
*/
|
|
555
|
+
export const groupAndFormatDoubleChanceOdds = (oddsArray: any[], commonData: HomeAwayTeams) => {
|
|
556
|
+
let probability1X = 0;
|
|
557
|
+
let probability12 = 0;
|
|
558
|
+
let probabilityX2 = 0;
|
|
559
|
+
|
|
560
|
+
oddsArray.forEach((odd) => {
|
|
561
|
+
if (odd.selection.includes(commonData.homeTeam)) {
|
|
562
|
+
if (odd.selection.includes(commonData.awayTeam)) {
|
|
563
|
+
probability12 = odd.price;
|
|
564
|
+
} else {
|
|
565
|
+
probability1X = odd.price;
|
|
566
|
+
}
|
|
567
|
+
} else if (odd.selection.includes(commonData.awayTeam)) {
|
|
568
|
+
probabilityX2 = odd.price;
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
if ([probability1X, probability12, probabilityX2].every((odd) => odd === ZERO)) {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
// Create the market object
|
|
576
|
+
const marketObject = {
|
|
577
|
+
homeTeam: commonData.homeTeam,
|
|
578
|
+
awayTeam: commonData.awayTeam,
|
|
579
|
+
line: 0,
|
|
580
|
+
odds: [probability1X, probability12, probabilityX2],
|
|
581
|
+
type: 'Double Chance',
|
|
582
|
+
typeId: 10003,
|
|
583
|
+
};
|
|
584
|
+
return [marketObject];
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
// used for home/away markets
|
|
442
588
|
export const adjustSpreadOnChildOdds = (
|
|
443
589
|
iterableGroupedOdds: any[],
|
|
444
590
|
spreadDataForSport: any,
|