Un ejemplo de una implementación estándar en Python para la optimización de una cartera de inversiones utilizando el método Markowitz. Hay muchas implementaciones de este método. Incluyendo Python. Implementado nuevamente (ver enlace en GitHub ).
Fuentes
Tomemos un poco de teoría de estas fuentes:
Mejor cartera de inversión a través de la simulación de Monte Carlo en la teoría de cartera de Python
Markowitz (Wikipedia)
Descarga de datos sobre cotizaciones
Usamos datos del servicio Yahoo.Finance
! pip install yfinance
import yfinance as yf
Tomamos varias acciones del mercado estadounidense durante los últimos 3 meses.
data = yf.download(['AAPL','GE','BAC','AMD','PLUG','F'],period='3mo')
Tasas de cierre
Usaremos tasas de cierre diarias en nuestros cálculos.
closeData = data.Close
closeData
Gráficos del curso
import matplotlib.pyplot as plt
for name in closeData.columns:
closeData[name].plot()
plt.grid()
plt.title(name)
plt.show()
Cambio de rumbo
A continuación, necesita cambios relativos al día anterior.
dCloseData = closeData.pct_change()
dCloseData
Gráficos de cambios de tasa relativa
for name in dCloseData.columns:
dCloseData[name].plot()
plt.title(name)
plt.grid()
plt.show()
Rendimiento medio
Rendimiento diario promedio de cada acción para calcular el rendimiento de la cartera.
dohMean = dCloseData.mean()
dohMean
Covarianza
Para calcular el riesgo de la cartera, se requiere una matriz de covarianza.
cov = dCloseData.cov()
cov
Cartera aleatoria
Generaremos carteras aleatorias. En ellos, la suma de las acciones es igual a 1 (uno).
import numpy as np
cnt = len(dCloseData.columns)
def randPortf():
res = np.exp(np.random.randn(cnt))
res = res / res.sum()
return res
r = randPortf()
print(r)
print(r.sum())
[0.07519908 0.07594622 0.20932539 0.40973202 0.1234458 0.10635148]
1.0
Rentabilidad de la cartera
El rendimiento de la cartera se calcula como la suma de las acciones de los rendimientos de cada acción de la cartera.
def dohPortf(r):
return np.matmul(dohMean.values,r)
r = randPortf()
print(r)
d = dohPortf(r)
print(d)
[0.0789135 0.13031559 0.25977124 0.21157419 0.13506695 0.18435853]
0.006588795350151513
Riesgo de cartera
Calculamos el riesgo de la cartera a través de productos matriciales de acciones de cartera y matrices de covarianza.
def riskPortf(r):
return np.sqrt(np.matmul(np.matmul(r,cov.values),r))
r = randPortf()
print(r)
rs = riskPortf(r)
print(rs)
[0.10999361 0.13739338 0.20412889 0.13648828 0.24021123 0.17178461]
0.02483674110724784
Nube de cartera
Generemos un conjunto de carteras y mostremos el resultado en un gráfico de riesgo-rendimiento. Encontremos los parámetros de la cartera óptima para el riesgo mínimo y el ratio máximo de Sharpe. Comparemos con los datos de la cartera promedio.
risk = np.zeros(N)
doh = np.zeros(N)
portf = np.zeros((N,cnt))
for n in range(N):
r = randPortf()
portf[n,:] = r
risk[n] = riskPortf(r)
doh[n] = dohPortf(r)
plt.figure(figsize=(10,8))
plt.scatter(risk*100,doh*100,c='y',marker='.')
plt.xlabel(', %')
plt.ylabel(', %')
plt.title(" ")
min_risk = np.argmin(risk)
plt.scatter([(risk[min_risk])*100],[(doh[min_risk])*100],c='r',marker='*',label=' ')
maxSharpKoef = np.argmax(doh/risk)
plt.scatter([risk[maxSharpKoef]*100],[doh[maxSharpKoef]*100],c='g',marker='o',label=' - ')
r_mean = np.ones(cnt)/cnt
risk_mean = riskPortf(r_mean)
doh_mean = dohPortf(r_mean)
plt.scatter([risk_mean*100],[doh_mean*100],c='b',marker='x',label=' ')
plt.legend()
plt.show()
Visualicemos los datos de las carteras encontradas.
import pandas as pd
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk[min_risk])*100.))
print(" = %1.2f%%" % (float(doh[min_risk])*100.))
print()
print(pd.DataFrame([portf[min_risk]*100],columns=dCloseData.columns,index=[', %']).T)
print()
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk[maxSharpKoef])*100.))
print(" = %1.2f%%" % (float(doh[maxSharpKoef])*100.))
print()
print(pd.DataFrame([portf[maxSharpKoef]*100],columns=dCloseData.columns,index=[', %']).T)
print()
print('---------- ----------')
print()
print(" = %1.2f%%" % (float(risk_mean)*100.))
print(" = %1.2f%%" % (float(doh_mean)*100.))
print()
print(pd.DataFrame([r_mean*100],columns=dCloseData.columns,index=[', %']).T)
print()
---------- ----------
= 1.80%
= 0.59%
, %
AAPL 53.890706
AMD 12.793389
BAC 4.117541
F 16.547201
GE 10.945462
PLUG 1.705701
---------- ----------
= 2.17%
= 0.88%
, %
AAPL 59.257114
AMD 8.317192
BAC 2.049882
F 8.689935
GE 4.772159
PLUG 16.913719
---------- ----------
= 2.33%
= 0.68%
, %
AAPL 16.666667
AMD 16.666667
BAC 16.666667
F 16.666667
GE 16.666667
PLUG 16.666667
conclusiones
Repetimos el método clásico de calcular las acciones de una cartera de inversiones. Obtuvimos resultados muy específicos.
La optimización de la cartera mediante el método Markowitz presupone la conservación de parámetros en el futuro (correlaciones entre instrumentos individuales y su nivel de rentabilidad). Pero esto no está garantizado. Esto se comprobará en los siguientes trabajos.
Está claro que no se debe esperar un resultado positivo de la verificación anterior. Pero luego puedes buscar cómo modificar el método Markowitz para obtener un ingreso más garantizado en el futuro. Aquí hay un tema para otro estudio.