5 votos

Calcular la cartera de tangencia con restricciones de asignación de activos

Estoy buscando calcular la cartera de tangencia de la frontera eficiente, pero teniendo en cuenta min_allocations y max_allocations para los pesos de activos en la cartera. Estas restricciones me hacen pensar que necesito usar una herramienta de optimización como cvxopt. La cartera de tangencia es la cartera que maximiza el ratio de Sharpe y creo que calcular la cartera de tangencia requiere los inputs compute_tanp(exp_ret_vec, cov_mat, min_allocations, max_allocations, rf).

Estas notas de clase pueden transformar el problema de optimización anterior al formato cuadrático estándar que se muestra a continuación, pero no estoy seguro de cómo formar correctamente las matrices para este enfoque.

¿Cómo formo las matrices para usar correctamente cvoxpt y encontrar la cartera con el máximo ratio de Sharpe? También estoy abierto a otras técnicas para calcular la cartera de tangencia con restricciones.

A continuación tengo una función de trabajo que encontrará los pesos de la cartera eficiente $W$ cuando se pase un retorno objetivo deseado. Utiliza cvxopt para manejar la optimización de la forma:

import pandas as pd
import numpy as np
import cvxopt as opt

def compute_ep(target_ret, exp_ret_vec, cov_mat, min_allocations, max_allocations):
    """
    calcula la cartera eficiente con mínima varianza para un retorno objetivo dado

    """
    # número de activos
    n = len(exp_ret_vec)

    one_vec = np.ones(n)

    # objetivo
    # minimizar (0.5)x^TPx _ q^Tx
    P = opt.matrix(cov_mat.values) # matriz de covarianza
    q = opt.matrix(np.zeros(n)) # cero

    # restricciones Gx <= h
    # >= retorno objetivo, >= mínimas asignaciones, <= máximas asignaciones
    G = opt.matrix(np.vstack((-exp_ret_vec,-np.identity(n), np.identity(n))))  

    h = opt.matrix(np.hstack((-target_ret,-min_allocations, max_allocations)))

    # restricciones Ax = b
    A = opt.matrix(np.ones(n)).T
    b = opt.matrix(1.0) # sum(w) = 1; no es neutral en el mercado

    # optimización convexa
    opt.solvers.options['show_progress'] = False
    sol = opt.solvers.qp(P, q, G, h, A, b)

    weights = pd.Series(sol['x'], index = cov_mat.index)

    w = pd.DataFrame(weights, columns=['peso'])

    return(w)

4voto

aspitzer Puntos 860

Si estás dispuesto a cambiar a CVXPY, viene con un bonito ejemplo de exactamente este ejercicio:

http://nbviewer.jupyter.org/github/cvxgrp/cvx_short_course/blob/master/applications/portfolio_optimization.ipynb

3voto

David Rickman Puntos 2787

Hay dos transformaciones de los datos de entrada que se deben hacer para pasar del primer problema al segundo:

  • los $\hat{\mu}$ se encuentran restando el escalar $r_f$ de todos los componentes del vector $\mu$: $$\hat{\mu}=\mu-r_f=(\mu_1-r_f,\mu_2-r_f,\cdots,\mu_N-r_f)^T$$

en otras palabras, los $\mu$ son rendimientos y los $\hat\mu$ son "rendimientos excedentes".

  • la matriz $\hat{A}$ se encuentra restando el vector columna $b$ de cada columna de la matriz $A$, es decir, $\hat{a}_{ij}=a_{ij}-b_i$

  • la matriz $Q$ (matriz de covarianza) no cambia en el problema 2 en comparación con el problema 1

Una vez resuelvas el problema 2, tendrás el óptimo $y$. Puedes encontrar el óptimo $x$ para el Problema 1 haciendo $x=\frac{y}{1^T y}$. Esto hace que los componentes de x sumen 1 (como se desea) aunque los componentes de y no lo hagan.

Espero que esto ayude

(No conozco lo suficiente R y cvxopt como para escribir el código, pero debería ser sencillo).

0 votos

¿A qué se refiere el vector columna $b$ en este caso?

3 votos

La columna b está en el lado derecho de la restricción de desigualdad $A x >= b$ en el Problema 1. Estos son, por ejemplo, límites de exposición a ciertas acciones o sectores de acciones, etc.

3 votos

Por ejemplo, si el peso de la primera acción debe estar entre 0.05 y 0.2, eso sería dos restricciones $x_1 >= 0.05$ y $-x_1 >= -0.2$ que se expresarían en la matriz A y el vector b.

1voto

Tofystedeth Puntos 255

Lo más sencillo es obtener el rango de retorno admisible utilizando el optimizador cvxopt con $q = \alpha \mu$ y $q = -\alpha \mu$ para un gran $\alpha$ en lugar de $q=0$ y luego ejecutar la función compute_ep de forma iterativa para encontrar la cartera con el mayor índice de Sharpe en este rango.

Utilizando un código similar al mostrado anteriormente, se pueden obtener los límites y el índice de Sharpe óptimo de la siguiente manera:

def compute_ep_bound(alpha, exp_ret_vec, cov_mat, min_allocations, max_allocations):
    n = len(exp_ret_vec)
    one_vec = np.ones(n)
    P = opt.matrix(cov_mat.values) # matriz de covarianza
    q = opt.matrix(exp_ret_vec * alpha) # cero
    G = opt.matrix(np.vstack((-np.identity(n), np.identity(n))))  
    h = opt.matrix(np.hstack((-min_allocations, max_allocations)))
    A = opt.matrix(np.ones(n)).T
    b = opt.matrix(1.0) # sum(w) = 1; no market-neutro
    opt.solvers.options['show_progress'] = False
    sol = opt.solvers.qp(P, q, G, h, A, b)
    weights = pd.Series(sol['x'], index = cov_mat.index)
    w = pd.DataFrame(weights, columns=['weight'])
    return np.dot(mu,sol['x'])

def compute_tanp(exp_ret_vec, cov_mat, min_allocations, max_allocations, rf):
    alpha = 10000
    e1 = compute_ep_bound(alpha, exp_ret_vec, cov_mat, min_allocations, max_allocations)
    e2 = compute_ep_bound(-alpha, exp_ret_vec, cov_mat, min_allocations, max_allocations)
    sharpemax = -1e300
    wmax = 1
    for target_ret in np.linspace(e2,e1):
        w = compute_ep(target_ret, exp_ret_vec, cov_mat, min_allocations, max_allocations)
        sharpe = (np.dot(mu,w)-rf)/np.dot(w.T,np.dot(cov_mat,w))
        if sharpe > sharpemax:
            print("sharpe=%f" % sharpe)
            wmax = w
            sharpemax = sharpe
    return wmax

La alternativa presentada en el paper es computar directamente la cartera con el máximo índice de Sharpe usando el problema equivalente $\hat{\mu}^t y = 1$ y $\hat{A} y > 0$:

def compute_tanp_direct(exp_ret_vec, cov_mat, min_allocations, max_allocations, rf):
    # número de activos
    n = len(exp_ret_vec)
    mu_hat = exp_ret_vec - rf

    # objetivo minimizar (0.5)x^TPx _ q^Tx
    P = opt.matrix(cov_mat.values) # matriz de covarianza
    q = opt.matrix(np.zeros(n)) # cero

    # restricciones Gx <= h: -hatA y > 0 matrix 
    G = opt.matrix(np.vstack((-np.identity(n)+min_allocations, np.identity(n)-max_allocations)))  
    h = opt.matrix(np.zeros(2*n))

    # restricciones Ax = b: hatmu . y = 1
    A = opt.matrix(mu_hat).T
    b = opt.matrix(1.0)

    # optimización convexa
    opt.solvers.options['show_progress'] = False
    sol = opt.solvers.qp(P, q, G, h, A, b)

    weights = pd.Series(sol['x'], index = cov_mat.index)
    w = pd.DataFrame(weights, columns=['weight'])
    w = w/np.dot(np.ones(n), w)
    sharpe = (np.dot(exp_ret_vec,w)-rf)/np.sqrt(np.dot(w.T,np.dot(cov_mat,w)))
    print("sharpe=%f" % sharpe)
    return(w)

Verifiqué en un ejemplo práctico con 6 activos que los métodos iterativos convergen hacia el índice de Sharpe óptimo calculado directamente.

Finanhelp.com

FinanHelp es una comunidad para personas con conocimientos de economía y finanzas, o quiere aprender. Puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X