nautilus_model/instruments/
mod.rs
1pub mod any;
19pub mod betting;
20pub mod binary_option;
21pub mod crypto_future;
22pub mod crypto_option;
23pub mod crypto_perpetual;
24pub mod currency_pair;
25pub mod equity;
26pub mod futures_contract;
27pub mod futures_spread;
28pub mod option_contract;
29pub mod option_spread;
30pub mod synthetic;
31
32#[cfg(feature = "stubs")]
33pub mod stubs;
34
35use enum_dispatch::enum_dispatch;
36use nautilus_core::UnixNanos;
37use rust_decimal::Decimal;
38use rust_decimal_macros::dec;
39use ustr::Ustr;
40
41pub use crate::instruments::{
43 any::InstrumentAny, betting::BettingInstrument, binary_option::BinaryOption,
44 crypto_future::CryptoFuture, crypto_option::CryptoOption, crypto_perpetual::CryptoPerpetual,
45 currency_pair::CurrencyPair, equity::Equity, futures_contract::FuturesContract,
46 futures_spread::FuturesSpread, option_contract::OptionContract, option_spread::OptionSpread,
47 synthetic::SyntheticInstrument,
48};
49use crate::{
50 enums::{AssetClass, InstrumentClass, OptionKind},
51 identifiers::{InstrumentId, Symbol, Venue},
52 types::{Currency, Money, Price, Quantity},
53};
54
55#[enum_dispatch]
56pub trait Instrument: 'static + Send {
57 fn into_any(self) -> InstrumentAny;
58 fn id(&self) -> InstrumentId;
59 fn symbol(&self) -> Symbol {
60 self.id().symbol
61 }
62 fn venue(&self) -> Venue {
63 self.id().venue
64 }
65 fn raw_symbol(&self) -> Symbol;
66 fn asset_class(&self) -> AssetClass;
67 fn instrument_class(&self) -> InstrumentClass;
68 fn underlying(&self) -> Option<Ustr>;
69 fn base_currency(&self) -> Option<Currency>;
70 fn quote_currency(&self) -> Currency;
71 fn settlement_currency(&self) -> Currency;
72 fn cost_currency(&self) -> Currency {
73 if self.is_inverse() {
74 self.base_currency()
75 .expect("Inverse instruments must have a base currency")
76 } else {
77 self.quote_currency()
78 }
79 }
80 fn isin(&self) -> Option<Ustr>;
81 fn option_kind(&self) -> Option<OptionKind>;
82 fn exchange(&self) -> Option<Ustr>;
83 fn strike_price(&self) -> Option<Price>;
84 fn activation_ns(&self) -> Option<UnixNanos>;
85 fn expiration_ns(&self) -> Option<UnixNanos>;
86 fn is_inverse(&self) -> bool;
87 fn is_quanto(&self) -> bool {
88 if let Some(base_currency) = self.base_currency() {
89 self.settlement_currency() != base_currency
90 } else {
91 false
92 }
93 }
94 fn price_precision(&self) -> u8;
95 fn size_precision(&self) -> u8;
96 fn price_increment(&self) -> Price;
97 fn size_increment(&self) -> Quantity;
98 fn multiplier(&self) -> Quantity;
99 fn lot_size(&self) -> Option<Quantity>;
100 fn max_quantity(&self) -> Option<Quantity>;
101 fn min_quantity(&self) -> Option<Quantity>;
102 fn max_notional(&self) -> Option<Money>;
103 fn min_notional(&self) -> Option<Money>;
104 fn max_price(&self) -> Option<Price>;
105 fn min_price(&self) -> Option<Price>;
106 fn margin_init(&self) -> Decimal {
107 dec!(0) }
109
110 fn margin_maint(&self) -> Decimal {
111 dec!(0) }
113
114 fn maker_fee(&self) -> Decimal {
115 dec!(0) }
117
118 fn taker_fee(&self) -> Decimal {
119 dec!(0) }
121 fn ts_event(&self) -> UnixNanos;
122 fn ts_init(&self) -> UnixNanos;
123
124 fn make_price(&self, value: f64) -> Price {
126 Price::new(value, self.price_precision())
127 }
128
129 fn make_qty(&self, value: f64) -> Quantity {
131 Quantity::new(value, self.size_precision())
132 }
133
134 fn calculate_notional_value(
142 &self,
143 quantity: Quantity,
144 price: Price,
145 use_quote_for_inverse: Option<bool>,
146 ) -> Money {
147 let use_quote_for_inverse = use_quote_for_inverse.unwrap_or(false);
148 let (amount, currency) = if self.is_inverse() {
149 if use_quote_for_inverse {
150 (quantity.as_f64(), self.quote_currency())
151 } else {
152 let amount =
153 quantity.as_f64() * self.multiplier().as_f64() * (1.0 / price.as_f64());
154 let currency = self
155 .base_currency()
156 .expect("Error: no base currency for notional calculation");
157 (amount, currency)
158 }
159 } else {
160 let amount = quantity.as_f64() * self.multiplier().as_f64() * price.as_f64();
161 let currency = self.quote_currency();
162 (amount, currency)
163 };
164
165 Money::new(amount, currency)
166 }
167
168 fn calculate_base_quantity(&self, quantity: Quantity, last_px: Price) -> Quantity {
170 let value = quantity.as_f64() * (1.0 / last_px.as_f64());
171 Quantity::new(value, self.size_precision())
172 }
173}
174
175pub const EXPIRING_INSTRUMENT_TYPES: [InstrumentClass; 4] = [
176 InstrumentClass::Future,
177 InstrumentClass::FuturesSpread,
178 InstrumentClass::Option,
179 InstrumentClass::OptionSpread,
180];