shufflecom-calculations 2.2.4 → 2.2.5
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/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/utils/calculate-sports-payout-odds.d.ts +3 -2
- package/lib/utils/calculate-sports-payout-odds.js +27 -19
- package/lib/utils/calculate-sports-payout-odds.js.map +1 -1
- package/package.json +2 -2
- package/src/utils/calculate-sports-payout-odds.spec.ts +36 -4
- package/src/utils/calculate-sports-payout-odds.ts +213 -206
|
@@ -1,130 +1,129 @@
|
|
|
1
1
|
import BigNumber from 'bignumber.js';
|
|
2
|
-
import { limitTotalOddsDecimalPlaces, oddsFractionToDecimal } from
|
|
3
|
-
import {
|
|
4
|
-
import { SportsSystemBetType, SportsSystemBetTypeCombinations } from
|
|
5
|
-
|
|
2
|
+
import { limitTotalOddsDecimalPlaces, oddsFractionToDecimal } from './odds';
|
|
3
|
+
import { OutcomeResult, SportsBetSelectionInterface, SportsBetSelectionStatus, SportsMarketSelectionInterface } from './sports.types';
|
|
4
|
+
import { SportsSystemBetType, SportsSystemBetTypeCombinations, SportsSystemBetTypeSelectionCountsToBetCounts } from './system-bet.types';
|
|
6
5
|
|
|
7
6
|
export const CUSTOM_BET_VOIDED_STATUSES = [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
7
|
+
SportsBetSelectionStatus.VOIDED,
|
|
8
|
+
SportsBetSelectionStatus.PROVIDER_VOIDED,
|
|
9
|
+
SportsBetSelectionStatus.PUSHED,
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export function generateSystemBetIndexCombinations(length: number, size: number): number[][] {
|
|
13
|
+
if (size === 0) return [[]];
|
|
14
|
+
if (size > length) return [];
|
|
15
|
+
if (size === length) return [Array.from({ length }, (_, i) => i)];
|
|
16
|
+
|
|
17
|
+
const combinations: number[][] = [];
|
|
18
|
+
|
|
19
|
+
function backtrack(start: number, combo: number[]) {
|
|
20
|
+
if (combo.length === size) {
|
|
21
|
+
combinations.push([...combo]);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (let i = start; i < length; i++) {
|
|
26
|
+
combo.push(i);
|
|
27
|
+
backtrack(i + 1, combo);
|
|
28
|
+
combo.pop();
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
backtrack(0, []);
|
|
34
|
-
return combinations;
|
|
35
30
|
}
|
|
36
|
-
|
|
31
|
+
|
|
32
|
+
backtrack(0, []);
|
|
33
|
+
return combinations;
|
|
34
|
+
}
|
|
37
35
|
|
|
38
36
|
export function getSystemBetSubBetSelections(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
const combinations = systemBetSizes.flatMap(size => generateSystemBetIndexCombinations(selections.length, size));
|
|
47
|
-
const combinationSelections = combinations.map(combination => combination.map(index => selections[index]));
|
|
48
|
-
|
|
49
|
-
return combinationSelections;
|
|
37
|
+
selections: SportsBetSelectionInterface[],
|
|
38
|
+
sportsSystemBetType: SportsSystemBetType,
|
|
39
|
+
): SportsBetSelectionInterface[][] {
|
|
40
|
+
const systemBetSizes = SportsSystemBetTypeCombinations[sportsSystemBetType];
|
|
41
|
+
if (!systemBetSizes) {
|
|
42
|
+
throw new Error('Invalid system bet type');
|
|
50
43
|
}
|
|
44
|
+
if (!SportsSystemBetTypeSelectionCountsToBetCounts[selections.length][sportsSystemBetType]) {
|
|
45
|
+
throw new Error('Invalid selection count');
|
|
46
|
+
}
|
|
47
|
+
const combinations = systemBetSizes.flatMap(size => generateSystemBetIndexCombinations(selections.length, size));
|
|
48
|
+
const combinationSelections = combinations.map(combination => combination.map(index => selections[index]));
|
|
49
|
+
|
|
50
|
+
return combinationSelections;
|
|
51
|
+
}
|
|
51
52
|
|
|
52
53
|
export function calculateSportsPayoutOdds(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
selections: SportsBetSelectionInterface[],
|
|
55
|
+
marketSelections: SportsMarketSelectionInterface[],
|
|
56
|
+
opt: {
|
|
57
|
+
sportsCustomBetTotalOddsDecimal: BigNumber | null;
|
|
58
|
+
subBetSelections: SportsBetSelectionInterface[][] | null;
|
|
59
|
+
isEstimation: boolean; // treats PENDING leg as WON
|
|
60
|
+
},
|
|
61
|
+
): BigNumber {
|
|
62
|
+
const sportsCustomBetTotalOddsDecimal = opt?.sportsCustomBetTotalOddsDecimal;
|
|
63
|
+
const subBetSelections = opt?.subBetSelections;
|
|
64
|
+
if (sportsCustomBetTotalOddsDecimal) {
|
|
65
|
+
if (selections.some(({ status }) => CUSTOM_BET_VOIDED_STATUSES.includes(status))) {
|
|
66
|
+
return new BigNumber(1);
|
|
67
|
+
} else if (selections.some(({ status }) => status === SportsBetSelectionStatus.LOST)) {
|
|
68
|
+
return new BigNumber(0);
|
|
69
|
+
} else {
|
|
70
|
+
return limitTotalOddsDecimalPlaces(sportsCustomBetTotalOddsDecimal);
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const subBetOdds = calculateSportsPayoutOdds(subBetSelection, subBetMarketSelections as SportsMarketSelectionInterface[], {
|
|
85
|
-
sportsCustomBetTotalOddsDecimal: null,
|
|
86
|
-
subBetSelections: null,
|
|
87
|
-
});
|
|
88
|
-
totalOddsDecimal = totalOddsDecimal.plus(subBetOdds);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const marketSelectionMap = marketSelections.reduce(
|
|
75
|
+
(map, selection) => {
|
|
76
|
+
map[selection.id] = selection;
|
|
77
|
+
return map;
|
|
78
|
+
},
|
|
79
|
+
{} as Record<string, SportsMarketSelectionInterface>,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (subBetSelections) {
|
|
83
|
+
let totalOddsDecimal = new BigNumber(0);
|
|
84
|
+
for (const subBetSelection of subBetSelections) {
|
|
85
|
+
const subBetMarketSelections = subBetSelection.map(selection => marketSelectionMap[selection.marketSelectionId]);
|
|
86
|
+
if (subBetMarketSelections.some(marketSelection => !marketSelection)) {
|
|
87
|
+
throw new Error('Sub bet market selections not found');
|
|
89
88
|
}
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
const subBetOdds = calculateSportsPayoutOdds(subBetSelection, subBetMarketSelections as SportsMarketSelectionInterface[], {
|
|
90
|
+
sportsCustomBetTotalOddsDecimal: null,
|
|
91
|
+
subBetSelections: null,
|
|
92
|
+
isEstimation: opt?.isEstimation,
|
|
93
|
+
});
|
|
94
|
+
totalOddsDecimal = totalOddsDecimal.plus(subBetOdds);
|
|
92
95
|
}
|
|
93
|
-
|
|
94
|
-
return limitTotalOddsDecimalPlaces(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
) {
|
|
96
|
+
|
|
97
|
+
return limitTotalOddsDecimalPlaces(totalOddsDecimal);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return limitTotalOddsDecimalPlaces(
|
|
101
|
+
selections.reduce((totalOddsDecimal, selection): BigNumber => {
|
|
102
|
+
// https://geniussports.atlassian.net/wiki/spaces/BID/pages/1239941172/Back-End+Integrations#Back-EndIntegrations-Whatareselectionresultingvalues%3F
|
|
103
|
+
const selectionOddsDecimal = oddsFractionToDecimal(selection.oddsNumerator, selection.oddsDenominator);
|
|
104
|
+
const marketSelection = marketSelectionMap[selection.marketSelectionId];
|
|
105
|
+
|
|
106
|
+
if (!marketSelection) {
|
|
107
|
+
throw new Error('Market selection not found');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (selection.status === SportsBetSelectionStatus.LOST) {
|
|
111
|
+
return BigNumber(0);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (selection.status === SportsBetSelectionStatus.WON) {
|
|
115
|
+
return totalOddsDecimal.multipliedBy(selectionOddsDecimal);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (selection.status === SportsBetSelectionStatus.PARTIAL) {
|
|
119
|
+
if (marketSelection.resultAdditionalData) {
|
|
120
|
+
if ('percentageWin' in marketSelection.resultAdditionalData && 'percentagePush' in marketSelection.resultAdditionalData) {
|
|
118
121
|
return totalOddsDecimal.multipliedBy(
|
|
119
122
|
selectionOddsDecimal
|
|
120
123
|
.multipliedBy(BigNumber(marketSelection.resultAdditionalData?.percentageWin).dividedBy(100))
|
|
121
124
|
.plus(BigNumber(marketSelection.resultAdditionalData?.percentagePush).dividedBy(100)),
|
|
122
125
|
);
|
|
123
|
-
} else if (
|
|
124
|
-
marketSelection.resultAdditionalData &&
|
|
125
|
-
'voidFactor' in marketSelection.resultAdditionalData &&
|
|
126
|
-
'outcome' in marketSelection.resultAdditionalData
|
|
127
|
-
) {
|
|
126
|
+
} else if ('voidFactor' in marketSelection.resultAdditionalData && 'outcome' in marketSelection.resultAdditionalData) {
|
|
128
127
|
if (marketSelection.resultAdditionalData.outcome === OutcomeResult.WINNING) {
|
|
129
128
|
return totalOddsDecimal.multipliedBy(
|
|
130
129
|
selectionOddsDecimal
|
|
@@ -132,114 +131,122 @@ export function calculateSportsPayoutOdds(
|
|
|
132
131
|
.plus(BigNumber(marketSelection.resultAdditionalData?.voidFactor)),
|
|
133
132
|
);
|
|
134
133
|
}
|
|
135
|
-
|
|
136
|
-
return totalOddsDecimal.multipliedBy(marketSelection.resultAdditionalData
|
|
137
|
-
} else if (
|
|
138
|
-
return totalOddsDecimal.multipliedBy(selectionOddsDecimal.multipliedBy(marketSelection.resultAdditionalData
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return totalOddsDecimal;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (selection.status === SportsBetSelectionStatus.PLACED) {
|
|
145
|
-
if (marketSelection.resultAdditionalData && 'countInPlace' in marketSelection.resultAdditionalData) {
|
|
146
|
-
return totalOddsDecimal.multipliedBy(selectionOddsDecimal.dividedBy(marketSelection.resultAdditionalData.countInPlace));
|
|
134
|
+
|
|
135
|
+
return totalOddsDecimal.multipliedBy(marketSelection.resultAdditionalData.voidFactor || 0);
|
|
136
|
+
} else if ('deadHeatFactor' in marketSelection.resultAdditionalData) {
|
|
137
|
+
return totalOddsDecimal.multipliedBy(selectionOddsDecimal.multipliedBy(marketSelection.resultAdditionalData.deadHeatFactor || 0));
|
|
147
138
|
}
|
|
148
|
-
|
|
149
|
-
return totalOddsDecimal;
|
|
150
139
|
}
|
|
151
|
-
|
|
152
|
-
if (selection.status === SportsBetSelectionStatus.PUSHED) {
|
|
153
|
-
return totalOddsDecimal;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (selection.status === SportsBetSelectionStatus.VOIDED || selection.status == SportsBetSelectionStatus.PROVIDER_VOIDED) {
|
|
157
|
-
return totalOddsDecimal;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (selection.status === SportsBetSelectionStatus.PENDING || selection.status === SportsBetSelectionStatus.CASHED_OUT) {
|
|
161
|
-
return calculateSelectionCashoutOdds(totalOddsDecimal, selectionOddsDecimal, marketSelection);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
selection.status satisfies never;
|
|
165
|
-
|
|
140
|
+
|
|
166
141
|
return totalOddsDecimal;
|
|
167
|
-
}
|
|
168
|
-
);
|
|
169
|
-
}
|
|
142
|
+
}
|
|
170
143
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
marketSelection: SportsMarketSelectionInterface,
|
|
175
|
-
) {
|
|
176
|
-
if (marketSelection?.additionalProbability) {
|
|
177
|
-
let cashoutOdds = BigNumber(0);
|
|
178
|
-
|
|
179
|
-
if (marketSelection.additionalProbability?.winProbability) {
|
|
180
|
-
if (marketSelection.additionalProbability.winProbability > 1) {
|
|
181
|
-
throw new Error('Win Probability is greater than 1');
|
|
144
|
+
if (selection.status === SportsBetSelectionStatus.PLACED) {
|
|
145
|
+
if (marketSelection.resultAdditionalData && 'countInPlace' in marketSelection.resultAdditionalData) {
|
|
146
|
+
return totalOddsDecimal.multipliedBy(selectionOddsDecimal.dividedBy(marketSelection.resultAdditionalData.countInPlace));
|
|
182
147
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// probability(ticket wins) * payoutIfTicketWins
|
|
186
|
-
cashoutOdds = cashoutOdds.plus(
|
|
187
|
-
totalOddsDecimal.multipliedBy(selectionOddsDecimal).multipliedBy(marketSelection.additionalProbability.winProbability),
|
|
188
|
-
);
|
|
148
|
+
|
|
149
|
+
return totalOddsDecimal;
|
|
189
150
|
}
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
throw new Error('Lose Probability is greater than 1');
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// payoutIfTicketLoses = 0
|
|
197
|
-
// probability(ticket loses) * payoutIfTicketLoses
|
|
198
|
-
cashoutOdds = cashoutOdds.plus(BigNumber(0).multipliedBy(marketSelection.additionalProbability.loseProbability));
|
|
151
|
+
|
|
152
|
+
if (selection.status === SportsBetSelectionStatus.PUSHED) {
|
|
153
|
+
return totalOddsDecimal;
|
|
199
154
|
}
|
|
200
|
-
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
throw new Error('Half Win Probability is greater than 1');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// payoutIfTicketWinsHalf = ticketStake * (((ticketOdds - 1) / 2) + 1)
|
|
207
|
-
// probability(ticket wins half) * payoutIfTicketWinsHalf
|
|
208
|
-
cashoutOdds = cashoutOdds.plus(
|
|
209
|
-
totalOddsDecimal
|
|
210
|
-
.multipliedBy(selectionOddsDecimal.minus(1).dividedBy(2).plus(1))
|
|
211
|
-
.multipliedBy(marketSelection.additionalProbability.halfWinProbability),
|
|
212
|
-
);
|
|
155
|
+
|
|
156
|
+
if (selection.status === SportsBetSelectionStatus.VOIDED || selection.status == SportsBetSelectionStatus.PROVIDER_VOIDED) {
|
|
157
|
+
return totalOddsDecimal;
|
|
213
158
|
}
|
|
214
|
-
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
159
|
+
|
|
160
|
+
if (selection.status === SportsBetSelectionStatus.CASHED_OUT) {
|
|
161
|
+
return calculateSelectionCashoutOdds(totalOddsDecimal, selectionOddsDecimal, marketSelection);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (selection.status === SportsBetSelectionStatus.PENDING) {
|
|
165
|
+
if (opt?.isEstimation) {
|
|
166
|
+
return totalOddsDecimal.multipliedBy(selectionOddsDecimal);
|
|
218
167
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
168
|
+
return calculateSelectionCashoutOdds(totalOddsDecimal, selectionOddsDecimal, marketSelection);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
selection.status satisfies never;
|
|
172
|
+
|
|
173
|
+
return totalOddsDecimal;
|
|
174
|
+
}, BigNumber(1)),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function calculateSelectionCashoutOdds(
|
|
179
|
+
totalOddsDecimal: BigNumber,
|
|
180
|
+
selectionOddsDecimal: BigNumber,
|
|
181
|
+
marketSelection: SportsMarketSelectionInterface,
|
|
182
|
+
) {
|
|
183
|
+
if (marketSelection?.additionalProbability) {
|
|
184
|
+
let cashoutOdds = BigNumber(0);
|
|
185
|
+
|
|
186
|
+
if (marketSelection.additionalProbability?.winProbability) {
|
|
187
|
+
if (marketSelection.additionalProbability.winProbability > 1) {
|
|
188
|
+
throw new Error('Win Probability is greater than 1');
|
|
223
189
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
190
|
+
|
|
191
|
+
// payoutIfTicketWins = ticketStake * ticketOdds
|
|
192
|
+
// probability(ticket wins) * payoutIfTicketWins
|
|
193
|
+
cashoutOdds = cashoutOdds.plus(
|
|
194
|
+
totalOddsDecimal.multipliedBy(selectionOddsDecimal).multipliedBy(marketSelection.additionalProbability.winProbability),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (marketSelection.additionalProbability?.loseProbability) {
|
|
199
|
+
if (marketSelection.additionalProbability.loseProbability > 1) {
|
|
200
|
+
throw new Error('Lose Probability is greater than 1');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// payoutIfTicketLoses = 0
|
|
204
|
+
// probability(ticket loses) * payoutIfTicketLoses
|
|
205
|
+
cashoutOdds = cashoutOdds.plus(BigNumber(0).multipliedBy(marketSelection.additionalProbability.loseProbability));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (marketSelection.additionalProbability?.halfWinProbability) {
|
|
209
|
+
if (marketSelection.additionalProbability.halfWinProbability > 1) {
|
|
210
|
+
throw new Error('Half Win Probability is greater than 1');
|
|
229
211
|
}
|
|
230
|
-
|
|
231
|
-
|
|
212
|
+
|
|
213
|
+
// payoutIfTicketWinsHalf = ticketStake * (((ticketOdds - 1) / 2) + 1)
|
|
214
|
+
// probability(ticket wins half) * payoutIfTicketWinsHalf
|
|
215
|
+
cashoutOdds = cashoutOdds.plus(
|
|
216
|
+
totalOddsDecimal
|
|
217
|
+
.multipliedBy(selectionOddsDecimal.minus(1).dividedBy(2).plus(1))
|
|
218
|
+
.multipliedBy(marketSelection.additionalProbability.halfWinProbability),
|
|
219
|
+
);
|
|
232
220
|
}
|
|
233
|
-
|
|
234
|
-
if (marketSelection?.
|
|
235
|
-
if (marketSelection.
|
|
236
|
-
throw new Error('Probability is greater than 1');
|
|
221
|
+
|
|
222
|
+
if (marketSelection.additionalProbability?.halfLoseProbability) {
|
|
223
|
+
if (marketSelection.additionalProbability.halfLoseProbability > 1) {
|
|
224
|
+
throw new Error('Half Lose Probability is greater than 1');
|
|
237
225
|
}
|
|
238
|
-
|
|
239
|
-
|
|
226
|
+
|
|
227
|
+
// payoutIfTicketLosesHalf = ticketStake / 2
|
|
228
|
+
// probability(ticket loses half) * payoutIfTicketLosesHalf
|
|
229
|
+
cashoutOdds = cashoutOdds.plus(totalOddsDecimal.dividedBy(2).multipliedBy(marketSelection.additionalProbability.halfLoseProbability));
|
|
240
230
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
231
|
+
|
|
232
|
+
if (marketSelection.additionalProbability?.refundProbability) {
|
|
233
|
+
// payoutIfTicketVoids = ticketStake
|
|
234
|
+
// probability(ticket voids) * payoutIfTicketVoids
|
|
235
|
+
cashoutOdds = cashoutOdds.plus(totalOddsDecimal.multipliedBy(marketSelection.additionalProbability.refundProbability));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return cashoutOdds;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (marketSelection?.probability) {
|
|
242
|
+
if (marketSelection.probability.isGreaterThan(1)) {
|
|
243
|
+
throw new Error('Probability is greater than 1');
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return totalOddsDecimal.multipliedBy(selectionOddsDecimal.multipliedBy(marketSelection.probability));
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return totalOddsDecimal
|
|
250
|
+
.multipliedBy(selectionOddsDecimal)
|
|
251
|
+
.dividedBy(oddsFractionToDecimal(marketSelection.oddsNumerator, marketSelection.oddsDenominator));
|
|
252
|
+
}
|