Trading, Money Management, Portfolio

Construyendo un portafolio (Parte 2)

@brberis
March 5, 2015

finanzas portafolio stocks acciones finance

Continuando con la construcción de portafolio en este articulo repaso la teoria de Markowitz.

Optimización de portafolios y frontera eficiente

Como había mencionado en un post anterior el coeficiente de correlación toma gran importancia al momento de analizar el riesgo del portafolio. Markowitz (Portfolio Theory 1952) desde esta perspectiva desarrolla la teoría moderna de portafolio que permite ubicar el punto óptimo entre diversificación y riesgo en base a la covarianza de los activos. Y para calcular esta frontera eficiente debemos estimar los valores mediante una matriz compuesta por los retornos de los papeles de la cartera.

Estimando la vairanza y covarianza del portafolio

Para facilitar los calculos utilizaremos un programa desarrollado en Python que permitirá también iterar sobre las variables y así optimizar nuestro portafolio. Finalmente dibujaremos la curva de la frontera eficiente.

Primero la explicación matemática:

Calculemos un papel individual,

$$ \overline R = \frac{\sum_{i=1}^n R}n $$

$$ Donde\: \overline R\: es\: la\: media\: de\: la\: suma\: de\: los\: n\: \\retornos\: para\: el\: periodo\: i. $$


La varianza y desviación estándar la calculamos a continuación:

$$ \sigma^2 = \frac{\sum_{i=1}^n (R-\overline R)^2}{n-1} $$

$$ \sigma = \sqrt{\sigma^2} $$

Ahora para n papeles en un portafolio y donde w es la ponderación (weight) de cada activo en el portafolio:

$$ R_p = \sum_{i=1}^n w_iR_i $$

Para una matriz de retorno n por m, donde n = periodos y m = activos.

$$ R= \begin{Bmatrix} R_{1,1} & R_{1,1} & \cdots & R_{1,m} \\ R_{1,2} & R_{2,2} & \cdots & R_{2,m} \\ \vdots & \vdots & \ddots & \vdots \\ R_{n,1} & R_{n,2} & \cdots & R_{n,m} \\ \end{Bmatrix} \begin{Bmatrix} R_{1,1} & R_{1,1} & \cdots & R_{1,m} \\ R_{1,2} & R_{2,2} & \cdots & R_{2,m} \\ \vdots & \vdots & \ddots & \vdots \\ R_{n,1} & R_{n,2} & \cdots & R_{n,m} \\ \end{Bmatrix} \\ \\ w=\big(w_1,w_2,w_3,...\: .w_m\big) $$

El retorno esperado del portafolio sería:

$$ E\big(R_p\big)=w\cdotp E\big(R\big) $$

Y la varianza del portafolio corresponde a:

$$ \sum= \begin{Bmatrix} \sigma_{1,1} & \sigma_{1,1} & \cdots & \sigma_{1,m} \\ \sigma_{1,2} & \sigma_{2,2} & \cdots & \sigma_{2,m} \\ \vdots & \vdots & \ddots & \vdots \\ \sigma_{n,1} & \sigma_{n,2} & \cdots & \sigma_{n,m} \\ \end{Bmatrix} \begin{Bmatrix} \sigma_{1,1} & \sigma_{1,1} & \cdots & \sigma_{1,m} \\ \sigma_{1,2} & \sigma_{2,2} & \cdots & \sigma_{2,m} \\ \vdots & \vdots & \ddots & \vdots \\ \sigma_{n,1} & \sigma_{n,2} & \cdots & \sigma_{n,m} \\ \end{Bmatrix} $$

$$ \sigma^2_{portafolio} = w \cdotp \sum \cdotp w^t \\ Donde\: w^t\: es\: la\: matriz\: traspuesta $$

 

Con el siguiente código y a modo de ejemplo calculamos la varianza de un portafolio de dos activos. 

	
	import numpy as np
retornos = matrix(np.array([[-0.22,0.12],
                       [-0.02,-0.32],
                       [-0.12,0.01],
                       [0.102,0.15],
                       [0.08,-0.08],
                       [0.21,0.12]]))
covar = retornos.T*retornos
pondr=matrix(np.array([0.54,0.46]))
varianza = pondr*covar*pondr.T
print(varianza)

[[ 0.07649577]]

El resultado muestra la dispercion de los retornos de los activos con respecto a su media. Mientras nos acercamos a cero, menor disperción.


Ratio de Sharpe

Para optimizar nuestro portafolio ponderando los activos que lo componen utilizaremos el ratio de Sharpe y de esta manera calcular el exceso de retorno con respecto a la volatilidad añadida.

Su calculo es el siguiente:

$$ Sharpe = \frac{E \big( R \big) - R_f}{\sigma_p} $$

R es el retorno de la inversion y Rf es el retorno del benchmark libre de riesgo (Ejemplo: Tasa Libor). Por lo tanto la parte superior de la equación es el rendimiento esperado y que se divide por su desviacion estandar.

El siguiente código buscará el ratio más alto iterando sobre los activos y su ponderación. 

Este ejemplo contempla el retorno de cuatro acciones; AAPL, X, WFM y CAT.

	
	from matplotlib.finance import quotes_historical_yahoo
import numpy as np
import pandas as pd
import scipy as sp
from scipy.optimize import fmin

activos = ('AAPL','X','WFM','CAT')
inicio=(2003,1,1)
fin=(2013,12,31)
rf= 0.003 #libre de riesgo
n_activos=len(activos)

def retorno_anual(activos,inicio,fin):     
    x=quotes_historical_yahoo(activos,inicio,fin,asobject=True,adjusted=True)     
    logret = log(x.aclose[1:]/x.aclose[:-1])     
    date=[]     
    d0=x.date     
    for i in range(0,size(logret)):         
        date.append(d0[i].strftime("%Y"))     
    y=pd.DataFrame(logret,date,columns=[activos])  
    return exp(y.groupby(y.index).sum())-1 

def varianza_portafolio(R,w):     #aka volatilidad
    
    cor = sp.corrcoef(R.T)     
    std_dev=sp.std(R,axis=0)     
    var = 0.0
    for x in range(n_activos):         
        for i in range(n_activos):           
            var += w[x]*w[i]*std_dev[x]*std_dev[i]*cor[x, i]     
    return var 


def ratio_r(R,w):     #ratio de Sharpe
    var = varianza_portafolio(R,w)     
    retorno_prom=mean(R,axis=0)     
    ret = sp.array(retorno_prom)     
    return (sp.dot(w,ret) - rf)/sqrt(var) 

def sharpe_neg_n_1(w):     
    w2=sp.append(w,1-sum(w))     
    return -ratio_r(R,w2) 

n=len(activos)             
x2=retorno_anual(activos[0],inicio,fin) #Calculo de retorno del primer activo
for i in range(1,n):     #append los demas activos
    x_=retorno_anual(activos[i],inicio,fin)     
    x2=pd.merge(x2,x_,left_index=True,right_index=True)
R = sp.array(x2) #R retorno todos los activos


print('Ratio de Sharpe del portafolio con ponderación equitativa') 
print(activos) 
pond_equi=sp.ones(n, dtype=float) * 1.0 /n 
print(pond_equi) 
print('Ratio: '+str(ratio_r(R,pond_equi))) 

Ratio de Sharpe del portafolio con ponderación equitativa
('AAPL', 'X', 'WFM', 'CAT')
[ 0.25  0.25  0.25  0.25]
Ratio: 0.835354108571

Optimización

	
	w0= sp.ones(n-1, dtype=float) * 1.0 /n 
w1 = fmin(sharpe_neg_n_1,w0) 
ponder = sp.append(w1, 1 - sum(w1)) 
sharpe_f = ratio_r(R,ponder) 
print ('Ponderación Óptima') 
print(activos) 
print (ponder) 
print ('Ratio de Sharpe ') 
print(sharpe_f)

Optimization terminated successfully.
         Current function value: -1.027654
         Iterations: 71
         Function evaluations: 129
Ponderación Óptima
('AAPL', 'X', 'WFM', 'CAT')
[ 0.49218205 -0.28685972 -0.17782237  0.97250003]
Ratio de Sharpe 
1.02765418775

 

Como resultado el ratio de Sharpe optimizado ponderó negativamente (corto) algunos activos. En el grafico se muestra el retorno del portafolio, aunque tiene posiciones cortas su resutado fue menos volatil y con mayor retorno.

Calculo de Frontera Eficiente

Utilizando Python con NumPy y Pandas.

	
	from numpy.linalg import inv, pinv 
inicio,fin = 2003,2013 
activos=['AAPL','X','WFM','CAT'] 

def ret_mensual(activos):     
    x = quotes_historical_yahoo(activos,(inicio,1,1),(fin,12,31),asobject=True,adjusted=True)     
    logret=log(x.aclose[1:]/x.aclose[:-1])     
    date=[]     
    d0=x.date     
    for i in range(0,size(logret)):         
        date.append(''.join([d0[i].strftime("%Y"),d0[i].strftime("%m")]))     
    y=pd.DataFrame(logret,date,columns=[activos])     
    return y.groupby(y.index).sum() 
#  function 2: objective function     

def calc_fr(W, R, target_ret):     
    madia_activo=np.mean(R,axis=0)       
    media_port=np.dot(W,madia_activo)            
    cov=np.cov(R.T)                        
    var_port=np.dot(np.dot(W,cov),W.T)    
    riesgo_p = 2000*abs(media_port-target_ret)
    return np.sqrt(var_port) + riesgo_p     
R0=ret_mensual(activos[0])     
n_activos=len(activos)
for i in range(1, n_activos):      
    x=ret_mensual(activos[i])   
    R0=pd.merge(R0,x,left_index=True,right_index=True) 
R=np.array(R0) 
media_op=[]
std_op=[]
pond_op=[] 
media_activo=np.mean(R,axis=0)     
#optimización de cartera
for r in np.linspace(np.min(media_activo), np.max(media_activo), num=100):     
    W = ones([n_activos])/n_activos         
    b_ = [(0,1) for i in range(n_activos)]      
    c_ = ({'type':'eq', 'fun': lambda W: sum(W)-1. })    
    result=optimize.minimize(calc_fr,W,(R,r),method='SLSQP',constraints=c_, bounds=b_)         
    if not result.success:                          
        raise BaseException(result.message)     
    media_op.append(round(r,4))               
    std_=round(np.std(np.sum(R*result.x,axis=1)),6)     
    std_op.append(std_)   
    pond_op.append(result.x) 
rcParams['figure.figsize'] = 10, 6
title('Frontera Eficiente '+str(activos)+' '+str(inicio)+' - '+str(fin)) 
xlabel('Risk') 
ylabel('Retorno del Portafolio') 
plot(std_op,(media_op)
     ,'-',lw=3, color='purple' )


 

Desventajas de la teoría de Markowitz

  • El cálculo y optimización de la cartera se fundamenta en información estadísticas de los precios en el pasado y el resultado tiene implícito el hecho de que los comportamientos de los activos serán similares en el futuro.

  • La teoría no considera factores fundamentales como; resultados de las empresas, management de los directivos, entorno macroeconómico etc.

  • El estudio asume que las distribución de precios es estándar y no considera formaciones como fat tail. Estos eventos menores menores son parte integrante de los mercados.

  • La ponderación de los activos sobre el portafolio no necesariamente es una información sencilla de incorporar en nuestro sistema. Esto se debe a que la utilización de estrategias de opciones sobre estos subyacentes tiene ponderación dinámica (Delta) e incorpora otros factores de ingreso como Theta.

blog comments powered by Disqus