SPEC 014 Earnings Report - rafaelfqueiroz/allmywallet GitHub Wiki
SPEC-014 — Earnings Report
| Status | Draft |
| PRD source | F5.3 (FR-5.14–5.20) |
| Depends on | SPEC-006 (ledger), SPEC-011 |
| Required by | SPEC-019 |
| Milestone | M5 |
Description
The report that answers how much income is this producing, and is it growing?
This is the report Marina's persona exists for: a dividend portfolio built explicitly to supplement income later, where the question is not "what is it worth" but "what does it pay, and is that payment growing". Grouping by wallet answers it directly — how much monthly income is my retirement money producing?
The measure that matters most here is yield on cost: income relative to what was actually paid for the position, not relative to today's price. An income investor who bought a stock at R$10 that now yields R$1.20 and trades at R$40 is earning 12% on their money, whatever the 3% current yield says.
Business rules
| ID | Rule |
|---|---|
| BR-014-01 | Show total proventos received in the period, broken out by type: dividend, JCP, rendimento, amortization, leilão de frações — the cash paid for a bonificação fraction B3 sold at auction (SPEC-007 BR-007-05a). (Amended 2026-09-15, #113 — see DL-014-08.) |
| BR-014-02 | JCP is reported separately from dividends. They are taxed differently in Brazil and users track them apart. |
| BR-014-03 | Monthly income chart with a 12-month moving average, to cut the lumpiness of quarterly and irregular payers. |
| BR-014-04 | Income breaks down by the selected grouping (SPEC-011) — asset class, wallet, individual asset — ranked, with each group's share of total income. |
| BR-014-05 | Yield on cost per asset and for the scope as a whole: income received relative to the cost basis of the position that generated it. |
| BR-014-06 | Current dividend yield per asset: trailing 12-month income over current price. Reported alongside yield on cost and never conflated with it. |
| BR-014-07 | Year-over-year income growth for the scope. |
| BR-014-08 | Earnings are recognised at pay date, consistent with SPEC-012 and SPEC-013. |
| BR-014-09 | Announced-but-unpaid earnings appear as expected upcoming income, clearly separated from received income, and never added to received totals. |
| BR-014-10 | Sourcing caveat: the free quote tier excludes dividend data and B3's Eventos Provisionados API is B2B-only, so BR-014-09 ships degraded in v1. Received earnings are unaffected — they come from the B3 Movimentação extract, the authoritative source. Where forward-looking data is unavailable the UI says so rather than showing an empty section that reads as "no upcoming income". |
| BR-014-11 | Earnings derive from active transactions of the earning types; unclassified rows are excluded (SPEC-006, BR-006-03). |
| BR-014-12 | Wallet-scoped income uses allocated quantities at the time each earning was received, not current allocations — reallocating a holding today must not rewrite last year's income history. |
| BR-014-13 | Total earnings for a period must equal the earnings component of the Portfolio Value decomposition (SPEC-013, BR-013-02) for the same scope and period. |
Acceptance Criteria
- Total proventos for a period are correct and broken out by all five types.
- A bonificação fraction's auction cash appears as leilão de frações, never as dividend or rendimento; a split or grupamento fraction's auction cash is a sale (SPEC-007 BR-007-04b), not a provento.
- JCP is reported separately from dividends.
- The monthly income chart is correct, and the 12-month moving average smooths a quarterly payer as expected.
- Income breaks down by every grouping dimension, ranked, with shares summing to 100%.
- Grouping by wallet gives correct monthly income for a single wallet — Marina's core question.
- Yield on cost matches a hand-computed figure for an asset with a known purchase history.
- Yield on cost and current yield differ appropriately for an asset whose price has risen substantially, and are never conflated in the UI.
- Year-over-year income growth is correct, including a year with no prior-period income.
- Earnings are recognised at pay date, not ex-date.
- Announced-but-unpaid earnings, where available, appear separately and are excluded from received totals.
- Where forward-looking data is unavailable, the UI states that rather than implying no upcoming income.
-
unclassifiedearning rows are excluded; classifying one changes the totals. - Reallocating a holding between wallets today does not change last year's wallet income.
- Total earnings equal the earnings driver in the Portfolio Value report for the same scope and period.
- Report runs at portfolio and wallet scope with every grouping (SPEC-011).
- A scope with no earnings renders an empty state, not a zero-filled chart.
Out of Scope
- Tax treatment of earnings: IR on JCP, exemption rules for dividends and FII rendimentos, DARF, or IR reporting.
- Dividend forecasting or projection from historical patterns.
- Dividend safety analysis, payout ratios or any fundamental screening.
- Dividend reinvestment modelling — earnings are not assumed reinvested (SPEC-012, BR-012-08).
- Alerts on announced earnings.
- Ex-date and record-date tracking beyond what the received record carries.
- Income from fixed income accrual — accrued interest is value change (SPEC-009), not a provento.
- Sourcing a forward-looking dividend calendar (PRD Q8).
Decision Logs
| # | Decision | Alternatives considered | Rationale |
|---|---|---|---|
| DL-014-01 | Yield on cost is the headline income measure. | Current dividend yield as the headline | For an investor holding a position for years, current yield describes what a new buyer would get — not what they are earning. Yield on cost is the number that answers "is the plan working?". Current yield is still shown, because it is what comparisons against other assets need. |
| DL-014-02 | JCP reported separately from dividends. | Combine as "proventos" | They are taxed differently in Brazil (JCP is withheld at source, dividends are currently exempt), so a combined figure cannot answer the tax question users will inevitably ask. Separating costs nothing and preserves the information. |
| DL-014-03 | 12-month moving average alongside monthly bars. | Monthly bars only; quarterly aggregation | Brazilian dividend payers are highly irregular — quarterly, semi-annual, one-off. Raw monthly bars look chaotic and hide the trend, which is the actual question. Quarterly aggregation would hide FII monthly rendimentos, which are the smooth part. |
| DL-014-04 | Earnings recognised at pay date, not ex-date. | Ex-date recognition | Pay date is when the money actually arrives and is what the B3 extract records. Ex-date recognition would show income the user has not received, and would disagree with their bank statement. |
| DL-014-05 | Wallet income uses allocations as they stood at the time of each earning. | Use current allocations throughout history | Otherwise moving a holding into the Retirement wallet today would retroactively rewrite years of that wallet's income history — a number that changes when nothing about the past changed. This is the same reasoning as SPEC-013 DL-013-05. |
| DL-014-06 | Ship the upcoming-income view degraded rather than dropping it. | Cut it from v1 entirely; delay v1 for a paid data source | Received income — the bulk of the report's value — is fully available from B3 extracts. Cutting the forward view entirely loses a real feature for users whose data does happen to be available; showing an empty section without explanation would read as "you have no upcoming income", which is worse than saying nothing is known. |
| DL-014-07 | Cross-report equality with Portfolio Value asserted. | Let each report compute earnings independently | Same reasoning as SPEC-013 DL-013-06: two reports disagreeing about how much income was received is a trust-destroying defect, and an automated assertion catches it in CI. |
| DL-014-08 | Leilão de frações is its own provento type. (2026-09-15, #113.) | Record it as rendimento; record it as dividend | IRPF guidance declares a bonificação fraction's auction proceeds as exempt income under Outros, distinct from dividends and FII rendimentos. Folding it into either would inflate that type's totals and its dividend yield with money that is neither — the same reasoning that keeps JCP apart (DL-014-02). |