trader.persistence.focus_list module

Repository for focus_list (issue #44).

A history, not a pure set: trading_day anchors each row to the session trader build-focus-list computed it for (Decision 7), so the intraday read _build_universe needs every cycle is a plain, indexed (trading_day, symbol) lookup — never a scan over watchlist_events’ JSON-in-text note, which is audit-only and never a worklist (see that module’s own docstring).

Structurally typed against discovery.candidates.Candidate via FocusListCandidate below, rather than importing it: persistence/ stays free of a discovery/ dependency, the mirror image of discovery/scan.py’s own duck-typed index_repository/consensus_repository parameters that keep that package free of a persistence/ dependency.

class trader.persistence.focus_list.FocusListCandidate(*args, **kwargs)[source]

Bases: Protocol

What upsert reads off each candidate. discovery.candidates.Candidate already has every one of these attributes, so it satisfies this structurally with no import needed on either side.

Declared as read-only @property members, not plain attributes: a plain Protocol attribute requires the implementer to support both read and write, and Candidate is frozen=True — assignment raises. upsert only ever reads these fields, so the Protocol should ask for no more than that, and mypy’s structural check enforces the read/write distinction even though it is invisible in an ad hoc, single-module reproduction.

property symbol: str
property theme: str
property origin: str
property headline: str | None
property url: str | None
property recommendation_mean: float | None
class trader.persistence.focus_list.FocusListRepository(session_factory)[source]

Bases: object

Writes and reads focus_list.

Parameters:

session_factory (sessionmaker[Session])

upsert(trading_day, candidates, *, generated_at)[source]

Record candidates as the focus list for trading_day.

UNIQUE(trading_day, symbol) is what makes a same-day rerun (a manual retry after a partial build-focus-list failure) update the existing row rather than fail on the constraint or leave a stale duplicate readable — the rerun’s data replaces the first attempt’s.

Parameters:
Return type:

None

symbols_for(trading_day)[source]

Every symbol recorded under trading_day.

[] when the batch never ran, or ran and stamped a different day — the same “this source contributes nothing” degrade themes: [] and a never-run scan-universe already give, extended to a third source (Decision 7).

Parameters:

trading_day (date)

Return type:

list[str]