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

Dê um exemplo de solução analítica e numérica do problema da maximização do Índice de Sharpe e da geração da fronteira eficiente de Markowitz com e sem ativo livre de risco

+1 voto
125 visitas
perguntada Dez 7, 2016 em Finanças por Marcelo Cruz (16 pontos)  
editado Dez 8, 2016 por Marcelo Cruz
Compartilhe

1 Resposta

+1 voto
respondida Dez 7, 2016 por Marcelo Cruz (16 pontos)  
editado Dez 9, 2016 por Marcelo Cruz

Apresento aqui a solução analítica e numérica dos exercícios 9, 10 e 11 da aula 18 do curso de Finanças da pós-graduação da UnB/FACE.

[Ex. 9] O Índice de Sharpe representa a razão entre o retorno extra de uma carteira (além do retorno do ativo liver de risco) e o risco dessa carteira. O problema aqui consiste em maximizar esse índice. Queremos escolher o vetor de pesos da carteira, w, de forma a maximizar a função

\[\Theta=\frac{R_{c}-R_{f}}{\sigma_{c}}=\frac{\langle w,r\rangle-R_{f}}{\sqrt{w^{T}\Sigma w}}\]

com a restrição

\[\sum w_{i}=1\]

em que Rc é o retorno da carteira, Rf é o retorno do ativo livre de risco e sigma_c o desvio padrão. Seja

\[\mathscr{\mathcal{L}}(w)=\frac{\langle w,r\rangle-R_{f}}{\sqrt{w^{T}\Sigma w}}-\lambda\big(\sum_{i}w_{i}-1\big)\]

Precisamos calcular

\[\mathscr{\mathcal{L}}_{i}(w)=\frac{\partial\mathscr{\mathcal{L}}}{\partial w_i}(w)\]

Vejamos inicialmente a derivada da forma quadrática \(w^{T}\Sigma w\). Seja \[\big[s_{1}...s_{n}\big]=\left[\begin{array}{c} s_{1}^{T}\\ ...\\ s_{n}^{T} \end{array}\right]=\Sigma\]

já que a matriz de covariâncias é simétrica. Temos

\[w^{T}\Sigma w = \sum_{j}w_{j}\langle s_{j},w\rangle\]

\[\frac{d}{dw_{i}}(w^{T}\Sigma w) = \sum_{j}\big(w_{j}s_{j}^{i}+\frac{dw_{j}}{dw_{i}}\langle s_{j},w\rangle\big)\]

\[= \big(\sum_{j}w_{j}s_{j}^{i}\big)+\langle s_{i},w\rangle\]

\[ = 2\langle s_{i},w\rangle\]

Usando as regras da cadeia e do quociente, temos

\[\frac{\partial\mathscr{\mathcal{L}}}{\partial wi}(w)=\frac{\frac{\partial(\langle w,r\rangle-R_{f})}{\partial w_{i}}.\sqrt{w^{T}\Sigma w}-(R_{c}-R_{f}).\frac{1}{2}.(w^{T}\Sigma w)^{-1/2}.2.\langle s_{i},w\rangle}{\big(\sqrt{w^{T}\Sigma w}\big)^{2}}-\lambda\]
\[\frac{\partial\mathscr{\mathcal{L}}}{\partial wi}(w)=\frac{r_{i}.\sqrt{w^{T}\Sigma w}-\frac{(R_{c}-R_{f}).\langle s_{i},w\rangle}{\sqrt{w^{T}\Sigma w}}}{w^{T}\Sigma w}-\lambda\]

Para o caso em que \(\frac{\partial\mathscr{\mathcal{L}}}{\partial w_i}(w)=0\), podemos multiplicar ambos os lados da equação por \(w^{T}\Sigma w\), e assim teremos a condição de primeira ordem

\[r_{i}\sqrt{w^{T}\Sigma w}-\frac{\big(R_{c}-R_{f}\big).\langle s_{i},w\rangle}{\sqrt{w^{T}\Sigma w}}=\bar{\lambda}\]

que pode ser re-escrita como

\[CPO_{i}:\;r_{i}.\sigma_{c}-\Theta . \langle s_{i},w\rangle=\bar{\lambda}\]

onde \(\bar{\lambda}=\lambda.w^{T}\Sigma w\). Isso quer dizer que para quaisquer i, j

\[r_{i}.\sigma_{c}-\Theta . \langle s_{i},w\rangle=r_{j}.\sigma_{c}-\Theta . \langle s_{j},w\rangle\]

[Ex. 10] Se impusermos a restrição adicional \(w_{i}\geq0\;(i)\), teremos agora

\[\mathscr{\mathcal{L}}(w)=\frac{\langle w,r\rangle-R_{f}}{\sqrt{w^{T}\Sigma w}}-\lambda.\big(\sum_{i}w_{i}-1\big)-\sum_{i}\lambda_{i}w_{i}\]

A CPOi passará a ser

\[CPO_{i}^{*}:\;r_{i}.\sigma_{c}-\Theta . \langle s_{i},w\rangle=\bar{\lambda}+\bar{\lambda_{i}}\]

Se a restrição i for ativa, isto é,

\[w_{i}=0 \; e \; \bar{\lambda_{i}}\neq0\]

não há informação relevante a respeito de i. Por outro lado, se a restrição não for ativa, teremos

\[w_{i}\neq0 \; e \; \bar{\lambda_{i}}=0\]

e valerá a CPOi conforme o caso acima. A conclusão nesse caso é de que para quaisquer i, j

\[\begin{cases} w_{i}=0;\;ou\\ w_{j}=0;\;ou\\ r_{i}.\sigma_{c}-\Theta . \langle s_{i},w\rangle=r_{j}.\sigma_{c}-\Theta . \langle s_{j},w\rangle \end{cases}\]

Seguimos com a parte numérica. Com três séries históricas de dados reais - bolsa americana, bolsa japonesa e títulos públicos americanos - veremos a nuvem de pontos gerados pela combinação convexa das carteiras, a carteira ótima pelo critério da maximização do Índice de Sharpe, a fronteira eficiente com restrição de venda a descoberto, e as carteiras ótimas no caso em que não existe um ativo livre de risco. Nesse último caso, como veremos, a maximização do Índice de Sharpe resulta em uma fronteira restrita ao lado esquerdo da nuvem de carteiras, desde a carteira de mínima variância global até a carteira ótima encontrada no caso em que há ativo livre de risco. Para completar, acrescentamos ainda a fronteira gerada pela maximização do retorno esperado (um critério alternativo ao Índice de Sharpe bastante razoável em aplicações reais).

Registro a seguir apenas a parte relevante do código Python de geração das carteiras e fronteiras eficientes.

São importadas as seguintes séries históricas reais. Essas séries podem ser obtidas em provedores de informação como Yahoo Finance, Bloomberg, Thomson Reuters etc.

  • S&P500 - principal índice de acoes americano
  • NIKKEI - principal índice de acoes japonês, em moeda local (iene)
  • USTreasury - título genérico que simula o investimento em uma cesta de títulos do tesouro americano de vários vencimentos, desde 1 a mais de 20 anos
# DIR = ..., ARQ = ..., PLAN = ...
xl = pd.ExcelFile(DIR + ARQ)
df = xl.parse(PLAN, index_col='Date')[['SP500', 'NIKKEI',
    'USTreasury']]

df.head()
"""
              SP500    NIKKEI  USTreasury
Date                                     
2001-01-05  1298.35  13867.61     1100.13
2001-01-06  1298.35  13867.61     1100.13
2001-01-07  1298.35  13867.61     1100.13
2001-01-08  1295.86  13867.61     1101.90
2001-01-09  1300.80  13610.51     1098.86
"""

Estamos interessados nos retornos trimestrais, em percentual

q_quotes = df.resample('Q')
q_returns = q_quotes.pct_change(1)[1:]

Medimos as covariâncias

sigma0 = pd.DataFrame(np.cov(q_returns.T),
    index = series_names, columns = series_names)

sigma0
"""
                   SP500        NIKKEI    USTreasury
SP500       4.306331e-03  4.463033e-03 -6.345169e-04
NIKKEI      4.463033e-03  8.841049e-03 -1.150345e-03
USTreasury -6.345169e-04 -1.150345e-03  2.944317e-04
"""

Em relação às expectativas de retorno, a opção naive seria usar os retornos históricos como input para os retornos esperados

4 * np.mean(q_returns)

"""
SP500         0.042836
NIKKEI        0.035582
USTreasury    0.044480
"""

Outra opção seria usar estimativas de mercado - pesquise na internet por "capital market assumptions" de bancos e outros agentes. Para fins de exercício, vamos escolher os seguintes retornos esperados e taxa livre de risco com base em um apanhado de análises

e_ret0 = [.062, .044, .012]
rf0 = .01

Para medir o desvio padrão das séries, tomemos os valores históricos (sem ponderação)

e_std0 = np.std(q_returns)

Principal função

# IN  pesos, retornos esperados,
#    matriz de covariancias, taxa livre de risco
# OUT pesos, retorno esperado carteira,
#    desvio padrao carteira, Sharpe

def carteira(w, e_ret, sigma, rf):  
    
    rc = w.dot(e_ret)                   # retorno da carteira
    var_c = w.dot(sigma.values.dot(w))  # variancia da carteira
    std_c = np.sqrt(var_c)              # desvio padrao da carteira 
    sharpe = (rc - rf) / std_c          # indice de Sharpe
    
    return (w, rc, std_c, sharpe)

Geramos uma nuvem de carteiras formadas por combinação convexa das carteiras principais (sem incluir o ativo livre de risco)

pesos = np.array([[0, 0, 0]])
for i in range(101):
    for j in range(101):
        if 100 >= i + j:
            pesos = np.append(pesos, [[i, j, 100 - (i + j)]], axis=0)
pesos = pesos[1:]
pesos = pesos[1:] * .01

nuvem0 = pd.DataFrame([carteira(_w, e_ret0, sigma0, rf0)
    for _w in pesos], columns = ['w', 'rc', 'std_c', 'sharpe'])

Encontramos a carteira ótima pelo critério de maximização do Índice de Sharpe

C_ = nuvem0.sort_values(['sharpe'], ascending = False).reset_index().iloc[0]

Testamos as CPO

rc = C_.rc
std_c = C_.std_c
w = C_.w
sharpe = C_.sharpe

r1 = e_ret0[0]
r2 = e_ret0[1]
r3 = e_ret0[2]

s1 = np.array(sigma0['SP500'])
s2 = np.array(sigma0['NIKKEI'])
s3 = np.array(sigma0['USTreasury'])

L1 = r1 * std_c - sharpe * s1.dot(w)
L2 = r2 * std_c - sharpe * s2.dot(w)
L3 = r3 * std_c - sharpe * s3.dot(w)

print('L1 = {}\nL2 = {}\nL3 = {}'.format(L1, L2, L3))

"""
L1 = 0.000138207748731
L2 = 0.00012400946778
L3 = 0.000154052396314
"""

Plotamos a nuvem de pontos, destacando as carteiras básicas e a carteira ótima

pl.figure()
pl.xlim(0., .1)
pl.scatter(nuvem0['std_c'], nuvem0['rc'], color='lightgrey')
pl.scatter(C_['std_c'], C_['rc'], color='blue', s = 60, marker = 's')
pl.scatter(e_std0, e_ret0, color='red', s = 50, marker = '^')

A imagem será apresentada aqui.

Vejamos a fronteira eficiente sem venda a descoberto - combinação convexa entre o ativo livre de risco e a carteira ótima. Observe que essa reta tangencia o conjunto das carteiras, o que significa (intuitivamente) que esse é o ponto em que o Índice de Sharpe - a inclinação dessa reta mantendo fixo o intercepto - é máximo

pl.plot([0, C_['std_c']], [rf0, C_['rc']], color='k', linestyle='-',
    linewidth=2)

A imagem será apresentada aqui.

[EX. 11] Para o caso em que não há ativo livre de risco, maximizamos o índice de Sharpe para cada montante de risco tolerado (medido pelo desvio padrão)

min_std = int(1000 * np.min(nuvem0['std_c']) + 1) / 1000.
x_ticks = np.arange(min_std , .100001, .001)
v_rc    = np.repeat(0., x_ticks.size)
v_std_c = np.repeat(0., x_ticks.size)

for t in range(x_ticks.size):
    tick = x_ticks[t]
    W_ = nuvem0[tick >= nuvem0['std_c']]
    C_ = W_.sort_values(['sharpe'], ascending = False).reset_index().iloc[0]
    v_rc[t] = C_['rc']
    v_std_c[t] = C_['std_c']

front1 = pd.DataFrame(dict({'rc':v_rc, 'std': v_std_c}))

Vejamos como fica a fronteira por esse critério

pl.figure()
pl.xlim(0., .1)
pl.scatter(nuvem0['std_c'], nuvem0['rc'], color='lightgrey')
pl.scatter(C_['std_c'], C_['rc'], color='blue', s = 60, marker = 's')
pl.scatter(e_std0, e_ret0, color='red', s = 50, marker = '^')
pl.scatter(front1['std'], front1['rc'], color='black')

A imagem será apresentada aqui.

Alternativamente, vamos agora escolher as carteiras ótimas pelo critério da maximização do retorno esperado da carteira

v_rc    = np.repeat(0., x_ticks.size)
v_std_c = np.repeat(0., x_ticks.size)
for t in range(x_ticks.size):
    tick = x_ticks[t]
    W_ = nuvem0[tick >= nuvem0['std_c']]
    C_ = W_.sort_values(['rc'], ascending = False).reset_index().iloc[0]
    v_rc[t] = C_['rc']
    v_std_c[t] = C_['std_c']

front2 = pd.DataFrame(dict({'rc':v_rc, 'std': v_std_c}))
pl.scatter(front2['std'], front2['rc'], color='black')

A imagem será apresentada aqui.

comentou Dez 8, 2016 por Peng Yaohao (101 pontos)  
Excelente Marcelo, resposta bem apresentada e bastante elucidativa!

Acho que só faltou discutir brevemente a intuição da função objetivo que você otimizou - por exemplo, por que de se assumir o desvio padrão como medida de risco. No mais, definir a notação que você usou: \(R_f\) é o retorno livre de risco e \(r\) seria o vetor com os retornos individuais de cada ativo, já que seu produto interno com o vetor de pesos da carteira retorna o retorno da carteira toda. A solução numérica também ficou muito bem conduzida!
comentou Dez 8, 2016 por Marcelo Cruz (16 pontos)  
Obrigado pelo comentário, Peng. Ainda estou desenvolvendo o resto da parte numérica e no final vou revisar tudo e acatar suas sugestões.
comentou Dez 9, 2016 por Camila (31 pontos)  
Ótima resposta, Marcelo! Explicação clara e exemplos bem detalhados!
...