Herwini
Herwini

Reputation: 447

Maximize objective using scipy (by kelly criterium)

I have the following two pandas dataframes: new & outcome

new = pd.DataFrame([[5,5,1.6],[0.22,0.22,0.56]]).T
new.index = ['Visitor','Draw','Home']
new.columns = ['Decimal odds', 'Win prob'] 
new['Bet amount'] = np.zeros((len(new),1))

With output:

         Decimal odds  Win prob  Bet amount
Visitor           5.0      0.22         0.0
Draw              5.0      0.22         0.0
Home              1.6      0.56         0.0

And dataframe 'outcome'

outcome = pd.DataFrame([[0.22,0.22,0.56],[100,100,100]]).T
outcome.index = ['Visitor win','Draw','Home win']
outcome.columns = ['Prob.','Starting bankroll']
outcome['Wins'] = ((new['Decimal odds'] - 1) * new['Bet amount']).values
outcome['Losses'] = [sum(new['Bet amount'][[1,2]]) , sum(new['Bet amount'][[0,2]]), sum(new['Bet amount'][[0,1]])]
outcome['Ending bankroll'] = outcome['Starting bankroll'] + outcome['Wins'] - outcome['Losses']
outcome['Logarithm'] = np.log(outcome['Ending bankroll'])

With output:

             Prob.  Starting bankroll  Wins  Losses  Ending bankroll  Logarithm
Visitor win   0.22              100.0   0.0     0.0            100.0    4.60517
Draw          0.22              100.0   0.0     0.0            100.0    4.60517
Home win      0.56              100.0   0.0     0.0            100.0    4.60517

Hereby the objective is calculated by the formula below:

objective = sum(outcome['Prob.'] * outcome['Logarithm'])

Now I want to maximize the objective by the values contained in column `new['Bet amount']. The constraints are that a, b, and c are bounded between 0 and 100. Also the summation of a, b and c must be below 100. Reason is that a,b,c resemble the ratio of your bankroll that is used to place a sports bet.

Want to achieve this using the scipy library. My code so far looks like:

from scipy.optimize import minimize

prob = new['Win prob']
decimal = new['Decimal odds']
bank = outcome['Starting bankroll'][0]

def constraint1(bet):
    a,b,c = bet
    
    return 100 - a + b + c

con1 = {'type': 'ineq', 'fun': constraint1}
cons = [con1]
    
b0, b1, b2 = (0,100), (0,100), (0,100)     
bnds = (b0, b1, b2)

def f(bet, sign = -1):
    global prob, decimal, bank
    p0,p1,p2 = prob
    d0,d1,d2 = decimal
    a,b,c = bet
    
    wins0 = a * (d0-1)
    wins1 = b * (d1-1)
    wins2 = c * (d2-1)
    
    loss0 = b + c
    loss1 = a + c
    loss2 = a + b

    log0 = np.log(bank + wins0 - loss0)
    log1 = np.log(bank + wins1 - loss1)
    log2 = np.log(bank + wins2 - loss2)
    
    objective = (log0 * p0 + log1 * p1 + log2 * p2)
    
    return sign * objective


bet = [5,8,7]

result = minimize(f, bet, method = 'SLSQP', bounds = bnds, constraints = cons)
    

This however, does not result in the desired result. Desired result would be:

a = 3.33
b = 3.33
c = 0

My question is also how to set the method and initial values? Results seem to differ a lot by assigning different method's and initial values for the bets.

Any help would be greatly appreciated!

(This is an example posted on the pinnacle website: https://www.pinnacle.com/en/betting-articles/Betting-Strategy/the-real-kelly-criterion/HZKJTFCB3KNYN9CJ)

Upvotes: 3

Views: 265

Answers (1)

Troy D
Troy D

Reputation: 2245

If you print out the "bet" values inside your function, you can see where it's going wrong.

[5. 8. 7.]
[5.00000001 8.         7.        ]
[5.         8.00000001 7.        ]
[5.         8.         7.00000001]
[5.00040728 7.9990977  6.99975556]
[5.00040729 7.9990977  6.99975556]
[5.00040728 7.99909772 6.99975556]
[5.00040728 7.9990977  6.99975558]
[5.00244218 7.99458802 6.99853367]
[5.0024422  7.99458802 6.99853367]

The algorithm is trying to optimize the formula with very small adjustments relative to your initial values, and it never adjusts enough to get to the values you're looking for.

If you check scipy webpage, you find https://docs.scipy.org/doc/scipy/reference/optimize.minimize-slsqp.html#optimize-minimize-slsqp

eps float
    Step size used for numerical approximation of the Jacobian.

result = minimize(f, bet, method='SLSQP', bounds=bnds, constraints=cons,
                  options={'maxiter': 100, 'ftol': 1e-06, 'iprint': 1, 'disp': True,
                           'eps': 1.4901161193847656e-08, 'finite_diff_rel_step': None})

So you're starting off with a step size of 1.0e-08, so your initial estimates are off by many orders of magnitude outside the range where the algorithm is going to be looking.

I'd recommend normalizing your bets to values between zero and 1. So instead of saying I'm placing a bet between 0 and 100, just say you're wagering a fraction of your net wealth between 0 and 1. A lot of algorithms are designed to work with standardized inputs (between 0 and 1) or normalized inputs (standard deviations from the mean).

Also, it looks like :

def constraint1(bet):
    a,b,c = bet
    
    return 100 - a + b + c

should be:

def constraint1(bet):
    a,b,c = bet
    
    return 100 - (a + b + c)

but I don't think that impacts your results

Upvotes: 1

Related Questions