Skip to main content

nautilus_model/instruments/
stubs.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use jiff::{Timestamp, civil::Date, tz::Offset};
17use nautilus_core::UnixNanos;
18use rstest::fixture;
19use rust_decimal::Decimal;
20use rust_decimal_macros::dec;
21use ustr::Ustr;
22
23use super::{
24    CryptoOption, betting::BettingInstrument, binary_option::BinaryOption, cfd::Cfd,
25    commodity::Commodity, futures_spread::FuturesSpread, index_instrument::IndexInstrument,
26    option_spread::OptionSpread, perpetual_contract::PerpetualContract,
27    synthetic::SyntheticInstrument, tokenized_asset::TokenizedAsset,
28};
29use crate::{
30    enums::{AssetClass, OptionKind},
31    identifiers::{InstrumentId, Symbol, Venue},
32    instruments::{
33        CryptoFuture, CryptoFuturesSpread, CryptoOptionSpread, CryptoPerpetual, CurrencyPair,
34        Equity, FuturesContract, OptionContract,
35    },
36    types::{Currency, Money, Price, Quantity},
37};
38
39fn timestamp(year: i16, month: i8, day: i8, hour: i8, minute: i8, second: i8) -> Timestamp {
40    let datetime = Date::new(year, month, day)
41        .expect("valid date")
42        .at(hour, minute, second, 0);
43    Offset::UTC.to_timestamp(datetime).expect("valid timestamp")
44}
45
46impl Default for SyntheticInstrument {
47    /// Creates a new default [`SyntheticInstrument`] instance for testing.
48    fn default() -> Self {
49        let btc_binance = InstrumentId::from("BTC.BINANCE");
50        let ltc_binance = InstrumentId::from("LTC.BINANCE");
51        let formula = "(BTC.BINANCE + LTC.BINANCE) / 2.0";
52        Self::builder()
53            .symbol(Symbol::new("BTC-LTC"))
54            .price_precision(2)
55            .components(vec![btc_binance, ltc_binance])
56            .formula(formula)
57            .ts_event(0.into())
58            .ts_init(0.into())
59            .build()
60            .unwrap()
61    }
62}
63
64#[fixture]
65pub fn crypto_future_btcusdt(
66    #[default(2)] price_precision: u8,
67    #[default(6)] size_precision: u8,
68    #[default(Price::from("0.01"))] price_increment: Price,
69    #[default(Quantity::from("0.000001"))] size_increment: Quantity,
70) -> CryptoFuture {
71    let activation = timestamp(2014, 4, 8, 0, 0, 0);
72    let expiration = timestamp(2014, 7, 8, 0, 0, 0);
73    CryptoFuture::builder()
74        .instrument_id(InstrumentId::from("ETHUSDT-123.BINANCE"))
75        .raw_symbol(Symbol::from("BTCUSDT"))
76        .underlying(Currency::from("BTC"))
77        .quote_currency(Currency::from("USDT"))
78        .settlement_currency(Currency::from("USDT"))
79        .is_inverse(false)
80        .activation_ns(UnixNanos::from(activation))
81        .expiration_ns(UnixNanos::from(expiration))
82        .price_precision(price_precision)
83        .size_precision(size_precision)
84        .price_increment(price_increment)
85        .size_increment(size_increment)
86        .max_quantity(Quantity::from("9000.0"))
87        .min_quantity(Quantity::from("0.000001"))
88        .min_notional(Money::new(10.00, Currency::from("USDT")))
89        .max_price(Price::from("1000000.00"))
90        .min_price(Price::from("0.01"))
91        .ts_event(0.into())
92        .ts_init(0.into())
93        .build()
94        .unwrap()
95}
96
97#[fixture]
98pub fn ethbtc_quanto(
99    #[default(5)] price_precision: u8,
100    #[default(3)] size_precision: u8,
101    #[default(Price::from("0.00001"))] price_increment: Price,
102    #[default(Quantity::from("0.001"))] size_increment: Quantity,
103) -> CryptoFuture {
104    let activation = timestamp(2014, 4, 8, 0, 0, 0);
105    let expiration = timestamp(2014, 7, 8, 0, 0, 0);
106    CryptoFuture::builder()
107        .instrument_id(InstrumentId::from("ETHBTC-123.BINANCE"))
108        .raw_symbol(Symbol::from("ETHBTC"))
109        .underlying(Currency::from("ETH"))
110        .quote_currency(Currency::from("BTC"))
111        .settlement_currency(Currency::from("USDT"))
112        .is_inverse(false)
113        .activation_ns(UnixNanos::from(activation))
114        .expiration_ns(UnixNanos::from(expiration))
115        .price_precision(price_precision)
116        .size_precision(size_precision)
117        .price_increment(price_increment)
118        .size_increment(size_increment)
119        .max_quantity(Quantity::from("9000.0"))
120        .min_quantity(Quantity::from("0.001"))
121        .min_notional(Money::new(1.0, Currency::from("USDT")))
122        .max_price(Price::from("1.0"))
123        .min_price(Price::from("0.00001"))
124        .ts_event(0.into())
125        .ts_init(0.into())
126        .build()
127        .unwrap()
128}
129
130#[fixture]
131pub fn xbtusd_inverse_perp(
132    // One-decimal tick (0.5 USD) and integer contract size
133    #[default(1)] price_precision: u8,
134    #[default(0)] size_precision: u8,
135    #[default(Price::from("0.5"))] price_increment: Price,
136    #[default(Quantity::from("1"))] size_increment: Quantity,
137) -> CryptoPerpetual {
138    CryptoPerpetual::builder()
139        // BitMEX uses XBT for BTC; keep the "-PERP" suffix for clarity
140        .instrument_id(InstrumentId::from("XBTUSD-PERP.BITMEX"))
141        .raw_symbol(Symbol::from("XBTUSD"))
142        // base
143        .base_currency(Currency::BTC())
144        // quote
145        .quote_currency(Currency::USD())
146        // settlement (inverse)
147        .settlement_currency(Currency::BTC())
148        .is_inverse(true)
149        .price_precision(price_precision)
150        .size_precision(size_precision)
151        .price_increment(price_increment)
152        .size_increment(size_increment)
153        .lot_size(Quantity::from("1"))
154        .max_notional(Money::from("10000000 USD"))
155        .min_notional(Money::from("1 USD"))
156        .max_price(Price::from("10000000"))
157        .min_price(Price::from("0.01"))
158        .margin_init(dec!(0.01))
159        .margin_maint(dec!(0.0035))
160        // maker_fee (rebate)
161        .maker_fee(dec!(-0.00025))
162        .taker_fee(dec!(0.00075))
163        .ts_event(UnixNanos::default())
164        .ts_init(UnixNanos::default())
165        .build()
166        .unwrap()
167}
168
169#[fixture]
170pub fn crypto_option_btc_deribit(
171    #[default(3)] price_precision: u8,
172    #[default(1)] size_precision: u8,
173    #[default(Price::from("0.001"))] price_increment: Price,
174    #[default(Quantity::from("0.1"))] size_increment: Quantity,
175) -> CryptoOption {
176    let activation = UnixNanos::from(1_671_696_002_000_000_000);
177    let expiration = UnixNanos::from(1_673_596_800_000_000_000);
178    CryptoOption::builder()
179        .instrument_id(InstrumentId::from("BTC-13JAN23-16000-P.DERIBIT"))
180        .raw_symbol(Symbol::from("BTC-13JAN23-16000-P"))
181        .underlying(Currency::from("BTC"))
182        .quote_currency(Currency::from("USD"))
183        .settlement_currency(Currency::from("BTC"))
184        .is_inverse(false)
185        .option_kind(OptionKind::Put)
186        .strike_price(Price::from("16000.000"))
187        .activation_ns(activation)
188        .expiration_ns(expiration)
189        .price_precision(price_precision)
190        .size_precision(size_precision)
191        .price_increment(price_increment)
192        .size_increment(size_increment)
193        .multiplier(Quantity::from(1))
194        .lot_size(Quantity::from(1))
195        .max_quantity(Quantity::from("9000.0"))
196        .min_quantity(Quantity::from("0.1"))
197        .min_notional(Money::new(10.00, Currency::from("USD")))
198        .maker_fee(dec!(0.0003))
199        .taker_fee(dec!(0.0003))
200        .ts_event(0.into())
201        .ts_init(0.into())
202        .build()
203        .unwrap()
204}
205
206#[fixture]
207pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
208    CryptoPerpetual::builder()
209        .instrument_id(InstrumentId::from("ETHUSDT-PERP.BINANCE"))
210        .raw_symbol(Symbol::from("ETHUSDT"))
211        .base_currency(Currency::from("ETH"))
212        .quote_currency(Currency::from("USDT"))
213        .settlement_currency(Currency::from("USDT"))
214        .is_inverse(false)
215        .price_precision(2)
216        .size_precision(3)
217        .price_increment(Price::from("0.01"))
218        .size_increment(Quantity::from("0.001"))
219        .max_quantity(Quantity::from("10000.0"))
220        .min_quantity(Quantity::from("0.001"))
221        .min_notional(Money::new(10.00, Currency::from("USDT")))
222        .max_price(Price::from("15000.00"))
223        .min_price(Price::from("1.0"))
224        .margin_init(dec!(1.0))
225        .margin_maint(dec!(0.35))
226        .maker_fee(dec!(0.0002))
227        .taker_fee(dec!(0.0004))
228        .ts_event(UnixNanos::default())
229        .ts_init(UnixNanos::default())
230        .build()
231        .unwrap()
232}
233
234#[fixture]
235pub fn xbtusd_bitmex() -> CryptoPerpetual {
236    CryptoPerpetual::builder()
237        .instrument_id(InstrumentId::from("BTCUSDT.BITMEX"))
238        .raw_symbol(Symbol::from("XBTUSD"))
239        .base_currency(Currency::BTC())
240        .quote_currency(Currency::USD())
241        .settlement_currency(Currency::BTC())
242        .is_inverse(true)
243        .price_precision(1)
244        .size_precision(0)
245        .price_increment(Price::from("0.5"))
246        .size_increment(Quantity::from("1"))
247        .max_notional(Money::from("10000000 USD"))
248        .min_notional(Money::from("1 USD"))
249        .max_price(Price::from("10000000"))
250        .min_price(Price::from("0.01"))
251        .margin_init(dec!(0.01))
252        .margin_maint(dec!(0.0035))
253        .maker_fee(dec!(-0.00025))
254        .taker_fee(dec!(0.00075))
255        .ts_event(UnixNanos::default())
256        .ts_init(UnixNanos::default())
257        .build()
258        .unwrap()
259}
260
261#[fixture]
262pub fn ethusdt_bitmex() -> CryptoPerpetual {
263    CryptoPerpetual::builder()
264        .instrument_id(InstrumentId::from("ETHUSD.BITMEX"))
265        .raw_symbol(Symbol::from("ETHUSD"))
266        .base_currency(Currency::ETH())
267        .quote_currency(Currency::USD())
268        .settlement_currency(Currency::ETH())
269        .is_inverse(true)
270        .price_precision(2)
271        .size_precision(0)
272        .price_increment(Price::from("0.05"))
273        .size_increment(Quantity::from("1"))
274        .max_price(Price::from("10000000"))
275        .min_price(Price::from("0.01"))
276        .margin_init(dec!(0.01))
277        .margin_maint(dec!(0.0035))
278        .maker_fee(dec!(-0.00025))
279        .taker_fee(dec!(0.00075))
280        .ts_event(UnixNanos::default())
281        .ts_init(UnixNanos::default())
282        .build()
283        .unwrap()
284}
285
286#[fixture]
287pub fn currency_pair_btcusdt() -> CurrencyPair {
288    CurrencyPair::builder()
289        .instrument_id(InstrumentId::from("BTCUSDT.BINANCE"))
290        .raw_symbol(Symbol::from("BTCUSDT"))
291        .base_currency(Currency::from("BTC"))
292        .quote_currency(Currency::from("USDT"))
293        .price_precision(2)
294        .size_precision(6)
295        .price_increment(Price::from("0.01"))
296        .size_increment(Quantity::from("0.000001"))
297        .max_quantity(Quantity::from("9000"))
298        .min_quantity(Quantity::from("0.000001"))
299        .max_price(Price::from("1000000"))
300        .min_price(Price::from("0.01"))
301        .margin_init(dec!(0.001))
302        .margin_maint(dec!(0.001))
303        .maker_fee(dec!(0.001))
304        .taker_fee(dec!(0.001))
305        .ts_event(UnixNanos::default())
306        .ts_init(UnixNanos::default())
307        .build()
308        .unwrap()
309}
310
311#[fixture]
312pub fn currency_pair_ethusdt() -> CurrencyPair {
313    CurrencyPair::builder()
314        .instrument_id(InstrumentId::from("ETHUSDT.BINANCE"))
315        .raw_symbol(Symbol::from("ETHUSDT"))
316        .base_currency(Currency::from("ETH"))
317        .quote_currency(Currency::from("USDT"))
318        .price_precision(2)
319        .size_precision(5)
320        .price_increment(Price::from("0.01"))
321        .size_increment(Quantity::from("0.00001"))
322        .max_quantity(Quantity::from("9000"))
323        .min_quantity(Quantity::from("0.00001"))
324        .max_price(Price::from("1000000"))
325        .min_price(Price::from("0.01"))
326        .margin_init(dec!(0.01))
327        .margin_maint(dec!(0.0035))
328        .maker_fee(dec!(0.0001))
329        .taker_fee(dec!(0.0001))
330        .ts_event(UnixNanos::default())
331        .ts_init(UnixNanos::default())
332        .build()
333        .unwrap()
334}
335
336/// # Panics
337///
338/// Panics if `symbol` does not contain a '/' delimiter.
339#[must_use]
340pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
341    let target_venue = venue.unwrap_or(Venue::from("SIM"));
342    let instrument_id = InstrumentId::new(symbol, target_venue);
343    let base_currency = symbol.as_str().split('/').next().unwrap();
344    let quote_currency = symbol.as_str().split('/').next_back().unwrap();
345    let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
346    let tick_scheme = if quote_currency == "JPY" {
347        "FOREX_3DECIMAL"
348    } else {
349        "FOREX_5DECIMAL"
350    };
351    let price_increment = Price::new(
352        1.0 / 10.0f64.powi(i32::from(price_precision)),
353        price_precision,
354    );
355    CurrencyPair::builder()
356        .instrument_id(instrument_id)
357        .raw_symbol(symbol)
358        .base_currency(Currency::from(base_currency))
359        .quote_currency(Currency::from(quote_currency))
360        .price_precision(price_precision)
361        .size_precision(0)
362        .price_increment(price_increment)
363        .size_increment(Quantity::from("1"))
364        .lot_size(Quantity::from("1000"))
365        .max_quantity(Quantity::from("1000000"))
366        .min_quantity(Quantity::from("100"))
367        .margin_init(dec!(0.03))
368        .margin_maint(dec!(0.03))
369        .maker_fee(dec!(0.00002))
370        .taker_fee(dec!(0.00002))
371        .tick_scheme(Ustr::from(tick_scheme))
372        .ts_event(UnixNanos::default())
373        .ts_init(UnixNanos::default())
374        .build()
375        .unwrap()
376}
377
378#[fixture]
379pub fn audusd_sim() -> CurrencyPair {
380    default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
381}
382
383#[fixture]
384pub fn gbpusd_sim() -> CurrencyPair {
385    default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
386}
387
388#[fixture]
389pub fn usdjpy_idealpro() -> CurrencyPair {
390    default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
391}
392
393#[fixture]
394pub fn equity_aapl() -> Equity {
395    Equity::builder()
396        .instrument_id(InstrumentId::from("AAPL.XNAS"))
397        .raw_symbol(Symbol::from("AAPL"))
398        .isin(Ustr::from("US0378331005"))
399        .currency(Currency::from("USD"))
400        .price_precision(2)
401        .price_increment(Price::from("0.01"))
402        .ts_event(UnixNanos::default())
403        .ts_init(UnixNanos::default())
404        .build()
405        .unwrap()
406}
407
408/// AAPL equity with ITCH-compatible precision (`price_precision=4`).
409///
410/// # Panics
411///
412/// Panics if the instrument definition is invalid.
413#[must_use]
414pub fn equity_aapl_itch() -> Equity {
415    Equity::builder()
416        .instrument_id(InstrumentId::from("AAPL.XNAS"))
417        .raw_symbol(Symbol::from("AAPL"))
418        .isin(Ustr::from("US0378331005"))
419        .currency(Currency::from("USD"))
420        .price_precision(4)
421        .price_increment(Price::from("0.0001"))
422        .ts_event(UnixNanos::default())
423        .ts_init(UnixNanos::default())
424        .build()
425        .unwrap()
426}
427
428/// # Panics
429///
430/// Panics if constructing the activation or expiration timestamp fails,
431/// e.g., if the provided dates are invalid or timestamp conversion returns `None`.
432#[must_use]
433pub fn futures_contract_es(
434    activation: Option<UnixNanos>,
435    expiration: Option<UnixNanos>,
436) -> FuturesContract {
437    let activation = activation.unwrap_or(UnixNanos::from(timestamp(2021, 9, 10, 0, 0, 0)));
438    let expiration = expiration.unwrap_or(UnixNanos::from(timestamp(2021, 12, 17, 0, 0, 0)));
439    FuturesContract::builder()
440        .instrument_id(InstrumentId::from("ESZ21.GLBX"))
441        .raw_symbol(Symbol::from("ESZ21"))
442        .asset_class(AssetClass::Index)
443        .exchange(Ustr::from("XCME"))
444        .underlying(Ustr::from("ES"))
445        .activation_ns(activation)
446        .expiration_ns(expiration)
447        .currency(Currency::USD())
448        .price_precision(2)
449        .price_increment(Price::from("0.01"))
450        .multiplier(Quantity::from(1))
451        .lot_size(Quantity::from(1))
452        .ts_event(UnixNanos::default())
453        .ts_init(UnixNanos::default())
454        .build()
455        .unwrap()
456}
457
458#[fixture]
459pub fn futures_spread_es() -> FuturesSpread {
460    let activation = timestamp(2022, 6, 21, 13, 30, 0);
461    let expiration = timestamp(2024, 6, 21, 13, 30, 0);
462    FuturesSpread::builder()
463        .instrument_id(InstrumentId::from("ESM4-ESU4.GLBX"))
464        .raw_symbol(Symbol::from("ESM4-ESU4"))
465        .asset_class(AssetClass::Index)
466        .exchange(Ustr::from("XCME"))
467        .underlying(Ustr::from("ES"))
468        .strategy_type(Ustr::from("EQ"))
469        .activation_ns(UnixNanos::from(activation))
470        .expiration_ns(UnixNanos::from(expiration))
471        .currency(Currency::USD())
472        .price_precision(2)
473        .price_increment(Price::from("0.01"))
474        .multiplier(Quantity::from(1))
475        .lot_size(Quantity::from(1))
476        .ts_event(UnixNanos::default())
477        .ts_init(UnixNanos::default())
478        .build()
479        .unwrap()
480}
481
482#[fixture]
483pub fn option_contract_appl() -> OptionContract {
484    let activation = timestamp(2021, 9, 17, 0, 0, 0);
485    let expiration = timestamp(2021, 12, 17, 0, 0, 0);
486    OptionContract::builder()
487        .instrument_id(InstrumentId::from("AAPL211217C00150000.OPRA"))
488        .raw_symbol(Symbol::from("AAPL211217C00150000"))
489        .asset_class(AssetClass::Equity)
490        .exchange(Ustr::from("GMNI"))
491        .underlying(Ustr::from("AAPL"))
492        .option_kind(OptionKind::Call)
493        .strike_price(Price::from("149.0"))
494        .currency(Currency::USD())
495        .activation_ns(UnixNanos::from(activation))
496        .expiration_ns(UnixNanos::from(expiration))
497        .price_precision(2)
498        .price_increment(Price::from("0.01"))
499        .multiplier(Quantity::from(1))
500        .lot_size(Quantity::from(1))
501        .ts_event(UnixNanos::default())
502        .ts_init(UnixNanos::default())
503        .build()
504        .unwrap()
505}
506
507#[fixture]
508pub fn option_spread() -> OptionSpread {
509    let activation = timestamp(2023, 11, 6, 20, 54, 7);
510    let expiration = timestamp(2024, 2, 23, 22, 59, 0);
511    OptionSpread::builder()
512        .instrument_id(InstrumentId::from("UD:U$: GN 2534559.GLBX"))
513        .raw_symbol(Symbol::from("UD:U$: GN 2534559"))
514        .asset_class(AssetClass::FX)
515        .exchange(Ustr::from("XCME"))
516        .underlying(Ustr::from("SR3"))
517        .strategy_type(Ustr::from("GN"))
518        .activation_ns(UnixNanos::from(activation))
519        .expiration_ns(UnixNanos::from(expiration))
520        .currency(Currency::USD())
521        .price_precision(2)
522        .price_increment(Price::from("0.01"))
523        .multiplier(Quantity::from(1))
524        .lot_size(Quantity::from(1))
525        .ts_event(UnixNanos::default())
526        .ts_init(UnixNanos::default())
527        .build()
528        .unwrap()
529}
530
531#[fixture]
532pub fn crypto_futures_spread_btc_deribit() -> CryptoFuturesSpread {
533    let activation = timestamp(2026, 5, 12, 0, 0, 0);
534    let expiration = timestamp(2026, 5, 19, 8, 0, 0);
535    CryptoFuturesSpread::builder()
536        .instrument_id(InstrumentId::from("BTC-FS-19MAY26_PERP.DERIBIT"))
537        .raw_symbol(Symbol::from("BTC-FS-19MAY26_PERP"))
538        .underlying(Currency::BTC())
539        .quote_currency(Currency::USD())
540        .settlement_currency(Currency::BTC())
541        .is_inverse(false)
542        .strategy_type(Ustr::from("FS"))
543        .activation_ns(UnixNanos::from(activation))
544        .expiration_ns(UnixNanos::from(expiration))
545        .price_precision(1)
546        .size_precision(0)
547        .price_increment(Price::from("0.5"))
548        .size_increment(Quantity::from("1"))
549        .multiplier(Quantity::from("10"))
550        .min_quantity(Quantity::from("1"))
551        .maker_fee(dec!(0.0003))
552        .taker_fee(dec!(0.0003))
553        .ts_event(0.into())
554        .ts_init(0.into())
555        .build()
556        .unwrap()
557}
558
559#[fixture]
560pub fn crypto_option_spread_btc_deribit() -> CryptoOptionSpread {
561    let activation = timestamp(2026, 5, 12, 0, 0, 0);
562    let expiration = timestamp(2026, 5, 19, 8, 0, 0);
563    CryptoOptionSpread::builder()
564        .instrument_id(InstrumentId::from("BTC-CS-19MAY26-70000_75000.DERIBIT"))
565        .raw_symbol(Symbol::from("BTC-CS-19MAY26-70000_75000"))
566        .underlying(Currency::BTC())
567        .quote_currency(Currency::USD())
568        .settlement_currency(Currency::BTC())
569        .is_inverse(false)
570        .strategy_type(Ustr::from("CS"))
571        .activation_ns(UnixNanos::from(activation))
572        .expiration_ns(UnixNanos::from(expiration))
573        .price_precision(4)
574        .size_precision(1)
575        .price_increment(Price::from("0.0001"))
576        .size_increment(Quantity::from("0.1"))
577        .multiplier(Quantity::from(1))
578        .min_quantity(Quantity::from("0.1"))
579        .maker_fee(dec!(0.0003))
580        .taker_fee(dec!(0.0003))
581        .ts_event(0.into())
582        .ts_init(0.into())
583        .build()
584        .unwrap()
585}
586
587#[fixture]
588pub fn betting() -> BettingInstrument {
589    let raw_symbol = Symbol::new("1-123456789");
590    let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR"));
591    let event_type_id = 6423;
592    let event_type_name = Ustr::from("American Football");
593    let competition_id = 12_282_733;
594    let competition_name = Ustr::from("NFL");
595    let event_id = 29_678_534;
596    let event_name = Ustr::from("NFL");
597    let event_country_code = Ustr::from("GB");
598    let event_open_date = UnixNanos::from(timestamp(2022, 2, 7, 23, 30, 0));
599    let betting_type = Ustr::from("ODDS");
600    let market_id = Ustr::from("1-123456789");
601    let market_name = Ustr::from("AFC Conference Winner");
602    let market_type = Ustr::from("SPECIAL");
603    let market_start_time = UnixNanos::from(timestamp(2022, 2, 7, 23, 30, 0));
604    let selection_id = 50214;
605    let selection_name = Ustr::from("Kansas City Chiefs");
606    let selection_handicap = 0.0;
607    let currency = Currency::GBP();
608    let price_increment = Price::from("0.01");
609    let size_increment = Quantity::from("0.01");
610    let max_quantity = Some(Quantity::from("1000"));
611    let min_quantity = Some(Quantity::from("1"));
612    let max_notional = Some(Money::from("10000 GBP"));
613    let min_notional = Some(Money::from("10 GBP"));
614    let max_price = Some(Price::from("100.00"));
615    let min_price = Some(Price::from("1.00"));
616    let margin_init = Some(Decimal::from(1));
617    let margin_maint = Some(Decimal::from(1));
618    let maker_fee = Some(Decimal::from(0));
619    let taker_fee = Some(Decimal::from(0));
620    let ts_event = UnixNanos::default();
621    let ts_init = UnixNanos::default();
622
623    BettingInstrument::builder()
624        .instrument_id(id)
625        .raw_symbol(raw_symbol)
626        .event_type_id(event_type_id)
627        .event_type_name(event_type_name)
628        .competition_id(competition_id)
629        .competition_name(competition_name)
630        .event_id(event_id)
631        .event_name(event_name)
632        .event_country_code(event_country_code)
633        .event_open_date(event_open_date)
634        .betting_type(betting_type)
635        .market_id(market_id)
636        .market_name(market_name)
637        .market_type(market_type)
638        .market_start_time(market_start_time)
639        .selection_id(selection_id)
640        .selection_name(selection_name)
641        .selection_handicap(selection_handicap)
642        .currency(currency)
643        .price_precision(price_increment.precision)
644        .size_precision(size_increment.precision)
645        .price_increment(price_increment)
646        .size_increment(size_increment)
647        .maybe_max_quantity(max_quantity)
648        .maybe_min_quantity(min_quantity)
649        .maybe_max_notional(max_notional)
650        .maybe_min_notional(min_notional)
651        .maybe_max_price(max_price)
652        .maybe_min_price(min_price)
653        .maybe_margin_init(margin_init)
654        .maybe_margin_maint(margin_maint)
655        .maybe_maker_fee(maker_fee)
656        .maybe_taker_fee(taker_fee)
657        .ts_event(ts_event)
658        .ts_init(ts_init)
659        .build()
660        .unwrap()
661}
662
663#[fixture]
664pub fn commodity_gold() -> Commodity {
665    Commodity::builder()
666        .instrument_id(InstrumentId::from("GOLD.COMEX"))
667        .raw_symbol(Symbol::from("GOLD"))
668        .asset_class(AssetClass::Commodity)
669        .quote_currency(Currency::from("USD"))
670        .price_precision(2)
671        .size_precision(0)
672        .price_increment(Price::from("0.01"))
673        .size_increment(Quantity::from("1"))
674        .lot_size(Quantity::from("1"))
675        .ts_event(UnixNanos::default())
676        .ts_init(UnixNanos::default())
677        .build()
678        .unwrap()
679}
680
681#[fixture]
682pub fn index_instrument_spx() -> IndexInstrument {
683    IndexInstrument::builder()
684        .instrument_id(InstrumentId::from("SPX.INDEX"))
685        .raw_symbol(Symbol::from("SPX"))
686        .currency(Currency::from("USD"))
687        .price_precision(2)
688        .size_precision(0)
689        .price_increment(Price::from("0.01"))
690        .size_increment(Quantity::from("1"))
691        .ts_event(UnixNanos::default())
692        .ts_init(UnixNanos::default())
693        .build()
694        .unwrap()
695}
696
697#[fixture]
698pub fn cfd_gold() -> Cfd {
699    Cfd::builder()
700        .instrument_id(InstrumentId::from("GOLD-CFD.SIM"))
701        .raw_symbol(Symbol::from("GOLD-CFD"))
702        .asset_class(AssetClass::Commodity)
703        .quote_currency(Currency::from("USD"))
704        .price_precision(2)
705        .size_precision(0)
706        .price_increment(Price::from("0.01"))
707        .size_increment(Quantity::from("1"))
708        .lot_size(Quantity::from("1"))
709        .ts_event(UnixNanos::default())
710        .ts_init(UnixNanos::default())
711        .build()
712        .unwrap()
713}
714
715#[fixture]
716pub fn perpetual_contract_eurusd() -> PerpetualContract {
717    PerpetualContract::builder()
718        .instrument_id(InstrumentId::from("EURUSD-PERP.AX"))
719        .raw_symbol(Symbol::from("EURUSD-PERP"))
720        .underlying(Ustr::from("EURUSD"))
721        .asset_class(AssetClass::FX)
722        .base_currency(Currency::from("EUR"))
723        .quote_currency(Currency::from("USD"))
724        .settlement_currency(Currency::from("USD"))
725        .is_inverse(false)
726        .price_precision(5)
727        .size_precision(0)
728        .price_increment(Price::from("0.00001"))
729        .size_increment(Quantity::from("1"))
730        .margin_init(dec!(0.03))
731        .margin_maint(dec!(0.03))
732        .maker_fee(dec!(0.00002))
733        .taker_fee(dec!(0.00002))
734        .ts_event(UnixNanos::default())
735        .ts_init(UnixNanos::default())
736        .build()
737        .unwrap()
738}
739
740#[fixture]
741pub fn binary_option() -> BinaryOption {
742    let raw_symbol = Symbol::new(
743        "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
744    );
745    let activation = timestamp(2023, 11, 6, 20, 54, 7);
746    let expiration = timestamp(2024, 2, 23, 22, 59, 0);
747    let price_increment = Price::from("0.001");
748    let size_increment = Quantity::from("0.01");
749    BinaryOption::builder()
750        .instrument_id(InstrumentId::from("{raw_symbol}.POLYMARKET"))
751        .raw_symbol(raw_symbol)
752        .asset_class(AssetClass::Alternative)
753        .currency(Currency::USDC())
754        .activation_ns(UnixNanos::from(activation))
755        .expiration_ns(UnixNanos::from(expiration))
756        .price_precision(price_increment.precision)
757        .size_precision(size_increment.precision)
758        .price_increment(price_increment)
759        .size_increment(size_increment)
760        .ts_event(UnixNanos::default())
761        .ts_init(UnixNanos::default())
762        .build()
763        .unwrap()
764}
765
766#[fixture]
767pub fn tokenized_asset_aaplx() -> TokenizedAsset {
768    TokenizedAsset::builder()
769        .instrument_id(InstrumentId::from("AAPLx/USD.KRAKEN"))
770        .raw_symbol(Symbol::from("AAPLxUSD"))
771        .asset_class(AssetClass::Equity)
772        .base_currency(Currency::get_or_create_crypto("AAPLx"))
773        .quote_currency(Currency::from("USD"))
774        .price_precision(2)
775        .size_precision(4)
776        .price_increment(Price::from("0.01"))
777        .size_increment(Quantity::from("0.0001"))
778        .min_quantity(Quantity::from("0.0001"))
779        .maker_fee(dec!(-0.0002))
780        .taker_fee(dec!(0.001))
781        .ts_event(UnixNanos::default())
782        .ts_init(UnixNanos::default())
783        .build()
784        .unwrap()
785}