MENU

Binance WebSoket API [Public/Rust]サンプル

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),
                    }
                }

            }
        }
    }
}
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次