1+ '''Módulo para otimização de portfólios.'''
2+
3+ import pandas as pd
4+ import numpy as np
5+ import matplotlib .pyplot as plt
6+
7+
8+ class Markowitz :
9+ '''
10+ Otimizador baseado na Teoria Moderna do Portfólio, de Harry Markowitz.
11+ A partir dos dados de fechamento, gera portfólios com pesos aleatórios e calcula
12+ os melhores pesos utilizando o risco e retorno da carteira.
13+
14+ Parâmetros:
15+ df_close (pd.DataFrame): DataFrame com os preços de fechamento dos ativos
16+ num_portfolios (int): números de portfólios gerados
17+ risk_free (float): taxa de risco livre utilizada para cálculo do sharpe ratio.
18+
19+ Atributos:
20+ wallets (dict): dicionário contendo os valores 'weights', 'returns', 'vol' e 'sharpe_ratio'
21+ de todos os portfólios gerados
22+
23+ '''
24+ def __init__ (self , df_close , num_portfolios = 10000 , risk_free = 0 ):
25+ self .df = df_close
26+ self .num_portfolios = num_portfolios
27+ self .risk_free = risk_free
28+
29+ # vetores de dados
30+ portfolio_weights = []
31+ portfolio_exp_returns = []
32+ portfolio_vol = []
33+ portfolio_sharpe = []
34+
35+ # retorno simples
36+ r = df .pct_change ()
37+ mean_returns = r .mean () * 252
38+
39+ # matriz de covariância
40+ covariance = np .cov (r [1 :].T )
41+
42+ for i in range (self .num_portfolios ):
43+ # gerando pesos aleatórios
44+ k = np .random .rand (len (df .columns ))
45+ w = k / sum (k )
46+
47+ # retorno
48+ R = np .dot (mean_returns , w )
49+
50+ # risco
51+ vol = np .sqrt (np .dot (w .T , np .dot (covariance , w ))) * np .sqrt (252 )
52+
53+ # sharpe ratio
54+ sharpe = (R - self .risk_free )/ vol
55+
56+ portfolio_weights .append (w )
57+ portfolio_exp_returns .append (R )
58+ portfolio_vol .append (vol )
59+ portfolio_sharpe .append (sharpe )
60+
61+ self .wallets = {'weights' : portfolio_weights ,
62+ 'returns' : portfolio_exp_returns ,
63+ 'vol' :portfolio_vol ,
64+ 'sharpe' : portfolio_sharpe }
65+
66+ def plot_efficient_frontier (self , method = 'sharpe_ratio' ):
67+ '''
68+ Plota gráfico com a fronteira eficiente dos portfólios gerados.
69+
70+ Args:
71+ method (string): Método utilizado para indicar o melhor portfólio
72+ 'sharpe_ratio' - Portfólio com melhor Sharpe ratio
73+ 'volatility' - Portfólio com menor volatilidade
74+ 'return' - Portfólio com maior retorno
75+
76+ '''
77+ vol = self .wallets ['vol' ]
78+ returns = self .wallets ['returns' ]
79+ sharpe = self .wallets ['sharpe' ]
80+
81+ if method == 'sharpe_ratio' :
82+
83+ indice = np .array (sharpe ).argmax ()
84+ y_axis = returns [indice ]
85+ X_axis = vol [indice ]
86+
87+ elif method == 'volatility' :
88+
89+ indice = np .array (vol ).argmin ()
90+ y_axis = returns [indice ]
91+ X_axis = vol [indice ]
92+
93+ elif method == 'return' :
94+
95+ indice = np .array (returns ).argmax ()
96+ y_axis = returns [indice ]
97+ X_axis = vol [indice ]
98+
99+ plt .scatter (vol , returns , c = sharpe , cmap = 'viridis' )
100+ plt .scatter (X_axis , y_axis , c = 'red' , s = 50 )
101+ plt .title ("Efficient Frontier" )
102+ plt .xlabel ("Volatility" )
103+ plt .ylabel ("Expected return" )
104+ plt .show ()
105+
106+ def best_portfolio (self , method = 'sharpe_ratio' ):
107+ '''
108+ Retorna os pesos do melhor portfólio de acordo com o método escolhido.
109+
110+ Args:
111+ method (string): Método utilizado para indicar o melhor portfólio
112+ 'sharpe_ratio' - Portfólio com melhor Sharpe ratio
113+ 'volatility' - Portfólio com menor volatilidade
114+ 'return' - Portfólio com maior retorno
115+ Returns:
116+ weights (np.array): Numpy array contendo os pesos do melhor portfólio.
117+
118+ '''
119+
120+ vol = self .wallets ['vol' ]
121+ returns = self .wallets ['returns' ]
122+ sharpe = self .wallets ['sharpe' ]
123+ weights = self .wallets ['weights' ]
124+
125+ if method == 'sharpe_ratio' :
126+
127+ indice = np .array (sharpe ).argmax ()
128+
129+ elif method == 'volatility' :
130+
131+ indice = np .array (vol ).argmin ()
132+
133+ elif method == 'return' :
134+
135+ indice = np .array (returns ).argmax ()
136+
137+ return weights [indice ]
0 commit comments