WebSocket-стрим
Одно соединение для рыночных данных, счёта, раскладки окон и настроек.
Адрес: ws://127.0.0.1:48180/v1/stream · Файл AsyncAPI
Соединение
- Адрес:
ws://127.0.0.1:48180/v1/stream. - Токен передаётся в заголовке
Authorization: Bearer <token>рукопожатия. Рукопожатию нужно правоmarket:read. Оно отклоняется по тем же правилам, что HTTP-запрос, с401или403. - Сервер шлёт WebSocket-ping каждые 20 с. Если pong не приходит 50 с, сервер закрывает соединение.
- Когда клиент закрывает соединение, сервер отвечает кадром закрытия с кодом клиента.
- Закрытие сокета снимает все подписки соединения. Явный
unsubscribeснимает подписку сразу. - Пара без подписчиков отпускается на бирже через 30 с.
Коды закрытия
| Код | Значение |
|---|---|
1001 | API выключили или токен перевыпустили |
4008 | клиент слишком медленный и не успевал дольше 5 с |
4029 | лимит соединений стрима на токен |
Лимиты
Лимиты возвращает GET /v1/meta. Отказ — это кадр error с
rate_limited в ответ на запрос или закрытие с кодом 4029.
| Лимит | Значение |
|---|---|
| соединений стрима на токен | 8 |
| подписок на соединение | 100 |
| инструментов, открытых только для API | 50, не считая открытых в окне |
| новых подписок на биржах в секунду | 5 |
Медленные клиенты
Клиент, который не читает, не замедляет ни терминал, ни других
клиентов. Кадры, которые он не забрал вовремя, отбрасываются. Для trades
кадр gap называет потерянные номера. Для остальных топиков следующий
кадр просто новее.
Порядковые номера
seq нумерует события подписки. У trades он растёт на единицу.
У топиков, где отправляется только последнее значение, пропуски в
номерах — это норма.
Запросы клиента
Что программа отправляет терминалу.
subscribe
Запросить топик.
Что нужно топику, зависит от его семейства:
- Рыночные топики — это
trades,ticker,mark_price,book.topиclusters.instrumentобязателен.connection_idвыбирается по общему правилу. Право —market:read. - Топики счёта — это
account.*.connection_idобязателен.instrumentне используется. Право —account:read. workspaceиsettingsне принимают ни того, ни другого. Права —workspace:readиsettings:read.
Только book.top использует params. Остальные топики их игнорируют.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "subscribe" | |
request_id | string | Необязателен. Возвращается в ответе как есть. |
topicобязательное | Topic | |
instrument | string | Адрес инструмента, exchange:market:symbol. Обязателен для рыночных топиков. |
connection_id | integer | Обязателен для топиков счёта. Необязателен для рыночных топиков. |
params | object | Параметры book.top. |
params.depth | integer | Число уровней с каждой стороны. Range: 1–200. По умолчанию: 50. |
params.interval_ms | integer | Минимальное время между двумя кадрами, миллисекунды. Range: ≥ 50. По умолчанию: 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
Снять подписку.
Принимает только sub, возвращённый в subscribed.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "unsubscribe" | |
request_id | string | |
subобязательное | string | Идентификатор из subscribed. |
{
"type": "unsubscribe",
"request_id": "r5",
"sub": "7:trades"
}Кадры сервера
Что терминал отвечает и присылает сам, кроме событий.
subscribed
Подписка заведена.
sub обозначает подписку во всех следующих кадрах. Это "{stream}:{topic}" у рыночных топиков, "{connection}:account.<topic>" у топиков счёта и "workspace" или "settings" у двух остальных. Все клиенты, подписанные на одну пару и топик, получают один и тот же sub. connection_id есть у рыночных топиков и топиков счёта. У workspace и settings его нет.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "subscribed" | |
request_id | string | |
subобязательное | string | |
connection_id | integer | Выбранное подключение. Отсутствует у workspace и settings. |
{
"type": "subscribed",
"request_id": "r1",
"sub": "7:trades",
"connection_id": 12
}unsubscribed
Подписка снята.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "unsubscribed" | |
request_id | string | |
subобязательное | string |
{
"type": "unsubscribed",
"request_id": "r5",
"sub": "7:trades"
}error
Запрос отклонён либо подписка снята сервером.
С request_id это ответ на запрос клиента. С sub и без request_id — подписку снял сервер. Коды — те же, что у HTTP-операций.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "error" | |
request_id | string | Присутствует, когда кадр отвечает на запрос. |
sub | string | Присутствует, когда кадр относится к подписке. |
codeобязательное | ErrorCode | |
messageобязательное | string | Текст для человека. |
{
"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, перед первым кадром после потери. Клиент не читал, и события с from_seq по to_seq потеряны. Прочитайте их ещё раз через GET /v1/market/trades с since_ts.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "gap" | |
subобязательное | string | |
from_seqобязательное | integer | |
to_seqобязательное | integer | |
reasonобязательное | string | slow_consumer. |
{
"type": "gap",
"sub": "7:trades",
"from_seq": 42,
"to_seq": 57,
"reason": "slow_consumer"
}Топики
У всех событий один конверт; data зависит от топика.
| Поле | Тип | Описание |
|---|---|---|
typeобязательное | "event" | |
subобязательное | string | Подписка, к которой относится событие. |
seqобязательное | integer | Номер события в подписке. |
tsобязательное | integer | Когда кадр собран, миллисекунды UTC. |
dataобязательное | any | Данные события. Их форма зависит от топика. |
| Топик | Требует | Право | Доставка |
|---|---|---|---|
trades | instrument | market:read | по порядку, о пропусках сообщается |
ticker | instrument | market:read | только последнее значение |
mark_price | instrument | market:read | только последнее значение |
book.top | instrument | market:read | только последнее значение |
clusters | instrument | market:read | только последнее значение |
account.orders | connection_id | account:read | только последнее значение |
account.positions | connection_id | account:read | только последнее значение |
account.balances | connection_id | account:read | только последнее значение |
account.fills | connection_id | account:read | кадр на каждое событие |
workspace | ничего | workspace:read | только последнее значение |
settings | ничего | settings:read | кадр на каждое событие |
trades
Сделки пары, по порядку.
data — массив сделок, как в GET /v1/market/trades. Сделки идут по порядку. Если клиент отстаёт, кадр gap называет, что потеряно.
Данные: 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
Суточный тикер пары.
data — тикер, как ticker в GET /v1/market/snapshot. Отправляется только последнее значение.
Данные: 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
Цена маркировки (mark) пары.
Отправляется только последнее значение.
Данные: MarkPrice
{
"type": "event",
"sub": "7:mark_price",
"seq": 5,
"ts": 1730294400130,
"data": {
"mark_price": "67001.0",
"ts": 1730294400120
}
}book.top
Верхушка стакана пары.
Каждый подписчик задаёт свои depth и interval_ms. Значения вне границ отклоняются с invalid_request. Первый кадр приходит, как только стакан готов. Дальше приходит не больше одного кадра за interval_ms, с последним стаканом. seq — версия стакана.
Данные: 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
Кластеры объёма инструмента.
data — один бакет, как элемент data в GET /v1/market/clusters. Кадр приходит по каждому изменившемуся бакету, не чаще раза в 16 мс, с его последним состоянием. Смена интервала в терминале видна в interval.
Данные: ClusterBucket
account.orders
Заявки и триггерные заявки одного инструмента после изменения.
Отправляется только последнее состояние инструмента. Подписка не обращается к бирже, и события идут с того момента, как их присылает подключение. Кадра-снимка при подписке нет. Текущее состояние читайте через GET /v1/account/orders.
Данные: InstrumentOrders
account.positions
Позиция одного инструмента после изменения.
Отправляется только последнее состояние. Закрытая позиция приходит как "position": null. Кадра-снимка при подписке нет.
Данные: InstrumentPosition
account.balances
Баланс одной валюты.
data — один баланс, как элемент data в GET /v1/account/balances. Отправляется только последнее значение. Кадра-снимка при подписке нет.
Данные: Balance
account.fills
Одно исполнение.
Один кадр на исполнение. Если клиент отстаёт, кадр теряется, и seq показывает пропуск. Прочитайте исполнения ещё раз через GET /v1/account/fills с since_ts.
Данные: Fill
workspace
Раскладка окон целиком.
data — раскладка, как data в GET /v1/workspace. Первый кадр — текущая раскладка, он отправляется сразу при подписке. Дальше кадр приходит на каждое изменение. Отправляется только последнее состояние.
Данные: WorkspaceSnapshot
settings
Одна записанная строка настроек.
Один кадр на каждую запись в таблицу настроек: из API, из Settings App и из самого окна. Снимка при подписке нет. Текущие значения читайте через GET /v1/settings. Если клиент отстаёт, кадр теряется, и seq показывает пропуск.
Данные: 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
}
}Типы данных
Объекты, которые несут события. Те же объекты возвращают операции HTTP.
Topic
tradestickermark_pricebook.topclustersaccount.ordersaccount.positionsaccount.balancesaccount.fillsworkspacesettings
ErrorCode
unauthorizedforbiddenforbidden_scopenot_foundinvalid_requestunsupportednot_activenot_tradablerate_limitedunavailable
Стабильный код, часть контракта. Могут добавляться новые коды.
| Код | HTTP | Когда |
|---|---|---|
unauthorized | 401 | токен неверный или API выключен |
forbidden | 403 | заголовок Origin или чужой Host |
forbidden_scope | 403 | у токена нет права этой операции |
not_found | 404 | нет такой операции или подключения, или для инструмента нет подключения |
invalid_request | 400 | неверный параметр: инструмент, интервал, глубина, чужой connection_id |
unsupported | 501 | биржа — MOEX, терминал собран без рыночных данных, или настройка не пишется через API |
not_active | 409 | кластеры инструмента ещё не посчитаны, или его плечо ещё не загружено |
not_tradable | 409 | подключение не может торговать: оно выключено, только для чтения или без учётных данных |
rate_limited | 429 | превышен лимит: соединений, подписок, инструментов, новых подписок в секунду, торговых команд, команд рабочего пространства или записей настроек |
unavailable | 503 | хранилище, биржа или окно не ответили, терминал занят, или данных ещё нет |
Trade
| Поле | Тип | Описание |
|---|---|---|
idобязательное | string | null | В v1 всегда null. |
priceобязательное | string | |
qtyобязательное | string | |
sideобязательное | "buy" | "sell" | |
tsобязательное | integer | Когда терминал получил сделку, миллисекунды UTC. |
exchange_tsобязательное | integer | Время биржи. |
Ticker
Суточный тикер.
| Поле | Тип | Описание |
|---|---|---|
priceобязательное | string | |
change_24h_pctобязательное | string | null | |
volume_24hобязательное | string | |
high_24hобязательное | string | null | |
low_24hобязательное | string | null | |
best_bidобязательное | string | null | |
best_askобязательное | string | null | |
tsобязательное | integer |
MarkPrice
| Поле | Тип | Описание |
|---|---|---|
mark_priceобязательное | string | |
tsобязательное | integer |
BookTop
Верхушка стакана: биды от лучшей цены вниз, аски от лучшей цены вверх.
| Поле | Тип | Описание |
|---|---|---|
tsобязательное | integer | |
depthобязательное | integer | |
bidsобязательное | PriceLevel[] | |
asksобязательное | PriceLevel[] |
ClusterBucket
| Поле | Тип | Описание |
|---|---|---|
intervalобязательное | string | |
tsобязательное | integer | Начало бакета, миллисекунды UTC. |
end_tsобязательное | integer | |
volumeобязательное | string | |
buy_volumeобязательное | string | |
sell_volumeобязательное | string | |
tradesобязательное | integer | |
vwapобязательное | string | null | |
min_priceобязательное | string | null | |
max_priceобязательное | string | null | |
completedобязательное | boolean | |
last_trade_tsобязательное | integer | Время последней сделки бакета. 0 у бакета из истории, у которого такого времени нет. |
levelsобязательное | ClusterLevel[] | Все ценовые уровни бакета, от низшей цены к высшей. |
InstrumentOrders
InstrumentPosition
| Поле | Тип | Описание |
|---|---|---|
instrumentобязательное | string | |
positionобязательное | Position | null |
Balance
| Поле | Тип | Описание |
|---|---|---|
currencyобязательное | string | |
totalобязательное | string | |
availableобязательное | string | null | |
lockedобязательное | string | null | |
tsобязательное | integer |
Fill
| Поле | Тип | Описание |
|---|---|---|
trade_idобязательное | string | |
order_idобязательное | string | Биржевой id заявки. |
instrumentобязательное | string | |
sideобязательное | string | buy или sell. |
priceобязательное | string | |
qtyобязательное | string | |
feeобязательное | string | |
fee_currencyобязательное | string | |
position_sideобязательное | string | null | long или short на биржах с режимом хеджирования, иначе null. |
tsобязательное | integer | Время биржи. |
WorkspaceSnapshot
| Поле | Тип | Описание |
|---|---|---|
windowsобязательное | Window[] | По возрастанию id. |
SettingWritten
Строка настроек, как она записана, без writable.
| Поле | Тип | Описание |
|---|---|---|
scopeобязательное | string | |
scope_idобязательное | string | null | |
connection_idобязательное | integer | null | |
categoryобязательное | string | |
keyобязательное | string | |
valueобязательное | any | Любое значение JSON. |
PriceLevel
string[]
[price, size], оба значения — десятичные строки.
ClusterLevel
| Поле | Тип | Описание |
|---|---|---|
priceобязательное | string | |
volumeобязательное | string | |
buy_volumeобязательное | string | |
sell_volumeобязательное | string | |
buy_tradesобязательное | integer | |
sell_tradesобязательное | integer |
Order
| Поле | Тип | Описание |
|---|---|---|
keyобязательное | string | null | Ключ заявки в терминале, t-…. null, пока он неизвестен. |
exchange_idобязательное | string | null | Биржевой id заявки. null, пока биржа не приняла заявку. |
instrumentобязательное | string | |
sideобязательное | "buy" | "sell" | |
typeобязательное | "limit" | "market" | |
priceобязательное | string | |
sizeобязательное | string | В базовой монете. |
filledобязательное | string | |
remainingобязательное | string | |
notionalобязательное | string | null | Сумма в валюте котировки, которую ввёл трейдер. null у заявки из снимка биржи. |
statusобязательное | "pending" | "open" | "partially_filled" | |
created_tsобязательное | integer | null | null, если заявка пришла из снимка биржи без времени. |
Trigger
| Поле | Тип | Описание |
|---|---|---|
idобязательное | string | Числовой id, по которому триггер снимают и меняют. |
instrumentобязательное | string | |
typeобязательное | TriggerKind | |
trigger_priceобязательное | string | |
ruleобязательное | TriggerRule | |
sizeобязательное | string | null | В базовой монете. |
notionalобязательное | string | null | В валюте котировки. |
statusобязательное | "open" | "finished" | "inactive" | "invalid" | |
order_typeобязательное | string | null | Тип заявки биржи, как пришёл. |
client_idобязательное | string | null | Ключ, возвращённый при постановке триггера. |
created_tsобязательное | integer | null |
Position
| Поле | Тип | Описание |
|---|---|---|
instrumentобязательное | string | |
sideобязательное | "long" | "short" | |
sizeобязательное | string | В базовой монете. |
lot_sizeобязательное | string | Базовых единиц в одном лоте. |
entry_priceобязательное | string | |
notionalобязательное | string | В валюте котировки. |
unrealized_pnlобязательное | string | Как считает терминал. |
mark_priceобязательное | string | null | Лучший bid для лонга, лучший ask для шорта. null до первого тика стакана. |
Window
| Поле | Тип | Описание |
|---|---|---|
idобязательное | integer | |
mainобязательное | boolean | Главное окно. Его закрытие завершает терминал. |
chart_windowобязательное | boolean | Вынесенное окно графиков. |
focusedобязательное | boolean | Окно в фокусе ОС. |
geometryобязательное | object | Положение и размер в логических пикселях экрана. |
geometry.xобязательное | integer | |
geometry.yобязательное | integer | |
geometry.widthобязательное | integer | |
geometry.heightобязательное | integer | |
geometry.maximizedобязательное | boolean | |
monitorобязательное | string | null | Имя монитора по данным ОС. |
tabsобязательное | Tab[] |
TriggerKind
stop_losstake_profitstop
TriggerRule
price_above_or_equalprice_below_or_equal
Tab
Stack
Стакан с лентой сделок и кластерами.
| Поле | Тип | Описание |
|---|---|---|
widget_idобязательное | integer | |
instrumentобязательное | string | null | null, пока стек пуст. |
connection_idобязательное | integer | null | |
emptyобязательное | boolean | |
frozenобязательное | boolean | Стек заморожен. |
positionобязательное | Cell | null |
Chart
| Поле | Тип | Описание |
|---|---|---|
widget_idобязательное | integer | |
instrumentобязательное | string | null | |
connection_idобязательное | integer | null | |
intervalобязательное | string | Интервал свечей: 1m, 5m, … |
owner_widget_idобязательное | integer | null | Стек, за которым следует график. null у самостоятельного графика. |
positionобязательное | Cell | null |
Cell
Ячейка сетки вкладки: колонка слева, строка сверху, ячейка внутри строки.
| Поле | Тип | Описание |
|---|---|---|
columnобязательное | integer | |
rowобязательное | integer | |
cellобязательное | integer |
Настройки стакана инструмента GET
Настройки стакана инструмента, как их видит стек этого подключения, по категориям и ключам. Значение берётся из первого места, где оно есть: строка пары на подключении, умолчание подключения, строка пары без подключения, глобальное умолчание. `resolved_from` называет это место.
Python SDK
Клиент, стрим, поиск терминала и ошибки пакета spectre-terminal.