Notebook
In [21]:
import numpy as np
import cvxopt as opt
from cvxopt import blas, solvers
import pandas as pd
import matplotlib.pyplot as plt

#returns
returns_annual = np.array([ 0.02257639,  0.02001225,  0.03068152,  0.0327813 ,  0.02896678,
        0.03317987,  0.03673188,  0.0559047 ,  0.05284086,  0.02560594,
        0.04733147,  0.06409186,  0.0692734 ,  0.07350909,  0.07606551,
        0.10042052,  0.07422095,  0.05269779,  0.04733147,  0.05060317,
        0.05584354])
In [22]:
#covariance matrix
cov = np.array([[  1.56250000e-04,   4.99558000e-06,  -7.89947000e-05,
         -3.70728000e-04,   4.39047000e-05,  -5.75173000e-05,
         -9.13886000e-05,   1.28452000e-04,   3.10231000e-04,
          2.31286000e-05,   1.96144000e-04,   8.97648000e-05,
          1.43986000e-04,   1.00257000e-04,   1.16656000e-04,
          2.19236000e-04,  -1.07805000e-04,   1.89716000e-04,
          2.41902000e-04,   5.51908000e-04,   9.80653000e-05],
       [  4.99558000e-06,   2.62144000e-05,   4.38364000e-05,
          2.67018000e-05,   1.95409000e-05,   1.96855000e-05,
         -1.22890000e-05,  -4.51820000e-05,  -5.58737000e-05,
          4.62366000e-05,  -4.80234000e-05,  -4.73159000e-05,
         -6.55687000e-05,  -7.52252000e-05,  -1.41030000e-06,
          8.31243000e-05,  -5.54015000e-05,  -2.74004000e-06,
         -6.19414000e-06,   6.51139000e-05,   7.44569000e-05],
       [ -7.89947000e-05,   4.38364000e-05,   1.40625000e-03,
          3.74700800e-03,   1.29643600e-03,   1.14230600e-03,
          9.68166000e-04,  -8.19559000e-04,  -1.45587000e-03,
          1.71401900e-03,  -1.17644000e-05,  -1.70673900e-03,
         -2.05590200e-03,  -2.58990700e-03,  -1.55470000e-03,
         -1.54628900e-03,  -1.19318000e-04,  -1.64028600e-03,
         -1.12550800e-03,  -6.83836000e-04,   2.54930900e-03],
       [ -3.70728000e-04,   2.67018000e-05,   3.74700800e-03,
          1.62562500e-02,   3.48697200e-03,   3.74239400e-03,
          3.64520300e-03,  -2.97503900e-03,  -4.51015200e-03,
          3.86404100e-03,   6.13606000e-04,  -6.41711500e-03,
         -7.28671000e-03,  -9.19932500e-03,  -6.54248700e-03,
         -7.17977400e-03,   4.19613000e-05,  -6.35566700e-03,
         -4.06152100e-03,  -5.67590600e-03,   5.64082300e-03],
       [  4.39047000e-05,   1.95409000e-05,   1.29643600e-03,
          3.48697200e-03,   2.83556300e-03,   1.55483400e-03,
          2.12341900e-03,   1.42886200e-03,   2.32479000e-04,
          2.66529200e-03,   1.27743900e-03,   4.24670000e-04,
          6.22514000e-04,  -1.75175000e-04,   1.50679000e-03,
          2.91928900e-03,   1.84070800e-03,   4.74614000e-04,
          6.44887000e-04,   2.43258300e-03,   4.84344600e-03],
       [ -5.75173000e-05,   1.96855000e-05,   1.14230600e-03,
          3.74239400e-03,   1.55483400e-03,   1.41015300e-03,
          1.92732500e-03,   6.21599000e-04,  -1.92977000e-04,
          1.92345000e-03,   8.76294000e-04,  -2.16693000e-05,
         -5.26392000e-05,  -6.85483000e-04,   6.61324000e-04,
          1.16190800e-03,   1.67399000e-03,  -2.99488000e-04,
          3.75704000e-05,   3.39338000e-04,   2.88240500e-03],
       [ -9.13886000e-05,  -1.22890000e-05,   9.68166000e-04,
          3.64520300e-03,   2.12341900e-03,   1.92732500e-03,
          3.88129000e-03,   3.03919200e-03,   1.57788400e-03,
          2.81988300e-03,   1.93642700e-03,   2.45555500e-03,
          2.92340700e-03,   2.15488800e-03,   4.50143700e-03,
          5.87788900e-03,   4.50415000e-03,   1.94843500e-03,
          1.88501500e-03,   2.49784600e-03,   4.25686500e-03],
       [  1.28452000e-04,  -4.51820000e-05,  -8.19559000e-04,
         -2.97503900e-03,   1.42886200e-03,   6.21599000e-04,
          3.03919200e-03,   7.29316000e-03,   5.28018900e-03,
          1.71714300e-03,   2.83293600e-03,   8.52663100e-03,
          1.01634750e-02,   1.04885630e-02,   1.09257860e-02,
          1.34714540e-02,   8.74433300e-03,   6.75213200e-03,
          5.38392900e-03,   6.59230300e-03,   1.76885600e-03],
       [  3.10231000e-04,  -5.58737000e-05,  -1.45587000e-03,
         -4.51015200e-03,   2.32479000e-04,  -1.92977000e-04,
          1.57788400e-03,   5.28018900e-03,   6.00935000e-03,
         -4.37287000e-04,   3.19925700e-03,   6.16541100e-03,
          7.38431400e-03,   7.40191700e-03,   7.48489700e-03,
          9.20490700e-03,   4.64672000e-03,   5.44304700e-03,
          4.96219100e-03,   4.82228700e-03,  -1.09809900e-03],
       [  2.31286000e-05,   4.62366000e-05,   1.71401900e-03,
          3.86404100e-03,   2.66529200e-03,   1.92345000e-03,
          2.81988300e-03,   1.71714300e-03,  -4.37287000e-04,
          6.78152300e-03,   6.67707000e-04,   2.48903900e-03,
          2.31903800e-03,   1.56509700e-03,   5.48758300e-03,
          7.03538500e-03,   4.36907900e-03,   1.65949900e-03,
          5.44662000e-04,   5.70043800e-03,   7.87890800e-03],
       [  1.96144000e-04,  -4.80234000e-05,  -1.17644000e-05,
          6.13606000e-04,   1.27743900e-03,   8.76294000e-04,
          1.93642700e-03,   2.83293600e-03,   3.19925700e-03,
          6.67707000e-04,   5.85684100e-03,   2.16491100e-03,
          2.58250600e-03,   1.23818800e-03,   2.90695700e-03,
          4.43971600e-03,   2.98432500e-03,   2.24538900e-03,
          2.83905400e-03,   1.47632700e-03,   1.56996400e-03],
       [  8.97648000e-05,  -4.73159000e-05,  -1.70673900e-03,
         -6.41711500e-03,   4.24670000e-04,  -2.16693000e-05,
          2.45555500e-03,   8.52663100e-03,   6.16541100e-03,
          2.48903900e-03,   2.16491100e-03,   2.08831400e-02,
          2.24025690e-02,   2.45185560e-02,   2.19790520e-02,
          2.38895730e-02,   1.75801840e-02,   1.30941960e-02,
          7.37368400e-03,   1.06966500e-02,  -4.81409000e-04],
       [  1.43986000e-04,  -6.55687000e-05,  -2.05590200e-03,
         -7.28671000e-03,   6.22514000e-04,  -5.26392000e-05,
          2.92340700e-03,   1.01634750e-02,   7.38431400e-03,
          2.31903800e-03,   2.58250600e-03,   2.24025690e-02,
          2.59596540e-02,   2.86543970e-02,   2.38707170e-02,
          2.68450790e-02,   2.01602870e-02,   1.50872350e-02,
          8.74069600e-03,   1.25378060e-02,   2.95306000e-04],
       [  1.00257000e-04,  -7.52252000e-05,  -2.58990700e-03,
         -9.19932500e-03,  -1.75175000e-04,  -6.85483000e-04,
          2.15488800e-03,   1.04885630e-02,   7.40191700e-03,
          1.56509700e-03,   1.23818800e-03,   2.45185560e-02,
          2.86543970e-02,   3.51765030e-02,   2.47405710e-02,
          2.74306820e-02,   2.27783690e-02,   1.60786020e-02,
          8.70456900e-03,   1.18615360e-02,  -1.30850700e-03],
       [  1.16656000e-04,  -1.41030000e-06,  -1.55470000e-03,
         -6.54248700e-03,   1.50679000e-03,   6.61324000e-04,
          4.50143700e-03,   1.09257860e-02,   7.48489700e-03,
          5.48758300e-03,   2.90695700e-03,   2.19790520e-02,
          2.38707170e-02,   2.47405710e-02,   2.97645310e-02,
          3.24564880e-02,   1.88608310e-02,   1.62506210e-02,
          9.77198900e-03,   1.55631710e-02,   2.81165700e-03],
       [  2.19236000e-04,   8.31243000e-05,  -1.54628900e-03,
         -7.17977400e-03,   2.91928900e-03,   1.16190800e-03,
          5.87788900e-03,   1.34714540e-02,   9.20490700e-03,
          7.03538500e-03,   4.43971600e-03,   2.38895730e-02,
          2.68450790e-02,   2.74306820e-02,   3.24564880e-02,
          4.68073230e-02,   1.99925610e-02,   2.02594600e-02,
          1.23138630e-02,   2.19358090e-02,   9.27374300e-03],
       [ -1.07805000e-04,  -5.54015000e-05,  -1.19318000e-04,
          4.19613000e-05,   1.84070800e-03,   1.67399000e-03,
          4.50415000e-03,   8.74433300e-03,   4.64672000e-03,
          4.36907900e-03,   2.98432500e-03,   1.75801840e-02,
          2.01602870e-02,   2.27783690e-02,   1.88608310e-02,
          1.99925610e-02,   2.70602500e-02,   9.52197600e-03,
          5.53900500e-03,   7.05394900e-03,   1.90384200e-03],
       [  1.89716000e-04,  -2.74004000e-06,  -1.64028600e-03,
         -6.35566700e-03,   4.74614000e-04,  -2.99488000e-04,
          1.94843500e-03,   6.75213200e-03,   5.44304700e-03,
          1.65949900e-03,   2.24538900e-03,   1.30941960e-02,
          1.50872350e-02,   1.60786020e-02,   1.62506210e-02,
          2.02594600e-02,   9.52197600e-03,   1.11420690e-02,
          6.87022300e-03,   1.08211710e-02,   2.70360000e-03],
       [  2.41902000e-04,  -6.19414000e-06,  -1.12550800e-03,
         -4.06152100e-03,   6.44887000e-04,   3.75704000e-05,
          1.88501500e-03,   5.38392900e-03,   4.96219100e-03,
          5.44662000e-04,   2.83905400e-03,   7.37368400e-03,
          8.74069600e-03,   8.70456900e-03,   9.77198900e-03,
          1.23138630e-02,   5.53900500e-03,   6.87022300e-03,
          5.68516000e-03,   6.90732300e-03,   1.23053500e-03],
       [  5.51908000e-04,   6.51139000e-05,  -6.83836000e-04,
         -5.67590600e-03,   2.43258300e-03,   3.39338000e-04,
          2.49784600e-03,   6.59230300e-03,   4.82228700e-03,
          5.70043800e-03,   1.47632700e-03,   1.06966500e-02,
          1.25378060e-02,   1.18615360e-02,   1.55631710e-02,
          2.19358090e-02,   7.05394900e-03,   1.08211710e-02,
          6.90732300e-03,   2.80756830e-02,   1.40792030e-02],
       [  9.80653000e-05,   7.44569000e-05,   2.54930900e-03,
          5.64082300e-03,   4.84344600e-03,   2.88240500e-03,
          4.25686500e-03,   1.76885600e-03,  -1.09809900e-03,
          7.87890800e-03,   1.56996400e-03,  -4.81409000e-04,
          2.95306000e-04,  -1.30850700e-03,   2.81165700e-03,
          9.27374300e-03,   1.90384200e-03,   2.70360000e-03,
          1.23053500e-03,   1.40792030e-02,   3.37328320e-02]])
In [23]:
names = [u'Inflation', u'U.S. Cash', u'U.S. Intermediate Treasuries',
       u'U.S. Long Treasuries', u'TIPS', u'U.S. Aggregate Bonds',
       u'U.S. Inv Grade Corporate Bonds', u'U.S. High Yield Bonds',
       u'U.S. Leveraged Loans', u'World ex-U.S. Government Bonds',
       u'U.S. Muni High Yield', u'U.S. Large Cap', u'U.S. Mid Cap',
       u'U.S. Small Cap', u'EAFE Equity', u'Emerging Markets Equity',
       u'U.S. REITs', u'Long Bias Hedge Funds', u'Relative Value Hedge Funds',
       u'Commodities', u'Gold']
In [24]:
cov_annual = pd.DataFrame(index = names, columns = names, data = cov )
In [25]:
# empty lists to store returns, volatility and weights of imiginary portfolios
port_returns = []
port_volatility = []
sharpe_ratio = []
stock_weights = []

selected = cov_annual.columns

# set the number of combinations for imaginary portfolios
num_assets = len(returns_annual)
num_portfolios = 25000

#set random seed for reproduction's sake
np.random.seed(101)

# populate the empty lists with each portfolios returns,risk and weights
for single_portfolio in range(num_portfolios):
    weights = np.random.random(num_assets)
    weights /= np.sum(weights)
    returns = np.dot(weights, returns_annual)
    volatility = np.sqrt(np.dot(weights.T, np.dot(cov_annual, weights)))
    sharpe = returns / volatility
    sharpe_ratio.append(sharpe)
    port_returns.append(returns)
    port_volatility.append(volatility)
    stock_weights.append(weights)

# a dictionary for Returns and Risk values of each portfolio
portfolio = {'Returns': port_returns,
             'Volatility': port_volatility,
             'Sharpe Ratio': sharpe_ratio}

# extend original dictionary to accomodate each ticker and weight in the portfolio
for counter,symbol in enumerate(selected):
    portfolio[symbol+' Weight'] = [Weight[counter] for Weight in stock_weights]

# make a nice dataframe of the extended dictionary
df = pd.DataFrame(portfolio)

# get better labels for desired arrangement of columns
column_order = ['Returns', 'Volatility', 'Sharpe Ratio'] + [stock+' Weight' for stock in selected]

# reorder dataframe columns
df = df[column_order]
In [26]:
solvers.options['show_progress'] = False
def optimal_portfolio(returns, cov):
    n = len(returns)
    
    
    N = 100
    mus = [10**(5.0 * t/N - 1.0) for t in range(N)]
    
    # Convert to cvxopt matrices
    S = opt.matrix(cov.values)
    pbar = opt.matrix(returns)
    
    # Create constraint matrices
    G = -opt.matrix(np.eye(n))   # negative n x n identity matrix
    h = opt.matrix(0.0, (n ,1))
    A = opt.matrix(1.0, (1, n))
    b = opt.matrix(1.0)
    
    # Calculate efficient frontier weights using quadratic programming
    portfolios = [solvers.qp(mu*S, -pbar, G, h, A, b)['x'] 
                  for mu in mus]
    ## CALCULATE RISKS AND RETURNS FOR FRONTIER
    returns = [blas.dot(pbar, x) for x in portfolios]
    risks = [np.sqrt(blas.dot(x, S*x)) for x in portfolios]
    return returns, risks

returns, risks = optimal_portfolio(returns_annual, cov_annual)
In [28]:
# plot frontier, max sharpe & min Volatility values with a scatterplot
df.plot.scatter(x='Volatility', y='Returns', c='Sharpe Ratio',
                cmap='RdYlGn', edgecolors='black', figsize=(10, 8), grid=True)
plt.plot(risks, returns, 'y-o')
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Returns')
plt.title('Efficient Frontier')
plt.show()