auradefi 0.1.1
PyPI GitHub

PnLReport

auradefi.accounting.report · source

PnL as of one instant, under one costing method.

as_of_ms is the instant the caller asked about, carried through verbatim; it is not read from a clock and need not coincide with any event. method is the method the state was replayed under. A report cannot be re-costed, because the lot ledger behind it has already been consumed one particular way.

realized is the exact sum of the disposals whose realised amount is KNOWN, and missing_realized_count is how many were left out of that sum; a caller that ignores the count will silently read an understated total, which is why the count is not optional. unrealized is None if ANY held asset is uncertain, so it is the conservative whole-portfolio figure rather than a partial sum: the per-asset detail in per_asset is where the known parts stay visible.

open_lots is sorted by (asset_id, opened_at_ms, lot_id): a total order with no ties, so the wire output is byte-stable across runs even when two lots were opened in the same millisecond.

UNDER method="acb", unrealized AND TaxLot.cost_basis DO NOT AGREE, and that is correct. ACB costs from a per-asset running POOL, so a disposal consumes pool_cost × take/pool and leaves the pool reduced proportionally; the lots behind it are untouched and keep reporting their own remaining basis, because they stay ground truth for lot-level reporting (docs/internal/DECISIONS.md, "ACB pooling"). Buy 1 at 10, 1 at 20 and 1 at 15, sell one, and the pool holds 30 while the surviving lots sum to 35: a permanent, intended gap of 5.

Summing TaxLot.cost_basis and comparing it with what unrealized implies is therefore the wrong check, and it is an easy one to reach for. basis_source names which cost unrealized actually subtracted, and unrealized_basis and open_lots_basis expose both figures, so the difference is inspectable rather than something a caller has to reverse-engineer and mistake for a bug.

Fields

as_of_msint
methodstr
realizedMoney
missing_realized_countint
unrealizedMoney | None
per_assetMapping[str, AssetPnL]
open_lotstuple[TaxLot, ...]
flagstuple[str, ...]default ()
basis_sourcestrdefault 'lots'
unrealized_basisMoney | Nonedefault None
open_lots_basispropertySum of every open lot's `cost_basis`, or `None` if any is.

__init__

__init__(self, as_of_ms: int, method: str, realized: Money, missing_realized_count: int, unrealized: Money | None, per_asset: Mapping[str, AssetPnL], open_lots: tuple[TaxLot, ...], flags: tuple[str, ...] = (), basis_source: str = lots, unrealized_basis: Money | None = None) -> None

Parameters

as_of_msrequired, int

none

methodrequired, str

none

realizedrequired, Money

none

missing_realized_countrequired, int

none

unrealizedrequired, Money | None

none

per_assetrequired, Mapping[str, AssetPnL]

none

open_lotsrequired, tuple[TaxLot, ...]

none

flagsoptional, tuple[str, ...], default ()

none

basis_sourceoptional, str, default 'lots'

none

unrealized_basisoptional, Money | None, default None

none