Portfolio selection (or portfolio management) is the art and science of making decisions about investment mix and policy, matching investments to objectives, asset allocation for individuals and institutions, and balancing risk against performance.

The aim of this tutorial is two-fold:

to show some modeling techniques to define portfolio selections problems;

to show how to efficiently implement those problems using MOSEK Optimizer API.

Consider the problem of an investor, who has to invest his money in $n$ different assets. Which allocation strategy should he use to obtain the maximum $\textit{benefit}$ from his investment?

Let $x_i$ be the percentage of money invested in assets $i$, and $\mu_i$ its expected return. Let assume we know the covariance matrix $\Sigma$ that link the assets together. We can then define two global measures of the portfolio performance:

For different choices of $x_i$ the investor will get different combinations of $\mu$ and $\sigma^2$. The set of all possible ($\sigma^2$, $\mu$) combinations is called the $\textit{attainable set}$.

With $\textit{risk}$ is intended a measurement of the likelihood that an investment will go up and down in value, and how often and by how much. The theory assumes that investors prefer to minimize risk; in fact, it assumes that given the choice of two portfolios with equal returns, investors will choose the one with the least risk.

In a fully-invested portfolio, every asset $i$ has his contribution in terms of risk. Let $RC_i(x)$ be this contribution, we have that the total risk of the invested portfolio is:

$$
\mathcal{R}(x) = \sum_{i=1}^n RC_i(x)
$$

where

$$
RC_i(x) = x_i \frac{\partial \mathcal{R}(x)}{\partial x_i}
$$

Let $b=(b_1,..,b_n)$ be a vector of $\textbf{budgets}$ such as $b_i > 0$ and $\sum_{i=1}^n b_i = 1$. We define a risk parity (or risk budgeting) portfolio as the solution of the following set of equations:

Since $\Sigma$ is positive semidefinite and the logarithm function is strictly concave, the objective function is $\textbf{strictly convex}$. From the first order condition, the unique solution is in corrispondence of the point where the gradient of the objective function is zero:

First of all, we have to import the MOSEK library into the project. Before setting up the optimization problem, a MOSEK $\textbf{environment}$ must be created. All tasks in the program should share the same environment (go to MOSEK Optimizer API Documentation for further information).

In [1]:

importmosekimportnumpyasnpenv=mosek.Env()

Next, an empty $\textbf{task}$ object is created:

In [2]:

task=env.Task()

Now, we create some sample data. In this case we have 10 assets and we randomly generate a semidefinite-positive covariance matrix and the budget vector.

In [3]:

n=10A=np.random.sample([n,n])#Q is our covariance matrixQ=np.dot(A,A.transpose())#b is the vector of budgets b=np.random.sample(n)b=b/np.sum(b)

For this type of problem, we have $n$ variables and $1$ constraint. So we set them into the task:

In [4]:

numvar=nnumcon=1task.appendvars(numvar)task.appendcons(numcon)

We have to write the constraint $x>0$. The bounds on variables are stored in the arrays:

The next step is to write down the objective function. Since the covariance matrix $\Sigma$ is symmetric, we can use only his upper-triangle:

In [7]:

#extract the indexes of the upper-triangle of the covariance matrixindexes=np.tril_indices(numvar)qsubi=indexes[0]qsubj=indexes[1]#create the upper-triangle matrix with the indexes previously extractedqval=np.tril(Q)[indexes]

Now, we can add the first part of the objective function to the task:

In [8]:

task.putqobj(qsubi,qsubj,qval)

Now, we have to add the logarithmic term to the objective function. MOSEK provides a simple interface for separable convex optimization problems, named SCopt, in which our problem perfecly fits. See official docs.

In SCopt each function must be expressed as sum of separable terms, i.e. simple univariate functions. IN the case of the logarithm, we can input a term of the form

$$
f \ln(g\cdot x + h),
$$

where $f,g,h$ are constants choosen in a way the function must be concave. In our case $f=-b$, $g=1$ and $h=0$.

In terms of implementation we do

In [9]:

#define logarithm for all n variablesopro=[mosek.scopr.log]*n#define the variable indexesoprjo=np.array([iforiinrange(n)])#define the multiplicative term (in this case we assume c=1)oprfo=-b#define the exponential termsoprgo=np.ones(n)#define the additive termsoprho=np.zeros(n)

We can now build the separable term using the function:

In [10]:

task.putSCeval(opro,oprjo,oprfo,oprgo,oprho)

Now we have to tell to the optimizer that this is a minimization problem:

/home/aszek/anaconda2/lib/python2.7/site-packages/numpy/core/_methods.py:26: VisibleDeprecationWarning: using a non-integer number instead of an integer will result in an error in the future
return umr_maximum(a, axis, None, out, keepdims)

In this notebook we showed how to cast in convex for the basic Risk Parity Portfolio problem. We then use the MOSEK SCopt Python interface to solve the problem. A simpler numerical example shows that the problem can be easily solved.

Further improvements may come from exploting sparsity in the covariance matrix. Additional constraints may be included, but the equivalence between of the convex formulation may not hold.