Skip to main content

An unknown cost basis is not zero

entry

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: None in the data, null in 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 to total_value rather than contributing zero, and is listed in an unpriced field 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 = entry is 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?