-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkelly.py
74 lines (62 loc) · 2.8 KB
/
kelly.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import numpy as np
class MultiKellyBettor:
'''
A Kelly calcalator that supports multiple exclusive outcomes.
Parameters
----------
odds : win odds of each competitor
prob : winning probability of each competitor
label : names of each competitor (optional)
Output
------
prop : a dictionary with the key being the competitor label and
the value being the proportion of the whole capital to bet on
Reference
---------
1. https://www.sportsbookreview.com/picks/tools/kelly-calculator/
2. https://www.sportsbookreview.com/forum/handicapper-think-tank/29624-simultaneous-event-kelly-calculator-beta.html
3. https://www.sportsbookreview.com/forum/handicapper-think-tank/521569-simple-closed-form-solution-unconstrained-simultaneous-bet-kelly-staking.html
'''
def __init__(self, odds, prob, label = []):
self.odds = np.array(odds)
self.prob = np.array(prob)
if len(self.odds.shape) != 1 or len(self.prob.shape) != 1:
raise ValueError('The dimension of odds and prob must be 1.')
if self.odds.shape != self.prob.shape:
raise ValueError('The length of odds does not match the length of prob.')
if np.sum(self.prob) - 1 > 1e6:
raise ValueError('prob does not sum up to 1.')
if np.any(self.odds <= 1):
raise ValueError('odds must be all larger than 1.')
if not label:
label = list(range(len(odds)))
elif len(odds) != len(label):
raise ValueError('The length of label does not match.')
self.label = label
def transform(self):
prop = {self.label[k] : 0 for k in range(len(self.odds))}
eret = np.array([o * p for o, p in zip(self.odds, self.prob)])
indices = np.argsort(eret)[::-1]
odds = self.odds[indices]
prob = self.prob[indices]
eret = eret[indices]
if eret[0] <= 1: #it means no edge so no betting
return prop
rodds = 1 / odds
csprob = np.cumsum(prob)
csrodds = np.cumsum(rodds)
tmp = (1 - csprob) / (1 - csrodds)
tmp = tmp[tmp > 0]
v = tmp.min()
bf = prob - v / odds
bf = np.where(bf < 0, 0, bf)
for k in range(len(tmp)):
prop[self.label[indices[k]]] = bf[k]
return prop
if __name__ == '__main__':
mkb = MultiKellyBettor(odds = [1.87, 3.4, 3.4],
prob = [0.592, 0.285, 0.123],
label = ['horse1', 'horse2', 'horse3'])
prop = mkb.transform()
print(prop) #{'horse1': 0.207625, 'horse2': 0.07359374999999999, 'horse3': 0.0}
print(sum(prop.values())) #total_prop : 0.28121874999999996