Risk/return analysis and clustering of Brazilian investment funds using public data from the CVM (Brazilian Securities and Exchange Commission).
Risk/Return analysis
- Downloads CVM monthly NAV data as
.zipfiles and consolidates into a single DataFrame - Persists data locally using DuckDB, loads from database on subsequent runs, downloads missing months automatically
- CSV ingestion parallelized with Dask for faster loading of large historical files
- Fund metric computation parallelized with Dask delayed across 26k+ funds
- Computes 6 metrics per fund: cumulative return, annualized return, volatility, Sharpe ratio, max drawdown, Calmar ratio
- Screens and ranks funds by class (FIA, FIM, FI-RF, FIC) with chainable filters
- Filters out corrupt NAV series (zero/negative quotes, extreme volatility or return outliers)
Unsupervised clustering
- Groups funds by risk/return profile using K-Means
- Elbow method + silhouette score to select optimal k
- PCA projection for 2D cluster visualization
- Cluster profile comparison (mean metrics per group)
- Cluster labels:
Conservative,Aggressive Growth,High Volatility,Distressed
Interactive Dashboard
- Built with Streamlit and Plotly for interactive visualization
- Sidebar filters: period, fund class, minimum Sharpe ratio, top N funds, minimum trading days
- Period selector shows which months are cached locally vs need to be downloaded
- All 6 charts are interactive with zoom, hover tooltips and dynamic filtering
| Metric | Description |
|---|---|
| Cumulative Return | Total NAV appreciation over the period |
| Annualized Return | Geometrically annualized equivalent |
| Annualized Volatility | Std. deviation of daily returns × √252 |
| Sharpe Ratio | Excess return per unit of risk (CDI as risk-free) |
| Maximum Drawdown | Largest peak-to-trough decline |
| Calmar Ratio | Annualized return / |Max Drawdown| |
Risk-free rate: CDI approximation (10.5% p.a. adjust
RISK_FREE_ANNUALinmetrics.py)
cvm-fund-analytics/
├── app.py # Streamlit interactive dashboard
├── data/
│ ├── raw/ # CVM CSV files (git-ignored)
│ └── cvm.duckdb # Local DuckDB database (git-ignored)
├── notebooks/
│ └── analysis.py # End-to-end analysis (jupytext percent format)
├── outputs/ # Saved charts
├── src/
│ ├── __init__.py
│ ├── ingest.py # CVM data download and register loading
│ ├── metrics.py # Risk/return metric calculations (Dask parallelized)
│ ├── screener.py # Fund filtering and ranking
│ ├── clustering.py # K-Means clustering + PCA visualization
│ ├── database.py # DuckDB persistence layer (Dask CSV ingestion)
│ ├── viz.py # Matplotlib charts (notebook/static export)
│ └── viz_plotly.py # Plotly charts (interactive dashboard)
├── .gitignore
├── LICENSE
├── requirements.txt
└── README.md
# Clone and install
git clone https://github.com/isa-labs/cvm-fund-analytics.git
cd cvm-fund-analytics
pip install -r requirements.txtRun the interactive dashboard:
streamlit run app.pyRun the analysis notebook:
jupytext --to notebook notebooks/analysis.py
jupyter lab notebooks/analysis.ipynbScreening example:
from src.database import get_or_load
from src.metrics import build_metrics_table
from src.screener import Screener
from src.ingest import load_register
# Loads from DuckDB if available, downloads from CVM otherwise
months = [f"2025-{m:02d}" for m in range(1, 13)]
daily = get_or_load(months)
register = load_register()
metrics = build_metrics_table(daily, min_days=60)
top = (
Screener(metrics, register)
.filter(fund_class="Ações", active_only=False, min_sharpe=0.0)
.rank_by("sharpe_ratio")
.top(20)
)Clustering example:
from src.clustering import assign_clusters, cluster_summary, find_optimal_k
from src.clustering import plot_elbow, plot_pca_clusters, plot_cluster_profiles
# Find optimal k
elbow_df = find_optimal_k(X_scaled, k_range=range(2, 9))
# Assign clusters with interpretable labels
clustered = assign_clusters(metrics_named, k=4)
print(cluster_summary(clustered))
# Visualize
plot_pca_clusters(metrics_named, clustered)
plot_cluster_profiles(clustered)Charts are saved to the outputs/ folder after running the notebook.
Top 15 Equity Funds - Sharpe Ratio

Risk vs Return - Multi-Strategy Funds

Cumulative Return - Top 5 Equity Funds

Optimal Number of Clusters (Elbow + Silhouette)

Cluster Profiles - Mean Metrics

All data is fetched directly from CVM's open data portal. Files are published monthly in .zip format, semicolon-separated, Latin-1 encoded. No authentication required.
| Dataset | URL |
|---|---|
| Daily fund NAV (inf_diario) | dados.cvm.gov.br/dados/FI/DOC/INF_DIARIO/DADOS/ |
| Fund register (cadastro) | dados.cvm.gov.br/dados/FI/CAD/DADOS/ |
| Class | Description |
|---|---|
Ações |
Equity funds (FIA) |
Multimercado |
Multi-strategy funds (FIM) |
Renda Fixa |
Fixed income funds |
FIDC |
Credit rights funds |
FIP |
Private equity funds |
Referenciado |
Index-tracking funds |
FIP Multi |
Multi-strategy private equity funds |
FIDC-NP |
Non-performing credit rights funds |
FII |
Real estate investment funds |
Curto Prazo |
Short-term funds |
FIC FIDC |
Fund of credit rights funds |
Cambial |
FX funds |
Dívida Externa |
External debt funds |
FIC FIP |
Fund of private equity funds |
FICFIDC-NP |
Fund of non-performing credit rights funds |
FMIEE |
Innovative companies investment funds |
FIP IE |
Infrastructure private equity funds |
FIP EE |
Energy efficiency private equity funds |
FIP CS |
Strategic sector private equity funds |
FUNCINE |
Film industry investment funds |
FMP-FGTS |
FGTS investment funds |
FII-FIAGRO |
Agribusiness real estate funds |
FIDCFIAGRO |
Agribusiness credit rights funds |
FIP PD&I |
R&D private equity funds |
FIP-FIAGRO |
Agribusiness private equity funds |
FIDC-PIPS |
Social inclusion credit rights funds |
MIT - data sourced from CVM under Brazil's Lei de Acesso à Informação (LAI).
