Dados B3 › Transparency
Transparency
Transparency — how you check the numbers yourself
Data version 2026-09-27-reapresentacao-agreg · page generated 2026-09-27 03:58 UTC. If these do not match /saude, you are reading a cached copy.
What is checked, before every publication — 466 automated tests, collected live
8 are DATA invariants (does the number tie out?) and 120 are product tests — access gate, navigation, language, AI connector. Calling all 466 '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? (8)
MED13— The usage record cannot reconstruct who asked for what. Knowing that a given endpoint family was called forty times guides a decision; knowing WHO asked about WHICH company guides nothing worth the cost of storing it. The test attacks both ends — the raw key and the ticker.MED14— The panel states SINCE WHEN it has been measuring key usage. Without that date, a zero in the first weeks reads as 'nobody used it' when it means 'we were not yet measuring' — the same error nearly made with the fact pages, and which its own invariant exists to prevent from the other side.MED15— Every authenticated call goes through the usage counter, and the counter exists. The strategy became 'make the people already using it dependent on the product, then monetise' — which requires knowing whether anyone comes BACK, and that was exactly the question with no instrument: the counter lived in an in-memory dictionary, wiped on every restart. We knew sixteen keys had been created and nothing about what happened next. Third time the same lesson appears: the channel we bet most on was the only one without an instrument.MED16— Tasting — the denominator of the tasting-to-key conversion — is counted in the middleware only for a DATA route of an open ticker, without a key, that succeeded and was requested by a person; it stores path family and day, never the ticker. The aggregate ratio comes out per week and says it is not an individual funnel: without cookies, who tasted is never linked to who created a key.MED17— Retention comes out by cohort of the week of a key's first use: how many started and how many came back N days or more later. A cohort that has not had that long yet is marked and has no ratio — not a zero — and the result never carries a key hash.MED18— /admin/metricas does not exist without ADMIN_TOKEN (404) and, with it, brings the four Block 5 metrics — tasting conversion, retention, arrivals by channel and watcher events per month (the same counter as the /vigia page) — with no e-mail, IP or key hash.MED19— Two synthetic scenarios on the same kind of day: people reading 10 pages many times is not suspicious; the same volume spread once per page over 1,000 pages is flagged as a suspected crawler. The cut-offs come from analytics_config.json, the table has no IP or cookie column, and the panel says "browser", not "human".MED20— derivado/search_console.jsonl (one line per day, entered by hand by the owner) is read by analytics and appears in /admin/metricas as google_real next to the concentration; pulso prints the real Google clicks next to the google.com referer and the days suspected of crawling.
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 (12)
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:
MED01— Every tool on the MCP connector goes through the counting point. The attack is to reintroduce a bare @mcp.tool() decorator on some tool: it would work perfectly and stay silent in the panel, which is the failure mode this project exists to avoid, because the number simply gets smaller and nothing warns you. The instrumentation is a single choke point rather than a line remembered in seventeen places, since this codebase has already paid three times for rules that depend on somebody remembering to apply them.MED02— Calling an MCP tool actually records the call. A decorator that exists but records nothing is worse than no decorator at all, because the panel then reports a confident zero, so this test calls a real tool and then looks for the row in the database. It records the tool name and nothing else: no arguments, no ticker, no API key, no IP address.MED03— A click in our own funnel is not counted as a visitor. Funnel events land in the same table with bot=0, so every checkout opened was inflating the human traffic count, and the error ran upward in a number used to decide where to invest. The first version of this test PASSED with the defect reintroduced, because the local database holds no event rows at all; an invariant that depends on the database happening to contain the case is an invariant that tests nothing on the day it does not. It now creates the rows it needs and measures the difference they make.MED04— The build alarm lights up on the fact, not on the instant. It went silent through seven consecutive failures in production because it keyed on the state being 'failed', while the retry loop rewrites the state to 'running' at the start of every new attempt: the alarm switched off for the whole duration of each build, and a build retrying hourly is 'running' most of the time. The health endpoint was answering with seven failures, a timeout error and a null alert on the same line while production served two-day-old data. An alarm that blinks is not an alarm.MED05— If the database being served is not the version the code promises, the health endpoint says so. This is the ground truth that depends on no bookkeeping at all, and bookkeeping is exactly what failed: while the build was breaking, production served the previous version for two days with the correction already written, tested and committed, and nothing anywhere said so because nobody compared the stamp in the database against the version in the code.MED06— 'Running' has a deadline, so a hung build cannot pass for a healthy one. Running is the only state without an alarm, which makes it the worst place for a process to die: stalled and working look identical, and the silence reads as health. The ceiling is six hours against the twenty minutes a rebuild takes, deliberately generous so a slow instance never triggers it, because an alarm that cries wolf stops being read.MED07— A page that publishes a live count also says when that count is from. The rule was already written and applied to only half the pages: the stamp helper says it exists for any page carrying a live number, and two of four had it. An external reviewer then read a cached copy of the methodology page showing one test count while the live home page showed another, and concluded a number had been hardcoded in the HTML. Nothing was hardcoded, and both pages serve the same number today: the actual defect was that the methodology page could not prove how old it was, while the transparency page, which does carry a stamp, let the same reviewer spot his own cache and not report it as a data error. A rule declared and half-applied is the failure this project exists to hunt, so it is now structural: any file with a live-count placeholder is rejected until it carries the stamp.MED08— A funnel step taken by a robot is not counted as human intent. The event recorder hardcoded the human flag regardless of user agent, and the arithmetic closed exactly: seventy-two hits on the subscribe route, forty-five human and twenty-seven robot, against seventy-two events, proving none were filtered. Counting crawlers turned noise into the metric that most resembles truth, which is measured purchase intent.MED09— A crawler following the subscribe link does not open a payment session. Every robot that passed through created a live checkout session at the payment provider as well as a funnel event: garbage on both sides, and of the worst kind, noise shaped like intent to buy. The test also checks that the agent check happens BEFORE the session is created, since a check that runs afterward has already opened the session it was meant to prevent.MED10— The free plan is visible in the served HTML, without executing any script. The readers who do not run JavaScript are the ones that matter most here: the AI crawlers and the search engine, three thousand hits a month and the only external channel that grows. An AI summarizing the site was quoting the monthly price with no way to mention the free key. The original design's honesty is preserved and tested: with the plan switched off the blocks stay hidden and the page does not promise what does not exist. The decision only moves from the client, where it depends on running code, to the server, where the fact is known.MED11— End-to-end counterproof for the rule above: the landing page as actually served carries the free plan. It is not enough for the function to work, it has to be wired into the path that serves the page — which was precisely the original defect, where the rule existed in the client script and the served HTML did not carry it.MED12— No page hand-writes a number the database already knows. This is the gap in the stamp rule, found while checking the AI connector page: the stamp rule only binds pages that USE a placeholder, and the page that teaches an AI how to connect escaped it by using none at all, carrying a typed 400-plus companies and sixteen years when the real figures were 456 and seventeen. The damage is specific and about as bad as it gets for that particular page: an AI reading it to answer questions about the product repeated numbers SMALLER than the truth, so we were understating ourselves on the one page whose audience is precisely the reader who will not check. Sixteen occurrences across seven files, English guides included, while the template helper already said in writing that a changing number must be a placeholder and never typed into the HTML — another rule written and never enforced. What the check deliberately does NOT flag is a third party's number: the comparison table cites a competitor's coverage, and that is a fact about them, while our own row in the same table uses the placeholder. A checker that rejected the competitor's figure would only teach people to route around it.
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 | 438 | 290 |
| 2024 | 444 | 296 |
| 2023 | 442 | 298 |
| 2022 | 429 | 295 |
| 2021 | 423 | 296 |
| 2020 | 410 | 274 |
| 2019 | 371 | 164 |
| 2018 | 326 | 164 |
| 2017 | 317 | 162 |
| 2016 | 311 | 152 |
| 2015 | 303 | 143 |
| 2014 | 301 | 139 |
| 2013 | 292 | 130 |
| 2012 | 291 | 111 |
| 2011 | 284 | 97 |
| 2010 | 277 | 34 |
Live coverage
456 companies · 876,076 price points · 8,654 recorded restatements · last refresh 2026-09-27 00:20:28. 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-09-27-reapresentacao-agreg · page generated 2026-09-27 03:58 UTC. If this does not match /saude, you are reading a cached copy.