trader.chat.context module

Rendering database rows into plain text a model prompt can carry.

Every function here is defensive on purpose: ChatService calls these against whatever ORDER BY … LIMIT handed back, and a chat turn must never crash because a row was empty or a JSON blob was oversized. This is the same grounding discipline trader.llm.prompt.build_messages uses for a trading decision, applied to a conversational answer instead.

trader.chat.context.decision_context(decision)[source]

The exact recorded row a decision-explain answer must be grounded in.

Everything here is what the pipeline actually wrote to decisions — the action taken, what the strategy said (reasoning), what actually happened (outcome_note), and what it saw (inputs_json) — never a fresh re-evaluation of the symbol. Issue #14’s whole point is that the answer traces back to this exact row.

reasoning and outcome_note are deliberately separate lines (issue #25): before outcome_note existed, a buy/sell’s reasoning held an execution summary instead of the strategy’s rationale, so a decision-explain answer for a filled trade could only ever describe what happened, never why. outcome_note is None for a hold/reject/error, which have nothing to execute, so it prints as “not executed” rather than a blank line that reads like a recording gap.

Parameters:

decision (Decision)

Return type:

str

trader.chat.context.performance_context(trips, total_realized_pl)[source]

Closed round trips and the running realized P/L, newest first.

Parameters:
  • trips (Sequence[RoundTrip])

  • total_realized_pl (Decimal)

Return type:

str

trader.chat.context.positions_context(snapshot)[source]

The latest account/position snapshot, or an honest “none yet”.

Parameters:

snapshot (AccountSnapshot | None)

Return type:

str

trader.chat.context.trades_context(trades)[source]

Recent submitted orders, newest first.

Parameters:

trades (Sequence[Trade])

Return type:

str