Skip to main content

nautilus_tardis/common/
enums.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use 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")]
39/// The instrument type for the symbol.
40pub 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")]
64/// The type of option.
65pub enum TardisOptionType {
66    Call,
67    Put,
68}
69
70/// The aggressor side of the trade.
71#[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/// The bar kind.
94#[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/// Normalized data type identifiers used in Tardis Machine Server requests.
118/// See <https://docs.tardis.dev/api/tardis-machine#normalized-data-types>.
119#[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    /// Returns the Tardis Machine Server data type string.
147    #[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")]
178/// Represents a crypto exchange.
179/// See <https://api.tardis.dev/v1/exchanges> for all supported exchanges.
180pub 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    Cryptofacilities,
207    Delta,
208    Deribit,
209    Dydx,
210    DydxV4,
211    Ftx,
212    FtxUs,
213    GateIo,
214    GateIoFutures,
215    Gemini,
216    Hitbtc,
217    Huobi,
218    HuobiDm,
219    HuobiDmLinearSwap,
220    HuobiDmOptions,
221    HuobiDmSwap,
222    Hyperliquid,
223    Kraken,
224    Kucoin,
225    KucoinFutures,
226    Lighter,
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    /// Option-specific exchanges that should be filtered out when options are not requested.
244    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],
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            "LIGHTER" => vec![Self::Lighter],
304            "MANGO" => vec![Self::Mango],
305            "OKCOIN" => vec![Self::Okcoin],
306            "OKEX" => vec![
307                Self::Okex,
308                Self::OkexFutures,
309                Self::OkexOptions,
310                Self::OkexSpreads,
311                Self::OkexSwap,
312            ],
313            "PHEMEX" => vec![Self::Phemex],
314            "POLONIEX" => vec![Self::Poloniex],
315            "SERUM" => vec![Self::Serum],
316            "STAR_ATLAS" => vec![Self::StarAtlas],
317            "UPBIT" => vec![Self::Upbit],
318            "WOO_X" => vec![Self::WooX],
319            _ => Vec::new(),
320        }
321    }
322
323    #[must_use]
324    pub const fn as_venue_str(&self) -> &str {
325        match self {
326            Self::Ascendex => "ASCENDEX",
327            Self::Binance => "BINANCE",
328            Self::BinanceDelivery => "BINANCE_DELIVERY",
329            Self::BinanceDex => "BINANCE",
330            Self::BinanceEuropeanOptions => "BINANCE",
331            Self::BinanceFutures => "BINANCE",
332            Self::BinanceJersey => "BINANCE",
333            Self::BinanceOptions => "BINANCE",
334            Self::BinanceUs => "BINANCE_US",
335            Self::Bitfinex => "BITFINEX",
336            Self::BitfinexDerivatives => "BITFINEX",
337            Self::Bitflyer => "BITFLYER",
338            Self::Bitget => "BITGET",
339            Self::BitgetFutures => "BITGET",
340            Self::Bitmex => "BITMEX",
341            Self::Bitnomial => "BITNOMIAL",
342            Self::Bitstamp => "BITSTAMP",
343            Self::BlockchainCom => "BLOCKCHAIN_COM",
344            Self::Bybit => "BYBIT",
345            Self::BybitOptions => "BYBIT",
346            Self::BybitSpot => "BYBIT",
347            Self::Coinbase => "COINBASE",
348            Self::CoinbaseInternational => "COINBASE_INTX",
349            Self::Coinflex => "COINFLEX",
350            Self::CryptoCom => "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::Lighter => "LIGHTER",
372            Self::Mango => "MANGO",
373            Self::Okcoin => "OKCOIN",
374            Self::Okex => "OKEX",
375            Self::OkexFutures => "OKEX",
376            Self::OkexOptions => "OKEX",
377            Self::OkexSpreads => "OKEX",
378            Self::OkexSwap => "OKEX",
379            Self::Phemex => "PHEMEX",
380            Self::Poloniex => "POLONIEX",
381            Self::Serum => "SERUM",
382            Self::StarAtlas => "STAR_ATLAS",
383            Self::Upbit => "UPBIT",
384            Self::WooX => "WOO_X",
385        }
386    }
387
388    #[must_use]
389    pub fn as_venue(&self) -> Venue {
390        Venue::from_ustr_unchecked(Ustr::from(self.as_venue_str()))
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use rstest::rstest;
397    use strum::IntoEnumIterator;
398
399    use super::*;
400
401    #[rstest]
402    fn test_exchange_to_venue_mapping() {
403        for exchange in TardisExchange::iter() {
404            let venue_str = exchange.as_venue_str();
405            assert!(
406                Venue::new_checked(venue_str).is_ok(),
407                "Tardis exchange '{exchange:?}' maps to invalid Nautilus venue '{venue_str}'",
408            );
409        }
410    }
411
412    #[rstest]
413    fn test_venue_to_exchange_mapping_bidirectional() {
414        let test_venues = [
415            "BINANCE",
416            "BITMEX",
417            "DERIBIT",
418            "KRAKEN",
419            "COINBASE",
420            "BYBIT",
421            "OKEX",
422            "HUOBI",
423            "GATE_IO",
424            "KUCOIN",
425            "BITFINEX",
426            "GEMINI",
427            "BITSTAMP",
428            "ASCENDEX",
429            "PHEMEX",
430            "POLONIEX",
431            "UPBIT",
432            "WOO_X",
433            "HYPERLIQUID",
434            "LIGHTER",
435            "CRYPTO_COM",
436            "DYDX",
437            "HITBTC",
438        ];
439
440        for venue_str in test_venues {
441            let venue = Venue::new(venue_str);
442            let exchanges = TardisExchange::from_venue_str(venue.as_str());
443
444            for exchange in exchanges {
445                assert_eq!(
446                    exchange.as_venue_str(),
447                    venue_str,
448                    "Bidirectional mapping failed: Nautilus venue '{venue_str}' -> Tardis exchange '{exchange:?}' -> Nautilus venue '{}'",
449                    exchange.as_venue_str()
450                );
451            }
452        }
453    }
454}