Dados B3 › Transparency
Transparency
Transparency — how you check the numbers yourself
Data version 2026-11-09-ancora-do-fre · page generated 2026-11-12 08:09 UTC. If these do not match /saude, you are reading a cached copy.
What is checked, before every publication — 1298 automated tests, collected live
14 are DATA invariants (does the number tie out?) and 131 are product tests — access gate, navigation, language, AI connector. Calling all 1298 '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? (10)
PS02— Spreadsheet route: petr4, PETR4.SA, BVMF:PETR4, PETR4F and a padded ticker are served as PETR4 with the same figures, HGLG11.SA as HGLG11 and an old ticker with a known change as the current one, using the same normalization as the other routes.PS04— Spreadsheet route: a key is always required, even for the open sample tickers, with a 401 that says where to get the free key and where to store it in the spreadsheet; an unknown key gets 401, free and Pro keys get 200 with the plan, production without Stripe answers 503 and responses are never publicly cached.PS05— Spreadsheet route: at most 300 tickers and 20 fields per call, an unknown field, a malformed, impossible or future date and an empty list answer 400 with the reason and use no quota, and exactly 300 tickers pass.PS06— Spreadsheet route: a call uses 1 quota unit per 100 cells (non-blank ticker times field, minimum 1), so 30 assets with 10 fields use 3 units in the same per-key table as the dashboard, the whole template (about 30 units per full refresh) fits the free plan six times a day, and a call that would pass the limit gets 429 with Retry-After and the remaining count before using anything.PS07— Spreadsheet route: the Google Apps Script user agent is not in the refused crawler list and gets 200, and robots.txt does not block the spreadsheet page.PS10— The spreadsheet page in Portuguese and English shows the three steps, every field of the route and the macro codes, the limits taken from the gate with the Pro price, the per-100-cells rule and that the price comes from Google; without a template link it says it is being prepared, a Google Sheets link becomes a button and any other link is ignored; it has no licence claim, sits in the API hub, the API page, llms.txt and the sitemap, and the home page did not grow.PS11— Static check of the Apps Script file: the key is only read and written through PropertiesService and no function that touches the key writes to a cell, the Dados B3 menu has the key, refresh and help entries, the custom function calls the batch route with the X-API-Key header in blocks of up to 300 tickers under 2 kB, caches up to 6 hours, has Portuguese messages for 401, 429 and network failure and no external library; the manifest is V8 with no dependencies and the template formulas use only known fields across the seven tabs and the brand colours are specified.PS12— The logo field (link unit, all three classes) returns the absolute dadosb3.com address of the file the asset page shows: the company logo by CVM code for stocks and the fund manager logo for FIIs and ETFs (HGLG11 is Patria, BOVA11 is BlackRock from the CVM fund registry, not the B3 administrator); every address answers 200 with a cached PNG, a missing file returns the short reason sem logo, the field also works with a date, and the manager route only accepts 14 digits.PS13— The manager logo collection maps each fund to its manager through the CVM fund registry (class to fund to manager tax ID) and takes the website the manager declares in the CVM portfolio manager registry; the icon comes only from that site with no Google favicon fallback, icons under 32 px or invisible on a light background are dropped, a blocked domain gets no request at all, an individual manager gets no logo, a withdrawn manager loses file and source line, and the source file records URL and date.PS14— The FII and ETF pages show the manager logo like the stock page: an img with class logo-cia before the h1, served from our own domain, with a title in Portuguese and English saying where it came from and when; no third party image is loaded on visit and a fund without a file shows no image.
Level 2 — Semantics: does the account mean what the calculation assumes? (4)
PS01— Spreadsheet route: one request with stock, REIT and ETF tickers and several fields returns one row per ticker in the same order, a blank row where the cell was blank, and each figure equals what the site already publishes (latest-year ROE from the indicators route, the page P/E and P/B cards, the latest EV/EBITDA and EPS from the multiples route, the stock DY and 12-month payouts, the REIT P/B and DY of the fund page).PS03— Spreadsheet route: every missing value comes with a short reason (a REIT has no P/E, an ETF has no ROE, not applicable to a bank, ticker not found and logged on the dashboard, index out of scope, statement not out yet on that date, today only for DY with a date, the right field for macro codes), never a blank cell, each reason within 60 characters.PS08— Spreadsheet route with a date: the statement year is the latest whose annual filing the CVM had received on that date, ROE is that year value and P/E is the trailing figure of the screener point-in-time rule, so 2019-06-30 uses 2018 and 2015-06-30 uses 2014.PS09— Spreadsheet macro codes on a database built in the test: latest period without a date, latest period up to the date for daily, monthly and quarterly series, percent as a fraction, the dollar in reais, the date field returning the period, and a short reason for a missing table, an empty series or a date before the first point.
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 | 441 | 290 |
| 2024 | 443 | 296 |
| 2023 | 441 | 298 |
| 2022 | 428 | 295 |
| 2021 | 422 | 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
457 companies · 884,647 price points · 8,650 recorded restatements · last refresh 2026-11-12 01:38:24. 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-11-09-ancora-do-fre · page generated 2026-11-12 08:09 UTC. If this does not match /saude, you are reading a cached copy.