WebSocket stream
One connection for market data, the account, the layout of the windows and settings.
Address: ws://127.0.0.1:48180/v1/stream · AsyncAPI file
Connection
- Address:
ws://127.0.0.1:48180/v1/stream. - The token goes in the
Authorization: Bearer <token>header of the handshake. The handshake requires themarket:readscope. It is rejected by the same rules as an HTTP request, with401or403. - The server sends a WebSocket ping every 20 s. If no pong comes for 50 s, the server closes the connection.
- When the client closes the connection, the server answers with a close frame that carries the code of the client.
- Closing the socket removes every subscription of the connection. An
explicit
unsubscriberemoves a subscription at once. - A pair with no subscribers is released on the exchange after 30 s.
Close codes
| Code | Meaning |
|---|---|
1001 | the API was switched off or the token was reissued |
4008 | the client is too slow and did not keep up for longer than 5 s |
4029 | the limit of stream connections per token |
Limits
GET /v1/meta returns the limits. A refusal is an error frame with
rate_limited in answer to the request, or a close with 4029.
| Limit | Value |
|---|---|
| stream connections per token | 8 |
| subscriptions per connection | 100 |
| instruments opened only for the API | 50, not counting those open in the window |
| new subscriptions on exchanges a second | 5 |
Slow clients
A client that does not read slows down neither the terminal nor other
clients. Frames it does not take in time are dropped. For trades a
gap frame names the lost numbers. For the other topics the next frame
is simply newer.
Sequence numbers
seq numbers the events of a subscription. For trades it grows by
one. For topics where only the latest value is sent, gaps in the
numbers are normal.
Client requests
What a program sends to the terminal.
subscribe
Ask for a topic.
What a topic needs depends on its family:
- Market topics are
trades,ticker,mark_price,book.topandclusters.instrumentis required.connection_idis chosen by the common rule. The scope ismarket:read. - Account topics are
account.*.connection_idis required.instrumentis not used. The scope isaccount:read. workspaceandsettingstake neither. The scopes areworkspace:readandsettings:read.
Only book.top uses params. Other topics ignore them.
| Field | Type | Description |
|---|---|---|
typerequired | "subscribe" | |
request_id | string | Optional. Returned in the answer as it is. |
topicrequired | Topic | |
instrument | string | Instrument address, exchange:market:symbol. Required for market topics. |
connection_id | integer | Required for account topics. Optional for market topics. |
params | object | Parameters of book.top. |
params.depth | integer | Levels on each side. Range: 1–200. Default: 50. |
params.interval_ms | integer | The shortest time between two frames, milliseconds. Range: ≥ 50. Default: 100. |
{
"type": "subscribe",
"request_id": "r1",
"topic": "trades",
"instrument": "binance:futures:BTCUSDT",
"connection_id": 12
}{
"type": "subscribe",
"request_id": "r2",
"topic": "book.top",
"instrument": "binance:futures:BTCUSDT",
"params": {
"depth": 10,
"interval_ms": 250
}
}{
"type": "subscribe",
"request_id": "r3",
"topic": "account.fills",
"connection_id": 12
}{
"type": "subscribe",
"request_id": "r4",
"topic": "workspace"
}unsubscribe
Drop a subscription.
Takes only the sub returned by subscribed.
| Field | Type | Description |
|---|---|---|
typerequired | "unsubscribe" | |
request_id | string | |
subrequired | string | The identifier from subscribed. |
{
"type": "unsubscribe",
"request_id": "r5",
"sub": "7:trades"
}Server frames
What the terminal answers and sends on its own, apart from events.
subscribed
The subscription is set up.
sub identifies the subscription in every later frame. It is "{stream}:{topic}" for market topics, "{connection}:account.<topic>" for account topics, and "workspace" or "settings" for the other two. Every client subscribed to the same pair and topic gets the same sub. connection_id is present for market and account topics. It is absent for workspace and settings.
| Field | Type | Description |
|---|---|---|
typerequired | "subscribed" | |
request_id | string | |
subrequired | string | |
connection_id | integer | The chosen connection. Absent for workspace and settings. |
{
"type": "subscribed",
"request_id": "r1",
"sub": "7:trades",
"connection_id": 12
}unsubscribed
The subscription is removed.
| Field | Type | Description |
|---|---|---|
typerequired | "unsubscribed" | |
request_id | string | |
subrequired | string |
{
"type": "unsubscribed",
"request_id": "r5",
"sub": "7:trades"
}error
A request is refused, or a subscription is removed by the server.
With request_id it answers a request of the client. With sub and without request_id the server has removed the subscription. The codes are the codes of the HTTP operations.
| Field | Type | Description |
|---|---|---|
typerequired | "error" | |
request_id | string | Present when the frame answers a request. |
sub | string | Present when the frame concerns a subscription. |
coderequired | ErrorCode | |
messagerequired | string | Text for a human. |
{
"type": "error",
"request_id": "r1",
"code": "not_found",
"message": "no connection for the instrument"
}{
"type": "error",
"sub": "7:trades",
"code": "unavailable",
"message": "the exchange refused the subscription"
}gap
Trades the client lost.
Sent only for trades, before the first frame after the loss. The client was not reading, and the events from from_seq to to_seq are lost. Read them again with GET /v1/market/trades and since_ts.
| Field | Type | Description |
|---|---|---|
typerequired | "gap" | |
subrequired | string | |
from_seqrequired | integer | |
to_seqrequired | integer | |
reasonrequired | string | slow_consumer. |
{
"type": "gap",
"sub": "7:trades",
"from_seq": 42,
"to_seq": 57,
"reason": "slow_consumer"
}Topics
Every event has the same envelope; data depends on the topic.
| Field | Type | Description |
|---|---|---|
typerequired | "event" | |
subrequired | string | The subscription the event belongs to. |
seqrequired | integer | The number of the event in the subscription. |
tsrequired | integer | When the frame was built, UTC milliseconds. |
datarequired | any | The data of the event. Its shape depends on the topic. |
| Topic | Requires | Scope | Delivery |
|---|---|---|---|
trades | instrument | market:read | in order, gaps are reported |
ticker | instrument | market:read | latest value only |
mark_price | instrument | market:read | latest value only |
book.top | instrument | market:read | latest value only |
clusters | instrument | market:read | latest value only |
account.orders | connection_id | account:read | latest value only |
account.positions | connection_id | account:read | latest value only |
account.balances | connection_id | account:read | latest value only |
account.fills | connection_id | account:read | one frame per event |
workspace | nothing | workspace:read | latest value only |
settings | nothing | settings:read | one frame per event |
trades
Trades of a pair, in order.
data is an array of trades, as in GET /v1/market/trades. Trades come in order. If the client falls behind, a gap frame names what was lost.
Data: Trade[]
{
"type": "event",
"sub": "7:trades",
"seq": 41,
"ts": 1730294400130,
"data": [
{
"id": null,
"price": "67000.1",
"qty": "0.012",
"side": "buy",
"ts": 1730294400125,
"exchange_ts": 1730294400119
}
]
}ticker
The 24-hour ticker of a pair.
data is the ticker, as ticker in GET /v1/market/snapshot. Only the latest value is sent.
Data: Ticker
{
"type": "event",
"sub": "7:ticker",
"seq": 12,
"ts": 1730294400130,
"data": {
"price": "67000.1",
"change_24h_pct": "1.25",
"volume_24h": "81234.5",
"high_24h": null,
"low_24h": null,
"best_bid": null,
"best_ask": null,
"ts": 1730294400120
}
}mark_price
The mark price of a pair.
Only the latest value is sent.
Data: MarkPrice
{
"type": "event",
"sub": "7:mark_price",
"seq": 5,
"ts": 1730294400130,
"data": {
"mark_price": "67001.0",
"ts": 1730294400120
}
}book.top
The top of the order book of a pair.
Each subscriber sets its own depth and interval_ms. Values outside the bounds are refused with invalid_request. The first frame comes as soon as the book is ready. After that at most one frame comes per interval_ms, with the latest book. seq is the version of the book.
Data: BookTop
{
"type": "event",
"sub": "7:book.top",
"seq": 9031,
"ts": 1730294400130,
"data": {
"ts": 1730294400123,
"depth": 10,
"bids": [
[
"67000.1",
"1.25"
]
],
"asks": [
[
"67000.2",
"0.40"
]
]
}
}clusters
Volume clusters of an instrument.
data is one bucket, as an element of data in GET /v1/market/clusters. A frame comes for every bucket that changed, at most once in 16 ms, with its latest state. A change of the interval in the terminal shows in interval.
Data: ClusterBucket
account.orders
Orders and trigger orders of one instrument after a change.
Only the latest state of the instrument is sent. Subscribing does not touch the exchange, and events come from the moment the connection sends them. There is no snapshot frame on subscribing. Read the current state with GET /v1/account/orders.
Data: InstrumentOrders
account.positions
The position of one instrument after a change.
Only the latest state is sent. A closed position arrives as "position": null. There is no snapshot frame on subscribing.
Data: InstrumentPosition
account.balances
The balance of one currency.
data is one balance, as an element of data in GET /v1/account/balances. Only the latest value is sent. There is no snapshot frame on subscribing.
Data: Balance
account.fills
One fill.
One frame per fill. If the client falls behind, a frame is lost and seq shows the gap. Read the fills again with GET /v1/account/fills and since_ts.
Data: Fill
workspace
The whole layout of the windows.
data is the layout, as data in GET /v1/workspace. The first frame is the current layout, sent at once on subscribing. Then a frame comes for every change. Only the latest state is sent.
Data: WorkspaceSnapshot
settings
One written settings row.
One frame for every write to the settings table: from the API, from Settings App and from the window itself. There is no snapshot on subscribing. Read the current values with GET /v1/settings. If the client falls behind, a frame is lost and seq shows the gap.
Data: SettingWritten
{
"type": "event",
"sub": "settings",
"seq": 3,
"ts": 1730294400130,
"data": {
"scope": "global",
"scope_id": "default",
"connection_id": null,
"category": "multiview",
"key": "stack_width_px",
"value": 300
}
}Data types
The objects events carry. The same objects are returned by the HTTP operations.
Topic
tradestickermark_pricebook.topclustersaccount.ordersaccount.positionsaccount.balancesaccount.fillsworkspacesettings
ErrorCode
unauthorizedforbiddenforbidden_scopenot_foundinvalid_requestunsupportednot_activenot_tradablerate_limitedunavailable
A stable code, part of the contract. New codes may be added.
| Code | HTTP | When |
|---|---|---|
unauthorized | 401 | the token is wrong or the API is off |
forbidden | 403 | an Origin header or a foreign Host |
forbidden_scope | 403 | the token lacks the scope of the operation |
not_found | 404 | no such operation or connection, or no connection for the instrument |
invalid_request | 400 | a wrong parameter: an instrument, an interval, a depth, a foreign connection_id |
unsupported | 501 | the exchange is MOEX, the terminal was built without market data, or the setting is not written through the API |
not_active | 409 | clusters of the instrument are not computed yet, or its leverage is not loaded yet |
not_tradable | 409 | the connection cannot trade: it is disabled, read-only or has no credentials |
rate_limited | 429 | a limit is exceeded: connections, subscriptions, instruments, new subscriptions a second, trading commands, workspace commands or settings writes |
unavailable | 503 | the storage, the exchange or the window did not answer, the terminal is busy, or there is no data yet |
Trade
| Field | Type | Description |
|---|---|---|
idrequired | string | null | Always null in v1. |
pricerequired | string | |
qtyrequired | string | |
siderequired | "buy" | "sell" | |
tsrequired | integer | When the terminal received the trade, UTC milliseconds. |
exchange_tsrequired | integer | Exchange time. |
Ticker
The 24-hour ticker.
| Field | Type | Description |
|---|---|---|
pricerequired | string | |
change_24h_pctrequired | string | null | |
volume_24hrequired | string | |
high_24hrequired | string | null | |
low_24hrequired | string | null | |
best_bidrequired | string | null | |
best_askrequired | string | null | |
tsrequired | integer |
MarkPrice
| Field | Type | Description |
|---|---|---|
mark_pricerequired | string | |
tsrequired | integer |
BookTop
The top of the book: bids from the best price down, asks from the best price up.
| Field | Type | Description |
|---|---|---|
tsrequired | integer | |
depthrequired | integer | |
bidsrequired | PriceLevel[] | |
asksrequired | PriceLevel[] |
ClusterBucket
| Field | Type | Description |
|---|---|---|
intervalrequired | string | |
tsrequired | integer | The start of the bucket, UTC milliseconds. |
end_tsrequired | integer | |
volumerequired | string | |
buy_volumerequired | string | |
sell_volumerequired | string | |
tradesrequired | integer | |
vwaprequired | string | null | |
min_pricerequired | string | null | |
max_pricerequired | string | null | |
completedrequired | boolean | |
last_trade_tsrequired | integer | The time of the last trade of the bucket. 0 for a bucket from history that has no such time. |
levelsrequired | ClusterLevel[] | Every price level of the bucket, from the lowest price to the highest. |
InstrumentOrders
InstrumentPosition
| Field | Type | Description |
|---|---|---|
instrumentrequired | string | |
positionrequired | Position | null |
Balance
| Field | Type | Description |
|---|---|---|
currencyrequired | string | |
totalrequired | string | |
availablerequired | string | null | |
lockedrequired | string | null | |
tsrequired | integer |
Fill
| Field | Type | Description |
|---|---|---|
trade_idrequired | string | |
order_idrequired | string | The order id of the exchange. |
instrumentrequired | string | |
siderequired | string | buy or sell. |
pricerequired | string | |
qtyrequired | string | |
feerequired | string | |
fee_currencyrequired | string | |
position_siderequired | string | null | long or short on exchanges with hedge mode, otherwise null. |
tsrequired | integer | Exchange time. |
WorkspaceSnapshot
| Field | Type | Description |
|---|---|---|
windowsrequired | Window[] | By ascending id. |
SettingWritten
A settings row as it was written, without writable.
| Field | Type | Description |
|---|---|---|
scoperequired | string | |
scope_idrequired | string | null | |
connection_idrequired | integer | null | |
categoryrequired | string | |
keyrequired | string | |
valuerequired | any | Any JSON value. |
PriceLevel
string[]
[price, size], both decimal strings.
ClusterLevel
| Field | Type | Description |
|---|---|---|
pricerequired | string | |
volumerequired | string | |
buy_volumerequired | string | |
sell_volumerequired | string | |
buy_tradesrequired | integer | |
sell_tradesrequired | integer |
Order
| Field | Type | Description |
|---|---|---|
keyrequired | string | null | The order key of the terminal, t-…. null until it is known. |
exchange_idrequired | string | null | The order id of the exchange. null until the exchange accepts the order. |
instrumentrequired | string | |
siderequired | "buy" | "sell" | |
typerequired | "limit" | "market" | |
pricerequired | string | |
sizerequired | string | In the base coin. |
filledrequired | string | |
remainingrequired | string | |
notionalrequired | string | null | The quote amount the trader entered. null for an order from a snapshot of the exchange. |
statusrequired | "pending" | "open" | "partially_filled" | |
created_tsrequired | integer | null | null for an order from a snapshot of the exchange that has no time. |
Trigger
| Field | Type | Description |
|---|---|---|
idrequired | string | The numeric id used to cancel and amend the trigger. |
instrumentrequired | string | |
typerequired | TriggerKind | |
trigger_pricerequired | string | |
rulerequired | TriggerRule | |
sizerequired | string | null | In the base coin. |
notionalrequired | string | null | In the quote currency. |
statusrequired | "open" | "finished" | "inactive" | "invalid" | |
order_typerequired | string | null | The order type of the exchange, as it came. |
client_idrequired | string | null | The key returned when the trigger was placed. |
created_tsrequired | integer | null |
Position
| Field | Type | Description |
|---|---|---|
instrumentrequired | string | |
siderequired | "long" | "short" | |
sizerequired | string | In the base coin. |
lot_sizerequired | string | Base units in one lot. |
entry_pricerequired | string | |
notionalrequired | string | In the quote currency. |
unrealized_pnlrequired | string | As the terminal computes it. |
mark_pricerequired | string | null | The best bid for a long, the best ask for a short. null until the first tick of the order book. |
Window
| Field | Type | Description |
|---|---|---|
idrequired | integer | |
mainrequired | boolean | The main window. Closing it ends the terminal. |
chart_windowrequired | boolean | A detached window of charts. |
focusedrequired | boolean | The window in the focus of the OS. |
geometryrequired | object | Position and size in logical pixels of the screen. |
geometry.xrequired | integer | |
geometry.yrequired | integer | |
geometry.widthrequired | integer | |
geometry.heightrequired | integer | |
geometry.maximizedrequired | boolean | |
monitorrequired | string | null | The name of the monitor as the OS reports it. |
tabsrequired | Tab[] |
TriggerKind
stop_losstake_profitstop
TriggerRule
price_above_or_equalprice_below_or_equal
Tab
Stack
An order book with the tape and clusters.
| Field | Type | Description |
|---|---|---|
widget_idrequired | integer | |
instrumentrequired | string | null | null while the stack is empty. |
connection_idrequired | integer | null | |
emptyrequired | boolean | |
frozenrequired | boolean | The stack is frozen. |
positionrequired | Cell | null |
Chart
| Field | Type | Description |
|---|---|---|
widget_idrequired | integer | |
instrumentrequired | string | null | |
connection_idrequired | integer | null | |
intervalrequired | string | The candle interval: 1m, 5m, … |
owner_widget_idrequired | integer | null | The stack the chart follows. null for a chart on its own. |
positionrequired | Cell | null |
Cell
A cell of the grid of the tab: column from the left, row from the top, cell inside the row.
| Field | Type | Description |
|---|---|---|
columnrequired | integer | |
rowrequired | integer | |
cellrequired | integer |
Order-book settings of an instrument GET
The order-book settings of an instrument as a stack of this connection sees them, by category and key. A value comes from the first place that has it: the row of the pair on the connection, the default of the connection, the row of the pair without a connection, the global default. `resolved_from` names that place.
Python SDK
The client, the stream, discovery and errors of the spectre-terminal package.