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