# Common Streams
Common streams это стримы, через которые доступны данные с нескольких источников.
Данные не приходят сразу после открытия соединения. Для их получения необходимо отправить запрос на подписку. При успешной подписке клиенту отправляется id подписки. Также по id подписки можно отписаться от получения изменений.
Максимальная длительность соединения на данный момент составляет 24 часа. Не нужно учитывать это в логике так как параметр может поменяться. Ограничение предназначено для автоматической ребалансировки нагрузки. Интервал обновления составляет 100 ms.
Запрос (MainNet):
wss://matcher.waves.exchange/ws/v0
Запрос (TestNet):
wss://matcher-testnet.waves.exchange/ws/v0
Опционально можно указать query-параметры для сбора статистики:
a_client - тип клиента и его версия в формате: <web|desktop|android|ios>-<version>
a_os - операционная система клиента и его версия, например: Mac OS X 10.15.6, Windows 10, Linux или Android 7
Пример запроса с параметрами (MainNet):
wss://matcher.waves.exchange/ws/v0?a_client=web-1.2.99&a_os=Windows%10%207
# Order Book Updates
Вы можете отправить запрос на подписку на обновления ордер бука.
См. базовые URL
Пример запроса:
{
"T": "obs",
"S": "WAVES-DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p",
"d": 10
}
В данном примере:
obs это тип сообщения: ob - order book, s - subscribe.
WAVES-DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p это id подписки, состоящий из: amount asset-price asset.
10 это price depth.
Запрос не будет иметь эффекта, если подписка на данный ордер бук была осуществлена ранее; В рамках одного соединения возможно подписаться на данные N ордер буков (в данный момент 10). Успешная подписка на ордер бук номер N+1 отменяет подписку на первый. При этом клиенту будет отправлено сообщение с ошибкой, например:
The limit of 10 subscriptions of this type was reached. The subscription of Co11Tbj83TeZCnuXrZQwv6Bs4XG2sm1vGQiQ4FLDjJWs-WAVES was stopped
# Возможные ошибки
RequestArgumentInvalid - запрос содержит неправильные аргументы, например, отрицательный depth. Рекомендация: проверить код на стороне Клиента;
AssetPairIsDenied - пара заблокирована. Возможны более детальные ошибки: AssetBlacklisted, AmountAssetBlacklisted или PriceAssetBlacklisted. Рекомендация: проверить логику на Клиенте и/или сообщить клиенту о том, что пара заблокирована;
AssetPairSameAssets - amount и price токены являются одним токеном. Рекомендация: проверить код на стороне Клиента;
OrderAssetPairReversed - пара имеет неправильный порядок, т.е. amount и price токены перепутаны местами. Рекомендация: проверить код на стороне Клиента;
SubscriptionsLimitReached - превышено количество подписок. Рекомендация: проверить код на стороне Клиента, поскольку количество подписок подобрано таким образом, чтобы удовлетворить нужды Клиента. Либо связаться с разработчиками Matcher Server;
WavesNodeConnectionBroken - ошибка запроса данных с Waves NODE. Рекомендация: сделать повторный запрос;
OrderBookStopped - ордер бук остановлен. Обычно это происходит при снятии пары с торгов и при удалении ордер бука;
Balancing - Matcher Server выполняет балансировку соединений и данное соединение было закрыто. Рекомендация: подключиться заново.
# Приходящие данные
Когда приходит snapshot, он содержит заданный order book с заданным значением depth - количеством ценовых уровней sell/buy ордеров. Если order book пуст, будет получен пустой JSON объект;
Когда приходит updates, обратите внимание на следующее:
Если вам пришло обновление, в котором amount = 0 и уровень (price), который есть в вашем локальном состоянии, такой уровень следует удалить.
Если вам пришло обновление, в котором amount = 0 и уровень (price), которого нет в вашем локальном состоянии, такое обновление уровня следует игнорировать. Такое может произойти, если ордер был поставлен и исполнен в интервале между веб-сокетными сообщениями.
Например, в интервале между веб-сокетными сообщениями кто-то поставил ордер на продажу по цене 16.95 - в результате amount в уровне увеличился на amount ордера. Затем кто-то исполнил ордер - в результате amount в уровне стал равен 0, а вам в следующем обновлении пришла информация о том, что "на уровне price=16.95 amount стал равен 0".
Last trade всегда приходит полностью, поскольку является "неделимой сущностью" и у него нет состояния меняющееся во времени: либо он был, либо нет.
Subscription Id отправляется всегда.
Пример:
{
"T": "ob",
"_": 1585148910776,
"S": "WAVES-DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p",
"U": 0,
"a": [ // запросы (sell orders) отсортированые по цене (in ascending order)
[
"0.12001", // цена
"1001.00000001" // сумма
],
...
],
"b": [ // bids (buy orders) отсортированые по цене (in ascending order)
[
"0.10001", // цена
"997.97226" // сумма
],
...
],
"t": [ // последняя сделка
"0.10001", // цена последней сделки
"997.97226", // сумма последней сделки,
"BUY" // тип последней сделки: BUY | SELL | buy | sell. Строчные буквы будут поддерживаться в будущих версиях. Можно использовать оба способа написания
],
"s": [ // настройки пары
"r": { // ограничения
// [min, step, max]
"a": ["0.00000001", "0.00000001", "1000000000"], // сумма
"p": ["0.00000001", "0.00000001", "1000000"] // цена
},
"m": { // настройик матчинга для пары
"t": "0.000001" // tick size
}
]
}
В данном примере:
ob это тип сообщения.
1585148910776 это Unix временная отметка обновления в миллисекундах. Данное поле предназначено для отладки.
WAVES-DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p это id подписки.
0 это id обновления.
См. описание др. полей в примере.
# Address Updates
Вы можете отправить запрос на подписку на обновления address updates.
См. базовые URL
Пример запроса:
{
"T": "aus",
"S": "3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4",
"t": "jwt",
"j": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaWciOiIzamRBeTg2WTVKSzg3RWVCM1pqdFdhTWJXUmYyOGlFUThmNmY0NGttQXRBRVlOWDJzbjN2V2taVExFb291WEFFdjF2Y3FqZXZid2p1eXRmeWJNdEVCVDNkIiwibmIiOiJXIiwidXNlcl9uYW1lIjoiNUE2TkJ4eTJNWXN5cmNuemgyVDdncEZoS0JVRnRtVXNTb0pNRHA3OExlcnMiLCJzY29wZSI6WyJnZW5lcmFsIl0sImx0IjoxNzY0LCJwayI6IjVBNk5CeHkyTVlzeXJjbnpoMlQ3Z3BGaEtCVUZ0bVVzU29KTURwNzhMZXJzIiwiZXhwIjoxNTg4MDU0MDg3LCJqdGkiOiJkNWM5M2Y2ZS05YTEwLTQ4ZTYtOTRkYi05NzhjYmI4MzgxMWUiLCJjaWQiOiJkZWZhdWx0LWNsaWVudCJ9.3Lwt0Akq2Xeg_UaDJHXlOq-D4kwpfdpMCTNuDG0IEMg"
"b": {
"f": ["-nft"]
}
}
В данном примере:
aus это тип сообщения: au - address updates, s - subscribe.
3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4 это id подписки: адрес аккаунта.
jwt это тип аутентификации. В данный момент доступен только тип "jwt".
j это поле с auth токеном JWT.
b это поле для опций баланса.
f это поле для фильтров. В данный момент доступен фильтр, который позволяет не отображать NFT в балансах (значение "-nft").
- Вы можете получить токен аутентификации с помощью сервиса аутентификации;
- Подписка отменяется когда заканчивается срок действия токена аутентификации;
- В рамках одного соединения возможно подписаться на данные N адресов (в данный момент 10). Успешная подписка на адрес номер N+1 отменяет подписку на первый. При этом Клиенту будет отправлено сообщение с ошибкой, например:
The limit of 10 subscriptions of this type was reached. The subscription of 3N9zmtMGwnk8ZY3T3RZGDUoXYymtDYJNJzN was stopped
- Для продления подписки необходимо отправить запрос с id активной подписки и обновленным токеном. В этом случае на Клиент не придет snapshot.
- Время действия подписки на адрес не влияет на время жизни соединения. Поэтому продлить подписку (без переподключения) более чем на 24 часа нельзя.
# Возможные ошибки
SubscriptionAuthTypeUnsupported - указан неподдерживаемый тип аутентификации. Рекомендация: проверить код на стороне Клиента;
JwtBroken - предоставленный токен не является JWT. Рекомендация: проверить код на стороне Клиента, токен должен браться с сервиса аутентификации;
SubscriptionTokenExpired - токен устарел. Рекомендация: проверить код на стороне Клиента, токен не должен быть истекшим на момент подписки;
JwtCommonError - прочие ошибки JWT. Рекомендация: проверить код на стороне клиента, токен должен браться с сервиса аутентификации;
JwtPayloadBroken - невозможн распарсить payload. Рекомендация: проверить код на стороне Клиента, токен должен браться с сервиса аутентификации;
TokenNetworkUnexpected - для подписки на изменения используется токен, выпущенный для другой сети. Рекомендация: проверить код на стороне Клиента, токен должен браться с сервиса аутентификации подходящей сети;
InvalidJwtPayloadSignature - неправильная подпись в payload токена. Рекомендация: проверить код на стороне Клиента, токен должен браться с сервиса аутентификации;
AddressAndPublicKeyAreIncompatible - владелец токена пытается подписаться на данные другого адреса. Рекомендация: проверить код на стороне Клиента;
SubscriptionsLimitReached - превышено количество подписок. Рекомендация: проверить код на стороне Клиента, поскольку количество подписок подобрано таким образом, чтобы удовлетворить нужды клиента. Либо свяжитесь с разработчиками Matcher Server;
Balancing - Matcher Server выполняет балансировку соединений и данное соединение было закрыто. Рекомендация: подключиться заново.
# Приходящие данные
Когда приходит snapshot, он содержит:
Текущие значения баланса по всем имеющимся у пользователя токенам в виде двух элементов массива:
Tradable - это доступный (opens new window) баланс аккаунта (на Ноде) за вычетом зарезервированного баланса по открытым ордерам и тратам по транзакциям в UTX pool. Это тот баланс, который матчер использует при валидации;
Reserved - суммарное количество токенов клиента, которое может быть потрачено в ходе исполнения всех открытых ордеров, включая комиссию матчера;
Все активные (не отмененные, и не выполненные) ордера;
Если это новый пользователь без WAVES и др. токенов, балансы не приходят:
{
"T": "au",
"_": 1585148910776,
"S": "3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4",
"U": 0
}
В данном примере:
au это тип сообщения, a - address, u - updates.
1585148910776 это Unix временная отметка обновления в миллисекундах. Данное поле предназначено для отладки.
3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4 это id подписки.
0 это id обновления.
Когда приходит updates, он содержит:
- Измененные балансы. Если балансы не были изменены, данное поле не приходит;
- Изменения по ордерам. В изменениях могут присутствовать не все поля. Например, price asset и amount asset не придут, если ордер отменился;
- Информация по транзакциям исполнения ордеров отправляется, когда ордер получает статус Filled или Partially filled (когда появляются транзакции исполнения). Транзакции могут отсутствовать, если ордер перешел в статус Filled из-за того, что он стал unmatchable (оставшаяся сумма стала слишком маленькой для исполнения). Статус Triggered могут получить только новые не исполненные ордера версии 4;
- Информация про исполненные ордера о том, был ли это "maker" или "taker" ордер. Отображается в поле mt, которое может иметь значение m (для maker ордера) или t (для taker ордера);
- Subscription Id отправляется всегда.
Пример:
См. описание полей в примере.
{
"T": "au", // еип сообщения
"_": 1585148910776, // Unix временная отметка обновления в миллисекундах. Данное поле предназначено для отладки.
"S": "3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4", // id подписки
"U": 1, // id обновления
"b": { // балансы
"DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p": [// id токена
"716.356", // tradable
"56.85" // reserved
]
...
},
"o": [ // Orders
{
"i": "89CyqyWeQmqG9QRdoXnYUGrV27eTNjy6HEDy3jBq8zDU", // id ордера
"t": 1579697243653, // временная отметка ордера
"A": "WAVES", // amount asset
"P": "DG2xFkPdDwKUoBkzGAhQtLpSGzfXLiCYPEzeKH2Ad24p", // price asset
"S": "sell", // тип сделки: BUY | SELL | buy | sell. Строчные буквы будут поддерживаться в будущих версиях. Можно использовать оба способа написания
"T": "limit", // тип ордера: LIMIT | MARKET | limit | market. Строчные буквы будут поддерживаться в будущих версиях. Можно использовать оба способа написания
"p": "0.84", // цена ордера
"a": "176.3002", // сумма ордера
"f": "0.003", // комиссия ордера
"F": "8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS", // токен комисии
"s": "PartiallyFilled", // статус ордера: ACCEPTED | FILLED | PARTIALLY_FILLED | CANCELLED | Accepted | PartiallyFilled | Filled | Cancelled | Triggered. Строчные буквы будут поддерживаться в будущих версиях. Можно использовать оба способа написания
"q": "15.01", // текущая выполненная сумма, включая этот и предыдущий матчинг
"Q": "0.001", // текущая выполненная сумма комиссии, включая этот и предыдущий матчинг
"r": "0.835", // средняя выполненная цена среди всех сделок с максималным priceDecimals после точки
"E": "12.53335" // общая сумма выполненных price assets.
"m": [ // ифнормация про транзакции матчинга
{
"i":"8Ktd2RvW2TiFU1Q8LNtu25uxj6t1r2itk4huG75H2DFD", // id транзакции матчинга
"t":1579699001581, // временная отметка (timestamp) транзакции
"p":"0.8925", // выполненная цена
"A":"46.044", // выполненное количество токена суммы
"P":"41.09427" // выполненное количетво токена цены
"mt": "t" // maker или taker ордер; "m" = maker, "t" = taker
},
...
]
},
...
]
}
# Частые вопросы (FAQ)
Q: Почему отправляются только активные ордера?
A: Хотя у нас сейчас есть ограничение, что мы храним только 100 последних закрытых ордеров, в будущем мы можем убрать это ограничение (напр. с помощью postgresql), в этом случае не понятно как реализовывать отдачу всех тысяч ордеров в snapshot.
Могут понадобиться фильтры, чтобы отдавать такую большую историю. Получается, что и для инициирования WebSockets нам нужно указывать фильтры для отправки snapshot. А это дублирование логики, которая в любом случае должна быть реализована в REST API.
Идеологически WS подразумевает некоторый realtime, который не нужен в закрытых ордерах - по ним уже никогда не придет никакой update, значит и не нужен snapshot.
Кроме того, такой функциональности нет у других бирж (snapshot есть только у Bitfinex) - отдаются только активные ордера.
Q: Почему в качестве subscription id используется адрес, а не открытый ключ?
A: При формировании адреса используется байт сети. Другими словами аккаунт с одним и тем же сидом в разных сетях:
- Будет иметь разный адрес;
- Будет иметь одинаковый закрытый и открытый ключи;
- Будет одинаково подписывать один и тот же набор байт в разных сетях;
Q: Что такое "maker" и "taker" ордера?
A: Maker ордер это limit ордер, который находится в ордер буке. Taker ордер это limit/market ордер, который получен матчером для последующего матчинга с ордерами, которые уже находятся в ордер буке.
# Rates Updates
Вы можете отправить запрос на подписку на обновления rates updates.
См. базовые URL
Пример запроса:
{
"T": "rus",
"S": "ru"
}
В данном примере:
rus это тип сообщения: ru - rates updates, s - subscribe.
ru это id подписки: все обновления rates.
# Приходящие данные
Когда приходит snapshot, он содержит все текущие rates;
Когда приходит updates, он содержит обновленные rates,значение "-1" означает удаление рейта. На данный момент все изменения приходят по одному сообщению на каждый изменившийся токен;
Пример:
{
"T":"ru",
"_":1600163991902,
"U":0,
"R":{
"8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS":"0.00041863",
"WAVES":"1"
}
}
В данном примере:
ru это тип сообщения, ru - rates updates.
1600163991902 это Unix временная отметка обновления в миллисекундах. Данное поле предназначено для отладки.
0 это Id обновления, 0 отзначает, что это snapshot
# Частые вопросы (FAQ)
Q: Почему при одновременном обновлении нескольких рейтов нельзя отправить все эти изменения в одном сообщении? Зачем отправлять несколько сообщений?
A: Пока матчер не позволяет обновлять рейты пачками, а только по одному токену. В связи с тем, что массовое обновление рейтов потребует множество запросов на обновление рейта каждого токена, то и изменения будут приходить по одному токену в сообщении. В дальнейшем планируется реализовать массовое обновление рейтов, после этого несколько обновлений будут приходить в одном сообщении.
# Отписка
Вы можете отписаться с помощью id подписки, чтобы перестать получать обновления.
Пример запроса:
{
"T": "u",
"S": "3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4"
}
В данном примере:
u это тип сообщения, u - unsubscribe.
3N93cuB7hDLhpg8n6QpyV7vbWaj5qwBXDF4 это id подписки.
Запрос не будет иметь эффекта, если подписки не существует;
Не нужно дожидаться ответа с сервера;