trading_agent.risk.risk_engine
¶
The risk engine: plain code that every order must pass before it reaches a broker.
No model can change it, talk to it or skip it. Before each order it reads the limits from TradingRules.md and the account from UBI, checks the loss limits and fires the kill switch when one is reached, and then checks the order against every rule, collecting every reason it fails so the model can fix the order in one step.
Two principles shape it. When the limits or the account cannot be read, the answer is no. And an order that only reduces or closes a desk's own position is never blocked by a limit or a halt, because getting out must always be possible.
Typical usage example:
decision = await engine.evaluate(order_request) if decision.approved: ...
RiskDecision
dataclass
¶
The engine's answer for one order.
Attributes:
| Name | Type | Description |
|---|---|---|
approved |
bool
|
Whether the order may be sent. |
reasons |
tuple[str, ...]
|
Why it was refused; empty when approved. |
estimated_margin |
float
|
The margin the order is estimated to block, in rupees. |
RiskServices
dataclass
¶
The parts the engine reads from and writes to.
Attributes:
| Name | Type | Description |
|---|---|---|
limits_reader |
TradingLimitsReader
|
Reads the limits from TradingRules.md. |
snapshot_reader |
AccountSnapshotReader
|
Reads the account from UBI. |
ledger |
RiskLedger
|
The engine's memory. |
kill_switch |
KillSwitch
|
Halts and flattens the account. |
margin_estimator |
MarginEstimator
|
Estimates an order's margin. |
exchange_hours |
ExchangeHours
|
Says whether an exchange is open. |
RiskEngine
¶
Checks every order against the limits and the account.
Creates the engine.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
services
|
RiskServices
|
What the engine reads and writes. |
required |
event_bus
|
EventBus
|
Where every decision is published. |
required |
ownership_grace
|
timedelta
|
How long a desk keeps a claim on an instrument it does not hold yet. |
required |
clock
|
Callable[[], datetime] | None
|
The source of the current time, replaceable in tests; None for the real clock. |
None
|
Source code in src/trading_agent/risk/risk_engine.py
evaluate
async
¶
Decides whether one order may be sent, and records an approval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
order
|
OrderRequest
|
The order. |
required |
Returns:
| Type | Description |
|---|---|
RiskDecision
|
The decision, with every reason when refused. |
Source code in src/trading_agent/risk/risk_engine.py
check_account
async
¶
Reads the account and fires the kill switch at a loss limit, without an order.
The market watcher calls this every cycle, because a losing position can reach the daily or weekly loss limit while no desk is placing orders.
Returns:
| Type | Description |
|---|---|
AccountSnapshot | None
|
The account now, or None when UBI cannot be read. |
Source code in src/trading_agent/risk/risk_engine.py
desk_day_pnl
¶
Today's profit or loss of each desk, from the instruments it owned today.
An instrument counts for the desk that last claimed it today, even after the position was closed and the claim released, so a loss taken on a closed trade still counts against the desk that took it. Positions no desk claimed, such as the user's own, count for no desk.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
snapshot
|
AccountSnapshot
|
The account now. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, float]
|
Each desk's profit or loss, keyed by desk name; desks with nothing today are absent. |