nautilus_model/instruments/
crypto_future.rs1use std::hash::{Hash, Hasher};
17
18use nautilus_core::{
19 correctness::{check_equal_u8, FAILED},
20 UnixNanos,
21};
22use rust_decimal::Decimal;
23use serde::{Deserialize, Serialize};
24use ustr::Ustr;
25
26use super::{any::InstrumentAny, Instrument};
27use crate::{
28 enums::{AssetClass, InstrumentClass, OptionKind},
29 identifiers::{InstrumentId, Symbol},
30 types::{
31 currency::Currency,
32 money::Money,
33 price::{check_positive_price, Price},
34 quantity::{check_positive_quantity, Quantity},
35 },
36};
37
38#[repr(C)]
40#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
41#[cfg_attr(
42 feature = "python",
43 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
44)]
45pub struct CryptoFuture {
46 pub id: InstrumentId,
48 pub raw_symbol: Symbol,
50 pub underlying: Currency,
52 pub quote_currency: Currency,
54 pub settlement_currency: Currency,
56 pub is_inverse: bool,
58 pub activation_ns: UnixNanos,
60 pub expiration_ns: UnixNanos,
62 pub price_precision: u8,
64 pub size_precision: u8,
66 pub price_increment: Price,
68 pub size_increment: Quantity,
70 pub multiplier: Quantity,
72 pub lot_size: Quantity,
74 pub margin_init: Decimal,
76 pub margin_maint: Decimal,
78 pub maker_fee: Decimal,
80 pub taker_fee: Decimal,
82 pub max_quantity: Option<Quantity>,
84 pub min_quantity: Option<Quantity>,
86 pub max_notional: Option<Money>,
88 pub min_notional: Option<Money>,
90 pub max_price: Option<Price>,
92 pub min_price: Option<Price>,
94 pub ts_event: UnixNanos,
96 pub ts_init: UnixNanos,
98}
99
100impl CryptoFuture {
101 #[allow(clippy::too_many_arguments)]
107 pub fn new_checked(
108 id: InstrumentId,
109 raw_symbol: Symbol,
110 underlying: Currency,
111 quote_currency: Currency,
112 settlement_currency: Currency,
113 is_inverse: bool,
114 activation_ns: UnixNanos,
115 expiration_ns: UnixNanos,
116 price_precision: u8,
117 size_precision: u8,
118 price_increment: Price,
119 size_increment: Quantity,
120 multiplier: Option<Quantity>,
121 lot_size: Option<Quantity>,
122 max_quantity: Option<Quantity>,
123 min_quantity: Option<Quantity>,
124 max_notional: Option<Money>,
125 min_notional: Option<Money>,
126 max_price: Option<Price>,
127 min_price: Option<Price>,
128 margin_init: Option<Decimal>,
129 margin_maint: Option<Decimal>,
130 maker_fee: Option<Decimal>,
131 taker_fee: Option<Decimal>,
132 ts_event: UnixNanos,
133 ts_init: UnixNanos,
134 ) -> anyhow::Result<Self> {
135 check_equal_u8(
136 price_precision,
137 price_increment.precision,
138 stringify!(price_precision),
139 stringify!(price_increment.precision),
140 )?;
141 check_equal_u8(
142 size_precision,
143 size_increment.precision,
144 stringify!(size_precision),
145 stringify!(size_increment.precision),
146 )?;
147 check_positive_price(price_increment.raw, stringify!(price_increment.raw))?;
148 check_positive_quantity(size_increment.raw, stringify!(size_increment.raw))?;
149
150 Ok(Self {
151 id,
152 raw_symbol,
153 underlying,
154 quote_currency,
155 settlement_currency,
156 is_inverse,
157 activation_ns,
158 expiration_ns,
159 price_precision,
160 size_precision,
161 price_increment,
162 size_increment,
163 multiplier: multiplier.unwrap_or(Quantity::from(1)),
164 lot_size: lot_size.unwrap_or(Quantity::from(1)),
165 margin_init: margin_init.unwrap_or_default(),
166 margin_maint: margin_maint.unwrap_or_default(),
167 maker_fee: maker_fee.unwrap_or_default(),
168 taker_fee: taker_fee.unwrap_or_default(),
169 max_quantity,
170 min_quantity,
171 max_notional,
172 min_notional,
173 max_price,
174 min_price,
175 ts_event,
176 ts_init,
177 })
178 }
179
180 #[allow(clippy::too_many_arguments)]
182 pub fn new(
183 id: InstrumentId,
184 raw_symbol: Symbol,
185 underlying: Currency,
186 quote_currency: Currency,
187 settlement_currency: Currency,
188 is_inverse: bool,
189 activation_ns: UnixNanos,
190 expiration_ns: UnixNanos,
191 price_precision: u8,
192 size_precision: u8,
193 price_increment: Price,
194 size_increment: Quantity,
195 multiplier: Option<Quantity>,
196 lot_size: Option<Quantity>,
197 max_quantity: Option<Quantity>,
198 min_quantity: Option<Quantity>,
199 max_notional: Option<Money>,
200 min_notional: Option<Money>,
201 max_price: Option<Price>,
202 min_price: Option<Price>,
203 margin_init: Option<Decimal>,
204 margin_maint: Option<Decimal>,
205 maker_fee: Option<Decimal>,
206 taker_fee: Option<Decimal>,
207 ts_event: UnixNanos,
208 ts_init: UnixNanos,
209 ) -> Self {
210 Self::new_checked(
211 id,
212 raw_symbol,
213 underlying,
214 quote_currency,
215 settlement_currency,
216 is_inverse,
217 activation_ns,
218 expiration_ns,
219 price_precision,
220 size_precision,
221 price_increment,
222 size_increment,
223 multiplier,
224 lot_size,
225 max_quantity,
226 min_quantity,
227 max_notional,
228 min_notional,
229 max_price,
230 min_price,
231 margin_init,
232 margin_maint,
233 maker_fee,
234 taker_fee,
235 ts_event,
236 ts_init,
237 )
238 .expect(FAILED)
239 }
240}
241
242impl PartialEq<Self> for CryptoFuture {
243 fn eq(&self, other: &Self) -> bool {
244 self.id == other.id
245 }
246}
247
248impl Eq for CryptoFuture {}
249
250impl Hash for CryptoFuture {
251 fn hash<H: Hasher>(&self, state: &mut H) {
252 self.id.hash(state);
253 }
254}
255
256impl Instrument for CryptoFuture {
257 fn into_any(self) -> InstrumentAny {
258 InstrumentAny::CryptoFuture(self)
259 }
260
261 fn id(&self) -> InstrumentId {
262 self.id
263 }
264
265 fn raw_symbol(&self) -> Symbol {
266 self.raw_symbol
267 }
268
269 fn asset_class(&self) -> AssetClass {
270 AssetClass::Cryptocurrency
271 }
272
273 fn instrument_class(&self) -> InstrumentClass {
274 InstrumentClass::Future
275 }
276
277 fn underlying(&self) -> Option<Ustr> {
278 Some(self.underlying.code)
279 }
280
281 fn base_currency(&self) -> Option<Currency> {
282 None
283 }
284
285 fn quote_currency(&self) -> Currency {
286 self.quote_currency
287 }
288
289 fn settlement_currency(&self) -> Currency {
290 self.settlement_currency
291 }
292
293 fn isin(&self) -> Option<Ustr> {
294 None
295 }
296
297 fn exchange(&self) -> Option<Ustr> {
298 None
299 }
300
301 fn option_kind(&self) -> Option<OptionKind> {
302 None
303 }
304
305 fn is_inverse(&self) -> bool {
306 self.is_inverse
307 }
308
309 fn price_precision(&self) -> u8 {
310 self.price_precision
311 }
312
313 fn size_precision(&self) -> u8 {
314 self.size_precision
315 }
316
317 fn price_increment(&self) -> Price {
318 self.price_increment
319 }
320
321 fn size_increment(&self) -> Quantity {
322 self.size_increment
323 }
324
325 fn multiplier(&self) -> Quantity {
326 Quantity::from(1)
327 }
328
329 fn lot_size(&self) -> Option<Quantity> {
330 Some(self.lot_size)
331 }
332
333 fn max_quantity(&self) -> Option<Quantity> {
334 self.max_quantity
335 }
336
337 fn min_quantity(&self) -> Option<Quantity> {
338 self.min_quantity
339 }
340
341 fn max_price(&self) -> Option<Price> {
342 self.max_price
343 }
344
345 fn min_price(&self) -> Option<Price> {
346 self.min_price
347 }
348
349 fn ts_event(&self) -> UnixNanos {
350 self.ts_event
351 }
352
353 fn ts_init(&self) -> UnixNanos {
354 self.ts_init
355 }
356
357 fn strike_price(&self) -> Option<Price> {
358 None
359 }
360
361 fn activation_ns(&self) -> Option<UnixNanos> {
362 Some(self.activation_ns)
363 }
364
365 fn expiration_ns(&self) -> Option<UnixNanos> {
366 Some(self.expiration_ns)
367 }
368
369 fn max_notional(&self) -> Option<Money> {
370 self.max_notional
371 }
372
373 fn min_notional(&self) -> Option<Money> {
374 self.min_notional
375 }
376}
377
378#[cfg(test)]
382mod tests {
383 use rstest::rstest;
384
385 use crate::instruments::{stubs::*, CryptoFuture};
386
387 #[rstest]
388 fn test_equality(crypto_future_btcusdt: CryptoFuture) {
389 let cloned = crypto_future_btcusdt;
390 assert_eq!(crypto_future_btcusdt, cloned);
391 }
392}