Bug description
Holding.gapfill synthesizes holdings for dates that have no real snapshot (weekends, market holidays, and gaps between trades) by carrying the previous day's values forward. It carries qty, price, currency, and amount, but it does not carry cost_basis, so every synthesized day is persisted with cost_basis = nil.
Both Holding::ForwardCalculator and Holding::ReverseCalculator compute cost_basis for the real, trade-driven dates, but each gap-filled day in between loses it. Holding#avg_cost prefers the stored cost_basis when present and otherwise falls back to a per-holding trade query. As a result, gap-filled days either report a different average cost through the fallback path, or report no trend at all (for example provider accounts that have no trades to fall back on). The visible effect is an inconsistent cost basis across consecutive days for the same position.
To Reproduce
Steps to reproduce the behavior:
- Create an investment account and add a single buy trade for a security (for example 100 shares at 100).
- Let the holdings materialize so the calculator fills the days that follow the trade date (which have no market price of their own).
- Inspect the materialized holdings for that security across consecutive days.
- The trade date holds a
cost_basis, while the carried-forward days have cost_basis = nil, so their average cost and trend differ from the surrounding real days.
Expected behavior
Gap-filled days should keep the same cost basis as the most recent real day, since cost basis only changes when a buy trade happens and gap-filled days have none. Average cost and trend should stay consistent across consecutive days for the same position.
Bug description
Holding.gapfillsynthesizes holdings for dates that have no real snapshot (weekends, market holidays, and gaps between trades) by carrying the previous day's values forward. It carriesqty,price,currency, andamount, but it does not carrycost_basis, so every synthesized day is persisted withcost_basis = nil.Both
Holding::ForwardCalculatorandHolding::ReverseCalculatorcomputecost_basisfor the real, trade-driven dates, but each gap-filled day in between loses it.Holding#avg_costprefers the storedcost_basiswhen present and otherwise falls back to a per-holding trade query. As a result, gap-filled days either report a different average cost through the fallback path, or report no trend at all (for example provider accounts that have no trades to fall back on). The visible effect is an inconsistent cost basis across consecutive days for the same position.To Reproduce
Steps to reproduce the behavior:
cost_basis, while the carried-forward days havecost_basis = nil, so their average cost and trend differ from the surrounding real days.Expected behavior
Gap-filled days should keep the same cost basis as the most recent real day, since cost basis only changes when a buy trade happens and gap-filled days have none. Average cost and trend should stay consistent across consecutive days for the same position.