websocket 流式传输 交易订单更新


对于从事加密货币行业的任何人来说,使用 REST api 从交易所查询实时数据并不总是最佳做法,原因有很多:

  • 效率低下:每个查询都需要时间,并且会显着影响性能,尤其是对于高频策略。
  • 交易所施加的限制很容易被打破,例如Binance的硬限制为每分钟 1200 个请求权重。
  • 您只能检索有限数量的历史数据,这也限制了回测的可能性。

因此,本文将介绍一种使用Websocket的替代方法,与 REST 相比,它具有更高的效率,并让您有机会从交易所流式传输实时数据并存储它以供将来分析或回测。

那么下面,就让我们一起来使用websocket来推送我们的账户信息


1.获取Binance API和Secret

如果您熟悉这些步骤,则可以跳过此会话。只需确保您将其设置为预期目的:现货和保证金交易或期货交易或两者兼而有之。

  1. 登录您的币安账户。
  2. 转到您的个人资料,然后在 API 管理页面下,输入新的 API 密钥名称以“创建”。
  3. Binance 会从您的电子邮件中索取代码,并从您的身份验证应用程序中索取代码。输入代码并单击“提交”。
  4. 现在已创建新的 API 密钥。请注意,密钥仅在这一步对您可见,之后将被隐藏。因此,请记住妥善保存它以备将来参考。
  5. 编辑 API 密钥权限。
  6. 通过从您的身份验证应用程序输入代码来保存更改。

2. 流式订单更新

与用于市场数据流的端点和通道不同,要流式传输订单和帐户更新,您需要创建一个具有唯一监听密钥的用户数据流,每次启动新连接时都需要生成它。

2.1. 生成监听键

Spot 和 Futures 的基本 API 端点不同:

  • 币安现货和保证金:https 😕/api.binance.com
  • 币安合约: https 😕/fapi.binance.com

在这里,我们使用币安的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 请求。

  • 获取用户流监听key
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获取listenKey
# curl -H "X-MBX-APIKEY: MY-APIKEY" -X 'POST' https://api.binance.com/api/v3/userDataStream
{"listenKey":"listenKey"}

2.2. Websocket 端点

Spot 和 Futures 的基本 websocket 端点:

  • 币安现货和保证金:wss://stream.binance.com:9443
  • 币安期货:wss://fstream.binance.com或wss://fstream-auth.binance.com

2.3. 流媒体连接

使用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 密钥。

2.4. 消息处理

2.4.1 订单更新

收到的消息将采用以下格式。对于事件类型"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; 下单时填上字段才会返回
}

可能的执行类型:

  • NEW 新订单
  • CANCELED 订单被取消
  • REPLACED (保留字段,当前未使用)
  • REJECTED 新订单被拒绝 (这信息只会在撤消挂单再下单中发生,下新订单被拒绝但撤消挂单请求成功。)
  • TRADE 订单有新成交
  • EXPIRED 订单失效(根据订单的Time In Force参数)

如果订单是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"
    }
  ]
}

2.4.2 余额更新

"balanceUpdate"从账户存款或取款的事件类型:

当下列情形发生时更新:

  • 账户发生充值或提取
  • 交易账户之间发生划转(例如 现货向杠杆账户划转)
{
  "e": "balanceUpdate",         // 事件类型
  "E": 1573200697110,           // 事件时间
  "a": "ABC",                   // 资产
  "d": "100.00000000",          // 余额变更
  "T": 1573200697068            // 具体时间
}

2.4.3 账户更新

每当帐户余额发生更改时,都会发送一个事件outboundAccountPosition,其中包含可能由生成余额变动的事件而变动的资产。

{
  "e": "outboundAccountPosition", // 事件类型
  "E": 1564034571105,             // 事件时间
  "u": 1564034571073,             // 账户末次更新时间戳
  "B": [                          // 余额
    {
      "a": "ETH",                 // 资产名称
      "f": "10000.000000",        // 可用余额
      "l": "0.000000"             // 冻结余额
    }
  ]
}

2.5 事件处理

按照不同的事件类型,处理消息,代码如下


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
}

2.6 补充说明

  • 用户数据流在创建后60 分钟内有效listenKey。为防止超时并使其保持活动状态,建议大约每 30 分钟发送一次 ping。

  • 在 a PUT上执行 a listenKey将其有效期延长 60 分钟。

  • 在 a DELETE上执行 a listenKey将关闭流并使listenKey.
    stream.binance.com的单次连接 仅在24 小时内有效;确实希望在 24 小时标记断开连接。

具体代码如下

  • 延长60分钟有效期
//定时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)
	}
}

参考链接:

  • Binance Websocket账户信息推送
    官方文档:https://binance-docs.github.io/apidocs/spot/cn/#websocket-2
  • Binance golang sdk:https://github.com/adshao/go-binance
  • Binance websocket账户接口:https://github.com/binance/binance-spot-api-docs/blob/master/user-data-stream_CN.md
  • 使用 Python 流式传输 Binance 订单更新:https://medium.com/coinmonks/streaming-binance-order-updates-using-python-90520e0d307f#ef2e
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰哥的技术杂货铺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值