Performance de um portfolio
Introdução
E ai? quanto rendeu seu portfólio? Fácil, $$R_p = V_{final}/V_{inicial} - 1$$ certo? sim, se você investiu em um único aporte. Normalmente, as pessoas fazem vários aportes ao longo do tempo. Então, se você fez um aporte de 1.000,00, e depois fez outro aporte de 1.000,00; o valor final do seu portfólio é 2.000,00. Se usarmos a fórmula acima iremos obter 100% de retorno, isso indica "performance"?
Se em seu aporte, escolhesse outros tipos de ativos, teria o mesmo resultado? Com o retorno acima, não sabemos avaliar a escolha dos ativos. Esse retorno apenas mostra a evolução percentual da carteira em dois períodos. Não tem muita utilidade além de saber a taxa que seu patrimônio aumentou ou diminuiu. Não nos permite avaliar e tomar decisões financeiras, por exemplo, os ativos que escolheu foram adequados ou você precisa alterá-los?
Por sorte, existem outros métodos de avaliar retorno da carteira. Vou discutir um pouco sobre eles.
Métodos para avaliar retorno de um portfolio
Existem dois métodos utilizados para medir retorno de uma carteira. O primeiro é o time weighted rate of return (TWR) e o outro money weighted rate of return MWR.
O TWR mede o retorno entre cada período delimitado pelos aportes. Esse retorno corrige para distorções causada pelo aporte e mede apenas a performance real dos ativos alocados.
O MWR é a taxa interna de retorno TIR ou IRR 1 dos fluxos de caixa. A IRR é a taxa de desconto que leva todos os fluxos de caixa ao valor presente de forma que resultem no valor investido. Então ela mede um retorno igual para todos os aportes de forma a levar o investido ao valor final.
Exemplo
Implementação
Preliminares
1import numpy as np
2import pandas as pd
3pd.options.display.float_format = '{:,.2f}'.format
4import matplotlib
5import matplotlib.pyplot as plt
6matplotlib.style.use('ggplot')
7import yahooquery as yf
Input
1trade_data = pd.read_csv('trade_data.csv')
2print(trade_data)
17/09/2018 Buy EGIE3 40 36,50 0 12/12/2018 Split EGIE3 10 0,00 1 15/03/2019 Sell EGIE3 25 39,00 2 25/03/2020 Buy EGIE3 15 38,00 3 25/03/2020 Buy EGIE3 10 37,50 4 27/07/2020 Buy EGIE3 32 44,70 5 03/10/2018 Buy ABEV3 28 18,26 6 16/10/2018 Buy ABEV3 30 17,29 7 07/11/2018 Buy ABEV3 25 16,80
Processar trades
1def process_trades(trade_data_file):
2 trades = pd.read_csv(trade_data_file, sep=',',
3 names=['date', 'type',
4 'ticker', 'volume',
5 'price'],
6 decimal=',',
7 parse_dates=['date'], infer_datetime_format=True)
8 trades['total'] = trades.apply(lambda x: x.price * x.volume
9 if x.type in ['Buy', 'Split']
10 else - x.price * x.volume, axis=1)
11 trades['vol_adj'] = trades.apply(lambda x: x.volume
12 if x.type in ['Buy', 'Split']
13 else
14 (-x.volume if x.type in ['Sell'] else 0), axis=1)
15 return trades
16trades = process_trades('trade_data.csv')
17print(trades)
date type ticker volume price total vol_adj 0 2018-09-17 Buy EGIE3 40 36.50 1,460.00 40 1 2018-12-12 Split EGIE3 10 0.00 0.00 10 2 2019-03-15 Sell EGIE3 25 39.00 -975.00 -25 3 2020-03-25 Buy EGIE3 15 38.00 570.00 15 4 2020-03-25 Buy EGIE3 10 37.50 375.00 10 5 2020-07-27 Buy EGIE3 32 44.70 1,430.40 32 6 2018-10-03 Buy ABEV3 28 18.26 511.28 28 7 2018-10-16 Buy ABEV3 30 17.29 518.70 30 8 2018-11-07 Buy ABEV3 25 16.80 420.00 25
Consolidar portfolio
1def consolidate_portfolio(trades):
2 portfolio = pd.DataFrame()
3 portfolio['vol_liq'] = (trades
4 .groupby('ticker')['vol_adj']
5 .sum())
6 portfolio['avg_price (R$)'] = (trades
7 .groupby('ticker')['total']
8 .sum() / portfolio.vol_liq)
9 portfolio = portfolio[portfolio.vol_liq != 0]
10 portfolio['quote (R$)'] = portfolio.apply(lambda x: yf.Ticker(x.name+'.SA').quotes[x.name+'.SA']['bid'], axis=1)
11 portfolio['p/l (%)'] = (portfolio['quote (R$)'] - portfolio['avg_price (R$)']) / portfolio['avg_price (R$)'] * 100
12 portfolio['current_value'] = portfolio['quote (R$)'] * portfolio['vol_liq']
13 portfolio['sector'] = portfolio.apply(lambda x: yf.Ticker(x.name+'.SA').asset_profile[x.name+'.SA']['sector'], axis=1)
14 return portfolio
15portfolio = consolidate_portfolio(trades)
16print(portfolio)
vol_liq avg_price (R$) quote (R$) p/l (%) current_value \ ticker ABEV3 83 17.47 15.79 -9.61 1,310.57 EGIE3 82 34.88 44.71 28.17 3,666.22 sector ticker ABEV3 Consumer Defensive EGIE3 Utilities
Retorno do portfolio
Para saber o valor total do portfolio na data anterior ao fluxo de caixa, precisamos saber:
- posição líquida antes do fluxo de caixa
- cotação de fechamento do dia anterior
1def consolidate_partial_portfolio(trades, date):
2 portfolio = pd.DataFrame()
3 portfolio['vol_liq'] = (trades[trades.date < date]
4 .groupby('ticker')['vol_adj']
5 .sum())
6 portfolio = portfolio[portfolio.vol_liq != 0]
7
8 def get_quote(row):
9 quote = yf.Ticker(row.name+'.SA').history(
10 start=date + pd.DateOffset(-25), end=date)['close'].values
11 return quote[-1]
12
13 portfolio['quote'] = portfolio.apply(get_quote, axis=1)
14 portfolio['total'] = portfolio.vol_liq * portfolio.quote
15 return portfolio
16
17def process_returns(trades):
18 r = []
19 for (row, cf) in trades.iterrows():
20 partial_portfolio = consolidate_partial_portfolio(trades, cf.date)
21 # initial investment
22 if partial_portfolio.total.sum() == 0:
23 r.append({'date': cf.date,
24 'ending_value': cf.total,
25 'cf_value': 0})
26 else:
27 r.append({'date': cf.date,
28 'ending_value': partial_portfolio.total.sum(),
29 'cf_value': cf.total})
30 print(cf.date, partial_portfolio.total.sum())
31
32 returns = pd.DataFrame(r)
33 returns = returns.sort_values('date')
34 returns = returns.groupby(['date', 'ending_value'], as_index=False)['cf_value'].sum()
35 def hold_period_return(row):
36 index = returns.index.get_loc(row.name)
37 if index == 0:
38 return 0
39 prev_row = returns.iloc[index - 1]
40 return row.ending_value / (prev_row.ending_value + prev_row.cf_value)
41 returns['hpr'] = returns.apply(hold_period_return, axis=1)
42 return returns
1rr = pd.read_csv('returns.csv', parse_dates=['date'])
2print(rr)
3print(rr['hpr'].apply(lambda x: x + 1).prod())
date ending_value cf_value hpr 0 2018-09-17 1,460.00 0.00 0.00 1 2018-10-03 1,152.64 511.28 -0.21 2 2018-10-16 1,623.80 518.70 -0.02 3 2018-11-07 2,277.12 420.00 0.06 4 2018-12-12 2,722.89 0.00 0.01 5 2019-03-15 3,528.78 -975.00 0.30 6 2020-03-25 1,881.32 570.00 -0.26 7 2020-03-25 1,881.32 375.00 -0.23 8 2020-07-27 3,448.69 1,430.40 0.53 0.9258180510082121
2018-09-17 00:00:00
Unnamed: 0 date ending_value cf_value hpr 0 0 2018-09-17 1,460.00 0.00 0.00 1 6 2018-10-03 1,152.64 511.28 -0.21 2 7 2018-10-16 1,623.80 518.70 -0.02 3 8 2018-11-07 2,277.12 420.00 0.06 4 1 2018-12-12 2,722.89 0.00 0.01 5 2 2019-03-15 3,528.78 -975.00 0.30 6 3 2020-03-25 1,881.32 570.00 -0.26 7 4 2020-03-25 1,881.32 375.00 -0.23 8 5 2020-07-27 3,448.69 1,430.40 0.53 0.9258180510082121
Resultados
Referências
Footnotes
Internal rate of return