Skip to content

Order Types - A Trader's Guide to Backpack Exchange

TL;DR

Basic Order Types:

  • Market Order - Execute immediately at best available price (fast, but price uncertain)
  • Limit Order - Execute at your specified price or better (price guaranteed, fill not guaranteed)

Time-in-Force (how long your order stays active):

  • GTC (Good-Til-Cancelled) - Stays on book until filled or cancelled
  • IOC (Immediate-or-Cancel) - Fill what you can now, cancel the rest
  • FOK (Fill-or-Kill) - All or nothing - fill completely or reject entirely

Execution Modifiers:

  • Post-Only - Only maker orders (rejected if it would take liquidity)
  • Reduce-Only - Only allows closing positions (won't increase exposure)

Trigger Orders:

  • Stop-Loss - Triggers when price moves against you (limit losses)
  • Take-Profit - Triggers when price moves in your favor (lock in gains)

Table of Contents

  1. Basic Order Types
  2. Time-in-Force
  3. Execution Modifiers
  4. Trigger Orders
  5. Order Lifecycle
  6. Common Mistakes & Gotchas

1. Basic Order Types

Market Order

What it does: Executes immediately at the best available price(s) in the order book.

When to use:

  • You need to get in/out NOW (urgency matters more than price)
  • High-liquidity markets where slippage is minimal
  • Emergency exits from bad positions

Example:

Order Book State:
  Asks: $100.10 (50), $100.20 (100), $100.30 (200)
  Bids: $100.00 (50), $99.90 (100)

You: Market BUY 80 units

Result:
  - 50 units filled at $100.10
  - 30 units filled at $100.20
  - Average price: $100.14 (not the "best" price!)

Key characteristics:

  • Always a taker (removes liquidity from book)
  • No price guarantee - you get whatever is available
  • Can specify quantity OR quote quantity (e.g., "buy $1000 worth")
  • Higher fees than limit orders (taker fees)

Warning: In thin order books, market orders can experience significant slippage - the difference between expected and actual execution price.

How Market Orders Match (Book Walking)

Market orders "walk the book" - they consume liquidity starting from the best price and moving to worse prices until filled.

For a BUY order: Matches against asks, starting at the lowest ask (best for buyer), then moves UP.

For a SELL order: Matches against bids, starting at the highest bid (best for seller), then moves DOWN.

Order Book:
  Asks: $100 (50), $101 (100), $102 (200)   ← Sells waiting
  Bids: $99 (50), $98 (100), $97 (200)      ← Buys waiting

┌─────────────────────────────────────────────────────────┐
│  Market BUY 120 units                                   │
├─────────────────────────────────────────────────────────┤
│  Step 1: Match $100 (best ask)                          │
│          → Fill 50 units @ $100                         │
│          → 70 units remaining                           │
│                                                         │
│  Step 2: Match $101 (next best ask)                     │
│          → Fill 70 units @ $101                         │
│          → 0 units remaining                            │
│                                                         │
│  Result: 120 units filled                               │
│          Avg price: (50×$100 + 70×$101) / 120 = $100.58 │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  Market SELL 120 units                                  │
├─────────────────────────────────────────────────────────┤
│  Step 1: Match $99 (best bid)                           │
│          → Fill 50 units @ $99                          │
│          → 70 units remaining                           │
│                                                         │
│  Step 2: Match $98 (next best bid)                      │
│          → Fill 70 units @ $98                          │
│          → 0 units remaining                            │
│                                                         │
│  Result: 120 units filled                               │
│          Avg price: (50×$99 + 70×$98) / 120 = $98.42    │
└─────────────────────────────────────────────────────────┘

Price-Time Priority: At each price level, orders are filled in the order they were placed (earlier orders first). The matching engine uses BTreeMap ordered by price, with order IDs (which are sequential) determining time priority within a price level.

Price Impact Protection

To prevent catastrophic slippage in thin order books, Backpack has price impact limits that stop market orders from penetrating too deep into the book.

How it works:

Configuration (per market):
  max_impact_multiplier: 1.10  (10% above best ask for buys)
  min_impact_multiplier: 0.90  (10% below best bid for sells)

Example - Market BUY with protection:
  Best ask (first price): $100
  Max allowed price: $100 × 1.10 = $110

  Order Book Asks: $100 (10), $105 (10), $112 (1000)

  You: Market BUY 500 units

  Step 1: Fill 10 @ $100 ✓
  Step 2: Fill 10 @ $105 ✓
  Step 3: Try $112... BLOCKED! ($112 > $110 limit)

  Result: Only 20 units filled, rest EXPIRED
  Reason: PriceImpact

For BUY orders:

price must be ≤ first_price × max_impact_multiplier

For SELL orders:

price must be ≥ first_price × min_impact_multiplier

Why this exists:

  • Prevents "fat finger" errors from wiping the entire book
  • Protects against manipulation via thin liquidity
  • Gives traders predictable worst-case slippage

What happens when triggered:

  • Order is partially filled up to the impact limit
  • Remaining quantity is expired with reason PriceImpact
  • You receive a partial fill, not a rejection

Pro tip: If your market order is getting expired due to price impact, either:

  1. Use a limit order with your max acceptable price
  2. Split into smaller orders
  3. Wait for more liquidity

Limit Order

What it does: Executes only at your specified price or better.

When to use:

  • You have a specific entry/exit price in mind
  • Price matters more than timing
  • You want to provide liquidity (make markets)
  • You want lower fees (maker rebates)

Example:

Current market: $100

You: Limit BUY at $99.50 for 100 units

Scenarios:
  A) Price stays at $100 → Order sits on book waiting
  B) Price drops to $99.50 → Order fills at $99.50 or better
  C) Price never reaches $99.50 → Order stays until you cancel

Key characteristics:

  • Can be maker (adds liquidity) or taker (takes liquidity)
  • Price guaranteed - but fill is not guaranteed
  • Maker orders earn rebates, taker orders pay fees
  • Default time-in-force is GTC (stays on book)

Maker vs Taker:

Order Book: Best ask $100.10, Best bid $100.00

Limit BUY at $99.90 → MAKER (below best ask, goes on book)
Limit BUY at $100.20 → TAKER (above best ask, executes immediately)

Limit SELL at $100.20 → MAKER (above best bid, goes on book)
Limit SELL at $99.90 → TAKER (below best bid, executes immediately)

When to Use Each

SituationOrder TypeWhy
Breaking news, need to exit NOWMarketSpeed > price
Setting a target entry priceLimitPrice precision
Market making / providing liquidityLimit (Post-Only)Earn rebates
Stop-loss executionMarket (trigger)Guaranteed exit
Take-profit at specific levelLimit (trigger)Price precision
DCA buyingLimitBetter average price
Closing a position urgentlyMarketCertainty of exit

2. Time-in-Force

Time-in-Force (TIF) determines how long your order remains active and what happens to unfilled portions.

GTC - Good-Til-Cancelled (Default)

What it does: Order stays on the book until completely filled or you cancel it.

When to use:

  • You're willing to wait for your price
  • Setting limit orders at support/resistance levels
  • Building positions over time

Example:

You: Limit BUY 100 units at $95.00, GTC

Day 1: Price at $100, order sits on book
Day 2: Price at $98, still waiting
Day 3: Price drops to $95, 100 units filled!

Total time: 3 days waiting for your price

IOC - Immediate-or-Cancel

What it does: Fill whatever you can immediately, cancel the rest. Partial fills allowed.

When to use:

  • You want execution now but don't want residual orders lingering
  • Testing liquidity at a price level
  • Avoiding stale orders on your account

Example:

Order Book Asks: $100 (50 units), $101 (100 units)

You: Limit BUY 80 units at $100, IOC

Result:
  - 50 units filled at $100
  - 30 units CANCELLED (no liquidity at $100)
  - You got partial fill, rest is gone

Key insight: IOC says "give me what's available NOW at my price, I don't want to wait."


FOK - Fill-or-Kill

What it does: Either fill the ENTIRE order immediately, or reject it completely. No partial fills.

When to use:

  • All-or-nothing situations (hedging, arbitrage)
  • You need exact position sizes
  • Avoiding partial fills that leave you exposed

Example:

Order Book Asks: $100 (50 units), $101 (100 units)

Scenario A: FOK BUY 40 units at $100
  → 40 units filled (book has 50, we need 40 ✓)

Scenario B: FOK BUY 80 units at $100
  → Order REJECTED (book only has 50 at $100)
  → No partial fill, nothing happens

Key insight: FOK says "I need exactly this much at this price, or forget it."


Comparison Table

TIFPartial Fill?Unfilled PortionUse Case
GTCYesStays on bookPatient trading
IOCYesCancelledImmediate execution
FOKNoOrder rejectedAll-or-nothing
Memory Trick:
  GTC = "I'll wait for my price" (patient)
  IOC = "Now or never, but I'll take what I can get" (flexible)
  FOK = "All or nothing" (strict)

3. Execution Modifiers

Post-Only

What it does: Ensures your order is always a maker (adds liquidity). If your order would immediately match (become a taker), it's rejected.

When to use:

  • Market making strategies
  • You specifically want maker rebates
  • Avoiding accidental taker fees

Example:

Order Book: Best ask $100.10, Best bid $100.00

Post-Only SELL at $100.05:
  → REJECTED! Would cross the spread and take from bids
  → Error: PostOnlyTaker

Post-Only SELL at $100.15:
  → Accepted! Goes on book above best ask
  → You're now a maker, earning rebates when filled

Common mistake: Setting post-only orders too aggressively. If your price would execute immediately, the order fails entirely.

Understanding the PostOnlyTaker Error

If you see the error "order would immediately match and take" (error code: PostOnlyTaker), it means your post-only order was rejected because it would have crossed the spread and taken liquidity.

Why does the exchange reject these orders?

Post-only is a promise: "I only want to be a maker." The exchange enforces this by checking if your order would immediately match:

Your order: Post-Only BUY at $100.50
Best ask: $100.40

What would happen WITHOUT post-only:
  → Your buy at $100.50 matches the $100.40 ask
  → You're a TAKER (removing liquidity)
  → You pay taker fees

What happens WITH post-only:
  → Exchange sees your order would immediately match
  → Rejects with PostOnlyTaker error
  → No order placed, no fees charged

Why do traders want this?

  1. Fee optimization - Makers often receive rebates; takers pay fees. A market maker placing thousands of orders wants to guarantee maker status.

  2. Execution certainty - If you're quoting a price, you want that exact price or nothing. You don't want to accidentally sweep the book.

  3. Strategy enforcement - Market making algorithms expect to rest on the book. An accidental taker fill can break position management logic.

How to fix it:

Adjust your price so your order rests on the book instead of crossing:

Order SideMust BeExample
Post-only BUYBelow best askIf best ask is $100.10, bid at $100.05 or lower
Post-only SELLAbove best bidIf best bid is $100.00, ask at $100.05 or higher

Pro tip: If you're consistently getting PostOnlyTaker errors, your pricing logic may be too aggressive. Consider adding a buffer from the best price, or fetching fresh order book data before placing orders.


Reduce-Only

What it does: Order can only reduce (close) an existing position. Cannot open new positions or increase exposure.

When to use:

  • Closing positions without accidentally flipping
  • Stop-loss orders (you only want to exit, not reverse)
  • Risk management to prevent over-exposure

Example:

Current Position: LONG 100 BTC

Reduce-Only SELL 50 BTC:
  → Accepted! Reduces position to 50 BTC

Reduce-Only SELL 150 BTC:
  → Only 100 BTC sold (reduced to 0)
  → Won't flip you SHORT

Without Reduce-Only SELL 150 BTC:
  → Sells 150 BTC
  → You're now SHORT 50 BTC (position flipped!)

Key insight: Reduce-only prevents accidental position reversals and is automatically applied to all stop-loss/take-profit orders.

Important behavior: The engine tracks your reduce-only orders and ensures your total pending reduce-only quantity doesn't exceed your position size.


Self-Trade Prevention (STP)

What it does: Prevents your orders from matching against each other (useful for market makers running multiple strategies).

Options:

ModeBehavior
RejectTaker (default)Incoming order is cancelled
RejectMakerResting order is cancelled
RejectBothBoth orders cancelled
AllowSelf-trades permitted

4. Trigger Orders

Trigger orders (stop-loss, take-profit) are conditional orders that only activate when a specified price is reached.

How Trigger Orders Work

Regular Limit Order:
  Submit → Immediately on order book → Can be filled anytime

Trigger Order:
  Submit → Stored separately (NOT on book) → Waits for trigger price
         → Trigger fires → Converts to regular order → Placed on book

Key insight: Trigger orders take a separate code path. They're stored in a TriggerOrders collection, not the main order book, until activated.


Stop-Loss

What it does: Triggers when price moves AGAINST your position. Limits downside.

For a LONG position:

You're LONG BTC at $100,000
Stop-Loss trigger at $95,000

Price drops to $95,000 → Stop triggers → Sells your position
Loss limited to ~5% instead of potentially more

For a SHORT position:

You're SHORT BTC at $100,000
Stop-Loss trigger at $105,000

Price rises to $105,000 → Stop triggers → Buys to close
Loss limited to ~5%

Take-Profit

What it does: Triggers when price moves IN FAVOR of your position. Locks in gains.

For a LONG position:

You're LONG BTC at $100,000
Take-Profit trigger at $110,000

Price rises to $110,000 → TP triggers → Sells your position
You lock in ~10% profit

For a SHORT position:

You're SHORT BTC at $100,000
Take-Profit trigger at $90,000

Price falls to $90,000 → TP triggers → Buys to close
You lock in ~10% profit

Trigger Price vs Limit Price

When placing a trigger order, you specify:

  1. Trigger Price - When does the order activate?
  2. Limit Price (optional) - What price to execute at once triggered?
Trigger Order with Limit Price:
  Trigger at $95,000, Limit at $94,500

  → When price hits $95,000: Creates a LIMIT SELL at $94,500
  → Guaranteed price of $94,500 or better
  → But might not fill if price gaps through!

Trigger Order without Limit Price (Stop-Market):
  Trigger at $95,000, no limit

  → When price hits $95,000: Creates a MARKET SELL
  → Guaranteed fill
  → But price might be worse than $95,000 (slippage)

Trade-off:

TypeFill GuaranteePrice Guarantee
Stop-LimitNoYes
Stop-MarketYesNo

Trigger Price Types

You can specify trigger prices in three ways:

TypeExampleUse Case
Fixed$95,000Most common - specific price level
Percentage-5%Trailing stops, dynamic targets
Relative-$500Fixed dollar offset from reference

Trigger Reference Price (TriggerBy)

What price does the system watch to fire your trigger?

OptionDescriptionWhen to Use
LastPrice (default)Most recent trade priceStandard trading
IndexPriceSpot index (aggregated exchanges)Avoid manipulation
MarkPriceFair price (index + basis)Futures, avoid wicks

Example of why this matters:

Scenario: Flash crash on Backpack only

LastPrice: Drops from $100,000 to $80,000 (bad data/manipulation)
IndexPrice: Stays at $99,000 (other exchanges stable)
MarkPrice: Stays at $99,500 (calculated fair value)

Your stop-loss at $95,000:
  - TriggerBy=LastPrice → TRIGGERED (bad!)
  - TriggerBy=IndexPrice → Safe (not triggered)
  - TriggerBy=MarkPrice → Safe (not triggered)

Pro tip: Use IndexPrice or MarkPrice for stop-losses to avoid getting stopped out by wicks or manipulation.


Trigger Quantity Options

You can also specify quantities dynamically:

TypeExampleBehavior
Fixed1.5 BTCAlways this exact amount
Percentage50%Percentage of current position

Percentage is powerful for scaling out:

Position: 10 BTC

Take-Profit 1: Trigger $105k, Quantity 50% → Sells 5 BTC
Take-Profit 2: Trigger $110k, Quantity 100% → Sells remaining 5 BTC

Automatic Stop-Loss/Take-Profit Creation

When you place an order with SL/TP parameters, the system automatically creates the trigger orders when your main order fills:

Order: Buy 10 BTC at $100,000
  - stop_loss_trigger_price: $95,000
  - take_profit_trigger_price: $110,000

After fill:
  1. You now own 10 BTC at $100,000
  2. Stop-Loss created: Sell 10 BTC if price hits $95,000
  3. Take-Profit created: Sell 10 BTC if price hits $110,000

Important: These orders are created with reduce_only: true automatically, so they can only close your position.


5. Order Lifecycle

Order States

StateMeaningTerminal?
NewOrder accepted, on book, unfilledNo
PartiallyFilledSome quantity executed, remainder on bookNo
FilledCompletely executedYes
CancelledCancelled by userYes
ExpiredCancelled by system (IOC, FOK, insufficient margin, etc.)Yes
TriggerPendingTrigger order waiting for price conditionNo
TriggerFailedTrigger couldn't activate (no position, etc.)Yes

State Machine Diagram

                    ┌──────────────┐
                    │   SUBMIT     │
                    └──────┬───────┘

              ┌────────────┼────────────┐
              │            │            │
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────────┐
        │   NEW    │ │ EXPIRED  │ │TRIGGER_PENDING│
        └────┬─────┘ └──────────┘ └───────┬──────┘
             │                            │
    ┌────────┼────────┐          ┌────────┼────────┐
    │        │        │          │                 │
    ▼        ▼        ▼          ▼                 ▼
┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐ ┌────────────┐
│PARTIAL │ │FILLED  │ │CANCELLED││   NEW    │ │TRIGGER_FAIL│
│FILLED  │ └────────┘ └────────┘ │(regular) │ └────────────┘
└───┬────┘                       └──────────┘

    ├───────────┬───────────┐
    ▼           ▼           ▼
┌────────┐ ┌────────┐ ┌──────────┐
│FILLED  │ │CANCELLED│ │ EXPIRED  │
└────────┘ └────────┘ └──────────┘

Order Events

As orders progress through their lifecycle, events are emitted:

EventWhen
AcceptedOrder reaches matching engine
PlacedLimit order rests on book (didn't fully fill)
FillPartial or complete execution
CancelledUser-initiated cancellation
ExpiredSystem-initiated cancellation with reason
TriggerPlacedTrigger order registered
TriggeredTrigger condition met, order activated
TriggerFailedTrigger couldn't fire (reason provided)

Expiry Reasons

When an order is expired (not by you), here's why:

Margin/Funds:

  • InsufficientFunds - Not enough balance
  • InsufficientMargin - Would breach margin requirements
  • NegativeEquity - Account underwater

Order Validation:

  • PostOnlyTaker - Post-only order would take liquidity
  • FillOrKill - FOK order couldn't fill completely
  • ImmediateOrCancel - IOC unfilled portion
  • SlippageToleranceExceeded - Price moved too far

Position/Risk:

  • ReduceOnlyNotReduced - Reduce-only order wouldn't reduce position
  • StopWithoutPosition - Trigger order for non-existent position
  • MaxStopOrdersPerPosition - Too many trigger orders
  • PositionLimit - Would exceed position limits

Market State:

  • OrderBookClosed - Market not accepting orders
  • PostOnlyMode - Market only accepting maker orders
  • InsufficientLiquidity - Not enough liquidity for market order

6. Common Mistakes & Gotchas

Mistake 1: Market Orders in Thin Books

Problem: Market order gets terrible fill due to slippage.

You expect: Buy at ~$100
Reality: $100 (10), $105 (10), $120 (80)
Your 100-unit market buy: Average price $116!

Solution: Use limit orders, or check order book depth before market orders.


Mistake 2: Stop-Loss Below Current Price for Shorts

Problem: Placing stop-loss on wrong side.

You're SHORT at $100
Wrong: Stop-loss at $95 (that's take-profit territory!)
Right: Stop-loss at $105 (protects against price rising)

Remember:

  • LONG stop-loss = BELOW entry (price dropping hurts you)
  • SHORT stop-loss = ABOVE entry (price rising hurts you)

Mistake 3: Stop-Market vs Stop-Limit in Fast Markets

Problem: Stop-limit doesn't fill during gap.

Your position: LONG at $100
Stop-limit: Trigger $95, Limit $94.50

Price gaps from $96 → $90 (fast crash)
Your limit order at $94.50 sits unfilled
You're still LONG as price continues to $80

Solution: For stop-losses, consider stop-market (no limit price) if guaranteed exit matters more than price.


Mistake 4: Post-Only Rejected at Market Price

Problem: Post-only order keeps getting rejected.

Best ask: $100.10
Best bid: $100.00

You try: Post-Only SELL at $100.05
Result: Rejected (would cross spread)

You try: Post-Only SELL at $100.00
Result: Rejected (would match bids)

You need: Post-Only SELL at $100.15 or higher

Solution: Post-only must be on YOUR side of the book:

  • Post-only SELL → Above best ask
  • Post-only BUY → Below best bid

Mistake 5: FOK with Insufficient Liquidity

Problem: FOK order fails because book can't fill entire size.

You need exactly 100 units
Book has: 50 at $100, 100 at $101

FOK BUY 100 at $100 → REJECTED (only 50 available)
IOC BUY 100 at $100 → Fills 50, cancels 50

Solution: Use IOC if partial fills are acceptable, or increase your limit price for FOK.


Mistake 6: Reduce-Only with No Position

Problem: Reduce-only order fails because there's nothing to reduce.

Position: None (flat)
Order: Reduce-only SELL 10 BTC

Result: Order REJECTED or EXPIRED
Reason: ReduceOnlyNotReduced

Solution: Only use reduce-only when you have an existing position to close.


Mistake 7: Trigger Orders Not Filling After Trigger

Problem: Trigger fires but order doesn't fill.

Stop-loss: Trigger $95, Limit $94.50
Price drops: $96 → $95 (trigger!) → $94 (below your limit)

Trigger fired, created limit order at $94.50
But price is now $94, no one wants to buy at $94.50
Order sits unfilled

Solution:

  • Use stop-market for guaranteed fills
  • Set limit price with buffer (e.g., trigger $95, limit $94)

Mistake 8: TriggerBy=LastPrice During Volatility

Problem: Stop triggered by a wick, not real price movement.

Your stop: $95,000
Last price: Briefly touches $94,900 (1 trade, immediately bounces)
Index price: Never dropped below $98,000

Using TriggerBy=LastPrice: You got stopped out
Using TriggerBy=IndexPrice: You're still in the trade

Solution: Use IndexPrice or MarkPrice for stops in volatile conditions.


Quick Reference: Order Type Decision Tree

Need to execute NOW?
├─ YES: How important is exact fill size?
│   ├─ Must fill exactly: FOK
│   ├─ Partial OK: IOC or Market
│   └─ Don't care: Market

└─ NO: Are you providing liquidity?
    ├─ YES: Limit + Post-Only + GTC
    └─ NO: Limit + GTC (most common)

Need to protect position?
├─ Limit losses: Stop-Loss
│   └─ Guaranteed exit? Stop-Market
│   └─ Better price? Stop-Limit
└─ Lock gains: Take-Profit
    └─ Same considerations as Stop

Summary Table

Order TypePrice CertaintyFill CertaintyBest For
MarketNoneHighUrgent exits
Limit GTCHighLowPatient entries
Limit IOCHighMediumImmediate execution, price cap
Limit FOKHighLowAll-or-nothing
Limit Post-OnlyHighLowMarket making
Stop-MarketLowHighGuaranteed stop-loss
Stop-LimitHighLowPrecise stop-loss
Take-ProfitHighLowLocking gains