1mod error;
2mod exchanges;
3
4pub use error::Error;
5pub use exchanges::{
6 binance::{
7 binance_inverse::BinanceInverseRestClient, binance_linear::BinanceLinearRestClient,
8 binance_option::BinanceOptionRestClient, binance_spot::BinanceSpotRestClient,
9 },
10 bitfinex::BitfinexRestClient,
11 bitget::*,
12 bithumb::*,
13 bitmex::BitmexRestClient,
14 bitstamp::BitstampRestClient,
15 bitz::*,
16 bybit::BybitRestClient,
17 coinbase_pro::CoinbaseProRestClient,
18 deribit::DeribitRestClient,
19 dydx::dydx_swap::DydxSwapRestClient,
20 ftx::FtxRestClient,
21 gate::*,
22 huobi::{
23 huobi_future::HuobiFutureRestClient, huobi_inverse_swap::HuobiInverseSwapRestClient,
24 huobi_linear_swap::HuobiLinearSwapRestClient, huobi_option::HuobiOptionRestClient,
25 huobi_spot::HuobiSpotRestClient,
26 },
27 kraken::{kraken_futures::KrakenFuturesRestClient, kraken_spot::KrakenSpotRestClient},
28 kucoin::*,
29 mexc::{mexc_spot::MexcSpotRestClient, mexc_swap::MexcSwapRestClient},
30 okx::OkxRestClient,
31 zb::*,
32 zbg::*,
33};
34
35use crypto_market_type::MarketType;
36use error::Result;
37use log::*;
38use std::time::{Duration, SystemTime};
39
40fn fetch_l2_snapshot_internal(
41 exchange: &str,
42 market_type: MarketType,
43 symbol: &str,
44) -> Result<String> {
45 let ret = match exchange {
46 "binance" => exchanges::binance::fetch_l2_snapshot(market_type, symbol),
47 "bitfinex" => exchanges::bitfinex::BitfinexRestClient::fetch_l2_snapshot(symbol),
48 "bitget" => exchanges::bitget::fetch_l2_snapshot(market_type, symbol),
49 "bithumb" => exchanges::bithumb::BithumbRestClient::fetch_l2_snapshot(symbol),
50 "bitmex" => exchanges::bitmex::BitmexRestClient::fetch_l2_snapshot(symbol),
51 "bitstamp" => exchanges::bitstamp::BitstampRestClient::fetch_l2_snapshot(symbol),
52 "bitz" => exchanges::bitz::fetch_l2_snapshot(market_type, symbol),
53 "bybit" => exchanges::bybit::BybitRestClient::fetch_l2_snapshot(symbol),
54 "coinbase_pro" => exchanges::coinbase_pro::CoinbaseProRestClient::fetch_l2_snapshot(symbol),
55 "deribit" => exchanges::deribit::DeribitRestClient::fetch_l2_snapshot(symbol),
56 "dydx" => exchanges::dydx::fetch_l2_snapshot(market_type, symbol),
57 "ftx" => exchanges::ftx::FtxRestClient::fetch_l2_snapshot(symbol),
58 "gate" => exchanges::gate::fetch_l2_snapshot(market_type, symbol),
59 "huobi" => exchanges::huobi::fetch_l2_snapshot(market_type, symbol),
60 "kraken" => exchanges::kraken::fetch_l2_snapshot(market_type, symbol),
61 "kucoin" => exchanges::kucoin::fetch_l2_snapshot(market_type, symbol),
62 "mexc" => exchanges::mexc::fetch_l2_snapshot(market_type, symbol),
63 "okx" => exchanges::okx::OkxRestClient::fetch_l2_snapshot(symbol),
64 "zb" => exchanges::zb::fetch_l2_snapshot(market_type, symbol),
65 "zbg" => exchanges::zbg::fetch_l2_snapshot(market_type, symbol),
66 _ => panic!("Unknown exchange {exchange}"),
67 };
68 match ret {
69 Ok(s) => Ok(s.trim().to_string()),
70 Err(_) => ret,
71 }
72}
73
74pub fn fetch_l3_snapshot_internal(
75 exchange: &str,
76 market_type: MarketType,
77 symbol: &str,
78) -> Result<String> {
79 let ret = match exchange {
80 "bitfinex" => exchanges::bitfinex::BitfinexRestClient::fetch_l3_snapshot(symbol),
81 "bitstamp" => exchanges::bitstamp::BitstampRestClient::fetch_l3_snapshot(symbol),
82 "coinbase_pro" => exchanges::coinbase_pro::CoinbaseProRestClient::fetch_l3_snapshot(symbol),
83 "kucoin" => exchanges::kucoin::fetch_l3_snapshot(market_type, symbol),
84 _ => panic!("{exchange} {market_type} does NOT provide level3 orderbook data"),
85 };
86 match ret {
87 Ok(s) => Ok(s.trim().to_string()),
88 Err(_) => ret,
89 }
90}
91
92pub fn fetch_open_interest(
96 exchange: &str,
97 market_type: MarketType,
98 symbol: Option<&str>,
99) -> Result<String> {
100 let ret = match exchange {
101 "binance" => exchanges::binance::fetch_open_interest(market_type, symbol.unwrap()),
102 "bitget" => exchanges::bitget::fetch_open_interest(market_type, symbol.unwrap()),
103 "bybit" => exchanges::bybit::BybitRestClient::fetch_open_interest(symbol.unwrap()),
104 "bitz" => exchanges::bitz::fetch_open_interest(market_type, symbol),
105 "deribit" => exchanges::deribit::DeribitRestClient::fetch_open_interest(symbol),
106 "dydx" => exchanges::dydx::fetch_open_interest(market_type),
107 "ftx" => exchanges::ftx::FtxRestClient::fetch_open_interest(),
108 "gate" => exchanges::gate::fetch_open_interest(market_type, symbol.unwrap()),
109 "huobi" => exchanges::huobi::fetch_open_interest(market_type, symbol),
110 "kucoin" => exchanges::kucoin::fetch_open_interest(market_type),
111 "okx" => exchanges::okx::OkxRestClient::fetch_open_interest(market_type, symbol),
112 "zbg" => exchanges::zbg::fetch_open_interest(market_type, symbol.unwrap()),
113 _ => panic!("{exchange} does NOT have open interest RESTful API"),
114 };
115 match ret {
116 Ok(s) => Ok(s.trim().to_string()),
117 Err(_) => ret,
118 }
119}
120
121pub fn fetch_long_short_ratio(
122 exchange: &str,
123 market_type: MarketType,
124 symbol: &str,
125) -> Result<String> {
126 let ret = match exchange {
127 "bybit" => exchanges::bybit::BybitRestClient::fetch_long_short_ratio(symbol),
128 _ => panic!("{exchange} {market_type} does NOT provide level3 orderbook data"),
129 };
130 match ret {
131 Ok(s) => Ok(s.trim().to_string()),
132 Err(_) => ret,
133 }
134}
135
136pub fn fetch_l2_snapshot(
141 exchange: &str,
142 market_type: MarketType,
143 symbol: &str,
144 retry: Option<u64>,
145) -> Result<String> {
146 retriable(exchange, market_type, symbol, fetch_l2_snapshot_internal, retry)
147}
148
149pub fn fetch_l3_snapshot(
154 exchange: &str,
155 market_type: MarketType,
156 symbol: &str,
157 retry: Option<u64>,
158) -> Result<String> {
159 retriable(exchange, market_type, symbol, fetch_l3_snapshot_internal, retry)
160}
161
162fn retriable(
165 exchange: &str,
166 market_type: MarketType,
167 symbol: &str,
168 crawl_func: fn(&str, MarketType, &str) -> Result<String>,
169 retry: Option<u64>,
170) -> Result<String> {
171 let retry_count = {
172 let count = retry.unwrap_or(1);
173 if count == 0 { u64::MAX } else { count }
174 };
175 if retry_count == 1 {
176 return crawl_func(exchange, market_type, symbol);
177 }
178 let mut backoff_factor = 0;
179 let cooldown_time = Duration::from_secs(2);
180 for _ in 0..retry_count {
181 let resp = crawl_func(exchange, market_type, symbol);
182 match resp {
183 Ok(msg) => return Ok(msg),
184 Err(err) => {
185 let current_timestamp =
186 SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis()
187 as u64;
188 warn!(
189 "{} {} {} {} {}, error: {}, back off for {} milliseconds",
190 current_timestamp,
191 backoff_factor,
192 exchange,
193 market_type,
194 symbol,
195 err,
196 (backoff_factor * cooldown_time).as_millis()
197 );
198 std::thread::sleep(backoff_factor * cooldown_time);
199 if err.0.contains("429") {
200 backoff_factor += 1;
201 } else {
202 backoff_factor *= 2;
204 }
205 }
206 }
207 }
208 Err(Error(format!(
209 "Failed {exchange} {market_type} {symbol} after retrying {retry_count} times"
210 )))
211}