Overview
The StreamTpslUpdates stream delivers the lifecycle of trigger and take-profit/stop-loss orders as ADD and REMOVE diffs. Trigger orders do not appear in the regular order book streams until they trigger. This stream is the way to observe resting TP/SL orders directly. Use it for trigger-order heatmaps, liquidation and stop monitoring, frontend overlays, and alerting around resting TP/SL orders.
gRPC Service: OrderBookStreaming
gRPC Method: StreamTpslUpdates
Update Model: Snapshot of currently-open trigger orders on subscribe, then add/remove diffs per block
How It Works
- On subscribe: The first message has
snapshot: trueand contains all currently-open trigger orders asTPSL_DIFF_TYPE_ADDdiffs - Per block thereafter: Each
TpslUpdatesUpdatecontainsADDdiffs for newly placed trigger orders andREMOVEdiffs for trigger orders that left the resting set. Removes carry areason(e.g.,"reduceOnlyCanceled") mirroring node order statuses - Maintain a local map of open trigger orders keyed by
oidif you need the current resting set
Trigger orders are perpetuals-only, so this stream covers perp coins. An empty coins list subscribes to all perp coins.
Data Structure
Each TpslUpdatesUpdate message contains the trigger-order diffs for one block:
{
"time": 1781109188721,
"height": 586405650,
"diffs": [
{ "diff_type": "TPSL_DIFF_TYPE_ADD", "oid": 54764664388, "coin": "ETH",
"user": "0xA5cace4Bd730bAC2E814CB50c4F3A58DDBA017BF", "side": "B",
"trigger_px": "1702.1", "limit_px": "1872.4", "sz": "0.0294",
"trigger_condition": "Price above 1702.1", "order_type": "Stop Market",
"reduce_only": true, "timestamp": 1781109188721 }
]
}
Request Parameters
| Field | Type | Required | Description |
|---|---|---|---|
| coins | repeated string | No | List of perp symbols to subscribe to (e.g., "BTC", "ETH"). Empty means all perp coins |
Coin Naming
Trigger orders exist for perpetuals only, so the coins parameter accepts perp names: human-readable symbols like "BTC", "ETH", "HYPE", "SOL". The spot @{index} format does not apply to this stream. An empty list subscribes to all perp coins.
Response Fields
TpslUpdatesUpdate
| Field | Type | Description |
|---|---|---|
| time | uint64 | Block timestamp in milliseconds |
| height | uint64 | Block height |
| snapshot | bool | True when the diffs carry the currently-open trigger orders as a snapshot (sent on subscribe) |
| diffs | TpslOrderDiff[] | Trigger-order add/remove diffs for this block |
TpslOrderDiff
| Field | Type | Description |
|---|---|---|
| diff_type | TpslDiffType | TPSL_DIFF_TYPE_ADD (trigger order placed) or TPSL_DIFF_TYPE_REMOVE (trigger order left the resting set) |
| oid | uint64 | Unique order ID |
| coin | string | Perp symbol (e.g., "BTC", "ETH") |
| user | string | Ethereum address of the order owner |
| side | string | "A" (Ask/Sell) or "B" (Bid/Buy) |
| trigger_px | string | Trigger price as a decimal string |
| limit_px | string | Limit price as a decimal string (for market trigger orders, the execution limit, which may be offset from trigger_px by a slippage allowance) |
| sz | string | Order size as a decimal string. "0.0" for position TP/SL orders, which are sized by the position |
| trigger_condition | string | Human-readable trigger condition (e.g., "Price above 1778") |
| order_type | string | Trigger order type (e.g., "Stop Market", "Stop Limit", "Take Profit Market", "Take Profit Limit") |
| is_position_tpsl | bool | Whether the order is a position take-profit/stop-loss (sized by the position; sz is "0.0") |
| reduce_only | bool | Whether the order is reduce-only |
| timestamp | uint64 | Order creation time in milliseconds |
| reason | string | Removal reason, present on REMOVE diffs (e.g., "reduceOnlyCanceled"). Mirrors node order statuses |
Proto Definition
StreamTpslUpdates is defined in orderbook.proto:
service OrderBookStreaming {
rpc StreamTpslUpdates (TpslUpdatesRequest) returns (stream TpslUpdatesUpdate);
}
message TpslUpdatesRequest {
repeated string coins = 1;
}
enum TpslDiffType {
TPSL_DIFF_TYPE_UNSPECIFIED = 0;
TPSL_DIFF_TYPE_ADD = 1;
TPSL_DIFF_TYPE_REMOVE = 2;
}
message TpslUpdatesUpdate {
uint64 time = 1;
uint64 height = 2;
repeated TpslOrderDiff diffs = 3;
bool snapshot = 4;
}
message TpslOrderDiff {
TpslDiffType diff_type = 1;
uint64 oid = 2;
string coin = 3;
string user = 4;
string side = 5;
string trigger_px = 6;
string limit_px = 7;
string sz = 8;
string trigger_condition = 9;
string order_type = 10;
bool is_position_tpsl = 11;
bool reduce_only = 12;
uint64 timestamp = 13;
string reason = 14;
}
Example Updates
Initial Snapshot (first message, truncated)
The first message has snapshot: true and contains all currently-open trigger orders as ADD diffs:
{
"time": 1781109147521,
"height": 586405394,
"snapshot": true,
"diffs": [
{ "diff_type": "TPSL_DIFF_TYPE_ADD", "oid": 54678088397, "coin": "ETH",
"user": "0x2308ccA96D9Ddde6F9A37D447C87d6B6b267E307", "side": "A",
"trigger_px": "1778.0", "limit_px": "1778.0", "sz": "0.0297",
"trigger_condition": "Price above 1778", "order_type": "Take Profit Market",
"reduce_only": true, "timestamp": 1780996670847 },
{ "diff_type": "TPSL_DIFF_TYPE_ADD", "oid": 54694008956, "coin": "BTC",
"user": "0xB996742Cc1BA8E8A949021dd609f7b45Ac032CC1", "side": "A",
"trigger_px": "60687.0", "limit_px": "60596.0", "sz": "0.00018",
"trigger_condition": "Price below 60687", "order_type": "Stop Limit",
"reduce_only": true, "timestamp": 1781017693515 }
]
}
Position TP/SL Order
Position TP/SL orders carry sz: "0.0" because they are sized by the position at trigger time:
{ "diff_type": "TPSL_DIFF_TYPE_ADD", "oid": 54629542173, "coin": "BTC",
"user": "0x0F15d3Ecc22BE9Ab1C1be2A0a265bfb715af3Ccc", "side": "B",
"trigger_px": "79089.0", "limit_px": "79089.0", "sz": "0.0",
"trigger_condition": "Price above 79089", "order_type": "Stop Market",
"is_position_tpsl": true, "reduce_only": true, "timestamp": 1780928051012 }
Live Add
{
"time": 1781109188721,
"height": 586405650,
"diffs": [
{ "diff_type": "TPSL_DIFF_TYPE_ADD", "oid": 54764664388, "coin": "ETH",
"user": "0xA5cace4Bd730bAC2E814CB50c4F3A58DDBA017BF", "side": "B",
"trigger_px": "1702.1", "limit_px": "1872.4", "sz": "0.0294",
"trigger_condition": "Price above 1702.1", "order_type": "Stop Market",
"reduce_only": true, "timestamp": 1781109188721 }
]
}
Live Remove (with reason)
{
"time": 1781109151770,
"height": 586405419,
"diffs": [
{ "diff_type": "TPSL_DIFF_TYPE_REMOVE", "oid": 54764554453, "coin": "BTC",
"user": "0x4f62EFb29bb38a975C1e54cb39aD3aA4E8777CF4", "side": "A",
"trigger_px": "61968.0", "limit_px": "61968.0", "sz": "0.00019",
"trigger_condition": "Price below 61968", "order_type": "Stop Market",
"reduce_only": true, "timestamp": 1781109080552, "reason": "reduceOnlyCanceled" }
]
}
API Usage
gRPC Streaming
// Specific perp coins
const request = {
coins: ['ETH', 'BTC']
};
// All perp coins
const allCoinsRequest = {
coins: []
};
StreamTpslUpdates is only available via the gRPC Streaming API (OrderBookStreaming service). It is not available through JSON-RPC or WebSocket.
Important Notes
- Trigger orders are not in the book streams: Resting TP/SL orders do not appear in
StreamL2Book/StreamL4Bookuntil they trigger and convert into regular orders. This stream is the direct view of the resting trigger set - Snapshot on subscribe and reconnect: The first message carries all currently-open trigger orders as
ADDdiffs, so reconnects rebuild state automatically - Position TP/SL sizing: Orders with
is_position_tpsl: truecarrysz: "0.0", as they are sized by the position rather than a fixed size reasonon removes: Remove diffs include areasonstring mirroring node order statuses (e.g.,"reduceOnlyCanceled"), useful for distinguishing cancels from triggers- Perp coins only: Trigger orders exist for perpetuals; an empty
coinslist means all perp coins - zstd compression recommended: Enable zstd compression on your gRPC channel to reduce bandwidth, especially when streaming all perp coins
Related Streams
- StreamL4BookUpdates - Typed per-order book diffs for regular resting orders
- Orders - Order lifecycle events (open, filled, canceled, etc.)
- Events - Balance changes, transfers, liquidations, and funding payments