Inspiration:

http://travisvaught.blogspot.de/2011/09/modern-portfolio-theory-python.html
http://www.casact.org/pubs/forum/95fforum/95ff355.pdf
http://www.quantandfinancial.com/2013/07/mean-variance-portfolio-optimization.html

Correlation $$ \rho_{X,Y}=\mathrm{corr}(X,Y)={\mathrm{cov}(X,Y) \over \sigma_X \sigma_Y} ={E[(X-\mu_X)(Y-\mu_Y)] \over \sigma_X\sigma_Y} $$

Covariance $$ r_{xy}=\frac{\sum\limits_{i=1}^n (x_i-\bar{x})(y_i-\bar{y})}{(n-1) s_x s_y} =\frac{\sum\limits_{i=1}^n (x_i-\bar{x})(y_i-\bar{y})} {\sqrt{\sum\limits_{i=1}^n (x_i-\bar{x})^2 \sum\limits_{i=1}^n (y_i-\bar{y})^2}}, $$

Define

$R = \left( \begin{array}{c} R_1\\ \vdots\\ R_N\\ \end{array} \right)$

to be the random vector of returns where

$E(R) = \left( \begin{array}{c} \mu_1\\ \vdots\\\ \mu_N\\ \end{array} \right)$

and $\Sigma$ to the covariance matrix of $R$. Let

$w = \left( \begin{array}{c} w_1\\ \vdots\\ w_N\\ \end{array} \right)$

be a vector of portfolio weights so that $w_1 + \ldots + w_N = 1^T w = 1$. The expected return of the portfolio is $$ \sum_{i=1}^N w_i \mu_i = w^T \mu$$

The variance of the return of the portfolio is

$$ w^T \Sigma w $$

Thus, given a target \mu_P, the efficient portfolio minimizes the variance above subject to:

$$w^T = \mu_P$$

and $$w^T 1 = 1$$

Quadratic programming function to be minimized

$$ \frac{1}{2} x^T D x - d^T x$$

In [12]:
#%%timeit

#Get the data

symbols = ['AAPL', 'GOOG', 'IBM']


from datetime import datetime
import pandas.io.pytables
import pandas.io.data as web
import time

t0 = datetime(2007, 2, 1)
t1 = datetime(2007, 5, 1)

if True:
    stx = web.DataReader(symbols, data_source="yahoo", start=t0, end=t1)
else:
    with pandas.io.pytables.get_store('snp500.h5') as store:
        stx = store['stx']
#stx.Returns = stx.Close / stx.Close.shift(1)
#print stx.Close

In [13]:
stx.Returns = stx.Close / stx.Close.shift(1)

print stx.Returns.quantile(q=0.05)


AAPL    0.978848
GOOG    0.978694
IBM     0.985152
dtype: float64

In [11]:
print stx.Returns.sum(axis=1).quantile(q=0.05)


2.944893798

In [19]:
%%time
stx.Close['Market'] = stx.Close.sum(axis=1)
stx.Returns = stx.Close / stx.Close.shift(1)
print stx.Returns.quantile(q=0.05)


AAPL      0.978848
GOOG      0.978694
IBM       0.985152
Market    0.979970
dtype: float64
CPU times: user 4 ms, sys: 0 ns, total: 4 ms
Wall time: 4 ms

In [16]:
print stx.Returns.quantile(q=0.05)


AAPL      0.978848
GOOG      0.978694
IBM       0.985152
Market    0.979970
dtype: float64

In [17]:
data = stx.Returns[t0:t1][ symbols ]

def calibrate( data ):
    mu = data.mean(axis=0)
    sigma = data.cov()
    return mu, sigm
def return_stats( w, mean, sigma):
    return w.T.dot(mean), w.dot(sigma).dot(w.T)
# def return_var( portfolio, sigma):

mu, s = calibrate(data)

w = np.array( [0.2, 0.38, 0.42])
print return_stats( w, mu, s )


(1.0007244186981565, 0.00010525705289880438)

In [18]:
A = np.concatenate( (np.ones(3), mu))
muP = np.linspace(0.01, 0.2, num=300)

sdP = muP.copy()

#A, muP, sdP

In [ ]:
## http://ivanidris.net/wordpress/index.php/2010/11/21/jarque-bera-the-capm-and-undervalued-stocks
## http://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize

In [15]:
import numpy as np
from scipy.optimize import minimize

def get_func( mean, sigma):
    def func(w, sign=1.0):
        return w.dot(sigma).dot(w.T)
    return func

def compute_optimal_portfolio(target_value, allow_short_selling=True):
    target_value = 1.05
    mean, sigma = calibrate(data)
    
    #cons = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1.0},
    #        {'type': 'eq', 'fun': lambda w: w.T.dot(mean) - target_value}]

    cons = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1.0},
            {'type': 'eq', 'fun': lambda w: w.T.dot(mean) - target_value})
    
    if not allow_short_selling:
        bnds = ((0, None) for x in mean)
        optimal_portfolio = minimize( get_func(mean, sigma), np.array([.2,.3,.5]), method='SLSQP', constraints = cons, bounds=bnds)
    else:
        optimal_portfolio = minimize( get_func(mean, sigma), np.array([.2,.3,.5]), method='SLSQP', constraints = cons)        
    
    return optimal_portfolio

In [ ]:
#  http://stackoverflow.com/questions/18218355/scipy-optimization-with-grouped-bounds
#  http://www.quantandfinancial.com/2013/07/mean-variance-portfolio-optimization.html

In [1]:
f = lambda x: 2 * x
f(4)


Out[1]:
8

In [16]:
for ret in returns:
    w = compute_optimal_portfolio(1.0, allow_short_selling=False)
    if w.success: print w


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-16-1b738ac96451> in <module>()
----> 1 for ret in returns:
      2     w = compute_optimal_portfolio(1.0, allow_short_selling=False)
      3     if w.success: print w

NameError: name 'returns' is not defined

In [51]:
w


Out[51]:
  status: 9
 success: False
    njev: 101
    nfev: 1142
     fun: 0.00026184585486113996
       x: array([  1.00024863e+00,  -1.29699470e-05,   5.89933152e-09])
 message: 'Iteration limit exceeded'
     jac: array([ 0.00052356,  0.00024216,  0.00015369,  0.        ])
     nit: 101

In [29]:
risk = np.linspace(0., 0.5, num=50)
returns = np.linspace( 0., 2.0, num=100)

np.meshgrid(returns, risk)
np.meshgrid??

Backtesting

From t_1 on.


In [61]:



Out[61]:
1.0000000000000011

In [6]:
from ggplot import *
tau = 250
theta = tau
t = np.linspace(1,tau, tau, endpoint=True)
t

w = np.exp( (t - tau) / theta)
plot(w)


Out[6]:
[<matplotlib.lines.Line2D at 0x10ec92c90>]
/usr/local/lib/python2.7/site-packages/matplotlib/font_manager.py:1236: UserWarning: findfont: Font family ['serif'] not found. Falling back to Bitstream Vera Sans
  (prop.get_family(), self.defaultFamily[fontext]))

In [14]:
import pandas

pandas.ewmcorr


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-14-0d9ef7874acd> in <module>()
      1 import pandas
----> 2 pandas.ewmcorr(stx.Returns)

TypeError: ewmcorr() takes at least 2 arguments (1 given)