openpit 0.1.3

Embeddable pre-trade risk SDK
Documentation

Pit: Pre-trade Integrity Toolkit

Verify Release Rust crates.io docs.rs License

openpit is an embeddable pre-trade risk SDK for integrating policy-driven risk checks into trading systems.

For full project documentation, see the repository README. For conceptual and architectural pages, see the project wiki.

Versioning Policy (Pre‑1.0)

Until Pit reaches a stable 1.0 release, the project follows a relaxed interpretation of Semantic Versioning.

During this phase:

  • PATCH releases are used for bug fixes and small internal corrections.
  • MINOR releases may introduce new features and may also change the public interface.

This means that breaking API changes can appear in minor releases before 1.0. Consumers of the library should take this into account when declaring dependencies and consider using version constraints that tolerate API evolution during the pre‑stable phase.

Getting Started

Visit the crate page on crates.io and the API documentation on docs.rs.

Install

Run the following Cargo command in your project directory:

cargo add openpit

Engine

Overview

The engine evaluates an order through a deterministic pre-trade pipeline:

  • start_pre_trade(order) runs lightweight start-stage policies
  • Request::execute() runs main-stage policies
  • Reservation::commit() applies reserved state
  • dropping Reservation rolls state back automatically
  • apply_execution_report(report) updates post-trade policy state

Start-stage policies stop on the first reject. Main-stage policies aggregate rejects and roll back registered mutations in reverse order when any reject is produced.

Built-in start-stage policies currently include:

  • OrderValidationPolicy
  • PnlKillSwitchPolicy
  • RateLimitPolicy
  • OrderSizeLimitPolicy

There are two types of rejections: a full kill switch for the account and a rejection of only the current request. This is useful in algorithmic trading when automatic order submission must be halted until the situation is analyzed.

Usage

use std::time::Duration;

use openpit::param::{Asset, Fee, Pnl, Price, Quantity, Side, Volume};
use openpit::pretrade::policies::{OrderSizeLimit, OrderSizeLimitPolicy};
use openpit::pretrade::policies::OrderValidationPolicy;
use openpit::pretrade::policies::PnlKillSwitchPolicy;
use openpit::pretrade::policies::RateLimitPolicy;
use openpit::pretrade::{ExecutionReport, Instrument, Order};
use openpit::Engine;

let usd = Asset::new("USD").expect("asset code must be valid");

// 1. Configure policies.
let pnl = PnlKillSwitchPolicy::new(
    (usd.clone(), Pnl::from_str("1000").expect("valid pnl literal")),
    [],
);

let rate_limit = RateLimitPolicy::new(100, Duration::from_secs(1));

let size = OrderSizeLimitPolicy::new(
    OrderSizeLimit {
        settlement_asset: usd.clone(),
        max_quantity: Quantity::from_str("500").expect("valid quantity literal"),
        max_notional: Volume::from_str("100000").expect("valid volume literal"),
    },
    [],
);

// 2. Build the engine (one time at the platform initialization).
let engine = Engine::builder()
    .check_pre_trade_start_policy(OrderValidationPolicy::new())
    .check_pre_trade_start_policy(pnl)
    .check_pre_trade_start_policy(rate_limit)
    .check_pre_trade_start_policy(size)
    .build()
    .expect("engine config must be valid");

// 3. Check an order.
let order = Order {
    instrument: Instrument::new(
        Asset::new("AAPL").expect("asset code must be valid"),
        usd.clone(),
    ),
    side: Side::Buy,
    quantity: Quantity::from_f64(100).expect("valid quantity value"),
    price: Price::from_f64(185).expect("valid price value"),
};

let request = engine
    .start_pre_trade(order)
    .expect("start-stage checks must pass");

// 4. Quick, lightweight checks, such as fat-finger scope or enabled killswitch,
// were performed during pre-trade request creation. The system state has not
// yet changed, except in cases where each request, even rejected ones, must be
// considered (for example, to prevent frequent transfers). Before the
// heavy-duty checks, other work on the request can be performed simply by
// holding the request object.

// 5. Real pre-trade and risk control.
let reservation = request.execute().expect("main-stage checks must pass");

// 6. If the request is successfully sent to the venue, it must be committed.
// The rollback must be called otherwise to revert all performed reservations.
reservation.commit();

// 5. The order goes to the venue and returns with an execution report.
let report = ExecutionReport {
    instrument: Instrument::new(
        Asset::new("AAPL").expect("asset code must be valid"),
        usd,
    ),
    pnl: Pnl::from_f64(-50).expect("valid pnl value"),
    fee: Fee::from_f64(3.4).expect("valid fee value"),
};

let result = engine.apply_execution_report(&report);

// 6. After each execution report is applied, the system may report that it has
// been determined in advance that all subsequent requests will be rejected if
// the account status does not change.
assert!(!result.kill_switch_triggered);

Errors

Rejects from start_pre_trade(order) and Request::execute() are returned as Err(Reject) and Result<Reservation, Vec<Reject>>.

Each Reject contains:

  • policy: policy name
  • code: stable machine-readable code (for example RejectCode::OrderQtyExceedsLimit)
  • reason: short human-readable reject type (for example "order quantity exceeded")
  • details: concrete case details (for example "requested 11, max allowed: 10")
  • scope: RejectScope::Order or RejectScope::Account

RejectCode values are standardized and stable across Rust, Python, and C FFI.