Skip to content

Domain Types

Eugene V. Palchukovsky edited this page Apr 28, 2026 · 5 revisions

Domain Types

Pit uses domain-specific value types so that quantities, prices, fees, P&L, cash flow, and identifiers are not mixed accidentally.

Public Types

Concept Meaning Go Python Rust
AccountId Type-safe account identifier openpit/param.AccountID openpit.param.AccountId openpit::param::AccountId
Asset Asset or currency identifier such as AAPL or USD openpit/param.Asset openpit.param.Asset openpit::param::Asset
Side Trade direction: buy or sell openpit/param.Side openpit.param.Side openpit::param::Side
PositionSide Hedge-mode leg: long or short openpit/param.PositionSide openpit.param.PositionSide openpit::param::PositionSide
Quantity Requested or filled size in instrument units openpit/param.Quantity openpit.param.Quantity openpit::param::Quantity
Price Signed per-unit price openpit/param.Price openpit.param.Price openpit::param::Price
PnL Realized profit or loss in the settlement asset openpit/param.Pnl openpit.param.Pnl openpit::param::Pnl
Fee Fee paid or rebate received in the settlement asset openpit/param.Fee openpit.param.Fee openpit::param::Fee
Volume Absolute monetary size of a trade order openpit/param.Volume openpit.param.Volume openpit::param::Volume
Notional Monetary position exposure: |price| × qty, used for margin and risk openpit/param.Notional openpit.param.Notional openpit::param::Notional
CashFlow Signed cash movement openpit/param.CashFlow openpit.param.CashFlow openpit::param::CashFlow
PositionSize Signed exposure size openpit/param.PositionSize openpit.param.PositionSize openpit::param::PositionSize
AdjustmentAmount Signed account-adjustment payload variant (delta or absolute) openpit.AdjustmentAmount openpit.param.AdjustmentAmount openpit::param::AdjustmentAmount
PositionMode Derivatives position accounting mode (netting or hedged) openpit/param.PositionMode openpit.param.PositionMode openpit::param::PositionMode
Leverage Per-order leverage multiplier openpit/param.Leverage openpit.param.Leverage and openpit.Leverage openpit::param::Leverage
FillType Execution-event classification such as trade or liquidation Not exposed openpit.param.FillType openpit::param::FillType
PositionEffect Position action reported by the venue: open or close openpit/param.PositionEffect openpit.param.PositionEffect openpit::param::PositionEffect

Account Identifiers

AccountId is designed optimized for speed and costs. Two constructors are available:

  • from_u64: zero cost, zero collision risk; preferred whenever the broker or venue assigns numeric account IDs.

  • from_str: hashes the string with FNV-1a 64-bit; collisions are theoretically possible. Collision probability for n distinct account strings:

    Accounts P(at least one collision)
    1 000 < 3 × 10⁻¹⁴
    10 000 < 3 × 10⁻¹²
    100 000 < 3 × 10⁻¹⁰
    1 000 000 < 3 × 10⁻⁸

    See http://www.isthe.com/chongo/tech/comp/fnv/ for the algorithm specification.

    If collision risk is unacceptable, maintain your own collision-free string→u64 mapping (e.g. a registry or a database sequence) and pass the resulting integer to from_u64.

    Empty and whitespace-only strings are rejected; all constructors return an error in that case.

Sign Conventions

Type Positive Negative
Pnl Profit Loss
Fee Fee paid Rebate received
CashFlow Inflow Outflow
PositionSize Long Short

Quantity, Volume, and Notional can never be negative.

Orders use a single trade amount field with context defined by its type where the target technology supports that shape. Otherwise, integrations may expose two separate fields/arguments (quantity and volume) with equivalent semantics.

  • trade amount as Quantity: run the order by instrument amount
  • trade amount as Volume: run the order by settlement notional
  • Price present: treat it as the worst execution price allowed
  • Price absent: treat the order as market-price execution

This means:

  • If a Price is provided, the engine must evaluate risk using that price as the worst possible price for the order.
  • It does not matter whether the order is expressed as quantity-based or volume-based trade_amount — wherever a price is required for risk evaluation, the provided price must be treated as the worst-case price.
  • If Price is not provided, the engine must obtain the current market price to evaluate the risk.
  • If a market price cannot be obtained, the risk check must fail with an error indicating insufficient data to evaluate risk.

Leverage

Property Value
Minimum 1x
Maximum 3000x
Step 0.1x

Numeric Behavior

Rust behavior:

  • Checked arithmetic returns Result<_, openpit::param::Error>
  • Rounded constructors accept an explicit RoundingStrategy
  • Price::calculate_volume(quantity) and Quantity::calculate_volume(price) compute absolute notional as |price| × quantity
  • Notional::from_price_quantity(price, quantity) computes position exposure as |price| × quantity
  • Notional::margin_required(leverage) computes required margin as notional / leverage using exact decimal arithmetic
  • Notional::from_volume(v) / Volume::from_notional(n) reinterpret between trade size and position exposure without numeric change

Python behavior:

  • All decimal-based param types behave like decimal.Decimal with domain-type safety: arithmetic works only between operands of the same type.
  • Constructor accepts Decimal, str, int, or float.
  • The .decimal property returns the underlying decimal.Decimal for cross-type computations or standard-library interop.
  • str(value) produces the canonical decimal string.
  • Invalid inputs raise ValueError.
  • ParamKind and RoundingStrategy are exposed as stable string constants.

Public Constants and Errors

Rust exposes:

  • openpit::param::ParamKind
  • openpit::param::RoundingStrategy
  • openpit::param::Error

Python exposes:

  • openpit.param.ParamKind
  • openpit.param.RoundingStrategy

The Python wrappers do not expose the Rust arithmetic error enum directly.

Example: Create Validated Values

Go:

asset, err := param.NewAsset("AAPL")
if err != nil {
    panic(err)
}
quantity, _ := param.NewQuantityFromString("10.5")
price, _ := param.NewPriceFromString("185")
pnl, _ := param.NewPnlFromString("-12.5")

assert(asset.String() == "AAPL")
assert(quantity.String() == "10.5")
assert(price.String() == "185")
assert(pnl.String() == "-12.5")

Python:

import openpit
from decimal import Decimal

# Build validated value objects at the integration boundary.
asset = openpit.param.Asset("AAPL")
quantity = openpit.param.Quantity("10.5")
price = openpit.param.Price(185)
pnl = openpit.param.Pnl(-12.5)

# Domain types with exact decimal semantics.
assert asset == "AAPL"
assert str(quantity) == "10.5"
assert str(price) == "185"
assert str(pnl) == "-12.5"

# The .decimal property provides the underlying Decimal for interop.
assert quantity.decimal == Decimal("10.5")
assert isinstance(price.decimal, Decimal)

Rust:

use openpit::param::{Asset, Pnl, Price, Quantity};

// Build validated value objects at the integration boundary.
let asset = Asset::new("AAPL").expect("asset code must be valid");
let quantity = Quantity::from_str("10.5").expect("quantity must be valid");
let price = Price::from_str("185").expect("price must be valid");
let pnl = Pnl::from_str("-12.5").expect("pnl must be valid");

// The wrappers normalize formatting while preserving domain meaning.
assert_eq!(asset.as_ref(), "AAPL");
assert_eq!(quantity.to_string(), "10.5");
assert_eq!(price.to_string(), "185");
assert_eq!(pnl.to_string(), "-12.5");

Example: Work With Directional Types

Go:

var side param.Side = param.SideBuy
var positionSide param.PositionSide = param.PositionSideLong

assert(side == param.SideBuy)
assert(positionSide == param.PositionSideLong)

Python:

import openpit

# Directional helpers keep side logic explicit instead of comparing raw strings.
side = openpit.param.Side.BUY
position_side = openpit.param.PositionSide.LONG

assert side.opposite().value == "sell"
assert side.sign() == 1
assert position_side.opposite().value == "short"

Rust:

use openpit::param::{PositionSide, Side};

// Directional helpers keep side logic explicit instead of comparing raw strings.
assert_eq!(Side::Buy.opposite(), Side::Sell);
assert_eq!(Side::Sell.sign(), -1);
assert_eq!(PositionSide::Long.opposite(), PositionSide::Short);

Example: Create Leverage

Go:

fromMultiplier, _ := param.NewLeverage("100")
fromFloat, _ := param.NewLeverage("100.5")

assert(string(fromMultiplier) == "100.000000000000000000")
assert(string(fromFloat) == "100.500000000000000000")

Python:

import openpit

# Leverage is a plain multiplier with direct int/float constructors.
from_multiplier = openpit.param.Leverage(100)
from_float = openpit.param.Leverage(100.5)

# Both constructors end up with the same strongly typed leverage wrapper.
assert from_multiplier.value == 100.0
assert from_float.value == 100.5

Rust:

use openpit::param::Leverage;

// Pick the constructor that matches the upstream representation you receive.
let from_multiplier = Leverage::from_u16(100).expect("valid leverage");
let from_float = Leverage::from_f64(100.5).expect("valid leverage");

// Both constructors end up with the same strongly typed leverage wrapper.
assert_eq!(from_multiplier.value(), 100.0);
assert_eq!(from_float.value(), 100.5);

Related Pages

Clone this wiki locally