alnurali / cvxstoc

Disciplined convex stochastic programming. For the cvxstoc home page, please see:
http://alnurali.github.io/cvxstoc/
31 stars 11 forks source link

How to solve portfolio optimization problem having externally generated Monte-carlo simulations #9

Open aisurfer opened 3 years ago

aisurfer commented 3 years ago

Hi! I checked out the first example in documentation http://www.alnurali.com/cvxstoc/index.html#what-can-i-do-with-cvxstoc Suppose I already have Monte-carlo simulated traces for each asset. How can I use it with cvxstoc framework?

Considering example from docs, I would like to replace NormalRandomVariable with my existing simulations

...
p = NormalRandomVariable(mu, Sigma)
alpha = -1
beta = 0.05

# Create and solve stochastic optimization problem.
x = Variable(n)
p = Problem(Maximize(expectation(x.T*p, num_samples=100)),
            [x >= 0, x.T*numpy.ones(n) == 1,
             prob(x.T*p <= alpha, num_samples=100) <= beta])
p.solve()

As I see the easiest way is to hack RandomVariable's method sample() and make it to return my simulations instead of pymc traces https://github.com/alnurali/cvxstoc/blob/7d327e8445574b65981b44c8e468cc1faef7476c/cvxstoc/random_variable.py#L249

aisurfer commented 3 years ago

@SteveDiamond Hallo again:) I appreciate the advice to try scipy.optimize, but I decided to try all cvx* packages at first. I found example in docs and test https://github.com/alnurali/cvxstoc/blob/master/tests/test_chance_constr.py#L111 that is exactly what I need, except using simulations. So following my task mentioned in https://github.com/cvxpy/cvxpy/issues/1362 I have implemented MonteCarloVariable for cvxstoc with predefined sample data.

But running test.py I obtain Segmentation fault (core dumped)

described in #8

Could you please help with it?

Working code snippet:

#
################################  test.py ###########################
from cvxstoc import prob                                                                                                                                                                                            
import cvxpy as cp
import numpy as np
np.random.seed(1)

from monte_carlo_variable import MonteCarloVariable

# Create problem data.
asset_count = 10
trials = 100
means = np.random.randn(asset_count) + 0.1
simulations = MonteCarloVariable(np.random.random((trials, asset_count)) - 0.5, name="mc_sims")
beta = 0.5

# Create and solve optimization problem.
x = cp.Variable(asset_count)
p1 = cp.Problem(
    cp.Maximize(x.T @ means),
    [prob(-x.T @ simulations >= 0, trials) <= beta, sum(x) == 1, x >= 0.01],
)
p1.solve(solver=cp.ECOS)
print("TEST RESULT", p1.value, x)

#
####################### monte_carlo_variable.py ##############
#

from cvxstoc import RandomVariable                                                                                                                                                                                  

class MonteCarloVariable(RandomVariable):
    """
    simulations is of shape (trials, asset_count)
    """
    def __init__(self, simulations, rv=None, model=None, name=None, val_map=None, metadata=None):
        self.simulations = simulations
        if name is not None:
            self._name = name
        self._metadata = metadata
        self._val_map = val_map
        self.set_rv_model_and_maybe_name(rv, model)
        self.set_shape()
        super(RandomVariable, self).__init__(self._shape, self._name)
        # other stuff (aisurfer)
        self._rv = None
        self._model = None

    @property
    def mean(self):
        raise Exception("TODO what is it for?")
        return self._metadata["mu"]

    def set_rv_model_and_maybe_name(self, rv, model):
        pass

    def set_shape(self):
        self._shape = (self.simulations.shape[1])

    def __deepcopy__(self, memo):
        # Override.
        return self.__class__(
            simulations = self.simulations,
            rv=self._rv,
            model=self._model,
            name=self.name(),
            val_map=self._val_map,
            metadata=self._metadata) 

    def sample(self, num_samples, num_burnin_samples=0):
        # num_samples and num_burnin_samples have no effect
        return self.simulations
SteveDiamond commented 3 years ago

@aisurfer the CVXSTOC author recommends using this package instead of CVXSTOC: https://github.com/cvxgrp/osmm It's a very similar idea.

aisurfer commented 3 years ago

Thank you so much I'll try it and report to this thread.