๐งญ Price Resolution
๐ก Purpose
LibreFolio uses one unified resolver as the primary valuation source for open positions, NAV, lot valuation, chart price lines, and data-quality flags. The resolver answers one daily question:
It is implemented by AssetPriceSeries.resolve(t) and built from two observation classes:
MARKET: asset-systemPriceHistory.closeTRADE: transaction-implied prices from BUY/SELL and priced ADJUSTMENT rows
๐งฎ Daily Tier Cascade
For each asset and date, observations are collapsed to one mark per day:
flowchart TD
A["Resolve asset a on date t"] --> B{"Market quote on t?"}
B -- "yes" --> C["MARKET"]
B -- "no" --> D{"Trade observations on t?"}
D -- "yes" --> E["TRADE_AVG"]
D -- "no" --> F{"Any observation before t?"}
F -- "yes" --> G["CARRIED (LOCF)"]
F -- "no" --> H["MISSING"]
The public engine schema maps resolver marks to valuation-source labels:
| Resolver source | Origin | Portfolio valuation source |
|---|---|---|
MARKET |
Same-day real quote | MARKET_PRICE |
TRADE_AVG |
Same-day transaction mark | LAST_TRADE_PRICE |
CARRIED from MARKET |
Stale real quote | MARKET_PRICE |
CARRIED from TRADE |
Stale transaction mark | LAST_TRADE_PRICE |
MISSING |
No observation on or before date | MISSING |
No legacy cascade
Current shipped code does not use a separate market โ last BUY โ seed cost valuation path. Trade-origin marks are observations inside the unified resolver; WAC remains cost basis, not valuation price.
๐ Currency and Scale
Resolver marks stay in their native currency. Consumers convert the mark at the valuation date:
This matters for carried marks: a quote or trade observed on \(s<t\) is translated using FX at \(t\), not FX at \(s\).
Cost basis uses different timing. Acquisition cost is pinned to the transaction date:
All resolver observations live on the market quote axis, including quote_base_quantity:
BUY/SELL unit prices and priced ADJUSTMENT overrides are multiplied by quote_base_quantity before entering the resolver, so bond-like assets quoted per 100 nominal units compare on the same axis as PriceHistory.close.
๐ท๏ธ Estimated and Stale
estimated=True means the resolved value is TRADE-origin:
A carried real market quote is stale but not estimated. Staleness is represented separately through BackwardFillInfo:
price_backward_fill.actual_rate_date stores the observation date and days_back stores the LOCF age. Portfolio data-quality warnings evaluate the valuation-date state, not a historical union of all past carried/estimated days.
โ ๏ธ Missing Marks
MISSING means there is no market or trade observation on or before the valuation date. In the portfolio engine, that position cannot contribute market value until a mark exists. In lots analysis, estimated-at-cost mode can still value open lots at cost when the asset has no market price series at all; see FIFO Lot Analysis.
Portfolio warnings are evaluated as of the valuation date. Trade-origin valuations older than the 14-day grace period feed the โassets valued at cost / no market price for more than two weeksโ warning; an asset that later receives a real market quote clears the warning.
๐ Related
- ๐ผ NAV โ consumes resolver marks for market value
- ๐ Book Value โ cost-basis side, independent from marks
- ๐ Net Annualized Return โ annualizes returns built on resolver valuations
- โ๏ธ Portfolio Engine โ full model