-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
Eugene Palchukovsky edited this page May 21, 2026
·
13 revisions
OpenPit ships public SDKs for:
- Go module
go.openpit.dev/openpit - Python package
openpit - Rust crate
openpit
All 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.
Prose uses the conceptual step names; exact API names stay inside the code blocks.
go get go.openpit.dev/openpitpip install openpitcargo add openpitGo
package main
import (
"fmt"
"log"
"time"
"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)
}
maxBrokerQty, err := param.NewQuantityFromString("10000")
if err != nil {
log.Fatal(err)
}
maxBrokerNotional, err := param.NewVolumeFromString("5000000")
if err != nil {
log.Fatal(err)
}
// 1. Build the engine (one time at the platform initialization).
engine, err := openpit.NewEngineBuilder().
FullSync().
Builtin(policies.BuildOrderValidation()).
Builtin(
policies.BuildPnlBoundsKillswitch().
BrokerBarriers(
policies.PnlBoundsBrokerBarrier{
SettlementAsset: usd,
LowerBound: optional.Some(lowerBound),
},
),
).
Builtin(
policies.BuildRateLimit().
BrokerBarrier(
policies.RateLimitBrokerBarrier{
Limit: policies.RateLimit{
MaxOrders: 100,
Window: time.Second,
},
},
),
).
Builtin(
policies.BuildOrderSizeLimit().
AssetBarriers(
policies.OrderSizeAssetBarrier{
SettlementAsset: usd,
Limit: policies.OrderSizeLimit{
MaxQuantity: maxQty,
MaxNotional: maxNotional,
},
},
).
BrokerBarrier(
policies.OrderSizeBrokerBarrier{
Limit: policies.OrderSizeLimit{
MaxQuantity: maxBrokerQty,
MaxNotional: maxBrokerNotional,
},
},
),
).
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 len(result.AccountBlocks) > 0 {
fmt.Println("halt new orders until the blocked state is cleared")
}
}Python
import datetime
import openpit
import openpit.pretrade.policies
# 1. Build the engine (one time at the platform initialization).
engine = (
openpit.Engine.builder()
.no_sync()
.builtin(openpit.pretrade.policies.build_order_validation())
.builtin(
openpit.pretrade.policies.build_pnl_bounds_killswitch()
.broker_barriers(
openpit.pretrade.policies.PnlBoundsBrokerBarrier(
settlement_asset="USD",
lower_bound=openpit.param.Pnl("-1000"),
),
)
)
.builtin(
openpit.pretrade.policies.build_rate_limit()
.broker_barrier(
openpit.pretrade.policies.RateLimitBrokerBarrier(
limit=openpit.pretrade.policies.RateLimit(
max_orders=100,
window=datetime.timedelta(seconds=1),
),
),
)
)
.builtin(
openpit.pretrade.policies.build_order_size_limit()
.asset_barriers(
openpit.pretrade.policies.OrderSizeAssetBarrier(
limit=openpit.pretrade.policies.OrderSizeLimit(
max_quantity=openpit.param.Quantity("500"),
max_notional=openpit.param.Volume("100000"),
),
settlement_asset="USD",
),
)
.broker_barrier(
openpit.pretrade.policies.OrderSizeBrokerBarrier(
limit=openpit.pretrade.policies.OrderSizeLimit(
max_quantity=openpit.param.Quantity("10000"),
max_notional=openpit.param.Volume("5000000"),
),
),
)
)
.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 not result.account_blocksRust
use std::str::FromStr;
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::{
OrderSizeAssetBarrier, OrderSizeBrokerBarrier,
OrderSizeLimit, OrderSizeLimitPolicy,
OrderValidationPolicy,
PnlBoundsBrokerBarrier, PnlBoundsKillSwitchPolicy,
RateLimit, RateLimitBrokerBarrier, RateLimitPolicy,
};
use openpit::{Engine, Instrument};
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let usd = Asset::new("USD")?;
# 1. Build the engine (one time at the platform initialization).
type Report = WithExecutionReportOperation<WithFinancialImpact<()>>;
let builder = Engine::builder::<OrderOperation, Report, ()>().no_sync();
let pnl_bounds = PnlBoundsKillSwitchPolicy::new(
[PnlBoundsBrokerBarrier {
settlement_asset: usd.clone(),
lower_bound: Some(Pnl::from_str("-1000")?),
upper_bound: None,
}],
[],
builder.storage_builder(),
)?;
let rate_limit = RateLimitPolicy::new(
Some(RateLimitBrokerBarrier {
limit: RateLimit {
max_orders: 100,
window: Duration::from_secs(1),
},
}),
[],
[],
[],
builder.storage_builder(),
)?;
let engine = builder
.pre_trade(OrderValidationPolicy::new())
.pre_trade(pnl_bounds)
.pre_trade(rate_limit)
.pre_trade(
OrderSizeLimitPolicy::new(
Some(OrderSizeBrokerBarrier {
limit: OrderSizeLimit {
max_quantity: Quantity::from_str("10000")?,
max_notional: Volume::from_str("5000000")?,
},
}),
[OrderSizeAssetBarrier {
limit: OrderSizeLimit {
max_quantity: Quantity::from_str("500")?,
max_notional: Volume::from_str("100000")?,
},
settlement_asset: usd.clone(),
}],
[],
)?,
)
.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.account_blocks.is_empty());
# 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(rejects) => {
for reject in rejects.iter() {
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 len(result.AccountBlocks) > 0 {
log.Print("halt new orders until the blocked state is cleared")
}Python
result = engine.apply_execution_report(report=report)
if result.account_blocks:
print("halt new orders until the blocked state is cleared")Rust
let result = engine.apply_execution_report(&report);
if !result.account_blocks.is_empty() {
eprintln!("halt new orders until the blocked state is cleared");
}- OpenPit is in-memory. Persistence belongs to the host system.
- OpenPit does not route orders or talk to venues.
- OpenPit does not calculate realized P&L from raw fills. The caller sends realized outcomes through execution reports.
- A shared engine instance behaves according to the chosen sync mode (full, local, or account). See Threading Contract for the per-mode contract.
- Treat custom-policy state according to the same sync mode. Under full
sync, shared state must be thread-safe; under local or account sync, do
not access it concurrently with engine calls. Prefer feeding state
corrections through
apply account adjustments.
- 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