-
Notifications
You must be signed in to change notification settings - Fork 1
Domain Types
OpenPit uses domain-specific value types so that quantities, prices, fees, P&L, cash flow, and identifiers cannot be mixed accidentally.
| 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 denominated in account currency in Spot Funds; asset scope belongs to the balance operation, not to Pnl
|
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 |
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, Pythonfrom_int, JSAccountId.fromInt(with abigint), C++AccountId::FromUint64, Rustfrom_u64, Copenpit_create_param_account_id_from_uint64. -
String - hashes the string with FNV-1a 64-bit; collisions are theoretically possible. Names per language: Go
NewAccountIDFromString, Pythonfrom_string, JSAccountId.fromString, C++AccountId::FromString, Rustfrom_str, Copenpit_create_param_account_id_from_string. Probability forndistinct 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.
| Type | Positive | Negative |
|---|---|---|
Pnl |
Profit | Loss |
Fee |
Fee paid | Rebate received |
CashFlow |
Inflow | Outflow |
PositionSize |
Long | Short |
Quantity, Volume, and Notional are non-negative.
Orders carry a single trade amount field whose variant defines the meaning:
-
trade amountasQuantity: order is sized by instrument amount. -
trade amountasVolume: 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:
-
Pricepresent: 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. -
Priceabsent: 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.
| Property | Value |
|---|---|
| Minimum | 1x |
| Maximum | 3000x |
| Step | 0.1x |
Go behavior:
- Constructors and checked arithmetic return
(value, error). -
errors.Isclassifies validation and arithmetic failures through theparam.Err*sentinels. - Rounded constructors accept an explicit
param.RoundingStrategy. - String constructors and
String()preserve exact decimal values.
Python behavior:
- Decimal-based
paramtypes behave likedecimal.Decimalwith domain-type safety: arithmetic works only between operands of the same type. - Constructors accept
Decimal,str,int, orfloat. - The
.decimalproperty returns the underlyingdecimal.Decimalfor cross-type computations or standard-library interop. -
str(value)produces the canonical decimal string. - Invalid inputs raise
ValueError.
JS behavior:
-
fromStringis the lossless constructor for decimal input, andtoString()returns the canonical exact value. -
fromIntaccepts exact integer input, includingbigint;fromFloatis an explicit convenience for an IEEE-754 boundary value. - Checked construction and arithmetic raise typed
ParamError/ value-error subclasses with stablecode,param, and optionalinputfields. - Rounded factories accept the
RoundingStrategystring union or a value fromRoundingStrategies.
C++ behavior:
-
FromStringis the exact constructor for monetary input;FromDoubleis 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)andQuantity::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 asnotional / leverageusing exact decimal arithmetic. -
Notional::from_volume(v)/Volume::from_notional(n)reinterpret between trade size and position exposure without numeric change.
The JavaScript SDK carries the decimal contract in the language's own terms, so
it needs a few rules of its own. Prices, quantities, and money cross the
boundary as decimal strings - the only lossless form for full- and
variable-scale instruments. This mirrors what major exchange APIs do on the
wire, and it sidesteps the IEEE-754 rounding that silently corrupts trailing
digits (0.1 + 0.2 === 0.30000000000000004).
The DecimalInput type accepted on input is:
type DecimalInput = string | number | bigint;-
stringis the recommended, lossless form ("100.50","0.00847000"). -
bigintis safe for exact integer values. -
numberis an IEEE-754 double and is accepted only as a convenience for small exact integers. Never usenumberfor fractional money.
Value types are constructed from DecimalInput and serialize back to a decimal
string; they never return a raw number for money.
import { Price } from "@openpit/engine/param";
const price = Price.fromString("100.50");
price.toString(); // "100.50"
price.toJSON(); // "100.50" (so JSON.stringify is lossless)
// Quantize to an instrument tick with an explicit rounding strategy.
Price.fromStringRounded("1.005", 2, "default").toString(); // "1.00"The canonical rounding strategies are "midpointNearestEven",
"midpointAwayFromZero", "up", and "down". Four ergonomic aliases are also
available: "default" and "banker" map to midpoint nearest-even, and
"conservativeProfit" and "conservativeLoss" map to round-down. The plain
strings are accepted directly; the package also exports a RoundingStrategies
constant, with a matching RoundingStrategy type, for autocomplete. Alongside
it the package exports RejectCode, RejectScope, and
SpotFundsPricingSource value sets for the stable strings it accepts, and the
param subpath exports the FillType wire values and the ParamKind names
used by cross-language error reporting.
Wrapper classes are not required to feed the engine. Scalar inputs accept a
plain value (an account id as a number, a side as "BUY", a price as
"185.00"), and every group accepts a plain object literal, so an order or an
execution report can be written inline. The wrapper classes remain available as
a typed alternative and are interchangeable with the plain forms.
Ordinary setters and input coercions borrow or clone wrapper values, so passing
a value type, a model group, or a similar value does not invalidate the caller's
handle. The clone methods are available when an independent copy is useful,
but are not required just to assign a value.
Only lifecycle handles and staged builders intentionally become unusable after
their terminal operation: Request, Reservation, Mutation, and a consumed
engine builder. Using one again raises the lifecycle error described in
Errors.
Go exposes:
openpit/param.Kindopenpit/param.FillTypeopenpit/param.RoundingStrategy-
openpit/param.ErrNegative,ErrDivisionByZero,ErrOverflow, and the other idiomatic sentinel errors used witherrors.Is
Python exposes:
openpit.param.Kindopenpit.param.FillTypeopenpit.param.RoundingStrategy-
openpit.param.ParamError, raised as a Python exception
JS exposes from @openpit/engine/param:
ParamKindFillType-
RoundingStrategyandRoundingStrategies -
ParamError, a typedRangeErrorbranch with structured fields
C++ exposes:
openpit::param::Kindopenpit::param::FillTypeopenpit::param::RoundingStrategy-
openpit::Error, with an optional native parameter error code
Rust exposes:
openpit::param::ParamKindopenpit::param::FillTypeopenpit::param::RoundingStrategyopenpit::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.
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.5C++
#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");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()); // => SHORTC++
#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);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.5JavaScript
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.5C++
#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);- Policies: Value types used by built-in and custom policies
- Architecture: Public integration model