Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como resolver numericamente em python o problema da Carteira de Markovitz?

0 votos
174 visitas
perguntada Dez 6, 2016 em Finanças por Caue (226 pontos)  

Resolva numericamente o problema \[min \frac{1}{2}ω^\primeΣω\]
sujeito a \[R_c \geq R\]

e use os resultados para traçar a fronteira média variância. Considere os casos onde vendas a descoberto são e não são permitidas (nesse caso precisará de uma restrição adicional).
Note que esse é um problema de Programação Quadrática.

[Carteira de Markovitz]

Compartilhe

1 Resposta

0 votos
respondida Dez 8, 2016 por Caue (226 pontos)  
editado Dez 8, 2016 por Caue

Solução Teórica

Após selecionar os ativos que poderão compor a carteira, devemos obter seus retornos médios e a matriz de covariância desses retornos. Tanto o vetor de retorno quanto a matriz de covariância devem ser anualizados, considerando que o ano possui 252 dias de negociação (dias úteis).

Para calcular a variância e o retorno esperado da carteira, utilizamos as seguintes equações:
\[ \sigma^2_{c} = \omega^\prime\Sigma\omega \\ R_{c} = \omega^\prime\mu \]

Onde temos:
\[\omega\text{: Vetor de proporção de cada ativo na carteira, com }\sum_{i=1}^{N}\omega_i=1 \\ \Sigma\text{: Matriz de covariância dos ativos anualizada} \\ \mu\text{: Vetor do retorno médio anualizado dos ativos} \]

Para solucionar o problema, devemos fixar um valor mínimo para o retorno da carteira. Com esse valor fixado, devemos minimizar a variância da carteira sujeito à restrição de que o retorno não seja inferior ao mínimo fixado.

\[min\space\sigma^2_{c} \\ s.t.\space R_{c} \geq R \]

A solução do problema acima, para cada \(R\) escolhido, nos dará um vetor \(\omega^*\) pertencente a fronteira. Assim, com as equações apresentadas acima, conseguimos calcular o retorno e a variância da carteira formada por \(\omega^*\).
O lugar geométrico formado pelos pares (\(\sigma_c,R_c\)), calculado para \(\omega^*\), para diversos valores escolhidos de \(R\), forma a fronteira média variância.

Implementação em Python

A simulação foi implementada em Python 3.5.1 e disponibilizada em um arquivo do tipo Jupyter Notebook para melhor visualização.

\(\checkmark\)Foram selecionados 5 ativos negociados na bolsa brasileira.

\(\checkmark\)Foi feita a captura dos preços desses ativos do período de 04/Jan/16 a 07/Dez/16.

first_price_date = dt.datetime(2016, 1, 1)
last_price_date = dt.datetime(2016, 12, 7)
symbols = ['VALE5', 'PETR4', 'BVMF3', 'ITUB4', 'BBAS3']

data = pd.DataFrame()
for sym in symbols:
    data[sym] = web.DataReader(sym, 'google', first_price_date, last_price_date)['Close']
data = data.dropna()

\(\checkmark\)Com os preços, foi feito o cálculo dos retornos diários e, em seguida, o retorno médio \(\mu\) e a matrix de covariância \(\Sigma\), ambos anualizados.

daily_returns = np.log(data / data.shift(1))
_252_DAYS = 252
covariance_matrix = np.asarray(_252_DAYS * daily_returns.cov())
annualized_returns = np.asarray(_252_DAYS * daily_returns.mean())

\(\checkmark\)Foram definidas as equações para calcular a variância e o retorno da carteira, dado \(\omega\).

def portfolio_variance(w):
    _w = np.asarray(w)
    return _w.dot(covariance_matrix).dot(_w)

def portfolio_return(w):
    _w = np.asarray(w)
    return _w.dot(annualized_returns)

\(\checkmark\)Foi definido um método que, dado um retorno mínimo, minimiza a variância da carteira e retorna o vetor com a proporção de cada ativo \(\omega^*\).

def minimize_portfolio_variance(min_return, short_sale_allowed=True):
    assets_number = len(symbols)

    bnds = None if short_sale_allowed \
        else [(0, None) for i in range(assets_number)] # If short sale not allowed, lower bound = 0

    initial_guess = [1 / assets_number for i in range(assets_number)]
    cons = ({'type': 'eq', 'fun': lambda w: w.sum() - 1},
            {'type': 'ineq', 'fun': lambda w: portfolio_return(w) - min_return})
    return minimize(portfolio_variance, initial_guess,
                    constraints=cons,
                    bounds=bnds,
                    options={'disp': False},
                    method='SLSQP',
                    jac=portfolio_variance_gradient)

\(\checkmark\)Foi definido um método que calcula a fronteira eficiente, executando o método anterior para diversos valores de retorno mínimo. Assim, para cada iteração, temos um ponto da fronteira desejada.

def calculate_mv_frontier(short_sale_allowed=True):
    frontier = pd.Series()
    weights = pd.DataFrame()
    for min_return in np.linspace(0, 1.2, num=2000):
        result = minimize_portfolio_variance(min_return, short_sale_allowed=short_sale_allowed)
        if not result.success:
            continue
        w_frontier = result.x
        port_sigma = np.sqrt(portfolio_variance(w_frontier))
        port_return = portfolio_return(w_frontier)
        frontier.set_value(port_sigma, port_return)

        for i, symbol in enumerate(symbols):
            weights.set_value(port_sigma, symbol, w_frontier[i])

    return frontier, weights

\(\checkmark\)Foi feito o cálculo das fronteiras eficientes: Uma calculada sem vendas a descoberto e a outra com vendas a descoberto.

frontier_ss_false, weights_ss_false = calculate_mv_frontier(short_sale_allowed=False)
frontier_ss_true, weights_ss_true = calculate_mv_frontier(short_sale_allowed=True)

\(\checkmark\)As fronteiras foram apresentadas, bem como as proporções dos ativos pra cada nível de risco. As figuras geradas na simulação são apresentadas a seguir:

A imagem será apresentada aqui.

A imagem será apresentada aqui.

A imagem será apresentada aqui.

É interessante notar que a fronteira que permite vendas a descoberto é maior, por possibilitar mais combinações de ativos. Porém, como esperado, na região em que a carteira ótima possui apenas proporções positivas, as duas fronteiras se confundem.

Outra característica importante que deve ser notada é que o retorno máximo da carteira quando não é permitido venda a descoberto é o maior retorno dentre os ativos, alcançado quando a carteira está totalmente alocada neste ativo (proporção de 100%).
Quando a venda a descoberto é permitida, alcançamos o mesmo retorno com um risco inferior e com outros ativos na carteira. É claro que pelo menos um ativo deve estar vendido.


Arquivo com a Implementação: Link

comentou Dez 9, 2016 por André Maranhão (11 pontos)  
Excelente simulação computacional! Traduz com riqueza situações onde pela dualidade das opções de otimização permite minimizar a medida de risco com uma restrição de retorno imposta ou ínves de maximizar o retorno com uma restrição sobre a medida de risco. Os resultados permitem  diferentes e interessantes situações hipóteticas. Particularmente, teria dado um pouco mais de enfase nos resultados teoricos, em especial mostrando como esse problema pode se tornar (especialmente para fins computacionais) um problema de programação de segunda ordem de cone convexo utilizando uma decomposição de Cholesky na matriz de variância-covariância, nesse caso, a medida de risco, conforme podemos encontrar nos capítulos 7 e 8 do livro "Optimization Methods in Finance" - Gerard Cornuejols and Reha Tutuncu. No mundo mais aplicado, a minimização da medida de risco, em geral não é implementado, pois a complexidade computacional aumenta rapidamente, além de outras questões de natureza inferêncial. Novamente excelente análise dos resultados.
comentou Dez 9, 2016 por Caue (226 pontos)  
Obrigado pelo comentário, André.
O foco desse problema realmente é a solução numérica / computacional, por isso houve menor ênfase em solução teórica.
Quanto à utilização no mundo, no Banco Central utilizamos esse tipo de otimização de carteira como um dos critérios de decisão em relação à alocação de nossas reservas internacionais em classes de ativos. É claro que o conjunto de restrições é maior, mas sempre fazemos a simulação para encontrar a carteira permitida que minimiza o risco. A complexidade computacional não costuma atrapalhar.
...