QDuv / Black-Litterman

Coding for a project
0 stars 0 forks source link

Issues with scipy.optimize.minimize #1

Open QDuv opened 6 years ago

QDuv commented 6 years ago

Hi !

I'm trying to use Black Litterman's implementation, but I have issues with the function scipy.optimize.minimize. I saw that there are troubles with this function on Python 3 (I'm using Jupyter- Python 3) but I didn't understand how to correct it. The error is : "Positive directional derivative for linesearch". Can someone help me ?

Here is the code :

%pylab
%matplotlib inline
pylab.rcParams['figure.figsize'] = (10, 6)
import scipy.optimize
from pandas import *
import random
R=[ 3.52914378e+00, 2.27067008e+00, 2.21718411e+00, 2.08698532e+00, 9.66720533e-01, 4.33678111e-01, 1.02800351e+00, 2.09130245e+00, 8.59740218e-01, 1.61942833e+00, 1.34750854e+00, 1.73252100e+00, 1.90469878e+00, 1.65190158e+00, 1.53015716e+00, 1.18641905e+00, 6.80904281e-01, 8.63817953e-01, 9.30231930e-01, 8.57781679e-01, 3.31322363e+00, 5.74726953e-01, 5.37853529e-02, 1.13779136e+00, 4.28458363e-01, 2.94717339e+03, 3.48582980e-01, 7.97226868e+00, 2.56852189e+00, 9.37279123e-01, 3.23706876e+00, 3.97094526e+00, -2.52958221e-01, 1.63888156e+00, 1.26917331e+00, 1.90669650e+00]
C= [[0 for i in range(36)] for j in range(36)]
for i in range(36):
    for j in range(36-i):
        C[i][j]=random.uniform(-1,1)
        C[j][i]=C[i][j] 
def port_mean(W, R):
    return sum(R * W)

# Calculates portfolio variance of returns
def port_var(W, C):
    return dot(dot(W, C), W)

# Combination of the two functions above - mean and variance of returns calculation
def port_mean_var(W, R, C):
    return port_mean(W, R), port_var(W, C)
def solve_frontier(R, C, rf): # Argument rendement, matrice de covariance et taux actif sans risque
     def fitness(W, R, C, r): #Pareil mais avec allocation et un rendement "cible" défini 
         # For given level of return r, find weights which minimizes portfolio variance.
        mean, var = port_mean_var(W, R, C)
        penalty = 100 * abs(mean - r)  
         # Big penalty for not meeting stated portfolio return effectively serves as optimization constraint
        return var + penalty
    frontier_mean, frontier_var = [], []
    n = len(R)  # Number of assets in the portfolio
    for r in linspace(min(R), max(R), num=500):  # Iterate through the range of returns on Y axis
        W = ones([n]) / n  # start optimization with equal weights
        b_ = [(0, 1) for i in range(n)]
        c_ = ({'type': 'eq', 'fun': lambda W: sum(W) - 1.})
        optimized = scipy.optimize.minimize(fitness, W, (R, C, r), method='SLSQP', constraints=c_, bounds=b_, tol=0.01)
        if not optimized.success:
            raise BaseException(optimized.message)
     # add point to the efficient frontier [x,y] = [optimized.x, r]
        frontier_mean.append(r)
        frontier_var.append(port_var(optimized.x, C))
    return array(frontier_mean), array(frontier_var)
solve_frontier(R,C,0.733)
rgommers commented 6 years ago

Not quite what I meant; I meant the SciPy Github issue tracker. That said, I assume you saw https://github.com/scipy/scipy/issues/4240?

Also, please change your code example so it actually runs (standalone). I get both an indentation error and "R is not defined". So hard to say what's wrong.

QDuv commented 6 years ago

@rgommers I modified the code, but C will still be missing.. The point is that C is a 36*36 matrix and I'm not sure that it would be very convenient to put it in the code... Should I still put it in ? I also saw the topic you sent, but I'm quiet new on github, could you explain me what you meant ?

rgommers commented 6 years ago

Yes, either put it in or find a replacement for it (e.g. maybe it also crashes with C = np.random.rand(36,36)?). Your example has to be runnable, or it's an order of magnitude harder to diagnose.

QDuv commented 6 years ago

I modified the code, it should run now.

rgommers commented 6 years ago

Your example still has inconsistent indentation and missing imports. Here is one that actually works;

from numpy import *
import scipy.optimize
from pandas import *
import random
R=[ 3.52914378e+00, 2.27067008e+00, 2.21718411e+00, 2.08698532e+00, 9.66720533e-01, 4.33678111e-01, 1.02800351e+00, 2.09130245e+00, 8.59740218e-01, 1.61942833e+00, 1.34750854e+00, 1.73252100e+00, 1.90469878e+00, 1.65190158e+00, 1.53015716e+00, 1.18641905e+00, 6.80904281e-01, 8.63817953e-01, 9.30231930e-01, 8.57781679e-01, 3.31322363e+00, 5.74726953e-01, 5.37853529e-02, 1.13779136e+00, 4.28458363e-01, 2.94717339e+03, 3.48582980e-01, 7.97226868e+00, 2.56852189e+00, 9.37279123e-01, 3.23706876e+00, 3.97094526e+00, -2.52958221e-01, 1.63888156e+00, 1.26917331e+00, 1.90669650e+00]
C= [[0 for i in range(36)] for j in range(36)]
for i in range(36):
    for j in range(36-i):
        C[i][j]=random.uniform(-1,1)
        C[j][i]=C[i][j] 

def port_mean(W, R):
    return sum(R * W)

# Calculates portfolio variance of returns
def port_var(W, C):
    return dot(dot(W, C), W)

# Combination of the two functions above - mean and variance of returns calculation
def port_mean_var(W, R, C):
    return port_mean(W, R), port_var(W, C)

def solve_frontier(R, C, rf): # Argument rendement, matrice de covariance et taux actif sans risque
    def fitness(W, R, C, r): #Pareil mais avec allocation et un rendement "cible" défini 
        # For given level of return r, find weights which minimizes portfolio variance.
        mean, var = port_mean_var(W, R, C)
        penalty = 100 * abs(mean - r)  
         # Big penalty for not meeting stated portfolio return effectively serves as optimization constraint
        return var + penalty

    frontier_mean, frontier_var = [], []
    n = len(R)  # Number of assets in the portfolio
    for r in linspace(min(R), max(R), num=500):  # Iterate through the range of returns on Y axis
        W = ones([n]) / n  # start optimization with equal weights
        b_ = [(0, 1) for i in range(n)]
        c_ = ({'type': 'eq', 'fun': lambda W: sum(W) - 1.})
        optimized = scipy.optimize.minimize(fitness, W, (R, C, r), method='SLSQP', constraints=c_, bounds=b_, tol=0.01)
        if not optimized.success:
            raise BaseException(optimized.message)

    # add point to the efficient frontier [x,y] = [optimized.x, r]
    frontier_mean.append(r)
    frontier_var.append(port_var(optimized.x, C))

    return array(frontier_mean), array(frontier_var)

solve_frontier(R,C,0.733)

This seems to run into gh-7618, which doesn't have a solution. I suggest trying to reformulate your problem to use a different constraint and see if that helps.