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