Automatizar o cálculo de métricas quantitativas e a comparação entre elas para vários portfólios. Funciona em paralelo com o módulo quant_tools.py
.
Um objeto Portfolio
assume a forma
p = Portfolio(name: str, tickers: list, start: datetime, end: datetime, source: str, crypto: bool)
onde
name
: nome (este parâmetro fica armazenado num dicionário interno à classe e referencia oPortfolio
; sendo assim, cada nome é único);tickers
: lista de ativos;start
eend
: data de início e término, respectivamente, da coleta dos preços de fechamento dos ativos contidos emtickers
, através desource
;source
: fonte da coleta dos dados (por enquanto, somente investing.com,source = 'iv'
, ou Yahoo Financesource = 'yf'
). Padrão:'iv'
.;crypto
: se os ativos emtickers
forem criptomoedas, setar comoTrue
para baixá-los do Yahoo Finance. Este paramêtro deve-se à formatação da string a ser utilizada na API na hora de realizar o download.
registered
: dicionário que armazena os nomes dosPortfolio
s. Dadop
como definido acima, teríamos
registered = {'name': p}
tal que podemos referenciar o Portfolio
pelo seu nome. Útil na comparação entre os Portfólio
s.
delta
: tolerância para verificar se a soma dos pesos de umPortfolio
é igual a 1. Padrão: 0.001. Isto é, quando umPortfolio
é instanciado, há a verificação (contida na propriedadePortfolio.weights
)
np.abs(1 - np.sum(weights)) > Portfolio.delta
se True
, ValueError
será lançado.
overwrite
: seTrue
, permite a sobrescrita dos nomes emPortfolio.registered
. Padrão:False
.
Para iniciarmos um objeto Portfolio
, portanto, basta seu nome e uma lista de tickers (ativos). Apesar da data de início e término não serem obrigatórias, quando não as inserimos estamos criando um Portfolio
com o DataFrame de preços vazios (podem ser preenchidos posteriormente). Isso é útil para situações onde os preços não podem ser baixados dos métodos yf.Ticker.history()
do Yahoo Finance ou iv.get_stock_historical_data()
do investing.com (até outras opções serem implementadas). Quando o Portfolio
é instanciado, seu nome é adicionado à Portfolio.registered
, os ativos recebem, por padrão, pesos iguais e os preços de fechamento são setados à propriedade prices
.
Por exemplo, para instanciarmos um Portfolio
com os ativos ITSA4, SULA11 e SAPR11, entre o período 01/01/2020 e 01/01/2021, do investing.com, fazemos
p1 = Portfolio(
name='exemplo',
tickers=['ITSA4', 'SULA11', 'SAPR11'],
start=datetime(2020, 1, 1),
end=datetime(2021, 1, 1)
)
Visualizamos os preços de fechamento, dos três ativos, com p1.prices
. Se fizermos, p1.weights
, obtemos
p1.weights -> array([0.33333333, 0.33333333, 0.33333333])
Se quisermos alterá-los, digamos, para 50%, 30% e 20%, fazemos
p1.weights = np.array([.5, .3, .2])
Para criptoativos, digamos, BTC e ETH, baixamos do Yahoo Finance com
p2 = Portfolio(
name='exemplo-cripto',
tickers=['BTC-USD', 'ETH-USD'],
start=datetime(2020, 1, 1),
end=datetime(2021, 1, 1)
source='yf',
crypto=True
)
Os preços de fechamento do IBOVESPA, por exemplo, são adquiridos através do método search_quotes()
do investing.com (ainda não implementado à classe). Sendo assim, para termos um Portfolio
do IBOVESPA, podemos baixar os preços separadamente, pelo método acima, criar um Portfolio
vazio e setar a propriedade prices
com os preços baixados. No módulo quant_tools.py
temos o método market_index()
que realiza o download:
ibvp_past_pr = qt.market_index('bvsp', datetime(2020, 1, 1), datetime(2021, 1, 1))
ibvp_past = Portfolio('IBVP Past', ['IBVP'])
ibvp_past.prices = ibvp_past_pr
ibvp_past.dates = (start_past_dt, end_past_dt)
Portfolio.register(ibvp_past)
Note, porém, que também é necessário setar as datas e registrar o objeto. Ao executarmos Portfolio.registered
, obtemos um dicionário com os nomes dos três Portfolio
s instanciados, como chaves, e seus endereços de memória como valor.
-
name
-
tickers
-
weights
-
dates
-
prices
-
__len__
: retornalen(self.tickers)
-
__add__
: é possível adiconar doisPortfolio
s.p = p1 + p2
gera um novoPortfolio
, realizando a concatenação dep1.prices
ep2.prices
, tal quep.name = p1.name + p2.name
(pode ser alterado) ep.tickers
é dado pelas colunas do DataFrame resultante de
pd.concat(
[p1, p2],
axis=1,
join='inner'
)
Também é possível realizar um outer join
, mas a implementação deve ser feita através de p1.__add__(p2, join='outer')
.
Seja p
um Portfolio
.
-
p.d_returns()
: retornos diários do portfólio como um todo ou dos ativos que o compõem (ver parâmetros) -
p.m_returns()
: análogo ad_returns()
, mas para retornos mensais -
p.total_returns()
: variação total do período (Pf - Pi) / Pi, onde Pf (Pi) é o preço final (inicial), anualizado ou não -
p.acm_returns()
: retornos acumulados, do portfólio como um todo ou dos ativos que o compõem -
p.portfolio_return()
: média ponderada dos retornos totais, i.e.,p.total_returns().dot(p.weights)
-
p.covariance()
: matriz de covariância -
p.benchmark([portfolios])
: plota o benchmark dep
contra osPortfolio
s em portfolios -
p.beta(benchmark)
: beta do portfólio, tendo como benchmark (e.g., IBOVESPA)benchmark
-
p.volatility()
: volatilidades diárias, mensais e anuais, do portfólio como um todo ou dos ativos que o compõem -
p.s_index()
: índice de Sharpe, ou Sortino, anualizado -
p.var()
: VaR histórico, paramétrico ou paramétrico ajustado -
p.cvar()
: CVaR histórico, paramétrico ou paramétrico ajustado -
p.all_vars()
: VaR histórico, paramétrico e paramétrico ajustado -
p.downside()
: downside (volatilidade dos retornos negativos) do portfólio -
p.upside()
: upside (volatilidade dos retornos positivos) do portfólio -
p.rol_drawdown()
: drawdown móvel -
p.calc_skewness()
: skewness -
p.calc_curtose()
: curtose -
p.shapiro_test()
: teste de Shapiro para verificar a hipótese de normalidade -
p.metrics()
: DataFrame contendo várias métricas do pórtfolio (e.g., todas listadas acima) -
p.transfer(new_name: str, new_weights: np.array)
: cria um novoPortfolio
, realizando uma cópia dep
, de nomenew_name
e pesosnew_weights
. Útil para compararmos portfólios de mesmos ativos e mesmas datas, mas de pesos diferentes
Seja p
um Portfolio
.
-
register(p)
: adiciona op
ao dicionárioPortfolio.registered
-
unregister(name)
: remove oPortfolio
de nome 'name' do dicionárioPortfolio.registered
-
all_rets()
: retornos de todos osPortfolio
s registrados, anualizado ou não -
all_vols()
: volatilidade de todos osPortfolio
s registrados -
all_sindex()
: índices de Sharpe, ou Sortino, de todos osPortfolio
s registrados -
all_weights()
: pesos de todos osPortfolio
s registrados -
all_metrics([portfolios])
: aplica o métodometrics()
a todos osPortfolio
s emportfolios
, concatena-os e retorna um DataFrame