#![allow(clippy::unwrap_used, clippy::expect_used)]
use futures_util::StreamExt;
use rustrade_data::{
exchange::hyperliquid::{Hyperliquid, HyperliquidMeta, Network},
instrument::MarketInstrumentData,
streams::{Streams, reconnect::stream::ReconnectingStream},
subscriber::WebSocketSubscriber,
subscription::{Subscription, book::OrderBooksL2, trade::PublicTrades},
};
use rustrade_instrument::instrument::market_data::kind::MarketDataInstrumentKind;
use tracing::{info, warn};
const MAX_EVENTS: usize = 10;
#[tokio::main]
async fn main() {
init_logging();
let trades = Streams::<PublicTrades>::builder()
.subscribe(
WebSocketSubscriber,
[(
Hyperliquid,
"btc",
"usdc",
MarketDataInstrumentKind::Perpetual,
PublicTrades,
)],
)
.subscribe(
WebSocketSubscriber,
[(
Hyperliquid,
"eth",
"usdc",
MarketDataInstrumentKind::Perpetual,
PublicTrades,
)],
)
.init()
.await
.unwrap();
let meta = HyperliquidMeta::fetch(Network::Mainnet, &["xyz"])
.await
.expect("Failed to read Hyperliquid market metadata");
let book_instruments = [("kpepe", "KPEPE"), ("xyz_tsla", "xyz:TSLA")].map(|(key, name)| {
let perp = meta
.perp_coin(name)
.unwrap_or_else(|| panic!("Hyperliquid lists no perpetual {name}"));
info!("{name} is subscribed as {}", perp.coin());
MarketInstrumentData::hyperliquid_perp(key, perp)
});
let books = Streams::<OrderBooksL2>::builder()
.subscribe(
WebSocketSubscriber,
book_instruments.map(|instrument| Subscription {
exchange: Hyperliquid,
instrument,
kind: OrderBooksL2,
}),
)
.init()
.await
.unwrap();
let mut trades_stream = trades
.select_all()
.with_error_handler(|error| warn!(?error, "Trade stream error"));
let mut books_stream = books
.select_all()
.with_error_handler(|error| warn!(?error, "Book stream error"));
info!("Subscribed to Hyperliquid BTC and ETH trades, kPEPE and xyz:TSLA L2 books");
info!("Receiving {} events then exiting...", MAX_EVENTS);
let mut event_count = 0;
while event_count < MAX_EVENTS {
tokio::select! {
Some(trade) = trades_stream.next() => {
info!("Trade: {:?}", trade);
event_count += 1;
}
Some(book) = books_stream.next() => {
info!("Book: {:?}", book);
event_count += 1;
}
else => break,
}
}
info!("Received {} events, exiting", event_count);
}
fn init_logging() {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::filter::EnvFilter::builder()
.with_default_directive(tracing_subscriber::filter::LevelFilter::INFO.into())
.from_env_lossy(),
)
.with_ansi(cfg!(debug_assertions))
.json()
.init()
}