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(1)),
206 Some(Quantity::from("9000.0")),
207 Some(Quantity::from("0.1")),
208 None,
209 Some(Money::new(10.00, Currency::from("USD"))),
210 None,
211 None,
212 None,
213 None,
214 Some(dec!(0.0003)),
215 Some(dec!(0.0003)),
216 0.into(),
217 0.into(),
218 )
219}
220
221#[fixture]
226pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
227 CryptoPerpetual::new(
228 InstrumentId::from("ETHUSDT-PERP.BINANCE"),
229 Symbol::from("ETHUSDT"),
230 Currency::from("ETH"),
231 Currency::from("USDT"),
232 Currency::from("USDT"),
233 false,
234 2,
235 3,
236 Price::from("0.01"),
237 Quantity::from("0.001"),
238 None,
239 None,
240 Some(Quantity::from("10000.0")),
241 Some(Quantity::from("0.001")),
242 None,
243 Some(Money::new(10.00, Currency::from("USDT"))),
244 Some(Price::from("15000.00")),
245 Some(Price::from("1.0")),
246 Some(dec!(1.0)),
247 Some(dec!(0.35)),
248 Some(dec!(0.0002)),
249 Some(dec!(0.0004)),
250 UnixNanos::default(),
251 UnixNanos::default(),
252 )
253}
254
255#[fixture]
256pub fn xbtusd_bitmex() -> CryptoPerpetual {
257 CryptoPerpetual::new(
258 InstrumentId::from("BTCUSDT.BITMEX"),
259 Symbol::from("XBTUSD"),
260 Currency::BTC(),
261 Currency::USD(),
262 Currency::BTC(),
263 true,
264 1,
265 0,
266 Price::from("0.5"),
267 Quantity::from("1"),
268 None,
269 None,
270 None,
271 None,
272 Some(Money::from("10000000 USD")),
273 Some(Money::from("1 USD")),
274 Some(Price::from("10000000")),
275 Some(Price::from("0.01")),
276 Some(dec!(0.01)),
277 Some(dec!(0.0035)),
278 Some(dec!(-0.00025)),
279 Some(dec!(0.00075)),
280 UnixNanos::default(),
281 UnixNanos::default(),
282 )
283}
284
285#[fixture]
286pub fn ethusdt_bitmex() -> CryptoPerpetual {
287 CryptoPerpetual::new(
288 InstrumentId::from("ETHUSD.BITMEX"),
289 Symbol::from("ETHUSD"),
290 Currency::ETH(),
291 Currency::USD(),
292 Currency::ETH(),
293 true,
294 2,
295 0,
296 Price::from("0.05"),
297 Quantity::from("1"),
298 None,
299 None,
300 None,
301 None,
302 None,
303 None,
304 Some(Price::from("10000000")),
305 Some(Price::from("0.01")),
306 Some(dec!(0.01)),
307 Some(dec!(0.0035)),
308 Some(dec!(-0.00025)),
309 Some(dec!(0.00075)),
310 UnixNanos::default(),
311 UnixNanos::default(),
312 )
313}
314
315#[fixture]
320pub fn currency_pair_btcusdt() -> CurrencyPair {
321 CurrencyPair::new(
322 InstrumentId::from("BTCUSDT.BINANCE"),
323 Symbol::from("BTCUSDT"),
324 Currency::from("BTC"),
325 Currency::from("USDT"),
326 2,
327 6,
328 Price::from("0.01"),
329 Quantity::from("0.000001"),
330 None,
331 None,
332 Some(Quantity::from("9000")),
333 Some(Quantity::from("0.000001")),
334 None,
335 None,
336 Some(Price::from("1000000")),
337 Some(Price::from("0.01")),
338 Some(dec!(0.001)),
339 Some(dec!(0.001)),
340 Some(dec!(0.001)),
341 Some(dec!(0.001)),
342 UnixNanos::default(),
343 UnixNanos::default(),
344 )
345}
346
347#[fixture]
348pub fn currency_pair_ethusdt() -> CurrencyPair {
349 CurrencyPair::new(
350 InstrumentId::from("ETHUSDT.BINANCE"),
351 Symbol::from("ETHUSDT"),
352 Currency::from("ETH"),
353 Currency::from("USDT"),
354 2,
355 5,
356 Price::from("0.01"),
357 Quantity::from("0.00001"),
358 None,
359 None,
360 Some(Quantity::from("9000")),
361 Some(Quantity::from("0.00001")),
362 None,
363 None,
364 Some(Price::from("1000000")),
365 Some(Price::from("0.01")),
366 Some(dec!(0.01)),
367 Some(dec!(0.0035)),
368 Some(dec!(0.0001)),
369 Some(dec!(0.0001)),
370 UnixNanos::default(),
371 UnixNanos::default(),
372 )
373}
374
375#[must_use]
379pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
380 let target_venue = venue.unwrap_or(Venue::from("SIM"));
381 let instrument_id = InstrumentId::new(symbol, target_venue);
382 let base_currency = symbol.as_str().split('/').next().unwrap();
383 let quote_currency = symbol.as_str().split('/').next_back().unwrap();
384 let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
385 let price_increment = Price::new(1.0 / 10.0f64.powi(price_precision as i32), price_precision);
386 CurrencyPair::new(
387 instrument_id,
388 symbol,
389 Currency::from(base_currency),
390 Currency::from(quote_currency),
391 price_precision,
392 0,
393 price_increment,
394 Quantity::from("1"),
395 None,
396 Some(Quantity::from("1000")),
397 Some(Quantity::from("1000000")),
398 Some(Quantity::from("100")),
399 None,
400 None,
401 None,
402 None,
403 Some(dec!(0.03)),
404 Some(dec!(0.03)),
405 Some(dec!(0.00002)),
406 Some(dec!(0.00002)),
407 UnixNanos::default(),
408 UnixNanos::default(),
409 )
410}
411
412#[fixture]
413pub fn audusd_sim() -> CurrencyPair {
414 default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
415}
416
417#[fixture]
418pub fn gbpusd_sim() -> CurrencyPair {
419 default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
420}
421
422#[fixture]
423pub fn usdjpy_idealpro() -> CurrencyPair {
424 default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
425}
426
427#[fixture]
432pub fn equity_aapl() -> Equity {
433 Equity::new(
434 InstrumentId::from("AAPL.XNAS"),
435 Symbol::from("AAPL"),
436 Some(Ustr::from("US0378331005")),
437 Currency::from("USD"),
438 2,
439 Price::from("0.01"),
440 None,
441 None,
442 None,
443 None,
444 None,
445 None,
446 None,
447 None,
448 None,
449 UnixNanos::default(),
450 UnixNanos::default(),
451 )
452}
453
454pub fn futures_contract_es(
463 activation: Option<UnixNanos>,
464 expiration: Option<UnixNanos>,
465) -> FuturesContract {
466 let activation = activation.unwrap_or(UnixNanos::from(
467 Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
468 .unwrap()
469 .timestamp_nanos_opt()
470 .unwrap() as u64,
471 ));
472 let expiration = expiration.unwrap_or(UnixNanos::from(
473 Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0)
474 .unwrap()
475 .timestamp_nanos_opt()
476 .unwrap() as u64,
477 ));
478 FuturesContract::new(
479 InstrumentId::from("ESZ21.GLBX"),
480 Symbol::from("ESZ21"),
481 AssetClass::Index,
482 Some(Ustr::from("XCME")),
483 Ustr::from("ES"),
484 activation,
485 expiration,
486 Currency::USD(),
487 2,
488 Price::from("0.01"),
489 Quantity::from(1),
490 Quantity::from(1),
491 None,
492 None,
493 None,
494 None,
495 None,
496 None,
497 None,
498 None,
499 UnixNanos::default(),
500 UnixNanos::default(),
501 )
502}
503
504#[fixture]
509pub fn futures_spread_es() -> FuturesSpread {
510 let activation = Utc.with_ymd_and_hms(2022, 6, 21, 13, 30, 0).unwrap();
511 let expiration = Utc.with_ymd_and_hms(2024, 6, 21, 13, 30, 0).unwrap();
512 FuturesSpread::new(
513 InstrumentId::from("ESM4-ESU4.GLBX"),
514 Symbol::from("ESM4-ESU4"),
515 AssetClass::Index,
516 Some(Ustr::from("XCME")),
517 Ustr::from("ES"),
518 Ustr::from("EQ"),
519 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
520 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
521 Currency::USD(),
522 2,
523 Price::from("0.01"),
524 Quantity::from(1),
525 Quantity::from(1),
526 None,
527 None,
528 None,
529 None,
530 None,
531 None,
532 None,
533 None,
534 UnixNanos::default(),
535 UnixNanos::default(),
536 )
537}
538
539#[fixture]
544pub fn option_contract_appl() -> OptionContract {
545 let activation = Utc.with_ymd_and_hms(2021, 9, 17, 0, 0, 0).unwrap();
546 let expiration = Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0).unwrap();
547 OptionContract::new(
548 InstrumentId::from("AAPL211217C00150000.OPRA"),
549 Symbol::from("AAPL211217C00150000"),
550 AssetClass::Equity,
551 Some(Ustr::from("GMNI")),
552 Ustr::from("AAPL"),
553 OptionKind::Call,
554 Price::from("149.0"),
555 Currency::USD(),
556 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
557 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
558 2,
559 Price::from("0.01"),
560 Quantity::from(1),
561 Quantity::from(1),
562 None,
563 None,
564 None,
565 None,
566 None,
567 None,
568 None,
569 None,
570 UnixNanos::default(),
571 UnixNanos::default(),
572 )
573}
574
575#[fixture]
580pub fn option_spread() -> OptionSpread {
581 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
582 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
583 OptionSpread::new(
584 InstrumentId::from("UD:U$: GN 2534559.GLBX"),
585 Symbol::from("UD:U$: GN 2534559"),
586 AssetClass::FX,
587 Some(Ustr::from("XCME")),
588 Ustr::from("SR3"),
589 Ustr::from("GN"),
590 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
591 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
592 Currency::USD(),
593 2,
594 Price::from("0.01"),
595 Quantity::from(1),
596 Quantity::from(1),
597 None,
598 None,
599 None,
600 None,
601 None,
602 None,
603 None,
604 None,
605 UnixNanos::default(),
606 UnixNanos::default(),
607 )
608}
609
610#[fixture]
615pub fn betting() -> BettingInstrument {
616 let raw_symbol = Symbol::new("1-123456789");
617 let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR").as_str());
618 let event_type_id = 6423;
619 let event_type_name = Ustr::from("American Football");
620 let competition_id = 12282733;
621 let competition_name = Ustr::from("NFL");
622 let event_id = 29678534;
623 let event_name = Ustr::from("NFL");
624 let event_country_code = Ustr::from("GB");
625 let event_open_date = UnixNanos::from(
626 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
627 .unwrap()
628 .timestamp_nanos_opt()
629 .unwrap() as u64,
630 );
631 let betting_type = Ustr::from("ODDS");
632 let market_id = Ustr::from("1-123456789");
633 let market_name = Ustr::from("AFC Conference Winner");
634 let market_type = Ustr::from("SPECIAL");
635 let market_start_time = UnixNanos::from(
636 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
637 .unwrap()
638 .timestamp_nanos_opt()
639 .unwrap() as u64,
640 );
641 let selection_id = 50214;
642 let selection_name = Ustr::from("Kansas City Chiefs");
643 let selection_handicap = 0.0;
644 let currency = Currency::GBP();
645 let price_increment = Price::from("0.01");
646 let size_increment = Quantity::from("0.01");
647 let max_quantity = Some(Quantity::from("1000"));
648 let min_quantity = Some(Quantity::from("1"));
649 let max_notional = Some(Money::from("10000 GBP"));
650 let min_notional = Some(Money::from("10 GBP"));
651 let max_price = Some(Price::from("100.00"));
652 let min_price = Some(Price::from("1.00"));
653 let margin_init = Some(Decimal::from(1));
654 let margin_maint = Some(Decimal::from(1));
655 let maker_fee = Some(Decimal::from(0));
656 let taker_fee = Some(Decimal::from(0));
657 let ts_event = UnixNanos::default();
658 let ts_init = UnixNanos::default();
659
660 BettingInstrument::new(
661 id,
662 raw_symbol,
663 event_type_id,
664 event_type_name,
665 competition_id,
666 competition_name,
667 event_id,
668 event_name,
669 event_country_code,
670 event_open_date,
671 betting_type,
672 market_id,
673 market_name,
674 market_type,
675 market_start_time,
676 selection_id,
677 selection_name,
678 selection_handicap,
679 currency,
680 price_increment.precision,
681 size_increment.precision,
682 price_increment,
683 size_increment,
684 max_quantity,
685 min_quantity,
686 max_notional,
687 min_notional,
688 max_price,
689 min_price,
690 margin_init,
691 margin_maint,
692 maker_fee,
693 taker_fee,
694 ts_event,
695 ts_init,
696 )
697}
698
699#[fixture]
704pub fn binary_option() -> BinaryOption {
705 let raw_symbol = Symbol::new(
706 "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
707 );
708 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
709 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
710 let price_increment = Price::from("0.001");
711 let size_increment = Quantity::from("0.01");
712 BinaryOption::new(
713 InstrumentId::from("{raw_symbol}.POLYMARKET"),
714 raw_symbol,
715 AssetClass::Alternative,
716 Currency::USDC(),
717 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
718 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
719 price_increment.precision,
720 size_increment.precision,
721 price_increment,
722 size_increment,
723 None,
724 None,
725 None,
726 None,
727 None,
728 None,
729 None,
730 None,
731 None,
732 None,
733 None,
734 None,
735 UnixNanos::default(),
736 UnixNanos::default(),
737 )
738}