1use nautilus_model::identifiers::Venue;
17use serde::{Deserialize, Serialize};
18use strum::{AsRefStr, Display, EnumIter, EnumString, FromRepr};
19use ustr::Ustr;
20
21#[derive(
22 Copy,
23 Clone,
24 Debug,
25 PartialEq,
26 Eq,
27 Hash,
28 Serialize,
29 Deserialize,
30 Display,
31 AsRefStr,
32 EnumIter,
33 EnumString,
34 FromRepr,
35)]
36#[strum(ascii_case_insensitive)]
37#[strum(serialize_all = "lowercase")]
38#[serde(rename_all = "lowercase")]
39pub enum TardisInstrumentType {
41 Spot,
42 Perpetual,
43 Future,
44 Option,
45 Combo,
46}
47
48#[derive(
49 Copy,
50 Clone,
51 Debug,
52 PartialEq,
53 Eq,
54 Hash,
55 Serialize,
56 Deserialize,
57 Display,
58 AsRefStr,
59 EnumIter,
60 EnumString,
61 FromRepr,
62)]
63#[serde(rename_all = "lowercase")]
64pub enum TardisOptionType {
66 Call,
67 Put,
68}
69
70#[derive(
72 Copy,
73 Clone,
74 Debug,
75 PartialEq,
76 Eq,
77 Hash,
78 Serialize,
79 Deserialize,
80 Display,
81 AsRefStr,
82 EnumIter,
83 EnumString,
84 FromRepr,
85)]
86#[serde(rename_all = "lowercase")]
87pub enum TardisTradeSide {
88 Buy,
89 Sell,
90 Unknown,
91}
92
93#[allow(missing_docs)]
95#[derive(
96 Copy,
97 Clone,
98 Debug,
99 PartialEq,
100 Eq,
101 Hash,
102 Serialize,
103 Deserialize,
104 Display,
105 AsRefStr,
106 EnumIter,
107 EnumString,
108 FromRepr,
109)]
110#[serde(rename_all = "lowercase")]
111pub enum TardisBarKind {
112 Time,
113 Volume,
114 Tick,
115}
116
117#[derive(
120 Copy,
121 Clone,
122 Debug,
123 PartialEq,
124 Eq,
125 Hash,
126 Serialize,
127 Deserialize,
128 Display,
129 AsRefStr,
130 EnumIter,
131 EnumString,
132 FromRepr,
133)]
134#[strum(serialize_all = "snake_case")]
135#[serde(rename_all = "snake_case")]
136pub enum TardisDataType {
137 Trade,
138 BookChange,
139 BookSnapshot,
140 DerivativeTicker,
141 Quote,
142 Disconnect,
143}
144
145impl TardisDataType {
146 #[must_use]
148 pub fn as_tardis_str(&self) -> &str {
149 match self {
150 Self::Trade => "trade",
151 Self::BookChange => "book_change",
152 Self::BookSnapshot => "book_snapshot",
153 Self::DerivativeTicker => "derivative_ticker",
154 Self::Quote => "quote",
155 Self::Disconnect => "disconnect",
156 }
157 }
158}
159
160#[derive(
161 Copy,
162 Clone,
163 Debug,
164 PartialEq,
165 Eq,
166 Hash,
167 Serialize,
168 Deserialize,
169 Display,
170 AsRefStr,
171 EnumIter,
172 EnumString,
173 FromRepr,
174)]
175#[strum(ascii_case_insensitive)]
176#[strum(serialize_all = "kebab-case")]
177#[serde(rename_all = "kebab-case")]
178pub enum TardisExchange {
181 Ascendex,
182 Binance,
183 BinanceDelivery,
184 BinanceDex,
185 BinanceEuropeanOptions,
186 BinanceFutures,
187 BinanceJersey,
188 BinanceOptions,
189 BinanceUs,
190 Bitfinex,
191 BitfinexDerivatives,
192 Bitflyer,
193 Bitget,
194 BitgetFutures,
195 Bitmex,
196 Bitnomial,
197 Bitstamp,
198 BlockchainCom,
199 Bybit,
200 BybitOptions,
201 BybitSpot,
202 Coinbase,
203 CoinbaseInternational,
204 Coinflex,
205 CryptoCom,
206 CryptoComDerivatives,
207 Cryptofacilities,
208 Delta,
209 Deribit,
210 Dydx,
211 DydxV4,
212 Ftx,
213 FtxUs,
214 GateIo,
215 GateIoFutures,
216 Gemini,
217 Hitbtc,
218 Huobi,
219 HuobiDm,
220 HuobiDmLinearSwap,
221 HuobiDmOptions,
222 HuobiDmSwap,
223 Hyperliquid,
224 Kraken,
225 Kucoin,
226 KucoinFutures,
227 Mango,
228 Okcoin,
229 Okex,
230 OkexFutures,
231 OkexOptions,
232 OkexSpreads,
233 OkexSwap,
234 Phemex,
235 Poloniex,
236 Serum,
237 StarAtlas,
238 Upbit,
239 WooX,
240}
241
242impl TardisExchange {
243 pub const OPTION_EXCHANGES: &'static [Self] = &[
245 Self::BinanceOptions,
246 Self::BinanceEuropeanOptions,
247 Self::BybitOptions,
248 Self::OkexOptions,
249 Self::HuobiDmOptions,
250 ];
251
252 #[must_use]
253 pub fn is_option_exchange(&self) -> bool {
254 Self::OPTION_EXCHANGES.contains(self)
255 }
256
257 #[must_use]
258 pub fn from_venue_str(s: &str) -> Vec<Self> {
259 let s = s.to_ascii_uppercase();
260 match s.as_str() {
261 "ASCENDEX" => vec![Self::Ascendex],
262 "BINANCE" => vec![
263 Self::Binance,
264 Self::BinanceDex,
265 Self::BinanceEuropeanOptions,
266 Self::BinanceFutures,
267 Self::BinanceJersey,
268 Self::BinanceOptions,
269 ],
270 "BINANCE_DELIVERY" => vec![Self::BinanceDelivery],
271 "BINANCE_US" => vec![Self::BinanceUs],
272 "BITFINEX" => vec![Self::Bitfinex, Self::BitfinexDerivatives],
273 "BITFLYER" => vec![Self::Bitflyer],
274 "BITGET" => vec![Self::Bitget, Self::BitgetFutures],
275 "BITMEX" => vec![Self::Bitmex],
276 "BITNOMIAL" => vec![Self::Bitnomial],
277 "BITSTAMP" => vec![Self::Bitstamp],
278 "BLOCKCHAIN_COM" => vec![Self::BlockchainCom],
279 "BYBIT" => vec![Self::Bybit, Self::BybitOptions, Self::BybitSpot],
280 "COINBASE" => vec![Self::Coinbase],
281 "COINBASE_INTX" => vec![Self::CoinbaseInternational],
282 "COINFLEX" => vec![Self::Coinflex],
283 "CRYPTO_COM" => vec![Self::CryptoCom, Self::CryptoComDerivatives],
284 "CRYPTOFACILITIES" => vec![Self::Cryptofacilities],
285 "DELTA" => vec![Self::Delta],
286 "DERIBIT" => vec![Self::Deribit],
287 "DYDX" => vec![Self::Dydx],
288 "DYDX_V4" => vec![Self::DydxV4],
289 "FTX" => vec![Self::Ftx, Self::FtxUs],
290 "GATE_IO" => vec![Self::GateIo, Self::GateIoFutures],
291 "GEMINI" => vec![Self::Gemini],
292 "HITBTC" => vec![Self::Hitbtc],
293 "HUOBI" => vec![
294 Self::Huobi,
295 Self::HuobiDm,
296 Self::HuobiDmLinearSwap,
297 Self::HuobiDmOptions,
298 ],
299 "HUOBI_DELIVERY" => vec![Self::HuobiDmSwap],
300 "HYPERLIQUID" => vec![Self::Hyperliquid],
301 "KRAKEN" => vec![Self::Kraken],
302 "KUCOIN" => vec![Self::Kucoin, Self::KucoinFutures],
303 "MANGO" => vec![Self::Mango],
304 "OKCOIN" => vec![Self::Okcoin],
305 "OKEX" => vec![
306 Self::Okex,
307 Self::OkexFutures,
308 Self::OkexOptions,
309 Self::OkexSpreads,
310 Self::OkexSwap,
311 ],
312 "PHEMEX" => vec![Self::Phemex],
313 "POLONIEX" => vec![Self::Poloniex],
314 "SERUM" => vec![Self::Serum],
315 "STAR_ATLAS" => vec![Self::StarAtlas],
316 "UPBIT" => vec![Self::Upbit],
317 "WOO_X" => vec![Self::WooX],
318 _ => Vec::new(),
319 }
320 }
321
322 #[must_use]
323 pub const fn as_venue_str(&self) -> &str {
324 match self {
325 Self::Ascendex => "ASCENDEX",
326 Self::Binance => "BINANCE",
327 Self::BinanceDelivery => "BINANCE_DELIVERY",
328 Self::BinanceDex => "BINANCE",
329 Self::BinanceEuropeanOptions => "BINANCE",
330 Self::BinanceFutures => "BINANCE",
331 Self::BinanceJersey => "BINANCE",
332 Self::BinanceOptions => "BINANCE",
333 Self::BinanceUs => "BINANCE_US",
334 Self::Bitfinex => "BITFINEX",
335 Self::BitfinexDerivatives => "BITFINEX",
336 Self::Bitflyer => "BITFLYER",
337 Self::Bitget => "BITGET",
338 Self::BitgetFutures => "BITGET",
339 Self::Bitmex => "BITMEX",
340 Self::Bitnomial => "BITNOMIAL",
341 Self::Bitstamp => "BITSTAMP",
342 Self::BlockchainCom => "BLOCKCHAIN_COM",
343 Self::Bybit => "BYBIT",
344 Self::BybitOptions => "BYBIT",
345 Self::BybitSpot => "BYBIT",
346 Self::Coinbase => "COINBASE",
347 Self::CoinbaseInternational => "COINBASE_INTX",
348 Self::Coinflex => "COINFLEX",
349 Self::CryptoCom => "CRYPTO_COM",
350 Self::CryptoComDerivatives => "CRYPTO_COM",
351 Self::Cryptofacilities => "CRYPTOFACILITIES",
352 Self::Delta => "DELTA",
353 Self::Deribit => "DERIBIT",
354 Self::Dydx => "DYDX",
355 Self::DydxV4 => "DYDX_V4",
356 Self::Ftx => "FTX",
357 Self::FtxUs => "FTX",
358 Self::GateIo => "GATE_IO",
359 Self::GateIoFutures => "GATE_IO",
360 Self::Gemini => "GEMINI",
361 Self::Hitbtc => "HITBTC",
362 Self::Huobi => "HUOBI",
363 Self::HuobiDm => "HUOBI",
364 Self::HuobiDmLinearSwap => "HUOBI",
365 Self::HuobiDmOptions => "HUOBI",
366 Self::HuobiDmSwap => "HUOBI_DELIVERY",
367 Self::Hyperliquid => "HYPERLIQUID",
368 Self::Kraken => "KRAKEN",
369 Self::Kucoin => "KUCOIN",
370 Self::KucoinFutures => "KUCOIN",
371 Self::Mango => "MANGO",
372 Self::Okcoin => "OKCOIN",
373 Self::Okex => "OKEX",
374 Self::OkexFutures => "OKEX",
375 Self::OkexOptions => "OKEX",
376 Self::OkexSpreads => "OKEX",
377 Self::OkexSwap => "OKEX",
378 Self::Phemex => "PHEMEX",
379 Self::Poloniex => "POLONIEX",
380 Self::Serum => "SERUM",
381 Self::StarAtlas => "STAR_ATLAS",
382 Self::Upbit => "UPBIT",
383 Self::WooX => "WOO_X",
384 }
385 }
386
387 #[must_use]
388 pub fn as_venue(&self) -> Venue {
389 Venue::from_ustr_unchecked(Ustr::from(self.as_venue_str()))
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use rstest::rstest;
396 use strum::IntoEnumIterator;
397
398 use super::*;
399
400 #[rstest]
401 fn test_exchange_to_venue_mapping() {
402 for exchange in TardisExchange::iter() {
403 let venue_str = exchange.as_venue_str();
404 assert!(
405 Venue::new_checked(venue_str).is_ok(),
406 "Tardis exchange '{exchange:?}' maps to invalid Nautilus venue '{venue_str}'",
407 );
408 }
409 }
410
411 #[rstest]
412 fn test_venue_to_exchange_mapping_bidirectional() {
413 let test_venues = [
414 "BINANCE",
415 "BITMEX",
416 "DERIBIT",
417 "KRAKEN",
418 "COINBASE",
419 "BYBIT",
420 "OKEX",
421 "HUOBI",
422 "GATE_IO",
423 "KUCOIN",
424 "BITFINEX",
425 "GEMINI",
426 "BITSTAMP",
427 "ASCENDEX",
428 "PHEMEX",
429 "POLONIEX",
430 "UPBIT",
431 "WOO_X",
432 "HYPERLIQUID",
433 "CRYPTO_COM",
434 "DYDX",
435 "HITBTC",
436 ];
437
438 for venue_str in test_venues {
439 let venue = Venue::new(venue_str);
440 let exchanges = TardisExchange::from_venue_str(venue.as_str());
441
442 for exchange in exchanges {
443 assert_eq!(
444 exchange.as_venue_str(),
445 venue_str,
446 "Bidirectional mapping failed: Nautilus venue '{venue_str}' -> Tardis exchange '{exchange:?}' -> Nautilus venue '{}'",
447 exchange.as_venue_str()
448 );
449 }
450 }
451 }
452}