尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗Statistical fraud detection for Brazilian investment funds, built on public CVM filings.
The toolkit ingests the CVM's open datasets — informe diário (daily NAV, flows, quotaholder counts), CDA (portfolio composition) and cadastro (fund registry) — and runs a battery of detectors over them, producing per-fund findings with plain-language briefs explaining what was flagged and what would corroborate it.
Detection patterns are drawn from documented Brazilian fund-industry cases: circular fund-to-fund flows, phantom assets, valuation smoothing, window dressing, and cross-fund issuer concentration.
Findings are leads, not proof. Every detector produces statistical red flags that require corroboration against records this toolkit cannot see — subscription ledgers, bank transfers, counterparty documentation. Nothing here establishes wrongdoing on its own.
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
Requirements are split so you install only what you need:
| File | Contents |
|---|---|
requirements.txt | Core runtime: pandas, numpy, requests, matplotlib, tqdm |
requirements-dev.txt | pytest, hypothesis, ruff |
requirements-optional.txt | yfinance, for the optional market-price validator |
requirements-notebooks.txt | JupyterLab, seaborn, for notebooks/ |
CVM publishes monthly ZIPs at https://dados.cvm.gov.br/dados. CVMCollector
downloads and extracts them into data/raw/:
from src.collectors.cvm_collector import CVMCollector
c = CVMCollector()
c.download_registro_fundo_classe() # fund/class registry
c.download_informe_diario(2026, 6) # daily NAV and flows
c.download_cda(2026, 6) # portfolio composition
Use registro_fundo_classe, not the legacy cad_fi.csv. Since RCVM 175 the
informe reports by class, and almost every record in cad_fi.csv has been
cancelled: it joins to 6.6% of the funds that actually report, against 88.9%
for the class registry, which also carries administrator and manager CNPJs.
The monthly CDA arrives as one ZIP of ten to twelve CSVs — eight position blocks split by asset class, plus summaries. All position blocks are read and unioned; pass the directory rather than a single file.
Match the registry to the period. The registry is a current snapshot while the informe is historical, so pairing today's registry with an old month drops funds that had not yet registered. A fund filter that matches nothing is reported as an error rather than producing an empty report.
Subclasses are folded. A class with subclasses reports one informe row per
subclass per day, each with its own quota, PL and holder count. The reader folds
them into one row per class per day: PL, flows and holder counts are summed
(derived), and VL_QUOTA is the quota the dominant subclass reported. Read as
separate rows they interleave into one series, and every detector that groups by
CNPJ sees jumps that never happened. read_informe_diario(consolidate_subclasses=False)
returns the raw rows.
The CDA is a balance sheet. CVM publishes liabilities ("Valores a pagar", swap payables, ...) as positive numbers in the same blocks as assets. The reconciliation detector subtracts them before comparing with PL.
python scripts/run_investigation.py \
--informe data/raw/inf_diario_fi_202606.csv \
--cda data/raw \
--cadastro data/raw/registro_classe.csv \
--strict
Outputs land in reports/investigation/<run_id>/:
findings/*.csv — one file per detectorbriefs/ — per-entity HTML and Markdown explanationssummary.json — run metadata, finding counts, and which analyzers ran--analysis selects individual detectors; --help lists them. There is also a
guided terminal flow at scripts/investigation_tui.py.
By default every fund in the loaded data is investigated. To narrow it:
# Every fund under one administrator (partial, case-insensitive name match)
--fund-mode administrator --fund-identifier "ACME DTVM" --active-funds-only
# Every fund under one manager
--fund-mode manager --fund-identifier "ACME GESTORA"
# An explicit list
--fund-mode cnpj_list --fund-identifier "12.345.678/0001-90,11222333000181"
Names are resolved against the registry, so --fund-mode administrator and
--fund-mode manager need one loaded. A full month covers every fund in Brazil,
so scoping the first run to one administrator is usually the right move.
Use --strict in automation. It exits non-zero if any analyzer crashed. A
report with no findings because a detector died looks exactly like a clean
result, and summary.json's execution block records which analyzers failed or
were skipped for lack of inputs.
| Area | Modules |
|---|---|
| Flow and NAV | anomaly_detector (flow z-scores, PL drops, redemption runs, flow/performance divergence) |
| Fraud schemes | fraud_schemes (circular flow, layered funds, asset inflation, shell networks) |
| Portfolio | enhanced_phantom_assets, portfolio_reconciliation, concentration, cost_basis_analyzer |
| Valuation | valuation_smoothing, window_dressing, price_divergence, benford_law |
| Structure | cross_fund_issuer, manager_network, fund_lifecycle, quotaholder_analyzer, peer_comparison |
| Market data | market_data — optional, requires yfinance |
All of these are selectable with --analysis; --help lists the values.
price_divergence works entirely offline: for each asset held by two or more
funds on the same date, it compares each fund's declared unit price
(VL_MERCADO / QT_POS) against the cross-fund median. It needs no external API
and covers unlisted credit — CRI, CRA, debêntures, cotas — which is where
mismarking is most likely.
以上為 README 的開頭部分。其餘內容請閱讀原文。 檢視完整 README ↗