extern crate kucoin_api;
use kucoin_api::futures::TryStreamExt;
use kucoin_api::{
client::{Kucoin, KucoinEnv},
model::websocket::{KucoinWebsocketMsg, WSTopic, WSType},
websocket::KucoinWebsocket,
};
use kucoin_arbitrage::broker::symbol::filter::symbol_with_quotes;
use kucoin_arbitrage::broker::symbol::kucoin::get_symbols;
use kucoin_arbitrage::model::symbol::SymbolInfo;
#[tokio::main]
async fn main() -> Result<(), kucoin_api::failure::Error> {
kucoin_arbitrage::logger::log_init();
log::info!("Log setup");
let credentials = kucoin_arbitrage::global::config::credentials();
let api = Kucoin::new(KucoinEnv::Live, Some(credentials))?;
let url = api.clone().get_socket_endpoint(WSType::Public).await?;
log::info!("Credentials setup");
let symbol_list = get_symbols(api.clone()).await;
log::info!("Total exchange symbols: {:?}", symbol_list.len());
let symbol_infos = symbol_with_quotes(&symbol_list, "BTC", "USDT");
log::info!("Total symbols in scope: {:?}", symbol_infos.len());
let subs = format_subscription_list(&symbol_infos);
log::info!("Total orderbook WS sessions: {:?}", subs.len());
for (i, sub) in subs.iter().enumerate() {
let mut ws = api.websocket();
ws.subscribe(url.clone(), sub.clone()).await?;
tokio::spawn(async move { sync_tickers(ws).await });
log::info!("{i:?}-th session of WS subscription setup");
}
kucoin_arbitrage::global::task::background_routine().await
}
async fn sync_tickers(mut ws: KucoinWebsocket) -> Result<(), kucoin_api::failure::Error> {
while let Some(msg) = ws.try_next().await? {
match msg {
KucoinWebsocketMsg::PongMsg(_) => {
log::info!("Connection maintained")
}
KucoinWebsocketMsg::WelcomeMsg(_) => {
log::info!("Connection setup")
}
KucoinWebsocketMsg::OrderBookMsg(msg) => {
let _ = msg.data;
kucoin_arbitrage::global::performance::increment().await;
}
_ => {
panic!("unexpected msgs received: {msg:?}")
}
}
}
Ok(())
}
fn format_subscription_list(infos: &[SymbolInfo]) -> Vec<Vec<WSTopic>> {
let symbols: Vec<String> = infos.iter().map(|info| info.symbol.clone()).collect();
let max_sub_count = 100;
let mut hundred_arrays: Vec<Vec<String>> = Vec::new();
let mut hundred_array: Vec<String> = Vec::new();
for symbol in symbols {
hundred_array.push(symbol);
if hundred_arrays.is_empty() && hundred_array.len() == max_sub_count - 1 {
hundred_arrays.push(hundred_array);
hundred_array = Vec::new();
continue;
}
if hundred_array.len() == max_sub_count {
hundred_arrays.push(hundred_array);
hundred_array = Vec::new();
}
}
if !hundred_array.is_empty() {
hundred_arrays.push(hundred_array);
}
let mut subs: Vec<Vec<WSTopic>> = Vec::new();
let mut sub: Vec<WSTopic> = Vec::new();
for sub_array in hundred_arrays {
sub.push(WSTopic::OrderBook(sub_array));
if sub.len() == 3 {
subs.push(sub);
sub = Vec::new();
}
}
subs
}