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    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    /// 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, 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}