0

I've been working through some of the example code trying to learn CVX and spinning my wheels trying to figure out the extension to the Kelly example in CVXR:

"Extensions: As observed in some trajectories above, wealth tends to drop by a significant amount before increasing eventually. One way to reduce this drawdown risk is to add a convex constraint as described in Busseti, Ryu, and Boyd (2016, 5.3) With CVXR, this can be accomplished in a single line using the log_sum_exp atom. Other extensions like wealth goals, betting restrictions, and VaR/CVaR bounds are also readily incorporated."

The R version of the CVXR example (without the extension) is here: https://cvxr.rbind.io/cvxr_examples/cvxr_kelly-strategy/

## Solve for Kelly optimal bets
b <- Variable(n)
obj <- Maximize(t(ps) %*% log(rets %*% b))
constraints <- list(sum(b) == 1, b >= 0)
prob <- Problem(obj, constraints)
result <- solve(prob)
bets <- result$getValue(b)

This works perfectly.

The python version (from the reference paper) with the extension is here: https://github.com/cvxgrp/kelly_code

"The finite outcome RCK problem (11) can be formulated and solved in CVXPY as"

b = Variable(n)
lambda_risk = Parameter(sign = ’positive’)
growth = ps.T*log(rets.T*b)
risk_constraint = (log_sum_exp (log(ps) - lambda_risk * log(rets.T*b)) <= 0)
constraints = [ sum_entries(b) == 1, b >= 0, risk_constraint ]
risk_constr_kelly = Problem(Maximize(growth),constraints)
risk_constr_kelly.solve()

In R with the above formulation, lambda_risk and risk_constraints should look like:

lambda_risk = Parameter(sign =  "POSITIVE")
risk_constraint = (log_sum_exp(log(ps) - lambda_risk * log(rets %*% b) ) ) 

However, this results in Error in CVXR::psolve(a, b, ...) : Problem does not follow DCP rules.

is_atom_convex(log_sum_exp(log(ps) - lambda_risk * log(rets %*% b) ))

TRUE

is_convex(log_sum_exp(log(ps) - lambda_risk * log(rets %*% b) ))

FALSE

is_dcp(log_sum_exp(log(ps) - lambda_risk * log(rets %*% b) ))

FALSE

curvature((sum(exp( log(ps) - lambda_risk * log(rets %*% b) ))))
curvature(log(sum(exp( log(ps) - lambda_risk * log(rets %*% b) ))))

CONVEX & UNKNOWN

sign((sum(exp( log(ps) - lambda_risk * log(rets %*% b) ))))
sign(log(sum(exp( log(ps) - lambda_risk * log(rets %*% b) ))))

POSITIVE & UNKNOWN

So it appears the final log is what disqualifies it.

If I substitute the Variable b with the actual results “bets” from the original iteration, it does say that it is_dcp TRUE

is_dcp(log_sum_exp(log(ps) - lambda_risk * log(rets %*% bets) ))

TRUE

Could it be as this unanswered post (same basic question) Log_sum_exp of a convex function is not dcp compliant? suggested that CVXR::log_sum_exp “is unable to deduce the convexity of log_sum_exp” , perhaps because it does not recognize the sign of the variables?

I would be surprised if there were an R bug, rather than something missing from my code.

Also, with lambda_risk@value set to zero, it should just return the straight Kelly Optimal bet. I only get that result if I overwrite the variable lambda_risk with the number 0.

is_dcp( log_sum_exp( log(ps) - 0 * log(rets %*% b) ) )
is_dcp(log_sum_exp( log(ps) - 0))

TRUE & TRUE

lambda_risk@value= 0
is_dcp( log_sum_exp( log(ps) - lambda_risk * log(rets %*% b) ) ) 

FALSE

I'm stumped.

There are python solutions available everywhere and I've also got the formulation I tried above to work in DEOPTIM (at considerably longer times), so I wouldn't have expected the CVXR implementation to be all that tricky. Any avenues of pursuit, or even just a confirmation that someone got it to work (not a bug), would be appreciated.

SCW
  • 3
  • 4

1 Answers1

0

This is actually a bug. Thanks for pointing it out! We've just released a fix in CVXR v0.99-5.

anqif
  • 73
  • 1
  • 6