Skip to content

Bug: Gap-filled holdings lose cost basis, breaking average cost and trend on carried-forward days #2475

Description

@cleanjunc

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:

  1. Create an investment account and add a single buy trade for a security (for example 100 shares at 100).
  2. Let the holdings materialize so the calculator fills the days that follow the trade date (which have no market price of their own).
  3. Inspect the materialized holdings for that security across consecutive days.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions