1use chrono::{TimeZone, Utc};
17use nautilus_core::UnixNanos;
18use rstest::*;
19use rust_decimal::Decimal;
20use rust_decimal_macros::dec;
21use ustr::Ustr;
22
23use super::{
24 CryptoOption, betting::BettingInstrument, binary_option::BinaryOption,
25 futures_spread::FuturesSpread, option_spread::OptionSpread, synthetic::SyntheticInstrument,
26};
27use crate::{
28 enums::{AssetClass, OptionKind},
29 identifiers::{InstrumentId, Symbol, Venue},
30 instruments::{
31 CryptoFuture, CryptoPerpetual, CurrencyPair, Equity, FuturesContract, OptionContract,
32 },
33 types::{Currency, Money, Price, Quantity},
34};
35
36impl Default for SyntheticInstrument {
37 fn default() -> Self {
39 let btc_binance = InstrumentId::from("BTC.BINANCE");
40 let ltc_binance = InstrumentId::from("LTC.BINANCE");
41 let formula = "(BTC.BINANCE + LTC.BINANCE) / 2.0".to_string();
42 SyntheticInstrument::new(
43 Symbol::new("BTC-LTC"),
44 2,
45 vec![btc_binance, ltc_binance],
46 formula.clone(),
47 0.into(),
48 0.into(),
49 )
50 }
51}
52
53#[fixture]
58pub fn crypto_future_btcusdt(
59 #[default(2)] price_precision: u8,
60 #[default(6)] size_precision: u8,
61 #[default(Price::from("0.01"))] price_increment: Price,
62 #[default(Quantity::from("0.000001"))] size_increment: Quantity,
63) -> CryptoFuture {
64 let activation = Utc.with_ymd_and_hms(2014, 4, 8, 0, 0, 0).unwrap();
65 let expiration = Utc.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
66 CryptoFuture::new(
67 InstrumentId::from("ETHUSDT-123.BINANCE"),
68 Symbol::from("BTCUSDT"),
69 Currency::from("BTC"),
70 Currency::from("USDT"),
71 Currency::from("USDT"),
72 false,
73 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
74 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
75 price_precision,
76 size_precision,
77 price_increment,
78 size_increment,
79 None,
80 None,
81 Some(Quantity::from("9000.0")),
82 Some(Quantity::from("0.000001")),
83 None,
84 Some(Money::new(10.00, Currency::from("USDT"))),
85 Some(Price::from("1000000.00")),
86 Some(Price::from("0.01")),
87 None,
88 None,
89 None,
90 None,
91 0.into(),
92 0.into(),
93 )
94}
95
96#[fixture]
97pub fn ethbtc_quanto(
98 #[default(5)] price_precision: u8,
99 #[default(3)] size_precision: u8,
100 #[default(Price::from("0.00001"))] price_increment: Price,
101 #[default(Quantity::from("0.001"))] size_increment: Quantity,
102) -> CryptoFuture {
103 let activation = Utc.with_ymd_and_hms(2014, 4, 8, 0, 0, 0).unwrap();
104 let expiration = Utc.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
105 CryptoFuture::new(
106 InstrumentId::from("ETHBTC-123.BINANCE"),
107 Symbol::from("ETHBTC"),
108 Currency::from("ETH"),
109 Currency::from("BTC"),
110 Currency::from("BTC"),
111 false,
112 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
113 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
114 price_precision,
115 size_precision,
116 price_increment,
117 size_increment,
118 None,
119 None,
120 Some(Quantity::from("9000.0")),
121 Some(Quantity::from("0.001")),
122 None,
123 Some(Money::new(1.0, Currency::from("BTC"))),
124 Some(Price::from("1.0")),
125 Some(Price::from("0.00001")),
126 None,
127 None,
128 None,
129 None,
130 0.into(),
131 0.into(),
132 )
133}
134
135#[fixture]
140pub fn xbtusd_inverse_perp(
141 #[default(1)] price_precision: u8,
143 #[default(0)] size_precision: u8,
144 #[default(Price::from("0.5"))] price_increment: Price,
145 #[default(Quantity::from("1"))] size_increment: Quantity,
146) -> CryptoPerpetual {
147 CryptoPerpetual::new(
148 InstrumentId::from("XBTUSD-PERP.BITMEX"),
150 Symbol::from("XBTUSD"),
151 Currency::BTC(), Currency::USD(), Currency::BTC(), true, price_precision,
156 size_precision,
157 price_increment,
158 size_increment,
159 None, Some(Quantity::from("1")), None, None, Some(Money::from("10000000 USD")), Some(Money::from("1 USD")), Some(Price::from("10000000")), Some(Price::from("0.01")), Some(dec!(0.01)), Some(dec!(0.0035)), Some(dec!(-0.00025)), Some(dec!(0.00075)), UnixNanos::default(), UnixNanos::default(), )
174}
175
176#[fixture]
181pub fn crypto_option_btc_deribit(
182 #[default(3)] price_precision: u8,
183 #[default(1)] size_precision: u8,
184 #[default(Price::from("0.001"))] price_increment: Price,
185 #[default(Quantity::from("0.1"))] size_increment: Quantity,
186) -> CryptoOption {
187 let activation = UnixNanos::from(1_671_696_002_000_000_000);
188 let expiration = UnixNanos::from(1_673_596_800_000_000_000);
189 CryptoOption::new(
190 InstrumentId::from("BTC-13JAN23-16000-P.DERIBIT"),
191 Symbol::from("BTC-13JAN23-16000-P"),
192 Currency::from("BTC"),
193 Currency::from("USD"),
194 Currency::from("BTC"),
195 false,
196 OptionKind::Put,
197 Price::from("16000.000"),
198 activation,
199 expiration,
200 price_precision,
201 size_precision,
202 price_increment,
203 size_increment,
204 Some(Quantity::from(1)),
205 Some(Quantity::from("9000.0")),
206 Some(Quantity::from("0.1")),
207 None,
208 Some(Money::new(10.00, Currency::from("USD"))),
209 None,
210 None,
211 None,
212 None,
213 Some(dec!(0.0003)),
214 Some(dec!(0.0003)),
215 0.into(),
216 0.into(),
217 )
218}
219
220#[fixture]
225pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
226 CryptoPerpetual::new(
227 InstrumentId::from("ETHUSDT-PERP.BINANCE"),
228 Symbol::from("ETHUSDT"),
229 Currency::from("ETH"),
230 Currency::from("USDT"),
231 Currency::from("USDT"),
232 false,
233 2,
234 3,
235 Price::from("0.01"),
236 Quantity::from("0.001"),
237 None,
238 None,
239 Some(Quantity::from("10000.0")),
240 Some(Quantity::from("0.001")),
241 None,
242 Some(Money::new(10.00, Currency::from("USDT"))),
243 Some(Price::from("15000.00")),
244 Some(Price::from("1.0")),
245 Some(dec!(1.0)),
246 Some(dec!(0.35)),
247 Some(dec!(0.0002)),
248 Some(dec!(0.0004)),
249 UnixNanos::default(),
250 UnixNanos::default(),
251 )
252}
253
254#[fixture]
255pub fn xbtusd_bitmex() -> CryptoPerpetual {
256 CryptoPerpetual::new(
257 InstrumentId::from("BTCUSDT.BITMEX"),
258 Symbol::from("XBTUSD"),
259 Currency::BTC(),
260 Currency::USD(),
261 Currency::BTC(),
262 true,
263 1,
264 0,
265 Price::from("0.5"),
266 Quantity::from("1"),
267 None,
268 None,
269 None,
270 None,
271 Some(Money::from("10000000 USD")),
272 Some(Money::from("1 USD")),
273 Some(Price::from("10000000")),
274 Some(Price::from("0.01")),
275 Some(dec!(0.01)),
276 Some(dec!(0.0035)),
277 Some(dec!(-0.00025)),
278 Some(dec!(0.00075)),
279 UnixNanos::default(),
280 UnixNanos::default(),
281 )
282}
283
284#[fixture]
285pub fn ethusdt_bitmex() -> CryptoPerpetual {
286 CryptoPerpetual::new(
287 InstrumentId::from("ETHUSD.BITMEX"),
288 Symbol::from("ETHUSD"),
289 Currency::ETH(),
290 Currency::USD(),
291 Currency::ETH(),
292 true,
293 2,
294 0,
295 Price::from("0.05"),
296 Quantity::from("1"),
297 None,
298 None,
299 None,
300 None,
301 None,
302 None,
303 Some(Price::from("10000000")),
304 Some(Price::from("0.01")),
305 Some(dec!(0.01)),
306 Some(dec!(0.0035)),
307 Some(dec!(-0.00025)),
308 Some(dec!(0.00075)),
309 UnixNanos::default(),
310 UnixNanos::default(),
311 )
312}
313
314#[fixture]
319pub fn currency_pair_btcusdt() -> CurrencyPair {
320 CurrencyPair::new(
321 InstrumentId::from("BTCUSDT.BINANCE"),
322 Symbol::from("BTCUSDT"),
323 Currency::from("BTC"),
324 Currency::from("USDT"),
325 2,
326 6,
327 Price::from("0.01"),
328 Quantity::from("0.000001"),
329 None,
330 Some(Quantity::from("9000")),
331 Some(Quantity::from("0.000001")),
332 None,
333 None,
334 Some(Price::from("1000000")),
335 Some(Price::from("0.01")),
336 Some(dec!(0.001)),
337 Some(dec!(0.001)),
338 Some(dec!(0.001)),
339 Some(dec!(0.001)),
340 UnixNanos::default(),
341 UnixNanos::default(),
342 )
343}
344
345#[fixture]
346pub fn currency_pair_ethusdt() -> CurrencyPair {
347 CurrencyPair::new(
348 InstrumentId::from("ETHUSDT.BINANCE"),
349 Symbol::from("ETHUSDT"),
350 Currency::from("ETH"),
351 Currency::from("USDT"),
352 2,
353 5,
354 Price::from("0.01"),
355 Quantity::from("0.00001"),
356 None,
357 Some(Quantity::from("9000")),
358 Some(Quantity::from("0.00001")),
359 None,
360 None,
361 Some(Price::from("1000000")),
362 Some(Price::from("0.01")),
363 Some(dec!(0.01)),
364 Some(dec!(0.0035)),
365 Some(dec!(0.0001)),
366 Some(dec!(0.0001)),
367 UnixNanos::default(),
368 UnixNanos::default(),
369 )
370}
371
372#[must_use]
376pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
377 let target_venue = venue.unwrap_or(Venue::from("SIM"));
378 let instrument_id = InstrumentId::new(symbol, target_venue);
379 let base_currency = symbol.as_str().split('/').next().unwrap();
380 let quote_currency = symbol.as_str().split('/').next_back().unwrap();
381 let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
382 let price_increment = Price::new(1.0 / 10.0f64, price_precision);
383 CurrencyPair::new(
384 instrument_id,
385 symbol,
386 Currency::from(base_currency),
387 Currency::from(quote_currency),
388 price_precision,
389 0,
390 price_increment,
391 Quantity::from("1"),
392 Some(Quantity::from("1000")),
393 Some(Quantity::from("1000000")),
394 Some(Quantity::from("100")),
395 None,
396 None,
397 None,
398 None,
399 Some(dec!(0.03)),
400 Some(dec!(0.03)),
401 Some(dec!(0.00002)),
402 Some(dec!(0.00002)),
403 UnixNanos::default(),
404 UnixNanos::default(),
405 )
406}
407
408#[fixture]
409pub fn audusd_sim() -> CurrencyPair {
410 default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
411}
412
413#[fixture]
414pub fn gbpusd_sim() -> CurrencyPair {
415 default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
416}
417
418#[fixture]
419pub fn usdjpy_idealpro() -> CurrencyPair {
420 default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
421}
422
423#[fixture]
428pub fn equity_aapl() -> Equity {
429 Equity::new(
430 InstrumentId::from("AAPL.XNAS"),
431 Symbol::from("AAPL"),
432 Some(Ustr::from("US0378331005")),
433 Currency::from("USD"),
434 2,
435 Price::from("0.01"),
436 None,
437 None,
438 None,
439 None,
440 None,
441 None,
442 None,
443 None,
444 None,
445 UnixNanos::default(),
446 UnixNanos::default(),
447 )
448}
449
450pub fn futures_contract_es(
459 activation: Option<UnixNanos>,
460 expiration: Option<UnixNanos>,
461) -> FuturesContract {
462 let activation = activation.unwrap_or(UnixNanos::from(
463 Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
464 .unwrap()
465 .timestamp_nanos_opt()
466 .unwrap() as u64,
467 ));
468 let expiration = expiration.unwrap_or(UnixNanos::from(
469 Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0)
470 .unwrap()
471 .timestamp_nanos_opt()
472 .unwrap() as u64,
473 ));
474 FuturesContract::new(
475 InstrumentId::from("ESZ21.GLBX"),
476 Symbol::from("ESZ21"),
477 AssetClass::Index,
478 Some(Ustr::from("XCME")),
479 Ustr::from("ES"),
480 activation,
481 expiration,
482 Currency::USD(),
483 2,
484 Price::from("0.01"),
485 Quantity::from(1),
486 Quantity::from(1),
487 None,
488 None,
489 None,
490 None,
491 None,
492 None,
493 None,
494 None,
495 UnixNanos::default(),
496 UnixNanos::default(),
497 )
498}
499
500#[fixture]
505pub fn futures_spread_es() -> FuturesSpread {
506 let activation = Utc.with_ymd_and_hms(2022, 6, 21, 13, 30, 0).unwrap();
507 let expiration = Utc.with_ymd_and_hms(2024, 6, 21, 13, 30, 0).unwrap();
508 FuturesSpread::new(
509 InstrumentId::from("ESM4-ESU4.GLBX"),
510 Symbol::from("ESM4-ESU4"),
511 AssetClass::Index,
512 Some(Ustr::from("XCME")),
513 Ustr::from("ES"),
514 Ustr::from("EQ"),
515 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
516 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
517 Currency::USD(),
518 2,
519 Price::from("0.01"),
520 Quantity::from(1),
521 Quantity::from(1),
522 None,
523 None,
524 None,
525 None,
526 None,
527 None,
528 None,
529 None,
530 UnixNanos::default(),
531 UnixNanos::default(),
532 )
533}
534
535#[fixture]
540pub fn option_contract_appl() -> OptionContract {
541 let activation = Utc.with_ymd_and_hms(2021, 9, 17, 0, 0, 0).unwrap();
542 let expiration = Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0).unwrap();
543 OptionContract::new(
544 InstrumentId::from("AAPL211217C00150000.OPRA"),
545 Symbol::from("AAPL211217C00150000"),
546 AssetClass::Equity,
547 Some(Ustr::from("GMNI")),
548 Ustr::from("AAPL"),
549 OptionKind::Call,
550 Price::from("149.0"),
551 Currency::USD(),
552 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
553 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
554 2,
555 Price::from("0.01"),
556 Quantity::from(1),
557 Quantity::from(1),
558 None,
559 None,
560 None,
561 None,
562 None,
563 None,
564 None,
565 None,
566 UnixNanos::default(),
567 UnixNanos::default(),
568 )
569}
570
571#[fixture]
576pub fn option_spread() -> OptionSpread {
577 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
578 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
579 OptionSpread::new(
580 InstrumentId::from("UD:U$: GN 2534559.GLBX"),
581 Symbol::from("UD:U$: GN 2534559"),
582 AssetClass::FX,
583 Some(Ustr::from("XCME")),
584 Ustr::from("SR3"),
585 Ustr::from("GN"),
586 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
587 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
588 Currency::USD(),
589 2,
590 Price::from("0.01"),
591 Quantity::from(1),
592 Quantity::from(1),
593 None,
594 None,
595 None,
596 None,
597 None,
598 None,
599 None,
600 None,
601 UnixNanos::default(),
602 UnixNanos::default(),
603 )
604}
605
606#[fixture]
611pub fn betting() -> BettingInstrument {
612 let raw_symbol = Symbol::new("1-123456789");
613 let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR").as_str());
614 let event_type_id = 6423;
615 let event_type_name = Ustr::from("American Football");
616 let competition_id = 12282733;
617 let competition_name = Ustr::from("NFL");
618 let event_id = 29678534;
619 let event_name = Ustr::from("NFL");
620 let event_country_code = Ustr::from("GB");
621 let event_open_date = UnixNanos::from(
622 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
623 .unwrap()
624 .timestamp_nanos_opt()
625 .unwrap() as u64,
626 );
627 let betting_type = Ustr::from("ODDS");
628 let market_id = Ustr::from("1-123456789");
629 let market_name = Ustr::from("AFC Conference Winner");
630 let market_type = Ustr::from("SPECIAL");
631 let market_start_time = UnixNanos::from(
632 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
633 .unwrap()
634 .timestamp_nanos_opt()
635 .unwrap() as u64,
636 );
637 let selection_id = 50214;
638 let selection_name = Ustr::from("Kansas City Chiefs");
639 let selection_handicap = 0.0;
640 let currency = Currency::GBP();
641 let price_increment = Price::from("0.01");
642 let size_increment = Quantity::from("0.01");
643 let max_quantity = Some(Quantity::from("1000"));
644 let min_quantity = Some(Quantity::from("1"));
645 let max_notional = Some(Money::from("10000 GBP"));
646 let min_notional = Some(Money::from("10 GBP"));
647 let max_price = Some(Price::from("100.00"));
648 let min_price = Some(Price::from("1.00"));
649 let margin_init = Some(Decimal::from(1));
650 let margin_maint = Some(Decimal::from(1));
651 let maker_fee = Some(Decimal::from(0));
652 let taker_fee = Some(Decimal::from(0));
653 let ts_event = UnixNanos::default();
654 let ts_init = UnixNanos::default();
655
656 BettingInstrument::new(
657 id,
658 raw_symbol,
659 event_type_id,
660 event_type_name,
661 competition_id,
662 competition_name,
663 event_id,
664 event_name,
665 event_country_code,
666 event_open_date,
667 betting_type,
668 market_id,
669 market_name,
670 market_type,
671 market_start_time,
672 selection_id,
673 selection_name,
674 selection_handicap,
675 currency,
676 price_increment.precision,
677 size_increment.precision,
678 price_increment,
679 size_increment,
680 max_quantity,
681 min_quantity,
682 max_notional,
683 min_notional,
684 max_price,
685 min_price,
686 margin_init,
687 margin_maint,
688 maker_fee,
689 taker_fee,
690 ts_event,
691 ts_init,
692 )
693}
694
695#[fixture]
700pub fn binary_option() -> BinaryOption {
701 let raw_symbol = Symbol::new(
702 "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
703 );
704 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
705 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
706 let price_increment = Price::from("0.001");
707 let size_increment = Quantity::from("0.01");
708 BinaryOption::new(
709 InstrumentId::from("{raw_symbol}.POLYMARKET"),
710 raw_symbol,
711 AssetClass::Alternative,
712 Currency::USDC(),
713 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
714 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
715 price_increment.precision,
716 size_increment.precision,
717 price_increment,
718 size_increment,
719 None,
720 None,
721 None,
722 None,
723 None,
724 None,
725 None,
726 None,
727 None,
728 None,
729 None,
730 None,
731 UnixNanos::default(),
732 UnixNanos::default(),
733 )
734}