SPEC 009 Valuation Engine - rafaelfqueiroz/allmywallet GitHub Wiki
SPEC-009 โ Valuation Engine
|
|
| Status |
Draft |
| PRD source |
ยง6.5, ยง6.7, CR-1, FR-6.14, DM-4 |
| Depends on |
SPEC-007 (positions), SPEC-008 (prices & index series) |
| Required by |
SPEC-012, SPEC-013, SPEC-015, SPEC-017, SPEC-019, SPEC-020, SPEC-021 |
| Milestone |
M4 |
Description
Turning positions into money: what each holding is worth today, what the portfolio was worth on any past date, and the daily snapshots every report reads from.
Different asset classes get different treatment because the available data genuinely differs, and the spec is explicit about which figures are observed prices and which are estimates:
- Listed assets (stocks, FIIs, BDRs, ETFs) โ marked at the observed market price.
- Tesouro Direto โ marked to market from the daily Tesouro Transparente file. A real observed price.
- CDB / LCI / LCA โ no public daily price exists for bank paper, so value is accrued from the contracted indexer. This is an estimate of carrying value and must be labelled as one.
Snapshots are persisted so reports do not recompute five years of history on every page load โ but they remain derived data, always rebuildable from the ledger, prices and index series.
Business rules
Listed assets
| ID |
Rule |
| BR-009-01 |
Value = quantity ร latest available price. Current value uses LatestQuote; historical values use the official close from PriceQuote. |
| BR-009-02 |
Historical valuation never uses an intraday quote (SPEC-008, BR-008-10). |
| BR-009-03 |
On a date with no quote (holiday, suspension, pre-listing), the last available close carries forward, and the carry-forward is visible rather than silent. |
| BR-009-04 |
Unrealized gain = current value โ (quantity ร average cost from SPEC-007). |
Tesouro Direto
| ID |
Rule |
| BR-009-05 |
Marked to market from the daily Tesouro Transparente price. This is an observed price, not an estimate, and is not flagged as one. |
| BR-009-06 |
Where a title has both buy and sell prices, the sell price is used โ it is what the holder would realise. |
CDB / LCI / LCA โ accrual
| ID |
Rule |
| BR-009-07 |
No public daily price exists, so value accrues daily from the contracted indexer captured in FixedIncomeContract (SPEC-005, BR-005-06). |
| BR-009-08 |
% of CDI: daily CDI (BCB SGS 12) scaled by the contracted percentage, compounded on business days. |
| BR-009-09 |
Prefixado: contracted annual rate compounded pro-rata on business days, 252-day convention. |
| BR-009-10 |
IPCA +spread: monthly IPCA (SGS 433) applied to principal, plus the spread accrued on business days. |
| BR-009-11 |
Accrued values are flagged as estimated in the UI with the basis shown on hover (CR-1). They approximate carrying value, not what a secondary-market sale would fetch. |
| BR-009-12 |
All fixed income figures are gross of IR and IOF in v1, stated in the UI. |
| BR-009-13 |
If the indexer or rate cannot be determined, the position is valued at cost, flagged as needing input, and surfaced in the "Needs attention" queue. It is never silently valued at zero or omitted. |
| BR-009-14 |
Accrual runs once daily. Fixed income has no intraday behaviour and showing one would be fiction. |
| BR-009-15 |
A matured instrument stops accruing at maturity and is valued at its maturity value until a redemption transaction is recorded. |
Snapshots & precision
| ID |
Rule |
| BR-009-16 |
DailyValuationSnapshot is persisted per user per day, holding total value, net contributions, earnings to date, and the breakdown by asset class. |
| BR-009-17 |
Snapshots are derived cache, never authoritative. If a snapshot disagrees with recomputation from the ledger, the ledger wins. |
| BR-009-18 |
A backdated or edited transaction invalidates and rebuilds snapshots from that date forward (SPEC-006, BR-006-14). |
| BR-009-19 |
Exact decimals throughout; rounding at display only (CR-2, CR-3). |
| BR-009-20 |
BRL only. BDRs trade in BRL on B3, so no FX layer exists. The consequence is stated in the UI: a BDR's BRL return blends the underlying asset's move with the USD/BRL move, and v1 does not decompose the two. |
Acceptance Criteria
Out of Scope
- Quote and index acquisition (SPEC-008).
- Cost basis and realized gain (SPEC-007).
- Return calculation โ TWR, XIRR, benchmark comparison (SPEC-012).
- IR and IOF modelling to produce net fixed-income values (PRD Q4) โ v1 is explicitly gross.
- Secondary-market marking of CDB/LCI/LCA; no such price source exists.
- Investment fund quota values (fundos are v2).
- Currency conversion or FX decomposition of BDR returns.
- Liabilities, cash balances or non-investment assets โ the reason the report is "Portfolio Value" and not "Net Worth".
Decision Logs
| # |
Decision |
Alternatives considered |
Rationale |
| DL-009-01 |
Fixed income accrued from contracted rate, not held at cost. |
Value CDB/LCI/LCA at cost until redemption |
Holding at cost makes a large slice of a Brazilian portfolio look flat for years, understating both Portfolio Value and performance. Accrual is an estimate, but a well-founded one โ and labelling it beats being confidently wrong in the other direction. |
| DL-009-02 |
Accrued values explicitly labelled estimated. |
Present all values identically |
The PRD's "honest over impressive" principle. A CDB's accrued value is not a market price, and blending it invisibly with observed prices would misrepresent precision the product does not have. |
| DL-009-03 |
Gross of IR/IOF in v1. |
Model the regressive IR table and IOF |
Accurate net modelling requires care disproportionate to v1, and a wrong net figure is worse than an explicitly gross one โ users would trust it and be wrong at redemption. Stated in the UI; data model supports adding it (PRD Q4). |
| DL-009-04 |
Tesouro at sell price, not buy price. |
Buy price; midpoint |
The sell price is what the holder would actually realise. Using the buy price would systematically overstate the position by the spread. |
| DL-009-05 |
Unreadable fixed income valued at cost and flagged, never omitted. |
Omit from the total; value at zero |
Omitting silently understates the portfolio, and the user cannot see why the total is wrong. Cost is a defensible floor, and the flag makes the gap actionable. |
| DL-009-06 |
Snapshots persisted but non-authoritative. |
Compute on demand; treat snapshots as authoritative state |
On-demand computation over five years of daily data cannot meet the 3s report budget. Treating them as authoritative would make every past calculation bug a permanent data problem. Cache-plus-rebuild gets both. |
| DL-009-07 |
No FX layer; BDR return not decomposed. |
Decompose BDR returns into asset move and USD/BRL move |
Every in-scope asset settles in BRL, so an FX layer adds real complexity for no v1 benefit. The consequence is real โ a BDR's BRL return blends two effects โ so it is disclosed rather than hidden. |
| DL-009-08 |
Business-day compounding for CDI and prefixado. |
Calendar-day compounding |
Brazilian fixed income conventionally accrues on business days with a 252-day year. Calendar-day accrual would drift measurably from the issuer's own figure over a multi-year holding. |