trader.persistence.snapshots module

Repository for account/position snapshots (requirements §10).

The only repository wired in Slice 1. Later slices add repositories for trades, decisions, watchlist events, and backtest runs against the same schema.

class trader.persistence.snapshots.SnapshotRepository(session_factory)[source]

Bases: object

Reads and writes portfolio snapshots.

Parameters:

session_factory (sessionmaker[Session])

save_snapshot(account, positions, captured_at=None)[source]

Persist an account snapshot and its positions. Returns the new id.

Parameters:
  • account (Account)

  • positions (Sequence[Position])

  • captured_at (datetime | None)

Return type:

int

latest_snapshot()[source]

The most recently captured snapshot, with positions loaded.

Return type:

AccountSnapshot | None

snapshots_in_range(start=None, end=None)[source]

Snapshots captured within [start, end] (both bounds inclusive), ordered oldest to newest — feeds trader.performance.benchmark. account_return (issue #19), which compares the FIRST snapshot in the list against the LAST.

Both bounds inclusive, unlike the news archive’s half-open window: that window exists to stop a decision seeing a headline it is being scored for anticipating, which has no analogue here — there is no lookahead risk in including a snapshot captured exactly at end.

None for either bound means “no floor”/”no ceiling” — omitting both returns every snapshot ever captured.

Positions are deliberately NOT eager-loaded here (contrast latest_snapshot): every caller of this method reads only captured_at/portfolio_value, so selectinload would cost an extra query per snapshot for data nothing uses.

Parameters:
  • start (datetime | None)

  • end (datetime | None)

Return type:

list[AccountSnapshot]

snapshots_since(start=None)[source]

Snapshots in chronological order, oldest first (§12.3 equity curve).

id is the tiebreak for equal captured_at values, ascending to match latest_snapshot’s descending tiebreak — both resolve a tie in favour of the row inserted later. Positions are deliberately not eager-loaded here: callers of this method (the equity curve) only read scalar account-level columns, so pulling position_snapshots along for every row would cost a join no caller needs.

Parameters:

start (datetime | None)

Return type:

Sequence[AccountSnapshot]