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 = '^')

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)

[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')

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')
