Policy contracts¶
Policies are regular Python objects that implement
openpit.pretrade.Policy. They return business decisions; they should
raise exceptions only for programming errors or unexpected runtime failures.
Start-stage checks¶
Implement check_pre_trade_start for fast admission checks.
import openpit
class BlockedAccountPolicy(openpit.pretrade.Policy):
@property
def name(self) -> str:
return "BlockedAccountPolicy"
def check_pre_trade_start(
self,
ctx: openpit.pretrade.Context,
order: openpit.Order,
) -> tuple[openpit.pretrade.PolicyReject, ...]:
del ctx
assert order.operation is not None
if order.operation.account_id == openpit.param.AccountId.from_int(1):
return (
openpit.pretrade.PolicyReject(
code=openpit.pretrade.RejectCode.ACCOUNT_BLOCKED,
reason="account is blocked",
details="account 1 cannot send new orders",
scope=openpit.pretrade.RejectScope.ACCOUNT,
),
)
return ()
def apply_execution_report(
self,
report: openpit.ExecutionReport,
) -> bool:
del report
return False
Start-stage checks return an iterable of PolicyReject objects. An empty
iterable means success.
Main-stage checks¶
Implement openpit.pretrade.Policy when a policy may reserve state or
needs to run in the main stage.
import openpit
class NotionalCapPolicy(openpit.pretrade.Policy):
def __init__(self, max_notional: openpit.param.Volume) -> None:
self._max_notional = max_notional
@property
def name(self) -> str:
return "NotionalCapPolicy"
def perform_pre_trade_check(
self,
ctx: openpit.pretrade.Context,
order: openpit.Order,
) -> openpit.pretrade.PolicyDecision:
del ctx
assert order.operation is not None
amount = order.operation.trade_amount
if amount.is_volume:
requested = amount.as_volume
else:
assert order.operation.price is not None
requested = order.operation.price.calculate_volume(amount.as_quantity)
if requested > self._max_notional:
return openpit.pretrade.PolicyDecision.reject(
rejects=[
openpit.pretrade.PolicyReject(
code=openpit.pretrade.RejectCode.RISK_LIMIT_EXCEEDED,
reason="notional cap exceeded",
details=f"requested {requested}, max {self._max_notional}",
scope=openpit.pretrade.RejectScope.ORDER,
)
]
)
return openpit.pretrade.PolicyDecision.accept()
def apply_execution_report(
self,
report: openpit.ExecutionReport,
) -> bool:
del report
return False
Use PolicyDecision.accept(mutations=[...]) when the policy has reserved state
that must be finalized or rolled back by the engine.
Mutations¶
A Mutation is a pair of callables:
commit: called when the reservation is committed.rollback: called when the main stage rejects or the reservation rolls back.
Register mutations when policy state changes before the final order-submission outcome is known.
Built-in policies¶
build_order_validation(): validates required order fields and basic shape.build_rate_limit(): rejects requests after a configured limit is reached.build_pnl_bounds_killswitch(): blocks accounts when accumulated P&L is outside configured bounds.build_order_size_limit(): enforces per-settlement-asset quantity and notional limits.