-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
Eugene V. Palchukovsky edited this page Apr 29, 2026
·
8 revisions
Pit has full public SDKs today:
- Go module
pit-go - Python package
openpit - Rust crate
openpit
This page uses the conceptual step names start stage, execute request,
finalize reservation, and apply execution report in prose. Exact API names
stay inside the code blocks.
SDKs follow the same operational flow:
- Build an engine once during application startup.
- Run the
start stagefor each order. -
Execute requestif the start stage passes. -
Finalize reservationexplicitly. -
Apply execution reportafter realized outcomes are known.
go get go.openpit.dev/openpitpip install openpitcargo add openpitGo
package main
import (
"fmt"
"log"
"go.openpit.dev/openpit"
"go.openpit.dev/openpit/model"
"go.openpit.dev/openpit/param"
"go.openpit.dev/openpit/pkg/optional"
"go.openpit.dev/openpit/pretrade/policies"
)
func main() {
usd, err := param.NewAsset("USD")
if err != nil {
log.Fatal(err)
}
lowerBound, err := param.NewPnlFromString("-1000")
if err != nil {
log.Fatal(err)
}
maxQty, err := param.NewQuantityFromString("500")
if err != nil {
log.Fatal(err)
}
maxNotional, err := param.NewVolumeFromString("100000")
if err != nil {
log.Fatal(err)
}
// 1. Configure policies.
pnlPolicy, err := policies.NewPnlBoundsKillSwitchPolicy(
policies.PnlBoundsBarrier{
SettlementAsset: usd,
LowerBound: optional.Some(lowerBound),
InitialPnl: param.PnlZero,
},
)
if err != nil {
log.Fatal(err)
}
defer pnlPolicy.Close()
sizePolicy, err := policies.NewOrderSizeLimitPolicy(
policies.OrderSizeLimit{
SettlementAsset: usd,
MaxQuantity: maxQty,
MaxNotional: maxNotional,
},
)
if err != nil {
log.Fatal(err)
}
defer sizePolicy.Close()
// 2. Build the engine (one time at the platform initialization).
builder, err := openpit.NewEngineBuilder()
if err != nil {
log.Fatal(err)
}
builder.BuiltinCheckPreTradeStartPolicy(
policies.NewOrderValidation(),
pnlPolicy,
policies.NewRateLimitPolicy(100, 1),
sizePolicy,
)
engine, err := builder.Build()
if err != nil {
log.Fatal(err)
}
defer engine.Stop()
// 3. Check an order.
order := model.NewOrder()
op := order.EnsureOperationView()
aapl, err := param.NewAsset("AAPL")
if err != nil {
log.Fatal(err)
}
op.SetInstrument(param.NewInstrument(aapl, usd))
op.SetAccountID(param.NewAccountIDFromInt(99224416))
op.SetSide(param.SideBuy)
price, _ := param.NewPriceFromString("185")
qty, _ := param.NewQuantityFromString("100")
op.SetTradeAmount(param.NewQuantityTradeAmount(qty))
op.SetPrice(price)
request, rejects, err := engine.StartPreTrade(order)
if err != nil {
log.Fatal(err)
}
if rejects != nil {
for _, r := range rejects {
fmt.Printf("rejected by %s [%d]: %s (%s)\n", r.Policy, r.Code, r.Reason, r.Details)
}
return
}
defer request.Close()
// 4. Quick, lightweight checks were performed during start stage. The
// system state has not yet changed (except controls that must observe every
// request). 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.
reservation, rejects, err := request.Execute()
if err != nil {
log.Fatal(err)
}
if rejects != nil {
for _, r := range rejects {
fmt.Printf("rejected by %s [%d]: %s (%s)\n", r.Policy, r.Code, r.Reason, r.Details)
}
return
}
defer reservation.Close()
// Optional shortcut for the same two-stage flow:
// reservation, rejects, err := engine.ExecutePreTrade(order)
// 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()
// 7. The order goes to the venue and returns with an execution report.
report := model.NewExecutionReport()
reportOp := model.NewExecutionReportOperation()
reportOp.SetInstrument(param.NewInstrument(aapl, usd))
reportOp.SetAccountID(param.NewAccountIDFromInt(99224416))
reportOp.SetSide(param.SideBuy)
report.SetOperation(reportOp)
pnl, _ := param.NewPnlFromString("-50")
fee, _ := param.NewFeeFromString("3.4")
impact := model.NewExecutionReportFinancialImpact()
impact.SetPnl(pnl)
impact.SetFee(fee)
report.SetFinancialImpact(impact)
result, err := engine.ApplyExecutionReport(report)
if err != nil {
log.Fatal(err)
}
// 8. 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.
if result.KillSwitchTriggered {
fmt.Println("halt new orders until the blocked state is cleared")
}
}Python
import openpit
# 1. Configure policies.
pnl_policy = openpit.pretrade.policies.PnlBoundsKillSwitchPolicy(
settlement_asset="USD",
lower_bound=openpit.param.Pnl("-1000"),
initial_pnl=openpit.param.Pnl("0"),
)
rate_limit_policy = openpit.pretrade.policies.RateLimitPolicy(
max_orders=100,
window_seconds=1,
)
order_size_policy = openpit.pretrade.policies.OrderSizeLimitPolicy(
limit=openpit.pretrade.policies.OrderSizeLimit(
settlement_asset="USD",
max_quantity=openpit.param.Quantity("500"),
max_notional=openpit.param.Volume("100000"),
)
)
# 2. Build the engine (one time at the platform initialization).
engine = (
openpit.Engine.builder()
.check_pre_trade_start_policy(
policy=openpit.pretrade.policies.OrderValidationPolicy(),
)
.check_pre_trade_start_policy(policy=pnl_policy)
.check_pre_trade_start_policy(policy=rate_limit_policy)
.check_pre_trade_start_policy(policy=order_size_policy)
.build()
)
# 3. Check an order.
order = openpit.Order(
operation=openpit.OrderOperation(
instrument=openpit.Instrument("AAPL", "USD"),
account_id=openpit.param.AccountId.from_u64(99224416),
side=openpit.param.Side.BUY,
trade_amount=openpit.param.TradeAmount.quantity(100.0),
price=openpit.param.Price(185.0),
),
)
start_result = engine.start_pre_trade(order=order)
if not start_result:
messages = ", ".join(
f"{r.policy} [{r.code}]: {r.reason}: {r.details}"
for r in start_result.rejects
)
raise RuntimeError(messages)
request = start_result.request
# 4. Quick, lightweight checks, such as fat-finger scope or enabled kill
# switch, 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. 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.
execute_result = request.execute()
# Optional shortcut for the same two-stage flow:
# execute_result = engine.execute_pre_trade(order=order)
if not execute_result:
messages = ", ".join(
f"{reject.policy} [{reject.code}]: {reject.reason}: {reject.details}"
for reject in execute_result.rejects
)
raise RuntimeError(messages)
reservation = execute_result.reservation
# 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.
try:
send_order_to_venue(order)
except Exception:
reservation.rollback()
raise
reservation.commit()
# 7. The order goes to the venue and returns with an execution report.
report = openpit.ExecutionReport(
operation=openpit.ExecutionReportOperation(
instrument=openpit.Instrument("AAPL", "USD"),
account_id=openpit.param.AccountId.from_u64(99224416),
side=openpit.param.Side.BUY,
),
financial_impact=openpit.FinancialImpact(
pnl=openpit.param.Pnl("-50"),
fee=openpit.param.Fee("3.4"),
),
)
result = engine.apply_execution_report(report=report)
# 8. 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 is FalseRust
use std::time::Duration;
use openpit::{
FinancialImpact, ExecutionReportOperation, OrderOperation,
WithFinancialImpact, WithExecutionReportOperation,
};
use openpit::param::{
AccountId, Asset, Fee, Pnl, Price, Quantity, Side, TradeAmount, Volume,
};
use openpit::pretrade::policies::{OrderSizeLimit, OrderSizeLimitPolicy};
use openpit::pretrade::policies::OrderValidationPolicy;
use openpit::pretrade::policies::{PnlBoundsBarrier, PnlBoundsKillSwitchPolicy};
use openpit::pretrade::policies::RateLimitPolicy;
use openpit::{Engine, Instrument};
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let usd = Asset::new("USD")?;
# 1. Configure policies.
let pnl_policy = PnlBoundsKillSwitchPolicy::new(
PnlBoundsBarrier {
settlement_asset: usd.clone(),
lower_bound: Some(Pnl::from_str("-1000")?),
upper_bound: None,
initial_pnl: Pnl::ZERO,
},
[],
)?;
let rate_limit_policy = RateLimitPolicy::new(100, Duration::from_secs(1));
let size_policy = OrderSizeLimitPolicy::new(
OrderSizeLimit {
settlement_asset: usd.clone(),
max_quantity: Quantity::from_str("500")?,
max_notional: Volume::from_str("100000")?,
},
[],
);
# 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_policy)
.check_pre_trade_start_policy(rate_limit_policy)
.check_pre_trade_start_policy(size_policy)
.build()?;
# 3. Check an order.
let order = OrderOperation {
instrument: Instrument::new(
Asset::new("AAPL")?,
usd.clone(),
),
account_id: AccountId::from_u64(99224416),
side: Side::Buy,
trade_amount: TradeAmount::Quantity(
Quantity::from_f64(100.0)?,
),
price: Some(Price::from_str("185")?),
};
let request = engine.start_pre_trade(order)?;
# 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()?;
# Optional shortcut for the same two-stage flow:
# let reservation = engine.execute_pre_trade(order)?;
# 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 = WithExecutionReportOperation {
inner: WithFinancialImpact {
inner: (),
financial_impact: FinancialImpact {
pnl: Pnl::from_str("-50")?,
fee: Fee::from_str("3.4")?,
},
},
operation: ExecutionReportOperation {
instrument: Instrument::new(
Asset::new("AAPL")?,
usd,
),
account_id: AccountId::from_u64(99224416),
side: Side::Buy,
},
};
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);
# Ok(())
# }Go
reservation, rejects, err := engine.ExecutePreTrade(order)
if err != nil {
log.Fatal(err)
}
if rejects != nil {
for _, r := range rejects {
log.Printf(
"rejected by %s [%d]: %s (%s)",
r.Policy,
r.Code,
r.Reason,
r.Details,
)
}
return
}
defer reservation.Close()
reservation.Commit()Python
execute_result = engine.execute_pre_trade(order=order)
if not execute_result:
messages = ", ".join(
f"{reject.policy} [{reject.code}]: {reject.reason}: {reject.details}"
for reject in execute_result.rejects
)
raise RuntimeError(messages)
execute_result.reservation.commit()Rust
let reservation = match engine.execute_pre_trade(order) {
Ok(reservation) => reservation,
Err(rejects) => {
for reject in &rejects {
eprintln!(
"rejected by {} [{}]: {} ({})",
reject.policy,
reject.code,
reject.reason,
reject.details
);
}
return;
}
};
reservation.commit();Go
request, rejects, err := engine.StartPreTrade(order)
if err != nil {
log.Fatal(err)
}
if rejects != nil {
for _, r := range rejects {
log.Printf(
"rejected by %s [%d]: %s (%s)",
r.Policy,
r.Code,
r.Reason,
r.Details,
)
}
return
}
defer request.Close()
reservation, rejects, err := request.Execute()
if err != nil {
log.Fatal(err)
}
if rejects != nil {
for _, r := range rejects {
log.Printf(
"rejected by %s [%d]: %s (%s)",
r.Policy,
r.Code,
r.Reason,
r.Details,
)
}
return
}
defer reservation.Close()
reservation.Commit()Python
start_result = engine.start_pre_trade(order=order)
if not start_result:
messages = ", ".join(
f"{r.policy} [{r.code}]: {r.reason}: {r.details}"
for r in start_result.rejects
)
raise RuntimeError(messages)
execute_result = start_result.request.execute()
if not execute_result:
messages = ", ".join(
f"{reject.policy} [{reject.code}]: {reject.reason}: {reject.details}"
for reject in execute_result.rejects
)
raise RuntimeError(messages)
execute_result.reservation.commit()Rust
let request = match engine.start_pre_trade(order) {
Ok(request) => request,
Err(reject) => {
eprintln!(
"rejected by {} [{}]: {} ({})",
reject.policy,
reject.code,
reject.reason,
reject.details
);
return;
}
};
let reservation = match request.execute() {
Ok(reservation) => reservation,
Err(rejects) => {
for reject in &rejects {
eprintln!(
"rejected by {} [{}]: {} ({})",
reject.policy,
reject.code,
reject.reason,
reject.details
);
}
return;
}
};
reservation.commit();Go
// Execution reports feed realized outcomes back into cumulative policy state.
result, err := engine.ApplyExecutionReport(report)
if err != nil {
log.Fatal(err)
}
if result.KillSwitchTriggered {
log.Print("halt new orders until the blocked state is cleared")
}Python
result = engine.apply_execution_report(report=report)
if result.kill_switch_triggered:
print("halt new orders until the blocked state is cleared")Rust
let result = engine.apply_execution_report(&report);
if result.kill_switch_triggered {
eprintln!("halt new orders until the blocked state is cleared");
}- Pit is in-memory. Persistence belongs to the host system.
- Pit does not route orders or talk to venues.
- Pit does not calculate realized P&L from raw fills for you. The caller sends realized outcomes through execution reports.
- One shared engine instance does not coordinate concurrent access for you.
- Do not read or mutate custom-policy state in parallel with engine calls on
the same engine instance. Either synchronize access manually, or feed
state corrections through
apply account adjustmentsonly.
- Pre-trade Pipeline: Request, reject, and reservation semantics
- Policies: Built-in controls and custom policy hooks
- Reject Codes: Standard business reject codes
- Architecture: Public integration model