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