Order Types¶
FastBoxEx has four ways to submit an order. This page explains each one, when to use it, and why the design is different from a traditional exchange.
The short version¶
| Method | What it does | When to use it |
|---|---|---|
submit_order(price, qty, side) |
Limit — rests at an explicit price | Market-making, passive quoting |
submit_market(qty, side) |
Market — fills at whatever is available | Immediate fill, don't care about price |
submit_delta(delta_ticks, qty, side) |
Delta — delta_ticks inside/through the spread |
Aggressive fill with price control |
submit_ioc(delta_ticks, qty, side) |
IOC delta — same, but cancel the rest if partially filled | One-shot execution, no resting orders |
delta_ticks=0 on a buy → executes at the best ask. On a sell → executes at the best bid.
This is called a touch order — the most common aggressive pattern.
1. Limit order¶
The baseline. Place a bid or offer at an explicit price. The order rests on the book until it is filled or cancelled.
# Limit buy at 99.95 display (= 999_500 raw ticks)
slot.submit_order(asset_id, price=999_500, qty=100, side=0)
# Limit sell at 100.05
slot.submit_order(asset_id, price=1_000_500, qty=100, side=1)
Price scale reminder
price is always in raw ticks. 1 display unit = 10 000 ticks.
Divide by 10_000 to get the price you see on screen.
2. Market order¶
Sweeps the opposite side of the book at whatever prices are available — you don't care what you pay, you just want the fill.
slot.submit_market(asset_id, qty=100, side=0) # buy 100 at market
slot.submit_market(asset_id, qty=100, side=1) # sell 100 at market
Empty book → rejection, not silence
If the opposite side of the book is empty, you get a BookEmpty rejection back
immediately. Unlike many real exchanges, nothing is silently dropped or queued.
Under the hood — how market differs from limit
Internally, a market order is a delta order with an infinite delta. On the wire,
this is encoded as price = u64::MAX (the sentinel value 0xFFFF_FFFF_FFFF_FFFF).
The matcher sees a BBO-relative order that is infinitely aggressive and can therefore
sweep any price level that exists. No special code path — same routing, same guarantees.
3. Delta order — BBO-relative pricing¶
The most powerful primitive. Instead of naming an absolute price, you say:
"I want to be N ticks inside/through the spread".
The exchange computes the final price at the moment the order arrives:
Buy delta: effective_price = best_ask + delta_ticks × tick_size
Sell delta: effective_price = best_bid + delta_ticks × tick_size
# Touch buy — execute at the current best ask, rest the remainder
slot.submit_delta(asset_id, delta_ticks=0, qty=200, side=0)
# Two ticks above the ask — more aggressive, useful in fast markets
slot.submit_delta(asset_id, delta_ticks=2, qty=200, side=0)
If only part of the order fills, the remainder rests at the computed price — it becomes
a regular limit order sitting at effective_price.
Why delta_ticks is in ticks, not in price
delta_ticks counts increments of tick_size, not raw ticks.
So delta_ticks=1 always means "one minimum price step", regardless of what
the tick_size is on a given asset.
If it were expressed in raw ticks you'd have to know the tick size of every asset to write portable strategy logic. With tick-count deltas:
delta_ticks=0is always "touch"delta_ticks=1is always "one tick past touch"- Works identically across JTW, arena assets, and future instruments
Another consequence: the computed price is always on the tick grid by definition — no rounding, no remainder, no off-grid rejections.
4. IOC delta — execute and cancel the rest¶
Same as a delta order, but the unfilled remainder is cancelled immediately rather than resting on the book.
# Try to buy 500 at the best ask — fill what you can, cancel the rest
slot.submit_ioc(asset_id, delta_ticks=0, qty=500, side=0)
You get one or more fills (possibly none), then a Cancelled ack for the unfilled portion.
No order is left on the book.
When to use IOC vs delta
- Delta (GTC): you want the full quantity but are willing to wait — typical for size that the top-of-book can't absorb in one shot.
- IOC: you want opportunistic fills — take what's there, leave no trace, no future exposure. Good for TWAP slices or signalling-sensitive strategies.
5. Passive peg (advanced)¶
Place at N ticks behind your own side of the spread:
Buy passive: effective_price = best_bid − delta_ticks × tick_size
Sell passive: effective_price = best_ask − delta_ticks × tick_size
delta_ticks=0 → join the best bid/ask exactly.
delta_ticks=1 → one tick behind — you're in the queue but not leading.
A convenience wrapper (submit_passive) is coming in v0.28. For now, use the raw wire:
from fastboxex.protocol.wire import Order, REFERENCE_MODE_DELTA, REMAINDER_REST
order = Order(
user_id=slot.user_id,
asset_id=asset_id,
price=delta_ticks, # interpreted as Δ in tick units
quantity=qty,
side=0, # BUY
_padding=[REFERENCE_MODE_DELTA, REMAINDER_REST, 1, 0, 0, 0],
# ^ delta_sign=1 → passive
)
slot.submit_raw(order)
Why passive pegs are useful for market-makers
A pure market-maker strategy wants to be at the top of the book but prefers to avoid
being the only one there (adverse selection risk). A passive peg with delta_ticks=1
lets you sit one tick behind the best bid/ask — you earn liquidity-provider rebates
without being the first to trade against an informed flow.
As the BBO moves, you can cancel and re-peg each tick rather than chasing it with absolute prices, which avoids race conditions where your cancel arrives after a fill.
Why this design is different from traditional exchanges¶
Traditional exchanges enumerate order types as a flat list: Limit, Market, IOC, FOK, Peg, Mid-Peg, Discretionary, TWAP… Each type has its own code path and its own edge cases. The interactions between them are often undocumented, and the definitions shift between venues.
FastBoxEx derives all of these from three binary parameters:
| Parameter | Wire field | 0 | 1 |
|---|---|---|---|
reference_mode |
_padding[0] |
Absolute price (Limit) | BBO-relative (Delta) |
remainder_mode |
_padding[1] |
Rest on book (GTC) | Cancel immediately (IOC) |
delta_sign |
_padding[2] |
Aggressive (through spread) | Passive (trail own side) |
Every combination is valid. The four methods in the SDK (submit_order, submit_market,
submit_delta, submit_ioc) are convenience wrappers for the most common vertices.
The big practical benefit is determinism:
- Empty book on a delta/market order →
BookEmptyrejection. Always. Never a silent drop. - Delta that would produce an astronomically wrong price →
DeltaOverflowrejection. - Price outside the order collar →
OutsidePriceBandrejection.
You always get an ack. You always know what happened.
Mathematical foundation
Let \(\tau\) be the tick size, \(B = \beta_\text{bid}\) and \(A = \beta_\text{ask}\) the current best bid and ask. The effective price is:
where \(\beta_\text{opp}\) is the best price on the opposite side for aggressive
orders (\(\sigma = +1\)) and the same side for passive pegs (\(\sigma = -1\)),
and \(\delta \in \mathbb{Z}_{\geq 0}\) is delta_ticks.
The market sentinel is \(\delta = \infty\) encoded as price = u64::MAX.
The remainder policy \(\rho \in \{0,1\}\) is applied after matching: \(\rho=1\) (IOC)
cancels any unfilled volume with a RemainderCancelled engine event, serialised as
OrderAck(Cancelled) with price = p_eff and remaining_volume = cancelled_qty.
The function is total: every valid input produces a deterministic output. The only partial inputs are the two explicit error cases (empty BBO, overflow) which map to named reject codes rather than undefined behaviour.
Rejection codes¶
| Code | Name | Meaning | What to do |
|---|---|---|---|
| 11 | BookEmpty |
Market/delta on empty opposite side | Wait for liquidity or use a limit |
| 12 | DeltaOverflow |
delta_ticks × tick_size overflows |
Use a smaller delta_ticks |
| 5 | OutsidePriceBand |
p_eff outside order collar |
Reduce delta_ticks |
| 7 | RiskCheckFailed |
Not enough cash (buy) or inventory (sell) | Check your balance |
| 8 | ZeroQuantity |
qty rounds to 0 after lot-size alignment |
Increase qty |
Quick reference¶
# ── Limit ──────────────────────────────────────────────────────────
slot.submit_order(asset_id, price=1_000_000, qty=100, side=0) # BUY at 100.00
# ── Market ─────────────────────────────────────────────────────────
slot.submit_market(asset_id, qty=100, side=0) # BUY at any price
# ── Delta (GTC) ────────────────────────────────────────────────────
slot.submit_delta(asset_id, delta_ticks=0, qty=100, side=0) # touch buy
slot.submit_delta(asset_id, delta_ticks=2, qty=100, side=0) # 2 ticks above ask
# ── IOC delta ──────────────────────────────────────────────────────
slot.submit_ioc(asset_id, delta_ticks=0, qty=100, side=0) # fill or cancel