binance_stream_handler/
lib.rs

1//! # binance-stream-handler
2//!
3//! Produce live Binance order books as Tokio `watch::Receiver<OrderBook>` streams.
4//!
5//! ## Quick start
6//!
7//! ```no_run
8//! use binance_stream_handler::generate_orderbooks;
9//! use chrono::NaiveTime;
10//!
11//! // Currency pairs must be defined as a 'static slice.
12//! pub static CURRENCY_PAIRS: &[&str] = &["ADAUSDT", "DOGEUSDT"];
13//!
14//! #[tokio::main(flavor = "multi_thread")]
15//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
16//!     let cutoffs = (NaiveTime::from_hms_opt(2,0,0).unwrap(),
17//!                    NaiveTime::from_hms_opt(18,42,0).unwrap());
18//!
19//!     let streams = generate_orderbooks(CURRENCY_PAIRS, 1024, 512, cutoffs);
20//!
21//!     let mut ada = streams["ADAUSDT"].clone();
22//!     tokio::spawn(async move {
23//!         while ada.changed().await.is_ok() {
24//!             let ob = ada.borrow().clone();
25//!             println!("{} best bid={:?} ask={:?}",
26//!                      ob.symbol, ob.bids.keys().last(), ob.asks.keys().next());
27//!         }
28//!     });
29//!
30//!     futures_util::future::pending::<()>().await;
31//!     Ok(())
32//! }
33//! ```
34
35//! ## The `OrderBook` type
36//!
37//! Each stream yields an [`OrderBook`], which contains the current snapshot
38//! of bids and asks for a symbol.
39//!
40//! ```text
41//! struct OrderBook {
42//!     symbol: String,                     // e.g. "ADAUSDT"
43//!     bids: BTreeMap<Price, Qty>,         // sorted descending
44//!     asks: BTreeMap<Price, Qty>,         // sorted ascending
45//!     last_u: Option<u64>,                // last update ID applied
46//!     snapshot_id: Option<u64>,           // REST snapshot ID
47//!     depth: u16                          // snapshot depth (default 1000)
48//! }
49//! ```
50//!
51//! - **`bids`**: map from price → quantity, sorted by price descending  
52//! - **`asks`**: map from price → quantity, sorted by price ascending  
53//! - **`last_u`**: last WebSocket update sequence number applied  
54//! - **`snapshot_id`**: ID of the REST snapshot used to initialize the book  
55//! - **`depth`**: the configured maximum depth (default: 1000)  
56//!
57//! You normally just clone the latest `OrderBook` from a `watch::Receiver` and
58//! inspect the maps to get the best bid/ask or traverse the book.
59
60use chrono::NaiveTime;
61
62
63use std::collections::HashMap;
64use tokio::sync::{watch};
65
66mod ob_manager;
67mod router;
68
69pub use crate::ob_manager::init_order_books;
70pub use crate::ob_manager::order_book::OrderBook;
71use crate::router::DualRouter;
72
73pub async fn generate_orderbooks(
74    currency_pairs: &'static [&'static str],
75    chan_cap: usize,
76    park_cap: usize,
77    switch_cutoffs: (NaiveTime, NaiveTime),
78) -> HashMap<String, watch::Receiver<OrderBook>> {
79    let dual_router = DualRouter::new(switch_cutoffs, currency_pairs);
80    let (receivers, connected) = dual_router.start_dual_router(chan_cap, park_cap);
81
82    connected.notified().await;
83    let ob_streams = init_order_books(currency_pairs, receivers);
84
85    ob_streams
86}