因此,本文将介绍一种使用Websocket的替代方法,与 REST 相比,它具有更高的效率,并让您有机会从交易所流式传输实时数据并存储它以供将来分析或回测。
那么下面,就让我们一起来使用websocket来推送我们的账户信息
如果您熟悉这些步骤,则可以跳过此会话。只需确保您将其设置为预期目的:现货和保证金交易或期货交易或两者兼而有之。
与用于市场数据流的端点和通道不同,要流式传输订单和帐户更新,您需要创建一个具有唯一监听密钥的用户数据流,每次启动新连接时都需要生成它。
Spot 和 Futures 的基本 API 端点不同:
在这里,我们使用币安的golang版本的的SDk
go get github.com/adshao/go-binance/v2
API 服务的初始化客户端。从您的币安账户获取 APIKey/SecretKey。
var (
apiKey = "your api key"
secretKey = "your secret key"
)
client := binance.NewClient(apiKey, secretKey)
futuresClient := binance.NewFuturesClient(apiKey, secretKey) // USDT-M Futures
deliveryClient := binance.NewDeliveryClient(apiKey, secretKey) // Coin-M Futures
一个服务实例代表一个 REST API 端点,由 client.NewClient 函数初始化。
只需以链式方式调用 API。最后调用 Do() 发送 HTTP 请求。
func GetListenKey(t *testing.T) {
client := binance.NewClient(apiKey, secretKey)
listenKey, err := client.NewStartUserStreamService().Do(context.Background())
if err != nil {
fmt.Println(err)
return
}
fmt.Println(listenKey)
}
# curl -H "X-MBX-APIKEY: MY-APIKEY" -X 'POST' https://api.binance.com/api/v3/userDataStream
{"listenKey":"listenKey"}
Spot 和 Futures 的基本 websocket 端点:
使用listen_key生成的和符合流媒体需求的 websocket 端点,详细代码如下:
func TestWsUserDataServe(t *testing.T) {
wsHandler := func(event *binance.WsUserDataEvent) {
fmt.Println(event)
}
errHandler := func(err error) {
fmt.Println(err)
}
doneC, _, err := binance.WsUserDataServe(listenKey, wsHandler, errHandler)
if err != nil {
fmt.Println(err)
return
}
<-doneC
}
将第 8 行替换listenKey为第 1 步中生成的 API 密钥。
收到的消息将采用以下格式。对于事件类型"executionReport"(订单更新):
{
"e": "executionReport", // 事件类型
"E": 1499405658658, // 事件时间
"s": "ETHBTC", // 交易对
"c": "mUvoqJxFIILMdfAW5iGSOW", // clientOrderId
"S": "BUY", // 订单方向
"o": "LIMIT", // 订单类型
"f": "GTC", // 有效方式
"q": "1.00000000", // 订单原始数量
"p": "0.10264410", // 订单原始价格
"P": "0.00000000", // 止盈止损单触发价格
"d": 4, // 追踪止损(Trailing Delta) 只有在追踪止损订单中才会推送.
"F": "0.00000000", // 冰山订单数量
"g": -1, // OCO订单 OrderListId
"C": "", // 原始订单自定义ID(原始订单,指撤单操作的对象。撤单本身被视为另一个订单)
"x": "NEW", // 本次事件的具体执行类型
"X": "NEW", // 订单的当前状态
"r": "NONE", // 订单被拒绝的原因
"i": 4293153, // orderId
"l": "0.00000000", // 订单末次成交量
"z": "0.00000000", // 订单累计已成交量
"L": "0.00000000", // 订单末次成交价格
"n": "0", // 手续费数量
"N": null, // 手续费资产类别
"T": 1499405658657, // 成交时间
"t": -1, // 成交ID
"I": 8641984, // 请忽略
"w": true, // 订单是否在订单簿上?
"m": false, // 该成交是作为挂单成交吗?
"M": false, // 请忽略
"O": 1499405658657, // 订单创建时间
"Z": "0.00000000", // 订单累计已成交金额
"Y": "0.00000000", // 订单末次成交金额
"Q": "0.00000000" // Quote Order Qty
"j": 1, // Strategy ID; 下单时填上字段才会返回
"J": 1000000 // Strategy Type; 下单时填上字段才会返回
}
可能的执行类型:
如果订单是OCO(二选一订单),则除了显示"executionReport"事件外,还将显示一个名为"ListStatus"的事件。
{
"e": "listStatus", // 事件类型
"E": 1564035303637, // 事件时间
"s": "ETHBTC", // 交易对
"g": 2, // OrderListId
"c": "OCO", // Contingency Type
"l": "EXEC_STARTED", // List Status Type
"L": "EXECUTING", // List Order Status
"r": "NONE", // List 被拒绝的原因
"C": "F4QN4G8DlFATFlIUQ0cjdD", // List Client Order ID
"T": 1564035303625, // 成交时间
"O": [
{
"s": "ETHBTC", // 交易对
"i": 17, // orderId
"c": "AJYsMjErWJesZvqlJCTUgL" // clientOrderId
},
{
"s": "ETHBTC",
"i": 18,
"c": "bfYPSQdLoqAJeNrOr9adzq"
}
]
}
"balanceUpdate"从账户存款或取款的事件类型:
当下列情形发生时更新:
{
"e": "balanceUpdate", // 事件类型
"E": 1573200697110, // 事件时间
"a": "ABC", // 资产
"d": "100.00000000", // 余额变更
"T": 1573200697068 // 具体时间
}
每当帐户余额发生更改时,都会发送一个事件outboundAccountPosition,其中包含可能由生成余额变动的事件而变动的资产。
{
"e": "outboundAccountPosition", // 事件类型
"E": 1564034571105, // 事件时间
"u": 1564034571073, // 账户末次更新时间戳
"B": [ // 余额
{
"a": "ETH", // 资产名称
"f": "10000.000000", // 可用余额
"l": "0.000000" // 冻结余额
}
]
}
按照不同的事件类型,处理消息,代码如下
func TestWsUserDataServe(t *testing.T) {
wsHandler := func(event *binance.WsUserDataEvent) {
fmt.Println(event)
//账户更新事件
if event.Event == "outboundAccountPosition" {
AccountUpdate := event.AccountUpdate
fmt.Println(AccountUpdate)
}
//余额更新事件
if event.Event == "balanceUpdate" {
balanceUpdate := event.BalanceUpdate
fmt.Println(balanceUpdate)
//现货交易订单更新事件
if event.Event == "executionReport" {
orderUpdate := event.OrderUpdate
fmt.Println(orderUpdate)
}
//OCO交易订单更新事件
if event.Event == "outboundAccountPosition" {
OCOUpdate := event.OCOUpdate
fmt.Println(OCOUpdate)
}
}
}
errHandler := func(err error) {
fmt.Println(err)
}
doneC, _, err := binance.WsUserDataServe(listenKey, wsHandler, errHandler)
if err != nil {
fmt.Println(err)
return
}
<-doneC
}
用户数据流在创建后60 分钟内有效listenKey。为防止超时并使其保持活动状态,建议大约每 30 分钟发送一次 ping。
在 a PUT上执行 a listenKey将其有效期延长 60 分钟。
在 a DELETE上执行 a listenKey将关闭流并使listenKey.
stream.binance.com的单次连接 仅在24 小时内有效;确实希望在 24 小时标记断开连接。
具体代码如下
//定时30分钟给listenKey续期
time.Sleep(1800 * time.Second)
func TestPutListenKey(t *testing.T) {
client := binance.NewClient(apiKey, secretKey)
err := client.NewKeepaliveUserStreamService().ListenKey(listenKey).Do(context.Background())
if err != nil {
fmt.Println(err)
}
}
func TestCloseListenKey(t *testing.T) {
client := binance.NewClient(apiKey, secretKey)
err := client.NewCloseUserStreamService().ListenKey(listenKey).Do(context.Background())
if err != nil {
fmt.Println(err)
}
}
参考链接: