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]
293pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
294 let target_venue = venue.unwrap_or(Venue::from("SIM"));
295 let instrument_id = InstrumentId::new(symbol, target_venue);
296 let base_currency = symbol.as_str().split('/').next().unwrap();
297 let quote_currency = symbol.as_str().split('/').next_back().unwrap();
298 let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
299 let price_increment = Price::new(1.0 / 10.0f64, price_precision);
300 CurrencyPair::new(
301 instrument_id,
302 symbol,
303 Currency::from(base_currency),
304 Currency::from(quote_currency),
305 price_precision,
306 0,
307 price_increment,
308 Quantity::from("1"),
309 Some(Quantity::from("1000")),
310 Some(Quantity::from("1000000")),
311 Some(Quantity::from("100")),
312 None,
313 None,
314 None,
315 None,
316 Some(dec!(0.03)),
317 Some(dec!(0.03)),
318 Some(dec!(0.00002)),
319 Some(dec!(0.00002)),
320 UnixNanos::default(),
321 UnixNanos::default(),
322 )
323}
324
325#[fixture]
326pub fn audusd_sim() -> CurrencyPair {
327 default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
328}
329
330#[fixture]
331pub fn usdjpy_idealpro() -> CurrencyPair {
332 default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
333}
334
335#[fixture]
340pub fn equity_aapl() -> Equity {
341 Equity::new(
342 InstrumentId::from("AAPL.XNAS"),
343 Symbol::from("AAPL"),
344 Some(Ustr::from("US0378331005")),
345 Currency::from("USD"),
346 2,
347 Price::from("0.01"),
348 None,
349 None,
350 None,
351 None,
352 None,
353 None,
354 None,
355 None,
356 None,
357 UnixNanos::default(),
358 UnixNanos::default(),
359 )
360}
361
362pub fn futures_contract_es(
367 activation: Option<UnixNanos>,
368 expiration: Option<UnixNanos>,
369) -> FuturesContract {
370 let activation = activation.unwrap_or(UnixNanos::from(
371 Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
372 .unwrap()
373 .timestamp_nanos_opt()
374 .unwrap() as u64,
375 ));
376 let expiration = expiration.unwrap_or(UnixNanos::from(
377 Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0)
378 .unwrap()
379 .timestamp_nanos_opt()
380 .unwrap() as u64,
381 ));
382 FuturesContract::new(
383 InstrumentId::from("ESZ21.GLBX"),
384 Symbol::from("ESZ21"),
385 AssetClass::Index,
386 Some(Ustr::from("XCME")),
387 Ustr::from("ES"),
388 activation,
389 expiration,
390 Currency::USD(),
391 2,
392 Price::from("0.01"),
393 Quantity::from(1),
394 Quantity::from(1),
395 None,
396 None,
397 None,
398 None,
399 None,
400 None,
401 None,
402 None,
403 UnixNanos::default(),
404 UnixNanos::default(),
405 )
406}
407
408#[fixture]
413pub fn futures_spread_es() -> FuturesSpread {
414 let activation = Utc.with_ymd_and_hms(2022, 6, 21, 13, 30, 0).unwrap();
415 let expiration = Utc.with_ymd_and_hms(2024, 6, 21, 13, 30, 0).unwrap();
416 FuturesSpread::new(
417 InstrumentId::from("ESM4-ESU4.GLBX"),
418 Symbol::from("ESM4-ESU4"),
419 AssetClass::Index,
420 Some(Ustr::from("XCME")),
421 Ustr::from("ES"),
422 Ustr::from("EQ"),
423 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
424 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
425 Currency::USD(),
426 2,
427 Price::from("0.01"),
428 Quantity::from(1),
429 Quantity::from(1),
430 None,
431 None,
432 None,
433 None,
434 None,
435 None,
436 None,
437 None,
438 UnixNanos::default(),
439 UnixNanos::default(),
440 )
441}
442
443#[fixture]
448pub fn option_contract_appl() -> OptionContract {
449 let activation = Utc.with_ymd_and_hms(2021, 9, 17, 0, 0, 0).unwrap();
450 let expiration = Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0).unwrap();
451 OptionContract::new(
452 InstrumentId::from("AAPL211217C00150000.OPRA"),
453 Symbol::from("AAPL211217C00150000"),
454 AssetClass::Equity,
455 Some(Ustr::from("GMNI")), Ustr::from("AAPL"),
457 OptionKind::Call,
458 Price::from("149.0"),
459 Currency::USD(),
460 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
461 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
462 2,
463 Price::from("0.01"),
464 Quantity::from(1),
465 Quantity::from(1),
466 None,
467 None,
468 None,
469 None,
470 None,
471 None,
472 None,
473 None,
474 UnixNanos::default(),
475 UnixNanos::default(),
476 )
477}
478
479#[fixture]
484pub fn option_spread() -> OptionSpread {
485 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
486 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
487 OptionSpread::new(
488 InstrumentId::from("UD:U$: GN 2534559.GLBX"),
489 Symbol::from("UD:U$: GN 2534559"),
490 AssetClass::FX,
491 Some(Ustr::from("XCME")),
492 Ustr::from("SR3"), Ustr::from("GN"),
494 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
495 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
496 Currency::USD(),
497 2,
498 Price::from("0.01"),
499 Quantity::from(1),
500 Quantity::from(1),
501 None,
502 None,
503 None,
504 None,
505 None,
506 None,
507 None,
508 None,
509 UnixNanos::default(),
510 UnixNanos::default(),
511 )
512}
513
514#[fixture]
519pub fn betting() -> BettingInstrument {
520 let raw_symbol = Symbol::new("1-123456789");
521 let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR").as_str());
522 let event_type_id = 6423;
523 let event_type_name = Ustr::from("American Football");
524 let competition_id = 12282733;
525 let competition_name = Ustr::from("NFL");
526 let event_id = 29678534;
527 let event_name = Ustr::from("NFL");
528 let event_country_code = Ustr::from("GB");
529 let event_open_date = UnixNanos::from(
530 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
531 .unwrap()
532 .timestamp_nanos_opt()
533 .unwrap() as u64,
534 );
535 let betting_type = Ustr::from("ODDS");
536 let market_id = Ustr::from("1-123456789");
537 let market_name = Ustr::from("AFC Conference Winner");
538 let market_type = Ustr::from("SPECIAL");
539 let market_start_time = UnixNanos::from(
540 Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
541 .unwrap()
542 .timestamp_nanos_opt()
543 .unwrap() as u64,
544 );
545 let selection_id = 50214;
546 let selection_name = Ustr::from("Kansas City Chiefs");
547 let selection_handicap = 0.0; let currency = Currency::GBP();
549 let price_increment = Price::from("0.01");
550 let size_increment = Quantity::from("0.01");
551 let max_quantity = Some(Quantity::from("1000"));
552 let min_quantity = Some(Quantity::from("1"));
553 let max_notional = Some(Money::from("10000 GBP"));
554 let min_notional = Some(Money::from("10 GBP"));
555 let max_price = Some(Price::from("100.00"));
556 let min_price = Some(Price::from("1.00"));
557 let margin_init = Some(Decimal::from(1));
558 let margin_maint = Some(Decimal::from(1));
559 let maker_fee = Some(Decimal::from(0));
560 let taker_fee = Some(Decimal::from(0));
561 let ts_event = UnixNanos::default(); let ts_init = UnixNanos::default(); BettingInstrument::new(
565 id,
566 raw_symbol,
567 event_type_id,
568 event_type_name,
569 competition_id,
570 competition_name,
571 event_id,
572 event_name,
573 event_country_code,
574 event_open_date,
575 betting_type,
576 market_id,
577 market_name,
578 market_type,
579 market_start_time,
580 selection_id,
581 selection_name,
582 selection_handicap,
583 currency,
584 price_increment.precision,
585 size_increment.precision,
586 price_increment,
587 size_increment,
588 max_quantity,
589 min_quantity,
590 max_notional,
591 min_notional,
592 max_price,
593 min_price,
594 margin_init,
595 margin_maint,
596 maker_fee,
597 taker_fee,
598 ts_event,
599 ts_init,
600 )
601}
602
603#[fixture]
608pub fn binary_option() -> BinaryOption {
609 let raw_symbol = Symbol::new(
610 "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
611 );
612 let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
613 let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
614 let price_increment = Price::from("0.001");
615 let size_increment = Quantity::from("0.01");
616 BinaryOption::new(
617 InstrumentId::from("{raw_symbol}.POLYMARKET"),
618 raw_symbol,
619 AssetClass::Alternative,
620 Currency::USDC(),
621 UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
622 UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
623 price_increment.precision,
624 size_increment.precision,
625 price_increment,
626 size_increment,
627 None,
628 None,
629 None,
630 None,
631 None,
632 None,
633 None,
634 None,
635 None,
636 None,
637 None,
638 None,
639 UnixNanos::default(),
640 UnixNanos::default(),
641 )
642}