trader.risk.guardrails module¶
Risk guardrails (requirements §8).
Checked here and never inside a strategy or evaluator. The app enforces limits on the thing being limited, rather than asking it to limit itself — the same principle as the Slice 1 safety gate.
- class trader.risk.guardrails.ExposureCounter(committed)[source]¶
Bases:
objectA mutable running total of capital committed this cycle.
Seeded before the symbol loop from open positions plus already-pending entry orders, then advanced by commit() every time an entry is actually submitted — so the next symbol in the same cycle sees the reduced headroom.
Mutable on purpose. A total computed once and never updated is blind to what the cycle itself is spending, which is precisely how twelve limit buys left one cycle on 2026-08-04, and the same mistake the discovered-position cap made before it.
- Parameters:
committed (Decimal)
- committed: Decimal¶
- class trader.risk.guardrails.Guardrails(config)[source]¶
Bases:
objectPosition-size and daily-loss limits.
- Parameters:
config (PipelineConfig)
- daily_loss_breached(account)[source]¶
Whether today’s drawdown has reached the configured limit.
Uses the broker’s own equity and last_equity rather than reconstructing realized P/L from trade history, which would have to model partial fills and corporate actions to arrive at the same number.
- Parameters:
account (Account)
- Return type:
bool
- check_entry(account, symbol, proposed_quantity, price, *, is_discovered=False, discovered_positions_held=0, max_discovered_positions=0, exposure=None)[source]¶
Approve, shrink, or reject a proposed entry.
The discovery arguments are keyword-only with defaults so that every pre-Slice-3d caller behaves exactly as before: a fixed ticker is never subject to the discovered cap.
- Parameters:
account (Account)
symbol (str)
proposed_quantity (int)
price (Decimal)
is_discovered (bool)
discovered_positions_held (int)
max_discovered_positions (int)
exposure (ExposureCounter | None)
- Return type:
- class trader.risk.guardrails.RiskDecision(allowed, reason, approved_quantity, code=None)[source]¶
Bases:
objectThe outcome of a guardrail check, and why.
A rejection is not an error — it is a recorded Decision explaining why the trade did not happen. reason is therefore populated on every path, including approval.
code is one of the module’s REASON_* constants on a refusal (allowed=False), and None on every approval path — including a shrink, which is not a refusal. decisions.rejection_reason (issue #21) is written from this, never from reason’s free text.
- Parameters:
allowed (bool)
reason (str)
approved_quantity (int)
code (str | None)
- allowed: bool¶
- reason: str¶
- approved_quantity: int¶
- code: str | None¶