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.
Excess precision relative to the catalog should error; 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.
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), # protocol 1e6 ticks
post_only=True,
)Protocol price ticks use fixed 1e6 scale; that is not the same as market tick-size alignment (the server still validates tick size).
Also see Scaled integers and Public IDs.