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+ self .wallets = self ._generate_wallets ()
29+
30+ def _generate_wallets (self ):
31+ '''
32+ Gera carteiras com pesos aleatórios.
33+
34+ Returns:
35+ wallets (dict): dicionário contendo os valores 'weights', 'returns', 'vol' e 'sharpe_ratio'
36+ de todos os portfólios gerados
37+ '''
38+ # vetores de dados
39+ portfolio_weights = []
40+ portfolio_exp_returns = []
41+ portfolio_vol = []
42+ portfolio_sharpe = []
43+
44+ # retorno simples
45+ r = self .df .pct_change ()
46+ mean_returns = r .mean () * 252
47+
48+ # matriz de covariância
49+ covariance = np .cov (r [1 :].T )
50+
51+ for i in range (self .num_portfolios ):
52+ # gerando pesos aleatórios
53+ k = np .random .rand (len (self .df .columns ))
54+ w = k / sum (k )
55+
56+ # retorno
57+ R = np .dot (mean_returns , w )
58+
59+ # risco
60+ vol = np .sqrt (np .dot (w .T , np .dot (covariance , w ))) * np .sqrt (252 )
61+
62+ # sharpe ratio
63+ sharpe = (R - self .risk_free )/ vol
64+
65+ portfolio_weights .append (w )
66+ portfolio_exp_returns .append (R )
67+ portfolio_vol .append (vol )
68+ portfolio_sharpe .append (sharpe )
69+
70+ wallets = {'weights' : portfolio_weights ,
71+ 'returns' : portfolio_exp_returns ,
72+ 'vol' :portfolio_vol ,
73+ 'sharpe' : portfolio_sharpe }
74+
75+ return wallets
76+
77+ def plot_efficient_frontier (self , method = 'sharpe_ratio' ):
78+ '''
79+ Plota gráfico com a fronteira eficiente dos portfólios gerados.
80+
81+ Args:
82+ method (string): Método utilizado para indicar o melhor portfólio
83+ 'sharpe_ratio' - Portfólio com melhor Sharpe ratio
84+ 'volatility' - Portfólio com menor volatilidade
85+ 'return' - Portfólio com maior retorno
86+
87+ '''
88+ vol = self .wallets ['vol' ]
89+ returns = self .wallets ['returns' ]
90+ sharpe = self .wallets ['sharpe' ]
91+
92+ if method == 'sharpe_ratio' :
93+
94+ indice = np .array (sharpe ).argmax ()
95+ y_axis = returns [indice ]
96+ X_axis = vol [indice ]
97+
98+ elif method == 'volatility' :
99+
100+ indice = np .array (vol ).argmin ()
101+ y_axis = returns [indice ]
102+ X_axis = vol [indice ]
103+
104+ elif method == 'return' :
105+
106+ indice = np .array (returns ).argmax ()
107+ y_axis = returns [indice ]
108+ X_axis = vol [indice ]
109+
110+ plt .scatter (vol , returns , c = sharpe , cmap = 'viridis' )
111+ plt .scatter (X_axis , y_axis , c = 'red' , s = 50 )
112+ plt .title ("Efficient Frontier" )
113+ plt .xlabel ("Volatility" )
114+ plt .ylabel ("Expected return" )
115+ plt .show ()
116+
117+ def best_portfolio (self , method = 'sharpe_ratio' ):
118+ '''
119+ Retorna os pesos do melhor portfólio de acordo com o método escolhido.
120+
121+ Args:
122+ method (string): Método utilizado para indicar o melhor portfólio
123+ 'sharpe_ratio' - Portfólio com melhor Sharpe ratio
124+ 'volatility' - Portfólio com menor volatilidade
125+ 'return' - Portfólio com maior retorno
126+ Returns:
127+ weights (np.array): Numpy array contendo os pesos do melhor portfólio.
128+
129+ '''
130+
131+ vol = self .wallets ['vol' ]
132+ returns = self .wallets ['returns' ]
133+ sharpe = self .wallets ['sharpe' ]
134+ weights = self .wallets ['weights' ]
135+
136+ if method == 'sharpe_ratio' :
137+
138+ indice = np .array (sharpe ).argmax ()
139+
140+ elif method == 'volatility' :
141+
142+ indice = np .array (vol ).argmin ()
143+
144+ elif method == 'return' :
145+
146+ indice = np .array (returns ).argmax ()
147+
148+ return weights [indice ]
149+
0 commit comments