Every market has tick size, step size, and minimums. The SDK loads spot and Zipper catalogs so decimal inputs can be converted to wire integers safely.
Wait for ready
Order and trigger write paths wait for catalog hydration when hydrate_catalogs: true (default). You can still call this explicitly before reads that need symbol ids/scales:
client.wait_for_catalogs().await?;Hydration is fail-closed: wait_for_catalogs returns an error when the attempt fails. See Catalog.
Decimal inputs
Order writes take typed Price / Quantity on create params (see Orders). Prefer decimal constructors for human inputs;
use ticks / scaled integers for bots. Strict decimal form is digits with an optional fractional
part (65000 / 65000.5); bare trailing dots (65000.) are rejected. Leading/trailing
whitespace is trimmed before validation.
Unscaled decimal quantities need a symbol (or an already-scaled Quantity) so the SDK can resolve
catalog scale. base_quantity_scale_for_symbol returns Option<u32> and never invents scale 8
for unknown or unhydrated symbols. Write paths fail as Error::Validation instead of
guessing. Scale-dependent reads and streams fail the same way when the catalog scale is unknown.
Scale 0 is valid for whole-unit assets and is distinct from a missing scale.
Protocol scale ceiling
Quantity and ledger formatters, parsers, and catalog hydration reject scales above MAX_PROTOCOL_SCALE (36). That ceiling is an SDK safety bound against pathological padding โ
it is not the ledger canonical scale. Ledger balances and amount_e18 remain fixed at
scale 18. Trading quantity scales come from the spot catalog and are typically well below 36.
Error::Validation before send.Scaled integers (bots)
use polyester::types::{Price, Quantity, QuantityDomain};
let price = Price::from_ticks(100_000_000, None)?; // protocol 1e6 ticks
let scale = client
.catalogs
.base_quantity_scale_for_symbol("BTC-USDT")
.ok_or_else(|| polyester::Error::validation("BTC-USDT quantity scale is unavailable"))?;
let qty = Quantity::from_scaled(
1_000_000,
Some(scale),
QuantityDomain::OrderBase,
Some("BTC-USDT".into()),
None,
)?;Protocol price ticks use fixed 1e6 scale; the server still validates market tick size.
Immutable money metadata
Price and Quantity metadata is fixed by their validated constructors. Read it through
immutable getters:
use polyester::types::{Price, Quantity, QuantityDomain};
let scale = 8;
let price = Price::from_ticks(100_000_000, None)?;
let qty = Quantity::from_scaled(
1_000_000,
Some(scale),
QuantityDomain::OrderBase,
Some("BTC-USDT".into()),
None,
)?;
assert_eq!(price.symbol(), None);
assert_eq!(qty.scale(), Some(scale));
assert_eq!(qty.domain(), QuantityDomain::OrderBase);
assert_eq!(qty.symbol(), Some("BTC-USDT"));
assert_eq!(qty.symbol_id(), None);This is an alpha breaking change. Migrate price.symbol to price.symbol() and migrate quantity.scale, quantity.domain, quantity.symbol, and quantity.symbol_id to the
corresponding getter calls. Metadata cannot be reassigned or supplied through struct literals;
construct a new validated value instead.
let id = client.catalogs.symbol_id_for_symbol("BTC-USDT");
let scale = client.catalogs.base_quantity_scale_for_symbol("BTC-USDT");Also see Scaled integers and Public IDs.