Skip to content

Domain Types

OpenPit uses domain-specific value types so that quantities, prices, fees, P&L, cash flow, and identifiers cannot be mixed accidentally.

Public Types

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

Account Identifiers

AccountId is optimized for speed and low overhead. Two constructor families are available:

  • Numeric - zero cost, zero collision risk. Preferred whenever the broker or venue assigns numeric account IDs. Names per language: Go NewAccountIDFromUint64, Python from_int, JS AccountId.fromInt (with a bigint), C++ AccountId::FromUint64, Rust from_u64, C openpit_create_param_account_id_from_uint64.
  • String - hashes the string with FNV-1a 64-bit; collisions are theoretically possible. Names per language: Go NewAccountIDFromString, Python from_string, JS AccountId.fromString, C++ AccountId::FromString, Rust from_str, C openpit_create_param_account_id_from_string. 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 a collision-free string-to-u64 mapping (registry or database sequence) and pass the resulting integer to from_u64.

Empty or 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 are non-negative.

Order Trade Amount and Price

Orders carry a single trade amount field whose variant defines the meaning:

  • trade amount as Quantity: order is sized by instrument amount.
  • trade amount as Volume: order is sized by settlement notional.

(When the target technology does not support a tagged-union shape, the integration exposes two equivalent fields, quantity and volume.)

Price semantics are independent of the trade-amount variant:

  • Price present: treated as the worst execution price allowed for the order and used as the worst-case input to every risk evaluation that needs a price.
  • Price absent: the engine must obtain the current market price for the risk evaluation. If a market price cannot be obtained, the risk check must fail with an error indicating insufficient data.

Leverage

Property Value
Minimum 1x
Maximum 3000x
Step 0.1x

Numeric Behavior

Go behavior:

  • Constructors and checked arithmetic return (value, error).
  • errors.Is classifies validation and arithmetic failures through the param.Err* sentinels.
  • Rounded constructors accept an explicit param.RoundingStrategy.
  • String constructors and String() preserve exact decimal values.

Python behavior:

  • Decimal-based param types behave like decimal.Decimal with domain-type safety: arithmetic works only between operands of the same type.
  • Constructors accept 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.

JS behavior:

  • fromString is the lossless constructor for decimal input, and toString() returns the canonical exact value.
  • fromInt accepts exact integer input, including bigint; fromFloat is an explicit convenience for an IEEE-754 boundary value.
  • Checked construction and arithmetic raise typed ParamError / value-error subclasses with stable code, param, and optional input fields.
  • Rounded factories accept the RoundingStrategy string union or a value from RoundingStrategies.

C++ behavior:

  • FromString is the exact constructor for monetary input; FromDouble is for boundary adapters where floating-point input is unavoidable.
  • Construction and checked arithmetic throw openpit::Error; Code() carries the native parameter error code when one is available.
  • Rounded constructors accept an explicit openpit::param::RoundingStrategy.

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.

Public Constants and Errors

Go exposes:

  • openpit/param.Kind
  • openpit/param.FillType
  • openpit/param.RoundingStrategy
  • openpit/param.ErrNegative, ErrDivisionByZero, ErrOverflow, and the other idiomatic sentinel errors used with errors.Is

Python exposes:

  • openpit.param.Kind
  • openpit.param.FillType
  • openpit.param.RoundingStrategy
  • openpit.param.ParamError, raised as a Python exception

JS exposes from @openpit/engine/param:

  • ParamKind
  • FillType
  • RoundingStrategy and RoundingStrategies
  • ParamError, a typed RangeError branch with structured fields

C++ exposes:

  • openpit::param::Kind
  • openpit::param::FillType
  • openpit::param::RoundingStrategy
  • openpit::Error, with an optional native parameter error code

Rust exposes:

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

The error shape is idiomatic per language: Go uses returned errors and sentinels, Python and JS raise exceptions, C++ throws, and Rust returns Result. Python does not expose the Rust arithmetic error enum directly.

Example: Create Validated Values

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

if asset.String() != "AAPL" {
    panic("unexpected asset")
}
if quantity.String() != "10.5" {
    panic("unexpected quantity")
}
if price.String() != "185" {
    panic("unexpected price")
}
if pnl.String() != "-12.5" {
    panic("unexpected pnl")
}
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)
JavaScript
import { Pnl, Price, Quantity } from "@openpit/engine/param";

// Build validated value objects at the integration boundary. Assets cross the
// boundary as plain strings, so there is no asset wrapper to construct.
const asset = "AAPL";
const quantity = Quantity.fromString("10.5");
const price = Price.fromString("185");
const pnl = Pnl.fromString("-12.5");

// The wrappers normalize formatting while preserving domain meaning, and
// serialize back to a lossless decimal string - never a raw number.
console.log(asset, quantity.toString(), price.toString(), pnl.toString());
// => AAPL 10.5 185 -12.5
C++
#include "openpit/openpit.hpp"

#include <cassert>

using openpit::param::Asset;
using openpit::param::Pnl;
using openpit::param::Price;
using openpit::param::Quantity;

// Build validated value objects at the integration boundary. Asset codes and
// the monetary fields are all exact, validated value types.
const auto asset = Asset("AAPL");
const auto quantity = Quantity::FromString("10.5");
const auto price = Price::FromString("185");
const auto pnl = Pnl::FromString("-12.5");

// The wrappers normalize formatting while preserving domain meaning.
assert(asset.View() == "AAPL");
assert(quantity.ToString() == "10.5");
assert(price.ToString() == "185");
assert(pnl.ToString() == "-12.5");
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

if side != param.SideBuy {
    panic("unexpected side")
}
if positionSide != param.PositionSideLong {
    panic("unexpected position side")
}
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"
JavaScript
import { PositionSide, Side } from "@openpit/engine/param";

// Directional helpers keep side logic explicit instead of comparing raw strings.
const side = Side.buy();
const positionSide = PositionSide.long();

console.log(side.opposite().toString(), side.sign()); // => SELL 1
console.log(positionSide.opposite().toString()); // => SHORT
C++
#include "openpit/openpit.hpp"

#include <cassert>

namespace model = openpit::model;

// Directional enums keep side logic explicit instead of comparing raw strings.
const model::Side side = model::Side::Buy;
const model::PositionSide positionSide = model::PositionSide::Long;

assert(side == model::Side::Buy);
assert(positionSide == model::PositionSide::Long);
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.NewLeverageFromUint16(100)
fromFloat := param.NewLeverageFromFloat32(100.5)

if fromMultiplier.Value() != 100.0 {
    panic("unexpected fromMultiplier value")
}
if fromFloat.Value() != 100.5 {
    panic("unexpected fromFloat value")
}
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
JavaScript
import { Leverage } from "@openpit/engine/param";

// Pick the constructor that matches the upstream representation you receive.
const fromMultiplier = Leverage.fromInt(100);
const fromFloat = Leverage.fromFloat(100.5);

// Both constructors end up with the same strongly typed leverage wrapper.
console.log(fromMultiplier.value, fromFloat.value); // => 100 100.5
C++
#include "openpit/openpit.hpp"

#include <cassert>

using openpit::param::Leverage;

// Pick the constructor that matches the upstream representation you receive.
const Leverage fromMultiplier = Leverage::FromUint16(100);
const Leverage fromFloat = Leverage::FromFloat(100.5F);

// Both constructors end up with the same strongly typed leverage wrapper.
assert(fromMultiplier.Value() == 100.0F);
assert(fromFloat.Value() == 100.5F);
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);