WebSocket是HTML5一种新的协议(Protocol)。它实现了客户端与服务器全双工通信, 使得数据可以快速地双向传播。通过一次简单的握手就可以建立客户端和服务器连接, 服务器根据业务规则可以主动推送信息给客户端。其优点如下:
- 客户端和服务器进行数据传输时,请求头信息比较小,大概2个字节。
- 客户端和服务器皆可以主动地发送数据给对方。
- 不需要多次创建TCP请求和销毁,节约宽带和服务器的资源。
强烈建议开发者使用WebSocket API获取市场行情和买卖深度等信息。
.地址
wss://ws.coinbene.vip/stream/ws
- 访问地址需要具备科学上网环境
- 连接上ws后需主动处理服务端的
ping
检测消息,用于连接保活- CoinBene服务器部署在美西,为最大限度地减少API访问延迟,建议您使用与美西通讯通畅的服务器
请求
{"op":"<value>","args": ["<value1>","<value2>"]}
其中op
可取值
subscribe
订阅unsubscribe
取消订阅login
登录
args
为执行op
操作的参数,视op
不同而不同.
成功响应
{"event": "<value>","topic":"<value>"}
{"topic":"<value>","data":"[<value1>,<value2>]"}
其中为了区分是首次全量和后续增量返回格式将会是
{"topic":"<value>","action":"<value>","data":["<value1>","<value2>"]}
失败响应
{"event":"<value>","message":"<errorMessage>","code":"<errorCode>"}
用户可以同时订阅一个或者多个Topic
指令格式
{"op":"subscribe", "args":["<topic>"]}
订阅时op
取值subscribe
args
为topic数组
示例
// send
{"op":"subscribe","args":["btc/orderBook.BTCUSDT.10","btc/tradeList.BTCUSDT"]}
// response
{"event":"subscribe","topic":"btc/orderBook.BTCUSDT.10"}
{"event":"subscribe","topic":"btc/tradeList.BTCUSDT"}
可以同时取消一个或者多个Topic
指令格式
{"op":"unsubscribe","args": ["<topic>"]}
示例
// send
{"op":"unsubscribe","args":["btc/orderBook.BTCUSDT.10","btc/tradeList.BTCUSDT"]}
// response
{"event":"unsubscribe","topic":"btc/orderBook.BTCUSDT.10"}
{"event":"unsubscribe","topic":"btc/tradeList.BTCUSDT"}
如果您希望订阅私有Topic,则必须先进行登录。
指令格式
{"op":"login","args":["<apiKey>","<expires>","<signature>"]}
参数 | 描述 | 生成规则 |
---|---|---|
apiKey | API key | 系统中申请获得 |
expires | 登录失效时间,超过设定的时间后本次登录失效. 请保证有效时长大于五分钟 | ISO8601标准时间格式,精确到秒。 例如 2019-06-18T01:51:51Z |
signature | 签名 | hex(HMAC_SHA256(apiSecret, expires + method + requestPath)) 此处method="GET" requestPath="/login" |
- apiKey在CoinBene系统的API管理中申请
- 申请apiKey的同时会生成apiSecret,请妥善保管.任何时候都不要透露给其他人或传输到服务器端
- 登录授权失效后,服务器会自动取消客户端订阅的私有Topic,客户端应注意重新登录来延长授权期限.
- method和requestPath为固定值 method="GET" requestPath="/login"
签名算法验证(参考)
/* 样例代码(Java版本): */
// 源串:2019-07-04T02:19:08ZGET/login
// secret:9daf13ebd76c4f358fc885ca6ede5e27
// 生成sign串:3ded9d0113133c9f06cfa50ce99618e6d983a534f5a2219ebbe3ffb02b6fbe16
public static void main(String[] args) {
String secret = "9daf13ebd76c4f358fc885ca6ede5e27";
String method = "GET"; // <1>
String requestPath = "/login"; // <2>
final DateTimeFormatter utcFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
String timestamp = utcFormatter.format(ZonedDateTime.now(ZoneOffset.UTC).plusMinutes(10));
String signStr = signForWebSocketApi(timestamp, method, requestPath, secret);
System.out.println(signStr);
}
/**
* 生成签名
*
* @param method 请求方法:POST或者GET
* @param requestPath requestPath
* @param secret 密钥
*/
private static String signForWebSocketApi(String timestamp, String method, String requestPath, String secret) {
String shaResource = timestamp + method + requestPath;
System.out.println(shaResource);
String signStr = sha256_HMAC(shaResource, secret);
return signStr;
}
/**
* sha256_HMAC加密
*
* @param resource 签名源字符串
* @param secret 秘钥
* @return 加密后字符串
*/
private static String sha256_HMAC(String resource, String secret) {
String hash = "";
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] bytes = sha256_HMAC.doFinal(resource.getBytes("UTF-8"));
hash = byteArrayToHexString(bytes);
} catch (Exception e) {
System.out.println("Error HmacSHA256 ===========" + e.getMessage());
}
return hash;
}
/**
* 将加密后的字节数组转换成字符串
*
* @param bytes 字节数组
* @return 字符串
*/
private static String byteArrayToHexString(byte[] bytes) {
StringBuffer buffer = new StringBuffer();
String stmp;
for (int index = 0; bytes != null && index < bytes.length; index++) {
stmp = Integer.toHexString(bytes[index] & 0XFF);
if (stmp.length() == 1) {
// 一位补零
buffer.append('0');
}
buffer.append(stmp);
}
return buffer.toString().toLowerCase();
}
示例
// send
{"op":"login","args":["0d999f8ba827adfc494931d9e1539df9","2019-07-04T02:19:08Z","3ded9d0113133c9f06cfa50ce99618e6d983a534f5a2219ebbe3ffb02b6fbe16"]}
// response
{"event":"login","success":true}
当用户的WebSocket客户端连接到CoinBene服务器后,服务器会定期(当前为5秒)向其发送字符串'ping'
来检测连接.WebSocket客户端接收到此心跳消息后,应返回文字字符串'pong'
作为回应.
当WebSocket服务器连续两次发送了
ping
消息却没有收到任何一次pong
消息返回后,服务器将主动断开与此客户端的连接。
连接限制:1次/s
订阅限制:每小时240次
公共Topic(无需登录)
名称 | 格式 | 示例 |
---|---|---|
深度列表 | btc/orderBook.{symbol}.{depth} | btc/orderBook.BTCUSDT.10 |
最新成交 | btc/tradeList.{symbol} | btc/tradeList.BTCUSDT |
Ticker信息 | btc/ticker.{symbol} | btc/ticker.BTCUSDT |
K线数据 | btc/kline.{symbol} | btc/kline.BTCUSDT |
私有Topic(需登录)
名称 | 格式 |
---|---|
用户账户 | btc/user.account |
用户持仓 | btc/user.position |
用户交易 | btc/user.order |
topic格式: btc/orderBook.{symbol}.{depth}
symbol
: 交易所支持的任一交易对
depth
: 目前支持深度5、10、20、50、100
订阅时将symbol
和depth
设置成目标值即可.
首次返回指定深度(depth)的委托列表,后续为增量
示例
// send 获取BTCUSDT交易对10挡的深度列表
{"op": "subscribe", "args": ["btc/orderBook.BTCUSDT.10"]}
//response
// 首次10挡
{
"topic": "btc/orderBook.BTCUSDT",
"action": "insert",
"data": [{
"asks": [
["5621.7", "58"],
["5621.8", "125"],
["5621.9", "100"],
["5622", "84"],
["5623.5", "90"],
["5624.2", "1540"],
["5625.1", "300"],
["5625.9", "350"],
["5629.3", "200"],
["5650", "1000"]
],
"bids": [
["5621.3", "287"],
["5621.2", "41"],
["5621.1", "2"],
["5621", "26"],
["5620.8", "194"],
["5620", "2"],
["5618.8", "204"],
["5618.4", "30"],
["5617.2", "2"],
["5609.9", "100"]
],
"version":1,
"timestamp": 1584412740809
}]
}
//后续增量
{
"topic": "btc/orderBook.BTCUSDT",
"action": "update",
"data": [{
"asks": [
["5621.7", "50"],
["5621.8", "0"],
["5621.9", "30"]
],
"bids": [
["5621.3", "10"],
["5621.2", "20"],
["5621.1", "80"],
["5621", "0"],
["5620.8", "10"]
],
"version":2,
"timestamp": 1584412740809
}]
}
首次
action = insert
后续增量action = update
一个挂单项是一个[price,size]的数组 例:["5621.7", "58"] 5621.7为深度价格,58为此价格数量 version 数据版本严格递增 client端可以根据version来判断数据是否连续
参数名 | 参数类型 | 描述 |
---|---|---|
symbol | string | 交易对 |
asks | string | 卖方深度 |
bids | string | 买方深度 |
version | number | 数据版本 |
timestamp | number | 时间戳(毫秒) |
-
本地维护orderBook副本(参考)
- 订阅
orderBook.{symbol}.{depth}
首次获取到{depth}深度orderBook副本 - 对后续的增量推送,以price为key按如下规则处理
- 如果出现新的price,说明有用户按新的价格挂单,此时需要将挂单项插入到挂单列表中,同时保证列表顺序
- price已存在,并且最新size值不为0,说明该价格的挂单刚刚进行了交易,此时需要用新的size值替换旧的size值
- price已存在,并且size值为0,说明该价位的挂单已经撤单或者被吃,应该移除这个价位
- 订阅
topic格式: btc/tradeList.{symbol}
symbol
: 交易所支持的任一交易对
示例
// send
{"op": "subscribe", "args": ["btc/tradeList.BTCUSDT"]}
// response
{
"topic": "btc/tradeList.BTCUSDT",
"data": [
[
"8600.0000",
"s",
"100",
1584412740809
]
]
}
// 一个数据项是一个[price,side,volume,timestamp]的数组
返回参数
参数名 | 参数类型 | 描述 |
---|---|---|
price | string | 成交价格 |
volume | string | 成交数量 |
side | string | 成交方向,s=主卖,b=主买 |
timestamp | number | 成交时间 |
获取平台合约的最新成交价、买一价、卖一价和24交易量
topic格式: btc/ticker.{symbol}
symbol
: 交易所支持的任一交易对
示例
// send
{"op": "subscribe", "args": ["btc/ticker.BTCUSDT","btc/ticker.ETHUSDT"]}
// response
{
"topic": "btc/ticker.BTCUSDT",
"data": [
{
"symbol": "BTCUSDT",
"lastPrice": "8548.0",
"markPrice": "8548.0",
"bestAskPrice": "8601.0",
"bestBidPrice": "8600.0",
"bestAskVolume": "1222",
"bestBidVolume": "56505",
"high24h": "8600.0000",
"low24h": "242.4500",
"volume24h": "4994",
"timestamp": 1584412736365
}
]
}
返回参数
参数名 | 参数类型 | 描述 |
---|---|---|
symbol | string | 合约名称,如BTCUSDT |
bestAskPrice | string | 卖一价 |
bestAskSize | string | 卖一量 |
bestBidPrice | string | 买一价 |
bestBidSize | string | 买一量 |
lastPrice | string | 最新价 |
markPrice | string | 标记价格 |
high24h | string | 24h最高价 |
low24h | string | 24h最低价 |
volume24h | string | 24h成交量USDT |
获取平台合约的K线数据
topic格式: btc/kline.{symbol}
*symbol:
*交易所支持的任一交易对
示例
// send
{"op": "subscribe", "args": ["btc/kline.BTCUSDT","btc/kline.ETHUSDT"]}
// response
{
"topic": "btc/kline.BTCUSDT",
"data": [
{
"c": 7513.01,
"h": 7513.37,
"l": 7510.02,
"o": 7510.24,
"m": 7512.03,
"v": 60.5929,
"t": 1578278880
}
]
}
返回参数
参数名 | 参数类型 | 描述 |
---|---|---|
c | number | 收盘价格 |
h | number | 最高价格 |
l | number | 最低价格 |
o | number | 开盘价格 |
v | number | 成交量(张) |
t | number | 时间戳 |
如下私有Topic在订阅时需要先进行登录操作.
获取账户信息,需先登录
topic格式: btc/user.account
示例
// send
{"op": "subscribe", "args": ["btc/user.account"]}
// response
{
"topic": "btc/user.account",
"data": [{
"asset": "BTC",
"availableBalance": "20.3859",
"frozenBalance": "0.7413",
"balance": "21.1272",
"timestamp": "2019-05-22T03:11:22.0Z"
}]
}
返回参数
参数名 | 参数类型 | 描述 |
---|---|---|
asset | string | 资产 |
availableBalance | string | 可用余额 |
frozenBalance | string | 冻结额度 |
balance | string | 账户余额 |
获取用户持仓信息,需先登录
topic格式: btc/user.position
示例
// send
{"op": "subscribe", "args": ["btc/user.position"]}
// response
{
"topic": "btc/user.position",
"data": [{
"availableQuantity": "100",
"avgPrice": "7778.1",
"leverage": "20",
"liquidationPrice": "5441.0",
"markPrice": "8086.5",
"positionMargin": "0.0285",
"quantity": "507",
"realisedPnl": "0.0069",
"side": "long",
"symbol": "BTCUSDT",
"marginMode": "1",
"createTime": "2019-05-22T03:11:22.0Z"
}]
}
返回参数
参数名 | 参数类型 | 描述 |
---|---|---|
availableQuantity | string | 可平仓数量 |
avgPrice | string | 开仓均价 |
leverage | string | 杠杆倍数 |
liquidationPrice | string | 强平价格 |
markPrice | string | 标记价格 |
postionMargin | string | 仓位保证金 |
quantity | string | 合约的持仓数量 |
realisedPnl | string | 已实现盈亏 |
side | string | 方向 |
symbol | string | 合约名称 |
marginMode | string | 保证金模式 1全仓 0逐仓 |
createTime | string | 仓位创建时间 |
获取用户交易信息,需先登录
topic格式: btc/user.order
示例
// send
{"op": "subscribe", "args": ["btc/user.order"]}
// response
{
"topic": "user.order",
"data": [{
"orderId": "580721369818955776",
"direction": "openLong",
"leverage": "20",
"symbol": "ETHUSDT",
"orderType": "limit",
"quantity": "7",
"orderPrice": "146.30",
"orderValue": "0.0010",
"fee": "0.0000",
"filledQuantity": "0",
"averagePrice": "0.00",
"orderTime": "2019-05-22T03:39:24.0Z",
"status": "new",
"lastFillQuantity": "0",
"lastFillPrice": "0",
"lastFillTime": ""
}]
}
参数名 | 参数类型 | 描述 |
---|---|---|
orderId | string | 订单Id |
direction | string | 方向 |
leverage | string | 杠杆倍数 |
orderType | string | 订单类型, 限价=limit 市价=market |
quantity | string | 委托量(张) |
orderPrice | string | 订单价格 |
orderValue | string | 订单价值 |
fee | string | 手续费 |
filledQuantity | string | 成交量(张) |
averagePrice | string | 平均成交价格 |
orderTime | string | 订单创建时间 |
lastFillPrice | string | 最新成交价格 (如果没有,推0) |
lastFillQuantity | string | 最新成交数量 (如果没有,推0) |
lastFillTime | string | 最新成交时间 (如果没有,推 "") |
status | string | 订单状态(new:挂单中,filled:完成成交,canceled:完全撤单,partiallyFilled:部分撤单 |
错误代码 | 描述 |
---|---|
429 | 请求太频繁 |
10501 | ping检测超时 |
10502 | 请求体错误 |
10503 | Topic不支持 |
10504 | 未登录 |
10505 | 签名错误 |
10506 | 参数错误 |
10507 | expires错误 |
10508 | App Id 错误 |
10509 | 不支持的orderBook深度 |
10500 | 系统错误 |