An unknown cost basis is not zero
Source: clawd scripts/portfolio-pricer.py and scripts/vix_portfolio_advisor.py,
2026-09-20. Found when adding a CBA Employee Share Plan holding.
What was wrong
The pricer computed per-position P&L as:
entry = h["entry"]
pnl = (price - entry) * qty
Adding a holding whose cost basis the registry does not state (an employee share plan)
forced a choice: invent an entry, or omit the holding. Setting entry = 0 was the
obvious-looking path and it is badly wrong: (152.43 - 0) * 170 reports 25,913.10 of
profit on a position whose gain is unknown. A zero cost basis is a claim that the
shares were free.
The same null then crashed the advisor:
TypeError: '>' not supported between instances of 'NoneType' and 'int'
at pnl_pct = ((price / entry) - 1) * 100 if entry > 0 else 0, and a second time at
value = price * qty when the quote lookup missed and price fell back to entry.
The rule
A field that can be genuinely absent needs three things: it must be allowed to be null, every arithmetic site must handle null, and the output must say "unknown" rather than a number.
- Null, never zero, for an unknown quantity.
entry: Nonein the data,nullin JSON. Zero is a value; unknown is the absence of one. They are not interchangeable and the failure mode is a fabricated figure, not an error. - Exclude from aggregates, do not substitute. A position with unknown P&L adds
nothing to
total_pnl. A position with unknown value adds nothing tototal_valuerather than contributing zero, and is listed in anunpricedfield so the gap is visible. - Skip the rules that need the missing field. Stop-loss and take-profit compare against the cost basis. With no basis there is no comparison to make, so the position is HOLD with a reason that says the basis is unknown. Emitting HOLD by accident is fine; emitting REVIEW_EXIT from a None comparison is not.
- Fix the fallback chain too.
price = entryis fine when entry exists and returns None when it does not. Order the fallbacks (live quote, then snapshot, then basis) and skip the position when all are absent. - A zero-quantity row is not a position. A closed plan shows balance 0 for bookkeeping. Include it in the verification scope and it can never be verified, so it blocks every run forever. Filter on quantity, not on presence.
Check for this class
- Which numeric fields can legitimately be absent? Search every arithmetic use of each.
- Does any default coerce absence to zero (
h.get("entry", 0),or 0,int(x or 0))? - Do aggregates distinguish "contributes nothing" from "contributes zero"?
- Does the output label unknown values as unknown, or print a number a reader would trust?
- Does a rule that compares against a possibly-absent field guard for absence first?