Events

Each subscription stream has a corresponding event that will be broadcasted over websocket.

Order Update

Update speed: real-time

{
  "type": "order_update",
  // timestamp of the event in nanoseconds
  "timestamp": "1695081920633151000", 
  "product_id": 1,
  // order digest
  "digest": "0xf7712b63ccf70358db8f201e9bf33977423e7a63f6a16f6dab180bdd580f7c6c",
  // remaining amount to be filled.
  // will be `0` if the order is either fully filled or cancelled.
  "amount": "82000000000000000",
  // any of: "filled", "cancelled", "placed"
  "reason": "filled"
  // an optional `order id` that can be provided when placing an order
  "id": 100
}

Example Scenarios:

Let's assume your initial order amount is 100 units and each match occurs for an amount of 10 units.

Note: The following events only include amount and reason for simplicity.

Scenario 1: Limit Order Partially Fills and Gets Placed

Your limit order matches against existing orders in the book.

You will receive the following events over websocket, each with the same timestamp but in sequential order:

  • Event 1: (90, "filled") โ€” 10 units of your order are filled.

  • Event 2: (80, "filled") โ€” Another 10 units are filled.

  • Event 3: (80, "placed") โ€” The remaining 80 units are placed on the book.

Scenario 2: Immediate-Or-Cancel (IOC) Order Partially Fills

Your IOC order matches against existing orders but is not completely filled.

The events you will receive are as follows:

  • Event 1: (90, "filled") โ€” 10 units are filled.

  • Event 2: (80, "filled") โ€” Another 10 units are filled.

  • Event 3: (0, "cancelled") โ€” The remaining order is cancelled. Note: If your IOC order is fully filled, the last event you will receive is (0, "filled").

Scenario 3: Resting Limit Order Gets Matched

Your existing, or "resting," limit order matches against an incoming order.

You will receive the following event: (90, "filled") โ€” 10 units of your resting limit order are filled.

Scenario 4: Resting Limit Order Gets Cancelled

Your resting limit order could be cancelled for various reasons, such as manual cancellation, expiration, failing health checks, or self-trade prevention.

In any of these cases, you will receive: (0, "cancelled")

Scenario 5: IOC order doesn't cross the book or FOK order fails to be entirely filled

In any of these cases, you will receive: (0, "cancelled")

Trade

Update speed: real-time

{
    "type": "trade",
    "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
    "product_id": 1,
    "price": "1000", // price the trade happened at, multiplied by 1e18
    // both taker_qty and maker_qty have the same value;
    // set to filled amount (min amount of taker and maker) when matching against book
    // set to matched amm base amount when matching against amm
    "taker_qty": "1000",
    "maker_qty": "1000",
    "is_taker_buyer": true,
    "is_maker_amm": true // true when maker is amm
}

Best Bid Offer

Update speed: real-time

{
    "type": "best_bid_offer",
    "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
    "product_id": 1,
    "bid_price": "1000", // the highest bid price, multiplied by 1e18
    "bid_qty": "1000", // quantity at the huighest bid, multiplied by 1e18. 
                       // i.e. if this is USDC with 6 decimals, one USDC 
                       // would be 1e12
    "ask_price": "1000", // lowest ask price
    "ask_qty": "1000" // quantity at the lowest ask
}

Fill

Update speed: real-time

{
    "type": "fill",
    "timestamp": "1676151190656903000", // timestamp of the event in nanoseconds
    "product_id": 1,
    // the subaccount that placed this order
    "subaccount": "0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43746573743000000000000000",
    // hash of the order that uniquely identifies it
    "order_digest": "0xf4f7a8767faf0c7f72251a1f9e5da590f708fd9842bf8fcdeacbaa0237958fff",
    // the amount filled, multiplied by 1e18
    "filled_qty": "1000",
    // the amount outstanding unfilled, multiplied by 1e18
    "remaining_qty": "2000",
    // the original order amount, multiplied by 1e18
    "original_qty": "3000",
    // fill price
    "price": "24991000000000000000000",
    // true for `taker`, false for `maker`
    "is_taker": true,
    "is_bid": true,
    // true when matching against amm
    "is_against_amm": true,
    // an optional `order id` that can be provided when placing an order
    "id": 100
}

Position Change

Update speed: real-time

{
    "type":"position_change",
    "timestamp": "1676151190656903000", // timestamp of event in nanoseconds
    "product_id":1,
     // whether this is a position change for the LP token for this product
    "is_lp":false,
    // subaccount who's position changed
    "subaccount":"0x7a5ec2748e9065794491a8d29dcf3f9edb8d7c43706d00000000000000000000",
    // new amount for this product
    "amount":"51007390115411548",
    // new quote balance for this product; zero for everything except non lp perps
    // the negative of the entry cost of the perp
    "v_quote_amount":"0"
}

Note: that it is possible that back to back position_change events have the same fields except for timestamp. Additionally, position_change events are not sent on interest and funding payments, and also are not sent on actions done through slow mode (except deposits). The full list of actions that will trigger a PositionChange event are:

  • Minting or burning LP tokens

  • Liquidating a subaccount

  • Matching orders

  • Depositing or withdrawing spot

  • Settling PNL

Book Depth

Update speed: once every 50ms

{
    "type":"book_depth",
    // book depth aggregates a number of events once every 50ms
    // these are the minimum and maximum timestamps from 
    // events that contributed to this response
    "min_timestamp": "1683805381879572835",
    "max_timestamp": "1683805381879572835",
    // the max_timestamp of the last book_depth event for this product
    "last_max_timestamp": "1683805381771464799",
    "product_id":1,
    // changes to the bid side of the book in the form of [[price, new_qty]]
    "bids":[["21594490000000000000000","51007390115411548"]],
    // changes to the ask side of the book in the form of [[price, new_qty]]
    "asks":[["21694490000000000000000","0"],["21695050000000000000000","0"]]
}

Note: To keep an updated local orderbook, do the following:

  1. Subscribe to the book_depth stream and queue up events.

  2. Get a market data snapshot by calling MarketLiquidity. The snapshot contains a timestamp in the response.

  3. Apply events with max_timestamp > snapshot timestamp.

  4. When you receive an event where its last_max_timestamp is not equal to the last event you've received, it means some events were lost and you should repeat 1-3 again.

Last updated