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

<!-- markdownlint-disable MD033 MD013 -->
# 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 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` |

## 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.

## JavaScript Decimals and Handles

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:

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples.readme.test.ts -->
```ts
type DecimalInput = string | number | bigint;
```

- `string` is the recommended, lossless form (`"100.50"`, `"0.00847000"`).
- `bigint` is safe for exact integer values.
- `number` is an IEEE-754 double and is accepted only as a convenience for
  small exact integers. **Never use `number` for fractional money.**

Value types are constructed from `DecimalInput` and serialize back to a decimal
string; they never return a raw `number` for money.

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples.readme.test.ts -->
```ts
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.

### Plain Values and Object Literals

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.

### Borrowed Versus Consumed Handles

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](Errors.md#javascript-and-typescript).

## 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

<details>
<summary>Go</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/go/examples_wiki_test.go -->
```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")
}
```

</details>

<details>
<summary>Python</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/python/tests/integration/test_examples_wiki.py -->
```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)
```

</details>

<details>
<summary>JavaScript</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples_wiki.test.ts -->
```ts
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
```

</details>

<details>
<summary>C++</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/cpp/test/wiki/domain_types_test.cpp -->
```cpp
#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");
```

</details>

<details>
<summary>Rust</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/crates/openpit/tests/examples_wiki.rs -->
```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");
```

</details>

### Example: Work With Directional Types

<details>
<summary>Go</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/go/examples_wiki_test.go -->
```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")
}
```

</details>

<details>
<summary>Python</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/python/tests/integration/test_examples_wiki.py -->
```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"
```

</details>

<details>
<summary>JavaScript</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples_wiki.test.ts -->
```ts
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
```

</details>

<details>
<summary>C++</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/cpp/test/wiki/domain_types_test.cpp -->
```cpp
#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);
```

</details>

<details>
<summary>Rust</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/crates/openpit/tests/examples_wiki.rs -->
```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);
```

</details>

### Example: Create Leverage

<details>
<summary>Go</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/go/examples_wiki_test.go -->
```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")
}
```

</details>

<details>
<summary>Python</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/python/tests/integration/test_examples_wiki.py -->
```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
```

</details>

<details>
<summary>JavaScript</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/js/tests/examples_wiki.test.ts -->
```ts
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
```

</details>

<details>
<summary>C++</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/bindings/cpp/test/wiki/domain_types_test.cpp -->
```cpp
#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);
```

</details>

<details>
<summary>Rust</summary>

<!-- Test mirror: https://github.com/openpitkit/pit/blob/main/crates/openpit/tests/examples_wiki.rs -->
```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);
```

</details>

## Related Pages

- [Policies](Policies.md): Value types used by built-in and custom policies
- [Architecture](Architecture.md): Public integration model
<!-- markdownlint-enable MD033 MD013 -->
