#![allow(clippy::unwrap_used, clippy::expect_used)]
use futures::StreamExt;
use rustrade_data::{
event::DataKind,
exchange::lse::{LseCrypto, LseFx, live::LseSubscriber, resume::LseResumeState},
streams::{
Streams,
consumer::MarketStreamResult,
reconnect::{Event, stream::ReconnectingStream},
},
subscription::{book::OrderBooksL1, trade::PublicTrades},
};
use rustrade_instrument::instrument::market_data::{
MarketDataInstrument, kind::MarketDataInstrumentKind,
};
use std::sync::Arc;
use tracing::{info, warn};
#[tokio::main]
async fn main() {
init_logging();
let subscriber = LseSubscriber::from_env()
.expect("set LSE_API_KEY - get a free key at https://londonstrategicedge.com/data");
let resume = Arc::new(LseResumeState::new());
let streams: Streams<MarketStreamResult<MarketDataInstrument, DataKind>> =
Streams::builder_multi()
.add(
Streams::<PublicTrades>::builder()
.subscribe(
subscriber.clone().with_resume(Arc::clone(&resume)),
[
(
LseCrypto::default(),
"btc",
"usd",
MarketDataInstrumentKind::Spot,
PublicTrades,
),
(
LseCrypto::default(),
"eth",
"usd",
MarketDataInstrumentKind::Spot,
PublicTrades,
),
],
)
.subscribe(
subscriber.clone().with_resume(Arc::clone(&resume)),
[(
LseFx::default(),
"eur",
"usd",
MarketDataInstrumentKind::Spot,
PublicTrades,
)],
),
)
.add(Streams::<OrderBooksL1>::builder().subscribe(
subscriber.with_resume(resume),
[
(
LseCrypto::default(),
"btc",
"usd",
MarketDataInstrumentKind::Spot,
OrderBooksL1,
),
(
LseCrypto::default(),
"eth",
"usd",
MarketDataInstrumentKind::Spot,
OrderBooksL1,
),
],
))
.init()
.await
.unwrap();
let mut joined_stream = streams
.select_all()
.with_error_handler(|error| warn!(?error, "MarketStream generated error"));
while let Some(event) = joined_stream.next().await {
let event = match event {
Event::Reconnecting(exchange) => {
warn!(%exchange, "MarketStream reconnecting");
continue;
}
Event::Item(event) => event,
};
match &event.kind {
DataKind::Trade(trade) => info!(
exchange = %event.exchange,
instrument = %event.instrument,
time_exchange = %event.time_exchange,
price = %trade.price,
amount = %trade.amount,
side = ?trade.side,
"tick as trade"
),
DataKind::OrderBookL1(l1) => info!(
exchange = %event.exchange,
instrument = %event.instrument,
time_exchange = %event.time_exchange,
best_bid = ?l1.best_bid.as_ref().map(|level| level.price),
best_ask = ?l1.best_ask.as_ref().map(|level| level.price),
"tick as top-of-book"
),
other => warn!(kind = other.kind_name(), "unexpected DataKind"),
}
}
}
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()
}