This file explains Phoenix perpetuals numeric units and market metadata, including negative base-lot decimals and RWA execution bands. The public market response was checked on 2026-10-03.
TL;DR
- Base lot is
10^-baseLotsDecimals; negative decimals mean lots larger than one unit. - Quote lot is one millionth of a USD; price tick depends on both tick size and base-lot decimals.
- Exact integer arithmetic prevents the SDK's floating-point floor trap.
- Market status and isolated-only eligibility are separate conditions.
- After-hours does not itself mean closed. Bands expire at reopening and must come from fresh metadata.
1. Exact units
For bld = baseLotsDecimals and integer tickSize:
baseLotUnits = 10^(-bld)
quoteLotUsd = 10^(-6)
tickUsdPerUnit = tickSize × 10^(bld - 6)
priceTicks = priceUsdPerUnit × 10^(6 - bld) / tickSize
Quote decimals 6 were verified against the on-chain global configuration on 2026-09-24. A negative bld, such as -2, yields a lot of 100 units. Such metadata was present in the public list on 2026-10-03.
Synthetic examples illustrate arithmetic, not actual current listings: tickSize=100, bld=4 gives a one-dollar price tick and a 0.0001-unit lot; tickSize=10, bld=2 gives a 0.001-dollar tick and a 0.01-unit lot.
Use a shortest-decimal representation converted to a rational and integer division. The evaluated Rise 0.5.28 priceToTicks uses floating-point multiplication and floors; a price intended to sit exactly on-grid can shift one tick. Resting nearest rounding and IoC buy-floor/sell-ceil are distinct operations. Size conversion uses caller-selected floor/ceil and a small numerical tolerance for double artifacts, without adding a full lot.
u64 and i64 parsers accept decimal strings and enforce their bounds. Numbers are rejected because precision may already be lost. One lot was sufficient for tested resting/IoC orders on 2026-09-24; an additional universal dollar floor was not established.
2. Metadata and statuses
Required market fields include symbol, asset ID, book and spline addresses, tick size, base-lot decimals, status, isolated-only flag, and leverage-tier data. The toolkit decoder accepts the documented array/envelope forms and rejects duplicates or malformed grids as a whole response.
Statuses from the protocol are uninitialized, active, postOnly, paused, closed, and tombstoned. Active and post-only can be listed; paused is unknown tradability, while closed/tombstoned/uninitialized are not tradable. A post-only market still needs the appropriate order instruction; packet acceptance in this mode was not tested in the evaluated implementation.
The 2026-10-03 public response contained 92 active markets, 9 isolated-only markets, and 43 RWA markets. These counts describe that response only. The first leverage tier supplies a maximum leverage; there is no per-account leverage setter in the evaluated protocol.
Isolated markets use child subaccounts according to the official SDK. The toolkit can decode their metadata and account rows; child registration/funding/trading orchestration is not implemented or live verified.
3. RWA after-hours metadata
RWA rows may carry commodityMetadata with after-hours flag, index anchor, band radius, mark band, execution band, and index-expiry timestamp. Calendar metadata includes a calendar URI, content hash, and next transition time.
REST execution bands use {min, max}; SDK stream shapes can use {lower, upper}. Validate both bounds, allowing a zero lower bound where applicable. A malformed band makes that band unusable without necessarily invalidating all otherwise valid market metadata.
The after-hours helper returns a band only when metadata is fresh, isAfterHours is true, and the calendar/index-expiry evidence has not expired. Stop using it at the next transition even if a cached response still says after-hours. A future expiry value is a response field, never the date of verification.
Public data observed 2026-10-03 showed calendar transition and index expiry values consistent with an expiry shortly after opening; that observation does not establish a permanent timing guarantee.
4. Band execution and caching
Off-hours trading inside a static execution band was observed on 2026-09-27. Out-of-band rejection is described by OutsideExecutionPriceBand in public Rust event source. Band-edge inclusivity and the fill behavior of an IoC clamped exactly to that edge remain unverified. clampIocToBand is therefore experimental.
The market cache has explicit TTL, freshness window, and failed-refresh backoff settings; freshness is not an implicit multiple of TTL. Reject an update that drops an in-use market or changes asset ID/tick/lot unexpectedly. Transaction-key pinning compares book, spline, tick, and lot metadata; a changed pin is a review condition, not permission to trade a newly mapped instrument.
Pitfalls
| What breaks | Why | Correct approach |
|---|---|---|
| Negative decimals produce a tiny lot | Decimal exponent was reversed | Use 10^-bld |
| On-grid price shifts down | Float-floor SDK conversion | Exact rational quantization |
| Weekend marked as delisted | After-hours confused with status | Inspect fresh execution band and calendar |
| A cached band blocks a reopened market | Transition evidence expired | Discard at transition |
| Cross account sends an isolated-only order | Eligibility ignored | Explicitly unsupported orchestration |
Open questions / not verified
- Band-edge inclusivity, clamped IoC behavior, and post-only market packet acceptance.
- Delegate permissions for isolated child registration/funding.
- Calendar or metadata transitions during an in-flight order.
Sources
Public Phoenix market metadata (GET checked 2026-10-03); List markets; Mark price and bounds; Market specs; Rise 0.5.28 numeric builders in Rise public source. After-hours observation: 2026-09-27. Cache/pinning policies are toolkit safeguards.
© markpaper authors. Licensed under CC BY 4.0: when publishing or adapting this material, credit “markpaper — Phoenix knowledge base” and link to the original and the license.