Skip to main content
Spline liquidity is a Phoenix-specific liquidity source for quoting a shaped curve without placing one FIFO order at every price level. A spline trader publishes a mid price and bid/ask regions. The matching engine materializes those regions into visible price levels when it builds the combined book.

Trader account restrictions

A trader account with any active spline registration cannot place FIFO limit orders or stop-loss orders on any market. Use a separate trader account, such as a dedicated isolated subaccount, for spline activity if you also need regular order-book activity. Other trader accounts owned by the same wallet authority are unaffected. See Accounts for account boundaries.

Spline data model

A spline trader controls: Phoenix separately tracks sequence ordering, expiry, consumed visible and hidden liquidity, and risk-limited bid and ask capacity. Each region is defined by:
  • start_offset, inclusive
  • end_offset, exclusive
  • density, in base lots per tick
  • top_level_hidden_take_size, in base lots
  • lifespan, in slots
Each side supports up to 10 regions. Regions on the same side must be ordered by increasing start_offset and cannot overlap; adjacent regions and gaps are allowed. A spline can quote only one side, but it must have at least one region in total. If both sides are configured, their first regions cannot both start at offset zero. Region offsets are tick distances from the spline mid:
  • bid offset xx maps to mid price−x\text{mid price} - x
  • ask offset xx maps to mid price+x\text{mid price} + x
  • region size is (end offset−start offset)×density(\text{end offset} - \text{start offset}) \times \text{density} base lots
For example, a spline with mid 100, bid region [1, 3), and density 5 exposes:
  • 5 lots at 99
  • 5 lots at 98
An ask region [1, 3) on the same spline exposes:
  • 5 lots at 101
  • 5 lots at 102

Region lifespan

lifespan is measured in slots from the spline’s user_update_slot. A region remains eligible through user_update_slot + lifespan, inclusive. Every accepted price update resets the lifespan of every configured region by setting the spline’s expiry anchor to that update’s user_update_slot. Each region then receives its full configured lifespan from the new anchor. This happens whether refresh_regions is true or false; that flag controls consumed liquidity, not expiry. Use GTC_LIFESPAN (u64::MAX) for a region that does not expire. A lifespan of 0 is eligible only at user_update_slot; it does not mean good-til-cancelled.

How spline levels become visible

A spline level is eligible for the book when:
  • the spline is active
  • the spline has a nonzero mid price
  • the region has not expired
  • the region still has unfilled size
  • the spline trader has remaining risk capacity on that side
The visible level is the first unfilled tick in the active region. If a tick is partially filled, only the remaining size at that tick is displayed. Once a tick is fully consumed, the next tick in the region becomes visible. Spline liquidity is therefore not a static list of orders. It is derived from the spline’s mid price, region definitions, region fill state, expiry, and current risk counters.

Updating a spline

Both price and parameter updates accept a refresh_regions flag, but preserving state has different effects for the two update types: Previously consumed liquidity is replenished only when refresh_regions=true.

Risk caps

Before matching, Phoenix caps each spline’s available bid and ask size using the spline trader’s current risk state. The cap accounts for:
  • current margin and position state
  • leverage_decrease_in_bps
  • optional max position size limits
  • the current risk action
After each spline fill, the in-memory risk counter is decremented. This prevents one instruction from consuming more spline liquidity than the trader can support across multiple price levels. max_position_size has two modes: Limit { long: 0, short: 0 } is reduce-only: the spline can quote only on the side that moves an existing position toward zero. When a limit is active, Phoenix clips each side to its remaining capacity:
  • bid capacity is capped by the distance from the current position to the maximum long position
  • ask capacity is capped by the distance from the current position to the maximum short position
At the long cap, bid liquidity becomes zero while ask liquidity can remain available to reduce the position. At the short cap, the inverse applies. leverage_decrease_in_bps reduces the effective collateral used to calculate spline risk capacity: effective collateral for spline capacity=effective collateral×10,000−leverage decrease in bps10,000\text{effective collateral for spline capacity} = \text{effective collateral} \times \frac{10{,}000 - \text{leverage decrease in bps}}{10{,}000} The value must be between 0 and 10,000. A value of 0 applies no haircut, 5,000 uses 50% of effective collateral, and 10,000 removes margin-backed capacity for increasing exposure. This setting does not change deposited collateral or the market’s leverage tiers.

Fees

The spline trader pays zero exchange fees on spline fills:
  • when an incoming order matches a spline, the incoming trader pays its configured taker fee and the spline trader pays zero
  • during uncrossing, the resting FIFO trader pays its configured maker fee, or receives its configured maker rebate, while the spline trader pays zero
See Fees for standard maker and taker rates.

Uncrossing and slide prices

Spline liquidity from different traders can cross when their mids and regions overlap. Phoenix resolves this before rendering or matching spline-only liquidity. The uncross path:
  1. Matches crossed splines against FIFO liquidity first when the spline crosses the explicit book.
  2. Computes slide prices for remaining crossed spline-only liquidity.
  3. Displays and matches more aggressive spline bids at the slide bid.
  4. Displays and matches more aggressive spline asks at the slide ask.
Slide prices make the remaining spline book non-crossing without writing synthetic orders into the FIFO book. Splines do not execute directly against other splines. A spline can execute against crossed FIFO liquidity during uncrossing; remaining spline-to-spline crossings are resolved using slide prices.

Pro-rata fills

At a single spline price level, FIFO price-time priority does not apply across spline traders. Phoenix allocates the fill pro-rata by available size: allocation=⌊available lots×fill sizetotal available⌋\text{allocation} = \left\lfloor \frac{\text{available lots} \times \text{fill size}}{\text{total available}} \right\rfloor Spline sequence number is still used for deterministic iteration and dust handling. It is not the primary priority rule. When integer rounding leaves unallocated lots, sequence order determines which spline receives the remainder. Important execution consequences:
  • one spline price level consumes one match_limit unit, even if multiple splines participate
  • one FIFO maker order consumes one match_limit unit
  • spline liquidity at the same price fills before FIFO liquidity
  • worse spline prices do not skip better FIFO prices

Hidden take size

top_level_hidden_take_size is configured per region in base lots. It is separate from the region’s displayed density and total size, and it is not available to incoming takers. It is available only when the spline itself takes FIFO liquidity during uncrossing. In that path, Phoenix can consume visible size at the region’s current top tick, then up to the remaining hidden take size at that same tick, then visible size through deeper ticks. Phoenix tracks consumed hidden size separately from visible fills, and refresh_regions applies to both.