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