Risk envelope
A RiskEnvelope is a declarative spec of the limits your strategy must operate inside. The runtime enforces it before any order leaves your machine. Strategies declare the envelope as a class attribute; the runtime reads it once at deploy time and gates every ctx.place() against it.
Available limits
from decimal import Decimal
from banger.risk import RiskEnvelope
class MyStrategy(Strategy):
risk = RiskEnvelope(
max_position_usd=Decimal("200"),
max_daily_loss_usd=Decimal("500"),
max_open_positions=5,
max_concentration_pct=0.25,
max_orders_per_minute=10,
)
...| Field | Type | Effect |
|---|---|---|
max_position_usd | Decimal | Single-position notional cap. Exceeding it raises RiskViolation. |
max_daily_loss_usd | Decimal | Halts new orders for the rest of the 24h window once cumulative loss crosses this. |
max_open_positions | int | Cap on simultaneous open positions across all venues. |
max_concentration_pct | float (0–1) | Max share of portfolio any one position can hold. |
max_orders_per_minute | int | Token-bucket rate limit on order placement. |
All fields are optional. Unset = no limit. Set what you need.
What happens on a violation
When ctx.place() would breach an envelope limit:
- The order is not placed.
RiskViolationis raised.- The runtime logs a
strategy.risk_violationevent with the reason. - The next event handler invocation continues normally.
Strategies can also gate proactively with ctx.can_open_position() — true if the envelope currently permits a new position.
Why declarative?
Two reasons. First, it’s auditable: the envelope is a single block of declarative config that anyone reading the file can see. No buried ifstatements scattered through your strategy code. Second, it’s consistent across paper and live — the same gate runs in both modes, so you can’t accidentally ship a strategy whose risk only exists in paper.
What the envelope doesn’t do
- It doesn’t hedge. If you want a market-neutral exposure, place the hedge leg yourself.
- It doesn’t close existing positions when a limit gets tighter — only blocks new ones.
- It doesn’t enforce the same limits across strategies. Account-wide aggregation is on the roadmap (per-strategy is what v0 ships).