Metadata-Version: 2.3
Name: comparar_fundos_br
Version: 0.2.4
Summary: Obter dados de fundos de investimento
Author: Rafael Rodrigues
Author-email: rafael.rafarod@gmail.com
Requires-Python: >=3.12,<3.14
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: certifi (>=2026.7.22,<2027.0.0)
Requires-Dist: fastexcel (>=0.21.0,<0.22.0)
Requires-Dist: lxml (>=6.1.2,<7.0.0)
Requires-Dist: matplotlib (>=3.11.1,<4.0.0)
Requires-Dist: pandas (>=3.0.5,<4.0.0)
Requires-Dist: pillow (>=12.3.0,<13.0.0)
Requires-Dist: polars (>=1.44.0,<2.0.0)
Requires-Dist: protobuf (>=7.36.0,<8.0.0)
Requires-Dist: pyettj (>=0.4.1,<0.5.0)
Requires-Dist: requests (>=2.34.2,<3.0.0)
Requires-Dist: scipy (>=1.18.1,<2.0.0)
Requires-Dist: tesouro-direto-br (>=0.3.4,<0.4.0)
Requires-Dist: urllib3 (>=2.7.0,<3.0.0)
Requires-Dist: yfinance (>=1.6.0,<2.0.0)
Project-URL: Homepage, https://github.com/rafa-rod/comparar_fundos_br.git
Description-Content-Type: text/markdown

<!-- buttons -->
<p align="center">
    <a href="https://www.linkedin.com/in/rafaelrod/">
        <img src="https://img.shields.io/badge/LinkedIn-0077B5?style=flat&logo=linkedin&logoColor=white"
            alt="LinkedIn"></a> &nbsp;
    <a href="https://www.python.org/">
        <img src="https://img.shields.io/badge/python-v3-brightgreen.svg"
            alt="python"></a> &nbsp;
    <a href="https://opensource.org/licenses/MIT">
        <img src="https://img.shields.io/badge/license-MIT-brightgreen.svg"
            alt="MIT license"></a> &nbsp;
    <a href="https://github.com/psf/black">
        <img src="https://img.shields.io/badge/code%20style-black-000000.svg"
            alt="Code style: black"></a> &nbsp;
    <a href="http://mypy-lang.org/">
        <img src="http://www.mypy-lang.org/static/mypy_badge.svg"
            alt="Checked with mypy"></a> &nbsp;
</p>

A biblioteca **comparar_fundos_br** possui uma série de funções que permitem:

- Capturar dados diários de fundos de investimento;
- Filtrar fundos por classe CVM (ex Fundos de Ações, Fundos de Renda Fixa, etc);
- Calcular risco (volatilidade) e retorno dos fundos;
- Cálculo de rentabilidade no período selecionado, rentabilidade diária, rentabilidade acumulada, rentabilidade anualizada;
- Filtrar fundos por CNPJ ou por nome (ex: fundos que contenham a palavra Bradesco);
- Comparar fundos com benchmarks como: CDI, Índice Bovespa, Ações, ETFs, Dólar Fechamento, Dólar Oficial PTAX (compra e venda),IMA-S, IMA-B, IMA-B 5, IMA-B 5+, IMA-B 5 P2, IRFM, IRFM P2, IHFA, IDA-DI, IDA-IPCA, IDA-Geral;
- Comparar fundos com sua carteira de investimentos;
- Plotar gráficos de comparação e evolução dos fundos em período específico juntamente com seus benchmarks.

### Instalação

```sh
pip install comparar_fundos_br
```

Caso necessite utilizar proxy, inicie com o código abaixo:

```python
import getpass

user = getpass.getuser().lower()
pwd = getpass.getpass(prompt="Senha: ")
proxy = "10.10.1.10"
porta = 3128
proxies = {
    "http": f"http://{user}:{pwd}@{proxy}:{porta}",
    "https": f"http://{user}:{pwd}@{proxy}:{porta}",
        }
```

Veja a seguir um exemplo para capturar dados dos Fundos de Investimento com seus respectivos filtros. Repare que é possível obter um formato de dataframe em `pandas` ou `polars` (auxilia para trabalhar com grandes dataframes).

```python
import comparar_fundos_br as comp

informe_diario_fundos_historico = comp.fundosbr(anos=range(2021,2022), #somente 2021
                                                  meses=range(1,3), #somente Jan e Fev
                                                  num_minimo_cotistas=10,
                                                  patriminio_liquido_minimo=1e6,
                                                  proxy=proxies,
                                                  output_format='pandas')
informe_diario_fundos_historico.head()
```

```
| DT_COMPTC           | CNPJ_FUNDO         |   NR_COTST | VL_PATRIM_LIQ   | VL_QUOTA   | VL_TOTAL       | CAPTC_DIA   | RESG_DIA   |
|---------------------|--------------------|------------|-----------------|------------|----------------|-------------|------------|
| 2022-01-03 00:00:00 | 41.778.228/0001-94 |         23 | 22.282.945,45   | 1,03       | 22.366.555,70  | 0,00        | 0,00       |
| 2022-01-03 00:00:00 | 05.090.905/0001-13 |         65 | 1.645.439,95    | 5,39       | 1.624.983,60   | 0,00        | 0,00       |
| 2022-01-03 00:00:00 | 34.271.097/0001-99 |         12 | 15.971.287,49   | 1,06       | 15.998.464,03  | 0,00        | 0,00       |
| 2022-01-03 00:00:00 | 18.298.411/0001-70 |         71 | 287.276.414,66  | 3,31       | 288.767.536,79 | 0,00        | 200.000,00 |
| 2022-01-03 00:00:00 | 40.905.548/0001-03 |         17 | 28.068.429,23   | 1.083,46   | 28.207.991,86  | 35.200,00   | 0,00       |
```

Os dados históricos dos fundos contém alguns problemas como: repetição do mesmo fundo em classes iguais com nomes diferentes e alterações em nome das colunas ou, até mesmo, ausência de alguma coluna. Para contornar, filtramos os tipos de fundos como: `'FI', 'FIF' ou'CLASSES - FIF` e não retornamos com essa coluna, mas a informação pode ser obtida a posteriori, veja a seguir.

Todas as informações adicionais sobre os fundos como: classificação ANBIMA, classe, Denominação Social (Nome do Fundo), Condomínio, Custodiante e outras estão disponíveis no cadastro dos fundos. Para obte-lo, basta invocar a função abaixo:

```python
cadastro = comp.get_cadastro_fundos(classe=comp.get_classes(), proxy=proxies,
                                    output_format='polars')
```

Caso deseje filtrar apenas algunas classes de Fundos, siga o exemplo abaixo que filtra por fundos de ações.

```python
print(comp.get_classes())
cadastro = comp.get_cadastro_fundos(classe=["Ações"], proxy=proxies,
                                    output_format='polars')
```

Importante ressaltar que todos os Fundos que são retornados do cadastro possuem situação CVM como "EM FUNCIONAMENTO NORMAL", além de retornar somente o tipo de classe `Classes de Cotas de Fundos FIF` e classificação não nula.

Para cruzar as informações diárias e de cadastro, execute:

```python
informe_completo = comp.mesclar_bases(cadastro, informe_diario_fundos_historico)
```

Os estudos com os fundos são executados sobre uma série temporal das cotas diárias dos fundos. Com `informe_completo` pode-se filtrar os fundos que interessam para sua análise. Uma coluna adicional foi criada para conjugar CNPJ do Fundo a seu Nome (CNPJ - Nome).

Caso esteja utilizando `polars`, obtenha a série temporal da seguinte forma:

```python
serie_temporal_fundos = informe_completo.pivot(index="DT_COMPTC", columns="CNPJ - Nome",
                                                 values="VL_QUOTA", aggregate_function='first')
serie_temporal_fundos = serie_temporal_fundos.sort(['DT_COMPTC'])
```

Agora se estiver utilizando o `pandas`:

```python
serie_temporal_fundos = informe_completo.pivot_table(index="DT_COMPTC", columns="CNPJ - Nome",
                                                     values="VL_QUOTA", aggfunc='first')
serie_temporal_fundos = serie_temporal_fundos.sort_values(['DT_COMPTC'])
```

Para obter o retorno dos Fundos, chame a função `calcula_risco_retorno_fundos` passando os dados dos fundos que acabou de obter. Caso esteja trabalhando até aqui com `polars`, deve-se mudar para `pandas` usando comando `serie_temporal_fundos.to_pandas().set_index('DT_COMPTC')`, pois as demais funções não foram implementadas em `polars`.

```python
(risco_retorno, rentabilidade_fundos_diaria,
    cotas_normalizadas, rentabilidade_fundos_acumulada,
    rentabilidade_acumulada_por_ano) = comp.calcula_risco_retorno_fundos(serie_temporal_fundos)
```

O primeiro dataframe indica tanto o risco (volatidade padrão) de cada fundo, por CNPJ - Nome, quanto sua rentabilidade, ambos anualizados. O segundo dataframe provê os retornos diários de cada fundo. Já o terceiro dataframe retorna o valor das cotas dos fundos normalizadas no período selecionado. Os demais dataframes retornam as rentabilidades acumulada por ano e a rentabilidade total no período, respectivamente.

A forma mais eficiente para comparar o desempenho dos Fundos é usando gráficos. Você pode plotar o risco x retorno dos Fundos e comparar com seu benchmark ou sua carteira de investimentos. Aqui não vamos calcular pra você a rentabilidade da sua carteira, apenas usar esse dado para comparar com os fundos selecionados. Veja o exemplo:

```python
risco_retorno_filtrado = risco_retorno[
                                    (risco_retorno["volatilidade"] <= 40)
                                    & (risco_retorno["rentabilidade"] >= 0)
                                    & (risco_retorno["rentabilidade"] <= 100)
                                    ]

comp.plotar_comparacao_risco_retorno(
                                risco_retorno_filtrado,
                                (21, 18), #(risco, retorno) da minha carteira
                                (19, 15), #(risco, retorno) do benchmark
                                nome_carteira="Minha Carteira",
                                nome_benchmark="Benchmark",
                                figsize=(15, 5),
                                posicao_texto_carteira=(30, 25),
                                posicao_texto_benchmark=(31, -25),
                                )
plt.suptitle("Risco x Retorno - Fundos de Ações")
plt.ylim(-10, 140)
plt.xlim(-3, 60)
plt.show()
```

![Figura3](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/figura3.png)

Uma outra forma de comparação é utilizando as cotas iniciando em um valor inicial de 1, arbitrário. Caso deseje visualizar a evolução do investimento, basta multiplicar o dataframe, assim a comparação fica facilitada.

A função `plotar_evolucao` encontra os Fundos tanto por CNPJ quanto por Nome, ou seja, se deseja obter todos os Fundos que possuam Bradesco no Nome, basta informa na variável `lista_fundos=["Bradesco"]`.

Para facilitar a comparação, você pode personalizar o gráfico para destacar o melhor e o pior Fundo, além de plotar o seu benchmark.

Os benchmarks disponíveis são: CDI, IMA-S, IMA-B5, IMA-B5+, IMA-B5 P2, IRFM, IRFM P2, IHFA, Ibovespa, DIVO11 (IDIV), SP500 e Ações diversas listadas na B3. A função retorna os valores de cada ticker, retorno diário e retorno acumulado no período indicado.

```python
dados = comp.DadosFinanceiros(proxy=None)

data_inicio, data_fim = "2008-01-01", "2026-08-01"

ptax = dados.cambio_ptax(data_inicio, data_fim, compra=False)
acoes_etfs = dados.stocks(["PETR4", "VALE3", "IVVB11"], data_inicio, data_fim)
multi = dados.benchmarks(data_inicio, data_fim, benchmark=["CDI", "IBOV", "sp500"], metodo_cdi="bacen")

df_benchmarks = pd.concat([multi, acoes_etfs], axis=1).sort_index() #cuidado com missing values

data = comp.plotar_evolucao(
                cotas_normalizadas*100,
                lista_fundos=["03.916.081/0001-62","06.916.384/0001-73"],
                figsize=(15, 5),
                color="darkblue",
                alpha=0.8
                )
plt.suptitle("Evolução dos Fundos")
plt.plot((1+df_benchmarks[['Retorno Acumulado CDI']].dropna().fillna(0))*100, label="CDI", color='red', linestyle='--')
plt.legend(frameon=False, loc="upper right")
plt.show()
```

![Figura1](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/figura1.png)

```python
data = comp.plotar_evolucao(
                            cotas_normalizadas*100,
                            lista_fundos=["Bradesco"],
                            figsize=(15, 5),
                            color="gray",
                            alpha=0.2,
                            color_maximo="orange",
                            color_minimo="blue",
                            color_seta_maximo="orange",
                            color_seta_minimo="blue",
                            posicao_texto_maximo=(-100, 35),
                            posicao_texto_minimo=(-100, 10),
                            )
plt.suptitle("Evolução dos Fundos que contenham Bradesco no nome")
plt.plot((1+ibov[['Retorno Acumulado IBOV']].fillna(0))*100, label="Ibovespa", color="red", lw=3)
plt.legend(frameon=False, loc="upper center")
plt.show()
```

![Figura2](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/figura2.png)

Ainda é possível listar os Fundos de maior e pior desempenho:

```python
melhores = data.iloc[-1:].T.dropna().sort_values(data.index[-1], ascending=False)
melhores.columns = ["Evolução"]
melhores.head()
```

```
| CNPJ - Nome                                                                                                  |   Evolução |
|--------------------------------------------------------------------------------------------------------------|------------|
| 03.916.081/0001-62 // BRADESCO FIF - CLASSE DE INVESTIMENTO EM AÇÕES PETROBRAS - RESPONSABILIDADE LIMITADA   |    141.402 |
| 03.922.006/0001-04 // BRADESCO H FIF - CLASSE DE INVESTIMENTO EM AÇÕES PETROBRAS - RESPONSABILIDADE LIMITADA |    141.294 |
| 04.884.567/0001-29 // BRADESCO BA FIF - CLASSE DE INVESTIMENTO EM AÇÕES VALE -RESP LIMITADA                  |    124.039 |
| 04.882.617/0001-39 // BRADESCO FIF - CLASSE DE INVESTIMENTO EM AÇÕES VALE - RESPONSABILIDADE LIMITADA        |    123.804 |
| 04.892.107/0001-42 // BRADESCO H FIF - CLASSE DE INVESTIMENTO EM AÇÕES VALE DO RIO DOCE - RESP LIMITADA      |    123.724 |
```

Uma forma eficiente de avaliar o desempenho dos fundos, é por meio de janelas móveis. Ao invocar a função `plotar_rentabilidade_janela_movel` informando a série temporal das cotas diárias, os benchmarks específicos e a janela de tempo ou _holding period (HP)_, veja:

```python
import random, matplotlib
random.seed(12)
matplotlib.style.use("fivethirtyeight")
seleciona_um_fundo_aleatoriamente = random.sample(serie_temporal_fundos.iloc[0].dropna().index.tolist(), 1)

comp.plotar_rentabilidade_janela_movel(serie_temporal_fundos[seleciona_um_fundo_aleatoriamente],
                                       3*252, df_benchmarks[['CDI', 'IBOV']] )
```

No exemplo acima, um fundo foi sorteado aleatoriamente; mas você pode ter outro critério para selecioná-lo. Importante a série dde benchmarks ter a mesma janela histórica disponível. **O HP escolhido foi de 3 anos, repare se o fundo selecionado tem dado suficiente para fazer essa análise.**

![plotar_rentabilidade_janela_movel](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/plotar_rentabilidade_janela_movel.png)

Outra forma de avaliação é uma visão mais geral em períodos específicos padronizados como: mensal (M), trimestral (Q), semestral (sem) ou anual (Y) destacando as cores do retornos mais positivos e negativos. Basta informar os retornos diários e selecionar o período:

```python
comp.plotar_heatmap_rentabilidade(serie_temporal_fundos[seleciona_um_fundo_aleatoriamente], period='M')
```

![plotar_heatmap_rentabilidade](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/plotar_heatmap_rentabilidade.png)

De forma complementar ao gráfico _heatmap_ anterior, pode ser exibido uma figura que compara os retornos do período com seu benchmark. A última coluna _Ultrapassa Retorno CDI_ (CDI escolhido no exemplo) representa o número de vezes que o fundo ultrapassou o benchmark sobre o total de períodos (se mensal, doze periodos).

```python
comp.plotar_heatmap_comparar_benchmark(serie_temporal_fundos[seleciona_um_fundo_aleatoriamente],
                                          df_benchmarks[['Retorno CDI']],
                                          "M")
```

![plotar_heatmap_comparar_benchmark](https://raw.githubusercontent.com/rafa-rod/comparar_fundos_br/main/media/plotar_heatmap_comparar_benchmark.png)

Agora fazendo de forma mais objetiva e menos visual, pode-se avaliar o desempenho de vários fundos com diversos benchmarks simultaneamente estabelecendo um limite de corte para exibir aqueles que superem os benchmarks em 60% das vezes, por exemplo. Veja o exemplo abaixo com 15 fundos e 3 benchmarks, em janela de 3 anos com limite de 60%:

```python
quinze_fundos_aleatorios = random.sample(serie_temporal_fundos_completo.iloc[0].dropna().index.tolist(), 15)
fundos_selecionados_por_corte = comp.supera_benchmark(serie_temporal_fundos[quinze_fundos_aleatorios],
                                                         df_benchmarks[['SP500', 'IBOV', 'CDI']], 3*252, 0.6)
fundos_selecionados_por_corte
```

```
| Fundo                                                                                                                   |   Supera IBOV (%) |   Supera CDI (%) |
|-------------------------------------------------------------------------------------------------------------------------|-------------------|------------------|
| 08.418.132/0001-40 // ITAU FLEXPREV VÉRTICE PRÉ FUNDO DE INVESTIMENTO FINANCEIRO RENDA FIXA - RESPONSABILIDADE LIMITADA |           67.7575 |          68.3916 |
| 00.977.449/0001-04 // BNP PARIBAS GERDAU PREVIDÊNCIA 1 CLASSE DE INVESTIMENTO RENDA FIXA CREDITO PRIVADO RESP LIMITADA  |           66.4209 |          97.8812 |
| 03.545.843/0001-61 // CARGILLPREV CD PREVIDENCIÁRIO MULTIMERCADO CRÉDITO PRIVADO - FUNDO DE INVESTIMENTO                |           66.2223 |          85.8377 |
```

Repare que apenas 3 fundos superaram seus benchmarks, exceto SP500, mais que 60% das vezes em janela móvel de 3 anos.

Complementando, a análise acima, veja quantas vezes esses fundos ultrapassaram, em média, seus benchmarks e também quanto, em média, ficaram abaixo dos benchmarks. Além disso, exibe quanto o fundo superou, em média, seu benchmark no período.

```python
corte_benchmark = 100
bench_corte = 'CDI'
janela_movel = 3*252 #3 anos
indice_superacao = comp.qto_supera_benchmark(serie_temporal_fundos[fundos_selecionados_por_corte.index.tolist()],
                                             df_benchmarks[['IBOV', 'CDI']], janela_movel, corte_benchmark, bench_corte)
indice_superacao
```

```
| Fundo                                                                                                                   |   % de vezes, em média, acima IBOV (%) |   % de vezes, em média, abaixo IBOV (%) |   % do IBOV, em média |   % de vezes, em média, acima CDI (%) |   % de vezes, em média, abaixo CDI (%) |   % do CDI, em média |
|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------|-----------------------------------------|-----------------------|---------------------------------------|----------------------------------------|----------------------|
| 00.977.449/0001-04 // BNP PARIBAS GERDAU PREVIDÊNCIA 1 CLASSE DE INVESTIMENTO RENDA FIXA CREDITO PRIVADO RESP LIMITADA  |                                35.1578 |                                -33.5267 |              55.9516  |                               2.10514 |                              -0.822388 |              107.204 |
| 08.418.132/0001-40 // ITAU FLEXPREV VÉRTICE PRÉ FUNDO DE INVESTIMENTO FINANCEIRO RENDA FIXA - RESPONSABILIDADE LIMITADA |                                33.008  |                                -23.7353 |              30.8463  |                              10.0789  |                              -7.12265  |              118.918 |
| 03.545.843/0001-61 // CARGILLPREV CD PREVIDENCIÁRIO MULTIMERCADO CRÉDITO PRIVADO - FUNDO DE INVESTIMENTO                |                                34.4475 |                                -33.8336 |               5.45296 |                               1.63382 |                              -0.470723 |              104.487 |
```

Para Fundos de Participação (FIPs) e Fundos de Direitos Creditórios (FIDCs), a sistemática é diferente. Enquanto os FIPs tem seus resultados divulgados trimestralmente, os FIDCs são mensalmente divulgados. Assim, para obte-los, basta codar:

```python
#Informe apenas o ano para obter os dados de FIPs disponíveis
fip = comp.get_fip(2026, proxy=proxies)

#Para FIDCs informe ano e mês
fidc = comp.get_fidc(2026, tabela = 'X', subtabela = 3, proxy=proxies)
```

_Os fundos exibidos acima são apenas exemplos mostrados aleatoriamente, não é recomendação de investimento ou desinvestimento._
