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]
101pub fn crypto_option_btc_deribit(
102 #[default(3)] price_precision: u8,
103 #[default(1)] size_precision: u8,
104 #[default(Price::from("0.001"))] price_increment: Price,
105 #[default(Quantity::from("0.1"))] size_increment: Quantity,
106) -> CryptoOption {
107 let activation = UnixNanos::from(1_671_696_002_000_000_000);
108 let expiration = UnixNanos::from(1_673_596_800_000_000_000);
109 CryptoOption::new(
110 InstrumentId::from("BTC-13JAN23-16000-P.DERIBIT"),
111 Symbol::from("BTC-13JAN23-16000-P"),
112 Currency::from("BTC"),
113 Currency::from("USD"),
114 Currency::from("BTC"),
115 false,
116 OptionKind::Put,
117 Price::from("16000.000"),
118 activation,
119 expiration,
120 price_precision,
121 size_precision,
122 price_increment,
123 size_increment,
124 Some(Quantity::from(1)),
125 Some(Quantity::from("9000.0")),
126 Some(Quantity::from("0.1")),
127 None,
128 Some(Money::new(10.00, Currency::from("USD"))),
129 None,
130 None,
131 None,
132 None,
133 Some(dec!(0.0003)),
134 Some(dec!(0.0003)),
135 0.into(),
136 0.into(),
137 )
138}
139
140#[fixture]
145pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
146 CryptoPerpetual::new(
147 InstrumentId::from("ETHUSDT-PERP.BINANCE"),
148 Symbol::from("ETHUSDT"),
149 Currency::from("ETH"),
150 Currency::from("USDT"),
151 Currency::from("USDT"),
152 false,
153 2,
154 3,
155 Price::from("0.01"),
156 Quantity::from("0.001"),
157 None,
158 None,
159 Some(Quantity::from("10000.0")),
160 Some(Quantity::from("0.001")),
161 None,
162 Some(Money::new(10.00, Currency::from("USDT"))),
163 Some(Price::from("15000.00")),
164 Some(Price::from("1.0")),
165 Some(dec!(1.0)),
166 Some(dec!(0.35)),
167 Some(dec!(0.0002)),
168 Some(dec!(0.0004)),
169 UnixNanos::default(),
170 UnixNanos::default(),
171 )
172}
173
174#[fixture]
175pub fn xbtusd_bitmex() -> CryptoPerpetual {
176 CryptoPerpetual::new(
177 InstrumentId::from("BTCUSDT.BITMEX"),
178 Symbol::from("XBTUSD"),
179 Currency::BTC(),
180 Currency::USD(),
181 Currency::BTC(),
182 true,
183 1,
184 0,
185 Price::from("0.5"),
186 Quantity::from("1"),
187 None,
188 None,
189 None,
190 None,
191 Some(Money::from("10000000 USD")),
192 Some(Money::from("1 USD")),
193 Some(Price::from("10000000")),
194 Some(Price::from("0.01")),
195 Some(dec!(0.01)),
196 Some(dec!(0.0035)),
197 Some(dec!(-0.00025)),
198 Some(dec!(0.00075)),
199 UnixNanos::default(),
200 UnixNanos::default(),
201 )
202}
203
204#[fixture]
205pub fn ethusdt_bitmex() -> CryptoPerpetual {
206 CryptoPerpetual::new(
207 InstrumentId::from("ETHUSD.BITMEX"),
208 Symbol::from("ETHUSD"),
209 Currency::ETH(),
210 Currency::USD(),
211 Currency::ETH(),
212 true,
213 2,
214 0,
215 Price::from("0.05"),
216 Quantity::from("1"),
217 None,
218 None,
219 None,
220 None,
221 None,
222 None,
223 Some(Price::from("10000000")),
224 Some(Price::from("0.01")),
225 Some(dec!(0.01)),
226 Some(dec!(0.0035)),
227 Some(dec!(-0.00025)),
228 Some(dec!(0.00075)),
229 UnixNanos::default(),
230 UnixNanos::default(),
231 )
232}
233
234#[fixture]
239pub fn currency_pair_btcusdt() -> CurrencyPair {
240 CurrencyPair::new(
241 InstrumentId::from("BTCUSDT.BINANCE"),
242 Symbol::from("BTCUSDT"),
243 Currency::from("BTC"),
244 Currency::from("USDT"),
245 2,
246 6,
247 Price::from("0.01"),
248 Quantity::from("0.000001"),
249 None,
250 Some(Quantity::from("9000")),
251 Some(Quantity::from("0.000001")),
252 None,
253 None,
254 Some(Price::from("1000000")),
255 Some(Price::from("0.01")),
256 Some(dec!(0.001)),
257 Some(dec!(0.001)),
258 Some(dec!(0.001)),
259 Some(dec!(0.001)),
260 UnixNanos::default(),
261 UnixNanos::default(),
262 )
263}
264
265#[fixture]
266pub fn currency_pair_ethusdt() -> CurrencyPair {
267 CurrencyPair::new(
268 InstrumentId::from("ETHUSDT.BINANCE"),
269 Symbol::from("ETHUSDT"),
270 Currency::from("ETH"),
271 Currency::from("USDT"),
272 2,
273 5,
274 Price::from("0.01"),
275 Quantity::from("0.00001"),
276 None,
277 Some(Quantity::from("9000")),
278 Some(Quantity::from("0.00001")),
279 None,
280 None,
281 Some(Price::from("1000000")),
282 Some(Price::from("0.01")),
283 Some(dec!(0.01)),
284 Some(dec!(0.0035)),
285 Some(dec!(0.0001)),
286 Some(dec!(0.0001)),
287 UnixNanos::default(),
288 UnixNanos::default(),
289 )
290}
291
292#[must_use]
296pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
297 let target_venue = venue.unwrap_or(Venue::from("SIM"));
298 let instrument_id = InstrumentId::new(symbol, target_venue);
299 let base_currency = symbol.as_str().split('/').next().unwrap();
300 let quote_currency = symbol.as_str().split('/').next_back().unwrap();
301 let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
302 let price_increment = Price::new(1.0 / 10.0f64, price_precision);
303 CurrencyPair::new(
304 instrument_id,
305 symbol,
306 Currency::from(base_currency),
307 Currency::from(quote_currency),
308 price_precision,
309 0,
310 price_increment,
311 Quantity::from("1"),
312 Some(Quantity::from("1000")),
313 Some(Quantity::from("1000000")),
314 Some(Quantity::from("100")),
315 None,
316 None,
317 None,
318 None,
319 Some(dec!(0.03)),
320 Some(dec!(0.03)),
321 Some(dec!(0.00002)),
322 Some(dec!(0.00002)),
323 UnixNanos::default(),
324 UnixNanos::default(),
325 )
326}
327
328#[fixture]
329pub fn audusd_sim() -> CurrencyPair {
330 default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
331}
332
333#[fixture]
334pub fn gbpusd_sim() -> CurrencyPair {
335 default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
336}
337
338#[fixture]
339pub fn usdjpy_idealpro() -> CurrencyPair {
340 default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
341}
342
343#[fixture]
348pub fn equity_aapl() -> Equity {
349 Equity::new(
350 InstrumentId::from("AAPL.XNAS"),
351 Symbol::from("AAPL"),
352 Some(Ustr::from("US0378331005")),
353 Currency::from("USD"),
354 2,
355 Price::from("0.01"),
356 None,
357 None,
358 None,
359 None,
360 None,
361 None,
362 None,
363 None,
364 None,
365 UnixNanos::default(),
366 UnixNanos::default(),
367 )
368}
369
370pub fn futures_contract_es(
379 activation: Option<UnixNanos>,
380 expiration: Option<UnixNanos>,
381) -> FuturesContract {
382 let activation = activation.unwrap_or(UnixNanos::from(
383 Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
384 .unwrap()
385 .timestamp_nanos_opt()
386 .unwrap() as u64,
387 ));
388 let expiration = expiration.unwrap_or(UnixNanos::from(
389 Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0)
390 .unwrap()
391 .timestamp_nanos_opt()
392 .unwrap() as u64,
393 ));
394 FuturesContract::new(
395 InstrumentId::from("ESZ21.GLBX"),
396 Symbol::from("ESZ21"),
397 AssetClass::Index,
398 Some(Ustr::from("XCME")),
399 Ustr::from("ES"),
400 activation,
401 expiration,
402 Currency::USD(),
403 2,
404 Price::from("0.01"),
405 Quantity::from(1),
406 Quantity::from(1),
407 None,
408 None,
409 None,
410 None,
411 None,
412 None,
413 None,
414 None,
415 UnixNanos::default(),
416 UnixNanos::default(),
417 )
418}
419
420#[fixture]
425pub fn futures_spread_es() -> FuturesSpread {
426 let activation = Utc.with_ymd_and_hms(2022, 6, 21, 13, 30, 0).unwrap();
427 let expiration = Utc.with_ymd_and_hms(2024, 6, 21, 13, 30, 0).unwrap();
428 FuturesSpread::new(
429 InstrumentId::from("ESM4-ESU4.GLBX"),
430 Symbol::from("ESM4-ESU4"),
431 AssetClass::Index,
432 Some(Ustr::from("XCME")),
433 Ustr::from("ES"),
434 Ustr::from("EQ"),
435 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
436 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
437 Currency::USD(),
438 2,
439 Price::from("0.01"),
440 Quantity::from(1),
441 Quantity::from(1),
442 None,
443 None,
444 None,
445 None,
446 None,
447 None,
448 None,
449 None,
450 UnixNanos::default(),
451 UnixNanos::default(),
452 )
453}
454
455#[fixture]
460pub fn option_contract_appl() -> OptionContract {
461 let activation = Utc.with_ymd_and_hms(2021, 9, 17, 0, 0, 0).unwrap();
462 let expiration = Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0).unwrap();
463 OptionContract::new(
464 InstrumentId::from("AAPL211217C00150000.OPRA"),
465 Symbol::from("AAPL211217C00150000"),
466 AssetClass::Equity,
467 Some(Ustr::from("GMNI")), Ustr::from("AAPL"),
469 OptionKind::Call,
470 Price::from("149.0"),
471 Currency::USD(),
472 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
473 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
474 2,
475 Price::from("0.01"),
476 Quantity::from(1),
477 Quantity::from(1),
478 None,
479 None,
480 None,
481 None,
482 None,
483 None,
484 None,
485 None,
486 UnixNanos::default(),
487 UnixNanos::default(),
488 )
489}
490
491#[fixture]
496pub fn option_spread() -> OptionSpread {
497 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
498 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
499 OptionSpread::new(
500 InstrumentId::from("UD:U$: GN 2534559.GLBX"),
501 Symbol::from("UD:U$: GN 2534559"),
502 AssetClass::FX,
503 Some(Ustr::from("XCME")),
504 Ustr::from("SR3"), Ustr::from("GN"),
506 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
507 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
508 Currency::USD(),
509 2,
510 Price::from("0.01"),
511 Quantity::from(1),
512 Quantity::from(1),
513 None,
514 None,
515 None,
516 None,
517 None,
518 None,
519 None,
520 None,
521 UnixNanos::default(),
522 UnixNanos::default(),
523 )
524}
525
526#[fixture]
531pub fn betting() -> BettingInstrument {
532 let raw_symbol = Symbol::new("1-123456789");
533 let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR").as_str());
534 let event_type_id = 6423;
535 let event_type_name = Ustr::from("American Football");
536 let competition_id = 12282733;
537 let competition_name = Ustr::from("NFL");
538 let event_id = 29678534;
539 let event_name = Ustr::from("NFL");
540 let event_country_code = Ustr::from("GB");
541 let event_open_date = UnixNanos::from(
542 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
543 .unwrap()
544 .timestamp_nanos_opt()
545 .unwrap() as u64,
546 );
547 let betting_type = Ustr::from("ODDS");
548 let market_id = Ustr::from("1-123456789");
549 let market_name = Ustr::from("AFC Conference Winner");
550 let market_type = Ustr::from("SPECIAL");
551 let market_start_time = UnixNanos::from(
552 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
553 .unwrap()
554 .timestamp_nanos_opt()
555 .unwrap() as u64,
556 );
557 let selection_id = 50214;
558 let selection_name = Ustr::from("Kansas City Chiefs");
559 let selection_handicap = 0.0; let currency = Currency::GBP();
561 let price_increment = Price::from("0.01");
562 let size_increment = Quantity::from("0.01");
563 let max_quantity = Some(Quantity::from("1000"));
564 let min_quantity = Some(Quantity::from("1"));
565 let max_notional = Some(Money::from("10000 GBP"));
566 let min_notional = Some(Money::from("10 GBP"));
567 let max_price = Some(Price::from("100.00"));
568 let min_price = Some(Price::from("1.00"));
569 let margin_init = Some(Decimal::from(1));
570 let margin_maint = Some(Decimal::from(1));
571 let maker_fee = Some(Decimal::from(0));
572 let taker_fee = Some(Decimal::from(0));
573 let ts_event = UnixNanos::default(); let ts_init = UnixNanos::default(); BettingInstrument::new(
577 id,
578 raw_symbol,
579 event_type_id,
580 event_type_name,
581 competition_id,
582 competition_name,
583 event_id,
584 event_name,
585 event_country_code,
586 event_open_date,
587 betting_type,
588 market_id,
589 market_name,
590 market_type,
591 market_start_time,
592 selection_id,
593 selection_name,
594 selection_handicap,
595 currency,
596 price_increment.precision,
597 size_increment.precision,
598 price_increment,
599 size_increment,
600 max_quantity,
601 min_quantity,
602 max_notional,
603 min_notional,
604 max_price,
605 min_price,
606 margin_init,
607 margin_maint,
608 maker_fee,
609 taker_fee,
610 ts_event,
611 ts_init,
612 )
613}
614
615#[fixture]
620pub fn binary_option() -> BinaryOption {
621 let raw_symbol = Symbol::new(
622 "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
623 );
624 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
625 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
626 let price_increment = Price::from("0.001");
627 let size_increment = Quantity::from("0.01");
628 BinaryOption::new(
629 InstrumentId::from("{raw_symbol}.POLYMARKET"),
630 raw_symbol,
631 AssetClass::Alternative,
632 Currency::USDC(),
633 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
634 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
635 price_increment.precision,
636 size_increment.precision,
637 price_increment,
638 size_increment,
639 None,
640 None,
641 None,
642 None,
643 None,
644 None,
645 None,
646 None,
647 None,
648 None,
649 None,
650 None,
651 UnixNanos::default(),
652 UnixNanos::default(),
653 )
654}