Skip to content

Exchange Concepts

Before writing strategies, understand how the exchange works.


Prices: raw ticks vs display units

All prices on the wire are integers in raw ticks.

1 display unit = 10 000 raw ticks
You see Internal value Display
bid = 1_000_000 1 000 000 ticks 100.0000
ask = 1_000_400 1 000 400 ticks 100.0400
spread = 400 400 ticks 0.0400

Always divide by 10_000 when printing prices to users.

print(f"bid = {md['bid'] / 10_000:.4f}")   # ✓ correct
print(f"bid = {md['bid'] / 100:.2f}")      # ✗ 100× too large

The tick_size for JTW is 100 — the smallest allowed price increment is 100 raw ticks (= 0.0100 display).


Order types

FastBoxEx uses a unified order framework: every order shares the same wire struct. Three control bytes select the pricing regime, remainder policy, and delta direction.

SDK method Behaviour
submit_order(asset_id, price, qty, side) Limit — rests at explicit price
submit_market(asset_id, qty, side) Market — sweeps book at any price
submit_delta(asset_id, delta_ticks, qty, side) Delta — executes at BBO ± delta_ticks × tick_size; rests if partial
submit_ioc(asset_id, delta_ticks, qty, side) IOC — same as delta, cancel remainder

delta_ticks=0 → touch order — executes at the best available quote.

The unified price function

All four methods are derived from one formula:

\[ p_{\text{eff}} = \begin{cases} p_{\text{wire}} & \texttt{reference_mode} = 0 \quad (\text{Limit}) \\ \beta_{\text{opp}} \pm \delta \cdot \tau & \texttt{reference_mode} = 1 \quad (\text{Delta / Market}) \end{cases} \]

Classical market orders are undefined on empty books. The delta framework is fully deterministic: an empty book returns BookEmpty, an overflow returns DeltaOverflow. No silent failures.

→ See the Order Types guide for the complete treatment.


The order lifecycle

stateDiagram-v2
    direction LR
    [*] --> Accepted : submit_order
    Accepted --> PartialFill : partial match
    PartialFill --> Filled : fully matched
    Accepted --> Filled : fully matched
    Accepted --> Cancelled : cancel_order / ctx.cancel_all
    Accepted --> Rejected : risk limit / bad price
    PartialFill --> Cancelled : cancel remaining
    Filled --> [*]
    Cancelled --> [*]
    Rejected --> [*]

Each state transition generates an OrderAck message delivered to your on_fill / ack ring. order_status values:

Value Status What to do
1 New (accepted) Order is live on the book
2 Partially filled Part filled; remainder still resting
3 Rejected Check the reject reason code
4 Cancelled Order removed (by you or IOC)
5 Filled Fully matched
Handling acks robustly in your strategy

Most strategies only need to handle on_fill — which fires for status 2 and 5. But for production-grade logic:

  • Status 3 (Rejected): the reject_reason field tells you why — BookEmpty, RiskCheckFailed, OutsidePriceBand, etc. Log it; don't silently retry in a loop.
  • Status 4 (Cancelled): arrives for explicit cancel_all, but also for the IOC remainder of a submit_ioc call. The remaining_volume field tells you how much was cancelled.
  • Status 2 (Partial): if you're tracking inventory, update on every partial fill — don't wait for the final fill.

Exchange lifecycle (sessions)

The exchange runs in sessions. Within each session, the matching engine moves through states:

SessionStart → BookOnly → Auction → Trade → SessionClosed → Settlement
                                    ↑
                            (normal trading here)
Phase What happens Can you trade?
BookOnly Orders enter the book, no matching Submit / Cancel only
Suspended Paused — orders held No
Auction Equilibrium price discovery — matched at close Submit only
Trade Continuous double-auction matching ✓ Full trading
SessionClosed All open orders cancelled No
Settlement P&L computed, balances updated No

Your strategy runs during Trade phase. The StrategyEngine buffers ticks that arrive in other phases and delivers them once trading opens.


Arenas

Arenas are ephemeral, isolated matching engines you create on demand.

  • Your own asset (separate from JTW), your own participants
  • No settlement — balances reset on destroy
  • Designed for AI training: no session timing, no real-world constraints

An arena has two participant roles:

Role Purpose
Generator Provides continuous liquidity — posts the 10-level bid/ask book
Trader Your strategy — interacts against the generator's book

→ See the Arenas guide for complete examples.


Price convention summary

PRICE_SCALE = 10_000    # ticks per currency unit
TICK_SIZE   = 100       # minimum price increment for JTW

# Convert raw → display
display_price = raw_price / PRICE_SCALE

# Convert display → raw
raw_price = int(display_price * PRICE_SCALE)

# Spread (raw ticks → display units)
spread_display = (md["ask"] - md["bid"]) / PRICE_SCALE

# PnL from fills (raw → display)
pnl_display = realized_pnl_raw / PRICE_SCALE