Dados B3 › Transparency
Transparency
Transparency — how you check the numbers yourself
Data version 2026-10-18-pl-classe-versoes · page generated 2026-10-23 00:42 UTC. If these do not match /saude, you are reading a cached copy.
What is checked, before every publication — 769 automated tests, collected live
26 are DATA invariants (does the number tie out?) and 124 are product tests — access gate, navigation, language, AI connector. Calling all 769 'invariants' would be inflating it: the split is checkable in the list itself. Not a hand-picked highlight reel: every test below was found by introspecting the actual test files just now, the same way pytest --collect-only would. Grouped by what it protects against, not by which file it lives in.
Level 1 — Integrity: does the data tie out? (26)
RC01— /fiis/screener: the investor floor includes whoever sits exactly on it (5,000 in, 4,999 out; a null count is zero); a flagged P/BV or yield is not a filterable number (the fund appears without it and passes no filter on that field); each bound (pvp_min/max, dy_min/max) includes the equal value; segment matching ignores case; order is by ascending P/BV with funds without a clean P/BV last; `limite` cuts the list but not `n`, default 50, ceiling 200; an unknown parameter or a non-numeric value is 400; an empty parameter counts as absent; a database from another version is 503 with Retry-After.RC02— /scores: Piotroski compares the latest fiscal year with the PREVIOUS one (year − 1), with the share counts of both years, and states `de: 9`; banks get no Piotroski and their type comes out as stored; Graham uses the latest multiple with both P/E and P/BV, only when both are positive, and P/E × P/BV = 22.5 still passes; rounding is 2 places (P/E, P/BV) and 1 (product).RC03— /dividendos: `proventos` carries every share class; the yearly summary and the 12-month yield only the class of the requested ticker (ALFA3 → ON), summed to 6 places; a distribution without a record date stays out of the summary; one whose record date is EXACTLY one year ago still counts for the yield; the yield exists only with a price and a positive sum; without a coverage row the status is “nao_verificado” with zero distributions; the note names the class.RC04— /empresas/{T}: a request by a secondary code answers with the PRIMARY one; name, tax id, CVM code, sector and type each come from their own column; coverage is the first and last indicator year plus the restatement count; a company with no row in the ticker table lists the primary code; every resource says `exige_chave` = false only for the free sample.RC05— /indicadores: banks and insurers get the intermediation note and the chart-of-accounts warning, never the minority-interest alert; a general company gets the alert only when controlling-shareholder income differs from total income by MORE than 1% in the latest consolidated year, with the relative difference to 6 places.RC06— /screener: `_min` and `_max` include the equal value; a flagged number does not pass; order is by the FIRST filter, descending (also with a single filter); `limite` defaults to 100 and cuts the list, not the total; `as_of` equal to today is valid (only the future is 400); a non-numeric value and an unknown parameter are 400; a receipt over the daily limit states the limit.RC07— The JSON restatements feed returns 100 by default, at least 1 and at most 500; without a key and without asking for JSON it is the page, in the requested language.RC08— /dump/{set}.csv carries the header with the column NAMES and every row of the set, with no null byte between chunks; an unknown set is 404.RC09— /etfs/{T}/diario returns the whole series without `desde` and from it (inclusive) with `desde`, with the closing price and premium of the same day; an unknown ETF is 404; a database from another version is 503 with Retry-After.RC10— /etfs/{T}/carteira: without `competencia`, the latest one; the NAV is the one of the largest position and the weight is value ÷ NAV (null for a position without value); available months come out as YYYY-MM, newest first; with `competencia`, that one; an unknown ETF is 404; a database from another version is 503.RC11— /empresas/{T}/indicadores/{name}: for a MULTIPLE the series comes from the multiples (with the price date); for an indicator, from the indicators; the “current” value compared with the universe is the latest one WITHOUT a flag — a newer flagged point stays out. On the real database, with a company whose latest point is flagged.RC12— /empresas/{T}/linhagem for an indicator the company did not publish in that year is 404 (not another code), with the year in the message.RC13— /empresas/{T}/eventos returns every corporate action (spin-offs included), newest first, with the SOURCE (declared by the exchange or found by us) and the SITUATION (what the price said) in separate fields, the declared factor, the approval date and the net effect of the day: the two TIMS3 events of 02/07/2025 (0.01x and 100x) are 1.0 together. A real backtest read inferred / not checked and treated a declared event as unproven.RC14— The corporate-actions table on the company page says where an event comes from only when it is NOT the exchange (found by us), shows the net effect of the day when there is more than one event in it, and spells out the situation instead of a single not-checked for everything. It used to say inferred for any flagged event, declared ones included.RC15— The events set of the dump has one row per event with ticker, declared factor, approval date, net effect of the day, the price-check situation and the source (declared by the exchange / found by us), spin-offs included, and is listed in the dump index.RC16— /empresas/{T}/precos returns the full DAILY series (open, high, low, close, average, financial volume, quantity, trades) oldest first in one call; de/ate cut it (inclusive at both ends); limite cuts from the oldest and says so; the corporate actions INSIDE the period come along (those outside do not) with the net effect of the day; and the answer says the price is NOT adjusted. A real backtest said the road to the full daily series was the biggest problem.RC17— precos with formato=csv returns the CSV with header and the same rows as the JSON (and signals a cut by header); a date outside YYYY-MM-DD, `de` after `ate` and an unknown format give 400 with the cause; an unknown ticker gives 404.RC18— `/empresas/{T}/desempenho` returns buy-and-hold and moving-average-crossover return, drawdown and volatility in a few numbers, on the split-ADJUSTED price (a 2:1 split is not a 40% drop), with the premises written in the answer and the crossover signal applied to the NEXT session only; invalid dates, a short average not below the long one and unknown tickers are rejected.RC19— `llms.txt` opens (within the first 2,500 characters) with the open performance route, stated as NO KEY and for any ticker, before the long sections: AI page readers summarise long files and lose what sits in the middle.RC20— The company page `/acoes/{T}` links to `/empresas/{T}/desempenho` (open, no key) and `llms.txt` no longer says only WEGE3 and the methodology are open: it names the any-ticker performance route in the closing sentence and in the key section. AI page readers summarise `llms.txt` and read the company page, so the hint must live there.RC21— `/etfs/{T}/desempenho` and `/fiis/{T}/desempenho` return return, drawdown and volatility for buy-and-hold and the 21x100 crossover with NO key, with fund premises (distributions left out, no split adjustment); a daily jump above 35% (an unregistered split) is listed in `saltos_suspeitos` with `confiavel: false` instead of silently becoming a -90% return; unknown tickers 404, bad dates 400.RC22— The FII and ETF pages show the buy-and-hold vs crossover table with the open route link and fund premises (no distributions); a suspicious daily jump shows a 'not reliable' notice; with no result the block disappears; `llms.txt` names the FII and ETF routes at the top.RC23— Performance now carries `retorno_total_pct` (and annualised and total drawdown): a dividend buys more shares at the close of the EX-DATE (first session after the record date); in the crossover only whoever is long on the ex-date receives it; price fields do not change; with no recorded dividends the answer stays price-only; FIIs count RENDIMENTO and leave AMORTIZACAO out.RC24— `retorno_total_liquido_pct` (and annualised and net drawdown) discounts the 15% withholding tax on JCP only; dividends and REIT distributions come in whole; the GROSS total and price returns do not change; with no JCP the net equals the gross and the page shows no extra column; with JCP the column appears with the tax label; premises and page say the gross is gross.RC25— A distribution paid in TWO equal installments with the same ex-date (PETR4: JCP of R$ 0.35048636, ex-date 01/06/2026, paid on 20/08 and 21/09) stays as two rows: each repetition in B3's list is an installment and the company supplement matches each one by payment-date order; running again does not duplicate; a dividend and a JCP of the same value on the same date are not installments. Found by a blind test with Claude on 12/10/2026 (PETR4 yield was 6.2% against 6.5% elsewhere).RC26— A database from the previous week (the rebuild REUSES it) with the old `dividendo` key, without the installment: on opening, the table is rebuilt with the installment in the key, every row stays, the index comes back, and from then on two equal installments coexist; opening again changes nothing. Found when the 12/10/2026 build kept the old key.
Level 2 — Semantics: does the account mean what the calculation assumes? (0)
Level 3 — Economic: is the result possible in the real world? (0)
Other product tests (0)
Access gate, navigation and the MCP connector — they test whether the product works, not whether a number is right, so they don't fit the 3 levels above by design. Still counted, still listed:
Reconstruct it yourself — net margin across 5 companies, 4 sectors
We don't ask you to trust the numbers. Here's the net margin of five companies in four different sectors, each rebuilt straight from its annual report (DFP) at the CVM — net income ÷ revenue, matching the published figure exactly. For ANY indicator, of any company, the whole chain down to the line in CVM's file is at /linhagem:
| Company · sector | Net income (CVM acct) | Revenue (CVM acct) | Margin =÷ |
|---|---|---|---|
| WEGE3 · industrial | R$ 6.78 bn DRE:3.11 | R$ 40.80 bn DRE:3.01 | 16.6% |
| VALE3 · mining | R$ 11.81 bn DRE:3.11 | R$ 213.59 bn DRE:3.01 | 5.5% |
| PETR4 · oil & gas | R$ 110.61 bn DRE:3.11 | R$ 497.55 bn DRE:3.01 | 22.2% |
| ITUB4 · bank | R$ 45.85 bn DRE:3.09 | R$ 387.12 bn DRE:3.01 | 11.8% |
| BBAS3 · bank | R$ 16.78 bn DRE:3.11 | R$ 319.46 bn DRE:3.01 | 5.3% |
Download any of these DFPs from the CVM, take the income and revenue accounts, divide — you get the same number. Banks use interest income as revenue (what makes sense for a bank), so the reconciliation is sector-aware; the others use sales revenue. Figures are the latest fiscal year.
Why we may differ from another site (and it's not an error)
A difference between two sites usually isn't one being wrong — it's a method choice. We disclose ours:
- Average vs ending capital: ROE and ROIC use average equity/capital (this year + last ÷ 2), not the ending balance — so they're not a naive single-year division.
- Controlling vs consolidated: we state which one each item uses.
- TTM vs annual: quarterly multiples use trailing-twelve-months earnings.
- IFRS 16, goodwill, cash, exceptional tax: each handled explicitly and flagged when it distorts.
Do the tests bite? Yes — real cases the suite has caught
A test that never fails could mean perfect data — or a weak test. These were born from real errors that slipped through, and now fail — only shown for tests that actually exist in the collected suite above:
- I-B16 · banks' P/E = 0: the CVM reports share count sometimes in units, sometimes in thousands (varying by company and year); market cap came out 1000× too small and P/E was zero. I-B16 (price × shares recomputed) caught it — we fixed 661 annual and 2,027 quarterly multiples.
- I-B24 · the vanishing profit: the profit/equity account varies by bank (Itaú 3.09, BB 3.11); if the label search fails, the number vanishes silently and ROE is born wrong. I-B24 makes that hole fail.
- H-B23 · the 800% dividend yield: old B3 dividends carry a price not adjusted for splits; without H-B23 the yield would look absurdly real. The flag keeps the record and warns.
- I-B20 · the missing COTAHIST 2023: on the first deploy one year of prices failed to download silently; the API shipped with ~50k fewer prices and the suite passed, because nothing checked coverage. We added I-B20 — a year without prices now fails the build loudly.
- I-B33 · Oil & Gas at −89.6% in one week: COTAHIST isn't split-adjusted; a 10:1 split read as a −90% weekly return and stayed in the cumulative return forever, with nothing failing (a blind amplitude filter didn't fix it either — it just flipped which direction was wrong). I-B33 (no sector week beyond ±35%, the ruler is the exchange itself) stops the build.
- I-B31 · 364 of 865 sector-index weeks frozen: extending the series back to 2010, a sector with no company carrying a market cap that week turned into a factor of 1.0 — a flat line that the cumulative return read as 'market stood still', and the 'since 2010' return came out fictional (+527%). I-B31 fails on any week repeated to the cent.
- I-E05 · EMBRAER's 2010 P/E priced with 2025 data: the point-in-time price lookup fell forward with no ceiling whenever the current ticker had no price at balance-sheet time (ticker change, share-class migration); the multiple came out priced 15 years into the future, in 22% of annual and quarterly rows, silently. The 45-day window (and I-E05) blocks it: no session inside the deadline, no multiple computed.
- I-E04 · AZUL at R$ 1.67 quadrillion market cap: the balance sheet carried 54.7 trillion shares from the judicial recovery issuance, but the price used was already on the post-reverse-split (1:150,000) basis — two ends measured on different bases, market cap wrong by orders of magnitude. I-E04 (market cap never above Brazil's GDP) fails the build on that absurdity.
How far back each block of data goes
No asterisks here: this is coverage year by year, counted right now. Indicators (ROE, ROIC, margins, growth) come from the filings alone and cover the whole series. Multiples (P/E, P/BV, EV/EBITDA) need a price and a share count — and the share count comes from CVM's reference form, which the further back you go the fewer companies filed in a usable format. The gap between the two columns is a source limit, not an ingestion hole. If you look for an old P/E and do not find it, it is because nobody has it — not because we hid it. The same numbers as JSON: /cobertura.
| Year | Companies with indicators | Companies with multiples |
|---|---|---|
| 2025 | 440 | 290 |
| 2024 | 444 | 296 |
| 2023 | 442 | 298 |
| 2022 | 429 | 295 |
| 2021 | 423 | 296 |
| 2020 | 410 | 274 |
| 2019 | 371 | 166 |
| 2018 | 326 | 167 |
| 2017 | 317 | 165 |
| 2016 | 311 | 154 |
| 2015 | 303 | 144 |
| 2014 | 301 | 140 |
| 2013 | 292 | 131 |
| 2012 | 291 | 112 |
| 2011 | 284 | 98 |
| 2010 | 277 | 34 |
Live coverage
456 companies · 880,805 price points · 8,656 recorded restatements · last refresh 2026-10-22 01:17:07. Full live counts at /saude.
Sources: CVM (open data, ODbL) and B3 (COTAHIST). Not affiliated with B3 or the CVM. Not investment advice.
Numbers on this page are live. Data version 2026-10-18-pl-classe-versoes · page generated 2026-10-23 00:42 UTC. If this does not match /saude, you are reading a cached copy.