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.