trader.decisions module¶
Reading an earlier decision back, for a strategy that may reuse it.
A Protocol for the same reason BrokerAdapter, LlmProvider and NewsProvider are ones: the thing that needs a previous decision is LlmStrategy, and a strategy must not depend on SQLAlchemy, a session factory or the decisions table’s column names. DecisionRepository satisfies this shape; a test satisfies it with a dict.
`StoredDecision.inputs` is deliberately an opaque `dict`, and no code in this module knows a single key inside it. LlmStrategy is the only thing that writes decisions.inputs_json for an LLM strategy, so it is the only thing that should read it: one owner for the key names. A repository that unpacked news_fingerprint, signal_action and bar_summary into typed fields would be a second definition of that schema, free to drift from the first — the bar_width(interval) failure CLAUDE.md already records twice. The price is that this module cannot type-check the contents; the alternative price is a silent disagreement about what a stored decision said, which is worse.
- class trader.decisions.DecisionMemory(*args, **kwargs)[source]¶
Bases:
ProtocolThe most recent decision a strategy recorded for a symbol.
- latest_decision(strategy_id, ticker)[source]¶
The newest recorded decision for this (strategy_id, ticker) pair.
None when there is none — including when inputs_json is absent or unreadable, because a decision whose inputs cannot be read is not evidence of anything a caller could act on.
Must never raise on ordinary data. A caller reads this to decide whether it can avoid work; a failure here has to degrade into doing the work, never into aborting a cycle.
- Parameters:
strategy_id (str)
ticker (str)
- Return type:
StoredDecision | None
- class trader.decisions.StoredDecision(decision_id, decided_at, action, reasoning, inputs)[source]¶
Bases:
objectOne row of the decisions table, as a value object.
action is what the pipeline did, which is not always what the strategy said — a veto can overwrite it (veto_class), and inputs[“signal_action”] is the strategy’s own opinion. A reader deciding whether an earlier answer may be carried forward wants the latter; both are reachable from here, and conflating them is how a rejected row would read as a refusal to trade.
- Parameters:
decision_id (int)
decided_at (datetime)
action (str)
reasoning (str)
inputs (dict[str, object])
- decision_id: int¶
- decided_at: datetime¶
- action: str¶
- reasoning: str¶
- inputs: dict[str, object]¶