rconvex-optimizationcvxcvxr

Extension to the CVXR example cvxr_kelly-strategy not DCP compliant?


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.


Solution

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