Skip to content

FAQ: Orders, Sessions and Positions

Questions that come up once you trade beyond the first day: why an order type was rejected or did not trigger, how average cost and saved orders behave, and what self-match prevention does. The first-day questions are in the Quick Start Guide's FAQ & Common Mistakes; how the exchange is installed, configured and operated is in the Operator's Guide's FAQ: Setup and Operation.

Order types

Why was my IOC order rejected?

IOC (Immediate-Or-Cancel) orders cannot rest on the book, so they are rejected during any non-matching phase (PRE_OPEN, OPENING_AUCTION, CLOSING_AUCTION, CLOSED). IOC is only valid during CONTINUOUS trading.

[09:20:11.403] REJECTED  6f5471b08c2d3eb970dc8c864bf270cb code=SESSION_NOT_PERMITTED  IOC orders not accepted during PRE_OPEN

The same applies to MARKET and FOK orders.

Fix: Type SESSION in the trader console to see the current phase, then wait for CONTINUOUS, or use a LIMIT order with TIF=DAY if you are willing to have the order rest.

Why didn't my STOP order trigger?

STOP orders only trigger from last trade price events. If no trades have occurred since you submitted the STOP, the trigger is never evaluated — regardless of the current bid/ask quotes.

# This SELL STOP at 148 won't fire if no trades have happened yet
NEW|SYM=AAPL|SIDE=SELL|TYPE=STOP|QTY=100|STOP=148.00

Fix: Check whether any trades have printed in the engine output (--verbose mode) or in the audit log. A stop only becomes relevant once the market is actively trading through its trigger level.

Also check the trigger direction. A SELL STOP triggers when last_trade_price <= stop_price. If the market is above your stop, it won't fire until price falls to or through it.

See Stop Trigger Logic for full details.

Why did my STOP_LIMIT order trigger but not fill?

This is expected behavior. When a STOP_LIMIT triggers, it converts to a LIMIT order at your specified PRICE=. If the market has moved past that limit price before the converted order can match, it sits on the book unfilled.

Example: SELL STOP_LIMIT with STOP=148.00 and PRICE=147.50. If the market gaps down to $146.00 before the converted limit can fill, your order rests at $147.50 — but nobody is bidding that high. You are protected from filling at $146.00, but you may not fill at all.

Tradeoff: STOP gives fill certainty. STOP_LIMIT gives price certainty. Neither gives both.

My FOK order was rejected even though there was liquidity. Why?

FOK checks whether the entire quantity can be filled at the limit price before executing. It does not execute at all unless the full size is available.

# Book has only 80 shares at 150.00, but you want 100
NEW|SYM=AAPL|SIDE=BUY|TYPE=FOK|QTY=100|PRICE=150.00
# → REJECTED because 80 < 100

Fix: Reduce QTY to match available liquidity, or use a LIMIT order and accept a partial fill.


Positions and P&L

Why does my average cost change when I buy more shares?

When you add to an existing position, your VWAP average cost updates to reflect the new shares at the new price. This is standard practice for tracking the true cost basis of a position built over multiple trades.

Example:

Buy 100 shares @ 150.00  →  avg_cost = 150.00
Buy 100 shares @ 152.00  →  avg_cost = (150 × 100 + 152 × 100) / 200 = 151.00

See P&L & Clearing — VWAP Average Cost.


Saved orders

My GTC orders didn't reload after restart. Why?

GTC orders are written to data/gtc_orders.json on engine shutdown (when you press Ctrl-C or the scheduler sends a CLOSED transition). If the engine was killed forcefully (kill -9 or a crash), the file was not written.

Fix: Check whether data/gtc_orders.json exists and has recent content. If the engine was killed mid-session, the file may be stale or missing.


Self-match prevention

What is SMP and why did my order get cancelled?

Self Match Prevention (SMP) stops you from accidentally trading against your own resting orders. If you submit a buy that would match a sell you already have on the same book, SMP kicks in.

# Submit a buy that would cross with your own resting sell
NEW|SYM=AAPL|SIDE=BUY|TYPE=LIMIT|QTY=100|PRICE=151.00|SMP=CANCEL_AGGRESSOR

With SMP=CANCEL_AGGRESSOR, the incoming buy is cancelled instead of filling against your own sell. Options:

Value Behaviour
NONE No SMP — you can trade against yourself
CANCEL_AGGRESSOR The incoming order is cancelled
CANCEL_RESTING The resting order is cancelled
CANCEL_BOTH Both orders are cancelled

Prices

Why does the engine use integer ticks instead of float prices?

Float arithmetic can introduce tiny representation errors in comparisons and aggregation. Using integer ticks makes price-time matching and level math exact.