userFills channel delivers every fill where your account is one of the counterparties — both fills where you were the taker (your order crossed the book and aggressed) and fills where you were the maker (your resting order was hit). To determine your role in each fill, compare the aggressor order ID (aid) and the resting order ID (rid) against your own order IDs. For the full public trade stream regardless of account, use the trades channel instead.
Pass the account’s wallet address (not the numeric account ID) as the user parameter.
Subscribing
Push message format
data array may contain multiple fill objects when several fills execute in the same block. Process them in array order.
Fill object fields
number
Contract ID on which the fill occurred.
string
Taker (aggressor) order ID — the order that crossed the book and matched against the resting order.
string
Maker (resting) order ID — the order that was already in the book and was hit by the aggressor.
string
Direction of the aggressor:
"B" means the buyer was the aggressor (bought into resting asks); "S" means the seller was the aggressor (sold into resting bids).string
Fill execution price (raw string). Apply the contract’s decimal precision before displaying to users.
string
Fill size (raw string) — the quantity that changed hands at price
p.string
Execution ID — globally unique on-chain identifier for this fill. Use this to deduplicate fills if you receive the same event from multiple sources.
string
Your signed position size before this fill settles: positive for long, negative for short,
0 if flat. The value is already picked for your side based on whether you were the taker (aid) or maker (rid) — you don’t derive it. Compute your post-fill position incrementally as startPosition ± size; when one resting order fills in several pieces, carry it forward (fill N’s sp equals the position computed from fill N−1). Position data is private, so this field appears only on the per-address userFills channel — never on the public trades channel.number
Which of your position buckets this fill lands in:
0 = ONE_WAY, 1 = LONG, 2 = SHORT. In HEDGE mode a contract has separate long and short positions, so sp must be accumulated per ps bucket. In ONE_WAY mode ps is always 0 and can be ignored.Determining your role
- You were the taker
- You were the maker
If
aid matches one of your order IDs, your order was the aggressor. You paid the taker fee and your order actively crossed the spread to execute.Envelope fields
string
"TradeFill" for a live incremental frame, "TradeFillHistorySnapshot" for the history snapshot sent on subscribe.string
"fills.<user_address>" — the address you subscribed with.number
Server send timestamp in Unix milliseconds. Use this as the authoritative event time for all fills in the
data array.History snapshot on subscribe
Immediately after a successful subscription the server pushes one frame carrying up to the last 10 fills, so a blotter can render history without a separate REST call. It differs from a live frame in two ways:notional, fee, realized_pnl, side_role, and liquidate_type appear only there.
Snapshot row fields
Working with the snapshot:
- The payload is at
data.rows, anddatais an object rather than an array. (openOrdersalso sends an object, but its payload key isorders.) sideis already your own direction — you do not need to derive it from taker/maker. The incrementalbis the aggressor’s direction, which is the opposite of yours when you were the maker. Do not run both through the same logic.side_rolestates your role directly, so there is no need to compareaidagainstridthe way you do for increments.fee,realized_pnl, andposition_beforeare your side’s values, not totals for the whole match.- Quoted fields are big integers carried as strings and can exceed the JavaScript safe-integer range — never pass them through
Number()before comparing or echoing them back. - Fewer than 10 rows, or none at all, is normal — the account may have little history, or the history service may be briefly unavailable. Do not treat it as an error.
- The snapshot and the increments that follow may overlap. Deduplicate by execution ID (
exec_idin the snapshot,ein increments); nothing is dropped. - The snapshot is fixed at 10 rows, with no paging and no cursor. For deeper history use
userFills.
