Skip to main content

nautilus_model/instruments/
any.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 enum_dispatch::enum_dispatch;
17use serde::{Deserialize, Serialize};
18
19use super::{
20    Instrument, betting::BettingInstrument, binary_option::BinaryOption, cfd::Cfd,
21    commodity::Commodity, crypto_future::CryptoFuture, crypto_futures_spread::CryptoFuturesSpread,
22    crypto_option::CryptoOption, crypto_option_spread::CryptoOptionSpread,
23    crypto_perpetual::CryptoPerpetual, currency_pair::CurrencyPair, equity::Equity,
24    futures_contract::FuturesContract, futures_spread::FuturesSpread,
25    index_instrument::IndexInstrument, option_contract::OptionContract,
26    option_spread::OptionSpread, perpetual_contract::PerpetualContract,
27    tokenized_asset::TokenizedAsset,
28};
29use crate::types::{Price, Quantity};
30
31#[derive(Clone, Debug, Serialize, Deserialize)]
32#[enum_dispatch(Instrument)]
33pub enum InstrumentAny {
34    Betting(BettingInstrument),
35    BinaryOption(BinaryOption),
36    Cfd(Cfd),
37    Commodity(Commodity),
38    CryptoFuture(CryptoFuture),
39    CryptoFuturesSpread(CryptoFuturesSpread),
40    CryptoOption(CryptoOption),
41    CryptoOptionSpread(CryptoOptionSpread),
42    CryptoPerpetual(CryptoPerpetual),
43    CurrencyPair(CurrencyPair),
44    Equity(Equity),
45    FuturesContract(FuturesContract),
46    FuturesSpread(FuturesSpread),
47    IndexInstrument(IndexInstrument),
48    OptionContract(OptionContract),
49    OptionSpread(OptionSpread),
50    PerpetualContract(PerpetualContract),
51    TokenizedAsset(TokenizedAsset),
52}
53
54// TODO: Probably move this to the `Instrument` trait too
55impl InstrumentAny {
56    #[must_use]
57    pub fn get_base_quantity(&self, quantity: Quantity, last_px: Price) -> Quantity {
58        match self {
59            Self::Betting(inst) => inst.calculate_base_quantity(quantity, last_px),
60            Self::BinaryOption(inst) => inst.calculate_base_quantity(quantity, last_px),
61            Self::Cfd(inst) => inst.calculate_base_quantity(quantity, last_px),
62            Self::Commodity(inst) => inst.calculate_base_quantity(quantity, last_px),
63            Self::CryptoFuture(inst) => inst.calculate_base_quantity(quantity, last_px),
64            Self::CryptoFuturesSpread(inst) => inst.calculate_base_quantity(quantity, last_px),
65            Self::CryptoOption(inst) => inst.calculate_base_quantity(quantity, last_px),
66            Self::CryptoOptionSpread(inst) => inst.calculate_base_quantity(quantity, last_px),
67            Self::CryptoPerpetual(inst) => inst.calculate_base_quantity(quantity, last_px),
68            Self::CurrencyPair(inst) => inst.calculate_base_quantity(quantity, last_px),
69            Self::Equity(inst) => inst.calculate_base_quantity(quantity, last_px),
70            Self::FuturesContract(inst) => inst.calculate_base_quantity(quantity, last_px),
71            Self::FuturesSpread(inst) => inst.calculate_base_quantity(quantity, last_px),
72            Self::IndexInstrument(inst) => inst.calculate_base_quantity(quantity, last_px),
73            Self::OptionContract(inst) => inst.calculate_base_quantity(quantity, last_px),
74            Self::OptionSpread(inst) => inst.calculate_base_quantity(quantity, last_px),
75            Self::PerpetualContract(inst) => inst.calculate_base_quantity(quantity, last_px),
76            Self::TokenizedAsset(inst) => inst.calculate_base_quantity(quantity, last_px),
77        }
78    }
79
80    /// Returns true if the instrument is a spread instrument.
81    #[must_use]
82    pub fn is_spread(&self) -> bool {
83        matches!(
84            self,
85            Self::FuturesSpread(_)
86                | Self::OptionSpread(_)
87                | Self::CryptoFuturesSpread(_)
88                | Self::CryptoOptionSpread(_)
89        )
90    }
91}
92
93impl PartialEq for InstrumentAny {
94    fn eq(&self, other: &Self) -> bool {
95        self.id() == other.id()
96    }
97}
98
99impl crate::data::HasTsInit for InstrumentAny {
100    fn ts_init(&self) -> nautilus_core::UnixNanos {
101        Instrument::ts_init(self)
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use rstest::rstest;
108
109    use super::*;
110    use crate::instruments::stubs::*;
111
112    #[rstest]
113    #[case::futures_spread(InstrumentAny::FuturesSpread(futures_spread_es()), true)]
114    #[case::option_spread(InstrumentAny::OptionSpread(option_spread()), true)]
115    #[case::crypto_futures_spread(
116        InstrumentAny::CryptoFuturesSpread(crypto_futures_spread_btc_deribit()),
117        true
118    )]
119    #[case::crypto_option_spread(
120        InstrumentAny::CryptoOptionSpread(crypto_option_spread_btc_deribit()),
121        true
122    )]
123    #[case::crypto_future(
124        InstrumentAny::CryptoFuture(crypto_future_btcusdt(
125            2,
126            6,
127            crate::types::Price::from("0.01"),
128            crate::types::Quantity::from("0.000001"),
129        )),
130        false
131    )]
132    #[case::crypto_option(
133        InstrumentAny::CryptoOption(crypto_option_btc_deribit(
134            3,
135            1,
136            crate::types::Price::from("0.001"),
137            crate::types::Quantity::from("0.1"),
138        )),
139        false
140    )]
141    fn test_is_spread(#[case] instrument: InstrumentAny, #[case] expected: bool) {
142        assert_eq!(instrument.is_spread(), expected);
143    }
144}