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 auto-await catalog hydration when hydrate_catalogs=True (default). You can still call this explicitly before reads that need symbol ids/scales:
await client.wait_for_catalogs()Hydration is fail-closed. wait_for_catalogs raises when a fetch
fails. Empty catalogs can still break a write with PolyesterValidationError. See Catalog.
Decimal inputs
Use decimal strings (or Price / Quantity) for human-facing qty / price. Do not pass
floats, binary floating point is not a financial representation. 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.
Decimals may carry zeros beyond their scale: qty="1.500" on a pair with base scale 2 is 1.5, and price="70000.0000000000" is 70000. A nonzero digit beyond the scale raises PolyesterValidationError; values are not silently rounded. Unscaled
decimal quantities need a symbol (or an already-scaled Quantity) so the SDK can resolve catalog
scale. Catalog lookups return None for unknown or unhydrated symbols; they never invent scale 8.
Write paths raise PolyesterValidationError instead of guessing.
await client.orders.create(
symbol="BTC-USDT",
side="buy",
order_type="limit",
tif="gtc",
qty="0.01",
price="64250.5",
post_only=True,
)post_only=True on market / IOC / FOK raises PolyesterValidationError before send.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.
An order quantity whose scaled value does not fit the signed 64-bit wire field (at most
9223372036854775807, which is 9.223372036854775807 at scale 18) raises PolyesterValidationError before anything is sent. A Decimal quantity, price, or amount is checked against its scale and
size first, so an extreme value such as Decimal("1e-1000000") is refused at once with a short
error. Decimal strings are not affected.
polyester.codecs.scalars.format_scaled(value, scale) formats an integer value / 10**scale as a
decimal string for any non-negative scale, for example a notional at base scale + 9. Order.cum_quote, Order.quote_used, and Order.quote_unspent work at every base quantity scale
from 0 to 36. Pass the pair's scales from the catalog (base_quantity_scale, and quote_quantity_scale for quote_unspent); a scale below 0 or above 36 raises PolyesterValidationError.
Scaled integers (bots)
Stay in integer space when your strategy already works in wire units:
from polyester import Price, Quantity
await client.orders.create(
symbol="BTC-USDT",
side="buy",
order_type="limit",
tif="gtc",
qty=Quantity.from_scaled(1_000_000, scale=8),
price=Price.from_ticks(100_000_000_000), # 100 in protocol 1e9 ticks
post_only=True,
)Protocol price ticks use a fixed 1e9 scale; that is not the same as market tick-size alignment (the server still validates tick size).
Also see Scaled integers and Public IDs.