BinanceのPublic WebSocketはまりどころ
Binance、トラフィックが多いからもあってPublicとPrivateでサーバ&メッセージが異なる。ちょっとハマったのでまとめました。
一番重要なのは、PublicとPrivateは別で、見るべきドキュメントの場所も異なるということ。
目次
エンドポイントがPublicとPrivateで別
エンドポイント | |
Public(MarketStream) | wss://stream.binance.com:9443 または wss://stream.binance.com:443(同じもの) |
Private(Websocket API) | wss://ws-api.binance.com:443/ws-api/v3 |
Request JSONフォーマットも別
SubScribeメッセージ例
{
"method": "SUBSCRIBE",
"params":
[
"btcusdt@aggTrade",
"btcusdt@depth"
],
"id": 1
}
Publicは上記のようにシンプル。一方Privateの方は、認証キーを入れるフォーマットになっている。当初Privateのドキュメントを見ながらメッセージを作っていたらエラーになってハマった。
参考情報
Binance WebSocketドキュメント
Websocket Market Stream
サンプルソースコード(Rust)
- テストモジュールとしてサンプル実装。
- Tradeメッセージを受信してそのままprint
- Pingは実装したが再接続はしない。
- 接続が切れるまで止まらないのでそのままプログラムにコピーするとテストSuiteが終わらなくて困る。
[cfg(test)]
mod test_websocket {
use core::panic;
use std::time::Duration;
use url::Url;
use tungstenite;
use tungstenite::protocol::Message;
use tokio_tungstenite::connect_async;
use futures_util::sink::SinkExt;
use futures_util::stream::StreamExt;
const SUBSCRIBE_MESSAGE: &str = r#"{"method": "SUBSCRIBE", "params": ["btcusdt@trade"], "id": 1}"#;
const WSS_URL: &str = "wss://stream.binance.com:443/ws/btcusdt@trade";
#[tokio::test]
async fn test_connect() {
let wss_url = WSS_URL;
let url = Url::parse(wss_url).unwrap();
let result = connect_async(url).await;
let (ws_stream, _ws_response) = match result {
Ok(connection) => connection,
Err(e) => {
eprintln!("Connect Error {:?}", e);
panic!();
}
};
let mut heartbeat_interval = tokio::time::interval(Duration::from_secs(60));
let (mut ws_sender, mut ws_receiver) = ws_stream.split();
match ws_sender.send(SUBSCRIBE_MESSAGE.into()).await {
Ok(_) => println!("subscribed message {}", SUBSCRIBE_MESSAGE),
Err(e) => println!("Error {:?}", e),
};
'receive_loop: loop {
tokio::select! {
msg_item = ws_receiver.next() => {
match msg_item {
Some(msg) => {
match msg {
Ok(Message::Text(json_string)) => {
println!("{}", json_string);
},
Ok(Message::Ping(message)) => println!("# Ping msg={:#?}", message),
Ok(Message::Pong(message)) => println!("# Pong msg={:#?}", message),
Ok(Message::Binary(binary)) => println!("> BINMESSAGE / {:?}", binary),
Ok(Message::Frame(frame)) => println!("> FRAME / {:?}", frame),
Ok(Message::Close(close_option)) => {
println!("CLOSED by PEER {:?}", close_option);
break 'receive_loop;
},
Err(e) => {
eprintln!("{:?}", e);
},
}
},
None => (),
}
}
// heart beat (send ping message)
_ = heartbeat_interval.tick() => {
match ws_sender.send(Message::Ping(vec![])).await {
Ok(_) => println!("send ping message"),
Err(e) => eprintln!("error sending ping message; err={}", e),
}
}
}
}
}
}
コメント