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 btcusd_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        // Bybit lists inverse contracts as BTCUSD; keep the "-PERP" suffix for clarity
140        .instrument_id(InstrumentId::from("BTCUSD-PERP.BYBIT"))
141        .raw_symbol(Symbol::from("BTCUSD"))
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        .ts_event(UnixNanos::default())
161        .ts_init(UnixNanos::default())
162        .build()
163        .unwrap()
164}
165
166#[fixture]
167pub fn crypto_option_btc_deribit(
168    #[default(3)] price_precision: u8,
169    #[default(1)] size_precision: u8,
170    #[default(Price::from("0.001"))] price_increment: Price,
171    #[default(Quantity::from("0.1"))] size_increment: Quantity,
172) -> CryptoOption {
173    let activation = UnixNanos::from(1_671_696_002_000_000_000);
174    let expiration = UnixNanos::from(1_673_596_800_000_000_000);
175    CryptoOption::builder()
176        .instrument_id(InstrumentId::from("BTC-13JAN23-16000-P.DERIBIT"))
177        .raw_symbol(Symbol::from("BTC-13JAN23-16000-P"))
178        .underlying(Currency::from("BTC"))
179        .quote_currency(Currency::from("USD"))
180        .settlement_currency(Currency::from("BTC"))
181        .is_inverse(false)
182        .option_kind(OptionKind::Put)
183        .strike_price(Price::from("16000.000"))
184        .activation_ns(activation)
185        .expiration_ns(expiration)
186        .price_precision(price_precision)
187        .size_precision(size_precision)
188        .price_increment(price_increment)
189        .size_increment(size_increment)
190        .multiplier(Quantity::from(1))
191        .lot_size(Quantity::from(1))
192        .max_quantity(Quantity::from("9000.0"))
193        .min_quantity(Quantity::from("0.1"))
194        .min_notional(Money::new(10.00, Currency::from("USD")))
195        .ts_event(0.into())
196        .ts_init(0.into())
197        .build()
198        .unwrap()
199}
200
201#[fixture]
202pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
203    CryptoPerpetual::builder()
204        .instrument_id(InstrumentId::from("ETHUSDT-PERP.BINANCE"))
205        .raw_symbol(Symbol::from("ETHUSDT"))
206        .base_currency(Currency::from("ETH"))
207        .quote_currency(Currency::from("USDT"))
208        .settlement_currency(Currency::from("USDT"))
209        .is_inverse(false)
210        .price_precision(2)
211        .size_precision(3)
212        .price_increment(Price::from("0.01"))
213        .size_increment(Quantity::from("0.001"))
214        .max_quantity(Quantity::from("10000.0"))
215        .min_quantity(Quantity::from("0.001"))
216        .min_notional(Money::new(10.00, Currency::from("USDT")))
217        .max_price(Price::from("15000.00"))
218        .min_price(Price::from("1.0"))
219        .margin_init(dec!(1.0))
220        .margin_maint(dec!(0.35))
221        .ts_event(UnixNanos::default())
222        .ts_init(UnixNanos::default())
223        .build()
224        .unwrap()
225}
226
227#[fixture]
228pub fn btcusd_bybit() -> CryptoPerpetual {
229    CryptoPerpetual::builder()
230        .instrument_id(InstrumentId::from("BTCUSD.BYBIT"))
231        .raw_symbol(Symbol::from("BTCUSD"))
232        .base_currency(Currency::BTC())
233        .quote_currency(Currency::USD())
234        .settlement_currency(Currency::BTC())
235        .is_inverse(true)
236        .price_precision(1)
237        .size_precision(0)
238        .price_increment(Price::from("0.5"))
239        .size_increment(Quantity::from("1"))
240        .max_notional(Money::from("10000000 USD"))
241        .min_notional(Money::from("1 USD"))
242        .max_price(Price::from("10000000"))
243        .min_price(Price::from("0.01"))
244        .margin_init(dec!(0.01))
245        .margin_maint(dec!(0.0035))
246        .ts_event(UnixNanos::default())
247        .ts_init(UnixNanos::default())
248        .build()
249        .unwrap()
250}
251
252#[fixture]
253pub fn ethusd_bybit() -> CryptoPerpetual {
254    CryptoPerpetual::builder()
255        .instrument_id(InstrumentId::from("ETHUSD.BYBIT"))
256        .raw_symbol(Symbol::from("ETHUSD"))
257        .base_currency(Currency::ETH())
258        .quote_currency(Currency::USD())
259        .settlement_currency(Currency::ETH())
260        .is_inverse(true)
261        .price_precision(2)
262        .size_precision(0)
263        .price_increment(Price::from("0.05"))
264        .size_increment(Quantity::from("1"))
265        .max_price(Price::from("10000000"))
266        .min_price(Price::from("0.01"))
267        .margin_init(dec!(0.01))
268        .margin_maint(dec!(0.0035))
269        .ts_event(UnixNanos::default())
270        .ts_init(UnixNanos::default())
271        .build()
272        .unwrap()
273}
274
275#[fixture]
276pub fn currency_pair_btcusdt() -> CurrencyPair {
277    CurrencyPair::builder()
278        .instrument_id(InstrumentId::from("BTCUSDT.BINANCE"))
279        .raw_symbol(Symbol::from("BTCUSDT"))
280        .base_currency(Currency::from("BTC"))
281        .quote_currency(Currency::from("USDT"))
282        .price_precision(2)
283        .size_precision(6)
284        .price_increment(Price::from("0.01"))
285        .size_increment(Quantity::from("0.000001"))
286        .max_quantity(Quantity::from("9000"))
287        .min_quantity(Quantity::from("0.000001"))
288        .max_price(Price::from("1000000"))
289        .min_price(Price::from("0.01"))
290        .margin_init(dec!(0.001))
291        .margin_maint(dec!(0.001))
292        .ts_event(UnixNanos::default())
293        .ts_init(UnixNanos::default())
294        .build()
295        .unwrap()
296}
297
298#[fixture]
299pub fn currency_pair_ethusdt() -> CurrencyPair {
300    CurrencyPair::builder()
301        .instrument_id(InstrumentId::from("ETHUSDT.BINANCE"))
302        .raw_symbol(Symbol::from("ETHUSDT"))
303        .base_currency(Currency::from("ETH"))
304        .quote_currency(Currency::from("USDT"))
305        .price_precision(2)
306        .size_precision(5)
307        .price_increment(Price::from("0.01"))
308        .size_increment(Quantity::from("0.00001"))
309        .max_quantity(Quantity::from("9000"))
310        .min_quantity(Quantity::from("0.00001"))
311        .max_price(Price::from("1000000"))
312        .min_price(Price::from("0.01"))
313        .margin_init(dec!(0.01))
314        .margin_maint(dec!(0.0035))
315        .ts_event(UnixNanos::default())
316        .ts_init(UnixNanos::default())
317        .build()
318        .unwrap()
319}
320
321/// # Panics
322///
323/// Panics if `symbol` does not contain a '/' delimiter.
324#[must_use]
325pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
326    let target_venue = venue.unwrap_or(Venue::from("SIM"));
327    let instrument_id = InstrumentId::new(symbol, target_venue);
328    let base_currency = symbol.as_str().split('/').next().unwrap();
329    let quote_currency = symbol.as_str().split('/').next_back().unwrap();
330    let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
331    let tick_scheme = if quote_currency == "JPY" {
332        "FOREX_3DECIMAL"
333    } else {
334        "FOREX_5DECIMAL"
335    };
336    let price_increment = Price::new(
337        1.0 / 10.0f64.powi(i32::from(price_precision)),
338        price_precision,
339    );
340    CurrencyPair::builder()
341        .instrument_id(instrument_id)
342        .raw_symbol(symbol)
343        .base_currency(Currency::from(base_currency))
344        .quote_currency(Currency::from(quote_currency))
345        .price_precision(price_precision)
346        .size_precision(0)
347        .price_increment(price_increment)
348        .size_increment(Quantity::from("1"))
349        .lot_size(Quantity::from("1000"))
350        .max_quantity(Quantity::from("1000000"))
351        .min_quantity(Quantity::from("100"))
352        .margin_init(dec!(0.03))
353        .margin_maint(dec!(0.03))
354        .tick_scheme(Ustr::from(tick_scheme))
355        .ts_event(UnixNanos::default())
356        .ts_init(UnixNanos::default())
357        .build()
358        .unwrap()
359}
360
361#[fixture]
362pub fn audusd_sim() -> CurrencyPair {
363    default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
364}
365
366#[fixture]
367pub fn gbpusd_sim() -> CurrencyPair {
368    default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
369}
370
371#[fixture]
372pub fn usdjpy_idealpro() -> CurrencyPair {
373    default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
374}
375
376#[fixture]
377pub fn equity_aapl() -> Equity {
378    Equity::builder()
379        .instrument_id(InstrumentId::from("AAPL.XNAS"))
380        .raw_symbol(Symbol::from("AAPL"))
381        .isin(Ustr::from("US0378331005"))
382        .currency(Currency::from("USD"))
383        .price_precision(2)
384        .price_increment(Price::from("0.01"))
385        .ts_event(UnixNanos::default())
386        .ts_init(UnixNanos::default())
387        .build()
388        .unwrap()
389}
390
391/// AAPL equity with ITCH-compatible precision (`price_precision=4`).
392///
393/// # Panics
394///
395/// Panics if the instrument definition is invalid.
396#[must_use]
397pub fn equity_aapl_itch() -> Equity {
398    Equity::builder()
399        .instrument_id(InstrumentId::from("AAPL.XNAS"))
400        .raw_symbol(Symbol::from("AAPL"))
401        .isin(Ustr::from("US0378331005"))
402        .currency(Currency::from("USD"))
403        .price_precision(4)
404        .price_increment(Price::from("0.0001"))
405        .ts_event(UnixNanos::default())
406        .ts_init(UnixNanos::default())
407        .build()
408        .unwrap()
409}
410
411/// # Panics
412///
413/// Panics if constructing the activation or expiration timestamp fails,
414/// e.g., if the provided dates are invalid or timestamp conversion returns `None`.
415#[must_use]
416pub fn futures_contract_es(
417    activation: Option<UnixNanos>,
418    expiration: Option<UnixNanos>,
419) -> FuturesContract {
420    let activation = activation.unwrap_or(UnixNanos::from(timestamp(2021, 9, 10, 0, 0, 0)));
421    let expiration = expiration.unwrap_or(UnixNanos::from(timestamp(2021, 12, 17, 0, 0, 0)));
422    FuturesContract::builder()
423        .instrument_id(InstrumentId::from("ESZ21.GLBX"))
424        .raw_symbol(Symbol::from("ESZ21"))
425        .asset_class(AssetClass::Index)
426        .exchange(Ustr::from("XCME"))
427        .underlying(Ustr::from("ES"))
428        .activation_ns(activation)
429        .expiration_ns(expiration)
430        .currency(Currency::USD())
431        .price_precision(2)
432        .price_increment(Price::from("0.01"))
433        .multiplier(Quantity::from(1))
434        .lot_size(Quantity::from(1))
435        .ts_event(UnixNanos::default())
436        .ts_init(UnixNanos::default())
437        .build()
438        .unwrap()
439}
440
441#[fixture]
442pub fn futures_spread_es() -> FuturesSpread {
443    let activation = timestamp(2022, 6, 21, 13, 30, 0);
444    let expiration = timestamp(2024, 6, 21, 13, 30, 0);
445    FuturesSpread::builder()
446        .instrument_id(InstrumentId::from("ESM4-ESU4.GLBX"))
447        .raw_symbol(Symbol::from("ESM4-ESU4"))
448        .asset_class(AssetClass::Index)
449        .exchange(Ustr::from("XCME"))
450        .underlying(Ustr::from("ES"))
451        .strategy_type(Ustr::from("EQ"))
452        .activation_ns(UnixNanos::from(activation))
453        .expiration_ns(UnixNanos::from(expiration))
454        .currency(Currency::USD())
455        .price_precision(2)
456        .price_increment(Price::from("0.01"))
457        .multiplier(Quantity::from(1))
458        .lot_size(Quantity::from(1))
459        .ts_event(UnixNanos::default())
460        .ts_init(UnixNanos::default())
461        .build()
462        .unwrap()
463}
464
465#[fixture]
466pub fn option_contract_appl() -> OptionContract {
467    let activation = timestamp(2021, 9, 17, 0, 0, 0);
468    let expiration = timestamp(2021, 12, 17, 0, 0, 0);
469    OptionContract::builder()
470        .instrument_id(InstrumentId::from("AAPL211217C00150000.OPRA"))
471        .raw_symbol(Symbol::from("AAPL211217C00150000"))
472        .asset_class(AssetClass::Equity)
473        .exchange(Ustr::from("GMNI"))
474        .underlying(Ustr::from("AAPL"))
475        .option_kind(OptionKind::Call)
476        .strike_price(Price::from("149.0"))
477        .currency(Currency::USD())
478        .activation_ns(UnixNanos::from(activation))
479        .expiration_ns(UnixNanos::from(expiration))
480        .price_precision(2)
481        .price_increment(Price::from("0.01"))
482        .multiplier(Quantity::from(1))
483        .lot_size(Quantity::from(1))
484        .ts_event(UnixNanos::default())
485        .ts_init(UnixNanos::default())
486        .build()
487        .unwrap()
488}
489
490#[fixture]
491pub fn option_spread() -> OptionSpread {
492    let activation = timestamp(2023, 11, 6, 20, 54, 7);
493    let expiration = timestamp(2024, 2, 23, 22, 59, 0);
494    OptionSpread::builder()
495        .instrument_id(InstrumentId::from("UD:U$: GN 2534559.GLBX"))
496        .raw_symbol(Symbol::from("UD:U$: GN 2534559"))
497        .asset_class(AssetClass::FX)
498        .exchange(Ustr::from("XCME"))
499        .underlying(Ustr::from("SR3"))
500        .strategy_type(Ustr::from("GN"))
501        .activation_ns(UnixNanos::from(activation))
502        .expiration_ns(UnixNanos::from(expiration))
503        .currency(Currency::USD())
504        .price_precision(2)
505        .price_increment(Price::from("0.01"))
506        .multiplier(Quantity::from(1))
507        .lot_size(Quantity::from(1))
508        .ts_event(UnixNanos::default())
509        .ts_init(UnixNanos::default())
510        .build()
511        .unwrap()
512}
513
514#[fixture]
515pub fn crypto_futures_spread_btc_deribit() -> CryptoFuturesSpread {
516    let activation = timestamp(2026, 5, 12, 0, 0, 0);
517    let expiration = timestamp(2026, 5, 19, 8, 0, 0);
518    CryptoFuturesSpread::builder()
519        .instrument_id(InstrumentId::from("BTC-FS-19MAY26_PERP.DERIBIT"))
520        .raw_symbol(Symbol::from("BTC-FS-19MAY26_PERP"))
521        .underlying(Currency::BTC())
522        .quote_currency(Currency::USD())
523        .settlement_currency(Currency::BTC())
524        .is_inverse(false)
525        .strategy_type(Ustr::from("FS"))
526        .activation_ns(UnixNanos::from(activation))
527        .expiration_ns(UnixNanos::from(expiration))
528        .price_precision(1)
529        .size_precision(0)
530        .price_increment(Price::from("0.5"))
531        .size_increment(Quantity::from("1"))
532        .multiplier(Quantity::from("10"))
533        .min_quantity(Quantity::from("1"))
534        .ts_event(0.into())
535        .ts_init(0.into())
536        .build()
537        .unwrap()
538}
539
540#[fixture]
541pub fn crypto_option_spread_btc_deribit() -> CryptoOptionSpread {
542    let activation = timestamp(2026, 5, 12, 0, 0, 0);
543    let expiration = timestamp(2026, 5, 19, 8, 0, 0);
544    CryptoOptionSpread::builder()
545        .instrument_id(InstrumentId::from("BTC-CS-19MAY26-70000_75000.DERIBIT"))
546        .raw_symbol(Symbol::from("BTC-CS-19MAY26-70000_75000"))
547        .underlying(Currency::BTC())
548        .quote_currency(Currency::USD())
549        .settlement_currency(Currency::BTC())
550        .is_inverse(false)
551        .strategy_type(Ustr::from("CS"))
552        .activation_ns(UnixNanos::from(activation))
553        .expiration_ns(UnixNanos::from(expiration))
554        .price_precision(4)
555        .size_precision(1)
556        .price_increment(Price::from("0.0001"))
557        .size_increment(Quantity::from("0.1"))
558        .multiplier(Quantity::from(1))
559        .min_quantity(Quantity::from("0.1"))
560        .ts_event(0.into())
561        .ts_init(0.into())
562        .build()
563        .unwrap()
564}
565
566#[fixture]
567pub fn betting() -> BettingInstrument {
568    let raw_symbol = Symbol::new("1-123456789");
569    let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR"));
570    let event_type_id = 6423;
571    let event_type_name = Ustr::from("American Football");
572    let competition_id = 12_282_733;
573    let competition_name = Ustr::from("NFL");
574    let event_id = 29_678_534;
575    let event_name = Ustr::from("NFL");
576    let event_country_code = Ustr::from("GB");
577    let event_open_date = UnixNanos::from(timestamp(2022, 2, 7, 23, 30, 0));
578    let betting_type = Ustr::from("ODDS");
579    let market_id = Ustr::from("1-123456789");
580    let market_name = Ustr::from("AFC Conference Winner");
581    let market_type = Ustr::from("SPECIAL");
582    let market_start_time = UnixNanos::from(timestamp(2022, 2, 7, 23, 30, 0));
583    let selection_id = 50214;
584    let selection_name = Ustr::from("Kansas City Chiefs");
585    let selection_handicap = 0.0;
586    let currency = Currency::GBP();
587    let price_increment = Price::from("0.01");
588    let size_increment = Quantity::from("0.01");
589    let max_quantity = Some(Quantity::from("1000"));
590    let min_quantity = Some(Quantity::from("1"));
591    let max_notional = Some(Money::from("10000 GBP"));
592    let min_notional = Some(Money::from("10 GBP"));
593    let max_price = Some(Price::from("100.00"));
594    let min_price = Some(Price::from("1.00"));
595    let margin_init = Some(Decimal::from(1));
596    let margin_maint = Some(Decimal::from(1));
597    let ts_event = UnixNanos::default();
598    let ts_init = UnixNanos::default();
599
600    BettingInstrument::builder()
601        .instrument_id(id)
602        .raw_symbol(raw_symbol)
603        .event_type_id(event_type_id)
604        .event_type_name(event_type_name)
605        .competition_id(competition_id)
606        .competition_name(competition_name)
607        .event_id(event_id)
608        .event_name(event_name)
609        .event_country_code(event_country_code)
610        .event_open_date(event_open_date)
611        .betting_type(betting_type)
612        .market_id(market_id)
613        .market_name(market_name)
614        .market_type(market_type)
615        .market_start_time(market_start_time)
616        .selection_id(selection_id)
617        .selection_name(selection_name)
618        .selection_handicap(selection_handicap)
619        .currency(currency)
620        .price_precision(price_increment.precision)
621        .size_precision(size_increment.precision)
622        .price_increment(price_increment)
623        .size_increment(size_increment)
624        .maybe_max_quantity(max_quantity)
625        .maybe_min_quantity(min_quantity)
626        .maybe_max_notional(max_notional)
627        .maybe_min_notional(min_notional)
628        .maybe_max_price(max_price)
629        .maybe_min_price(min_price)
630        .maybe_margin_init(margin_init)
631        .maybe_margin_maint(margin_maint)
632        .ts_event(ts_event)
633        .ts_init(ts_init)
634        .build()
635        .unwrap()
636}
637
638#[fixture]
639pub fn commodity_gold() -> Commodity {
640    Commodity::builder()
641        .instrument_id(InstrumentId::from("GOLD.COMEX"))
642        .raw_symbol(Symbol::from("GOLD"))
643        .asset_class(AssetClass::Commodity)
644        .quote_currency(Currency::from("USD"))
645        .price_precision(2)
646        .size_precision(0)
647        .price_increment(Price::from("0.01"))
648        .size_increment(Quantity::from("1"))
649        .lot_size(Quantity::from("1"))
650        .ts_event(UnixNanos::default())
651        .ts_init(UnixNanos::default())
652        .build()
653        .unwrap()
654}
655
656#[fixture]
657pub fn index_instrument_spx() -> IndexInstrument {
658    IndexInstrument::builder()
659        .instrument_id(InstrumentId::from("SPX.INDEX"))
660        .raw_symbol(Symbol::from("SPX"))
661        .currency(Currency::from("USD"))
662        .price_precision(2)
663        .size_precision(0)
664        .price_increment(Price::from("0.01"))
665        .size_increment(Quantity::from("1"))
666        .ts_event(UnixNanos::default())
667        .ts_init(UnixNanos::default())
668        .build()
669        .unwrap()
670}
671
672#[fixture]
673pub fn cfd_gold() -> Cfd {
674    Cfd::builder()
675        .instrument_id(InstrumentId::from("GOLD-CFD.SIM"))
676        .raw_symbol(Symbol::from("GOLD-CFD"))
677        .asset_class(AssetClass::Commodity)
678        .quote_currency(Currency::from("USD"))
679        .price_precision(2)
680        .size_precision(0)
681        .price_increment(Price::from("0.01"))
682        .size_increment(Quantity::from("1"))
683        .lot_size(Quantity::from("1"))
684        .ts_event(UnixNanos::default())
685        .ts_init(UnixNanos::default())
686        .build()
687        .unwrap()
688}
689
690#[fixture]
691pub fn perpetual_contract_eurusd() -> PerpetualContract {
692    PerpetualContract::builder()
693        .instrument_id(InstrumentId::from("EURUSD-PERP.AX"))
694        .raw_symbol(Symbol::from("EURUSD-PERP"))
695        .underlying(Ustr::from("EURUSD"))
696        .asset_class(AssetClass::FX)
697        .base_currency(Currency::from("EUR"))
698        .quote_currency(Currency::from("USD"))
699        .settlement_currency(Currency::from("USD"))
700        .is_inverse(false)
701        .price_precision(5)
702        .size_precision(0)
703        .price_increment(Price::from("0.00001"))
704        .size_increment(Quantity::from("1"))
705        .margin_init(dec!(0.03))
706        .margin_maint(dec!(0.03))
707        .ts_event(UnixNanos::default())
708        .ts_init(UnixNanos::default())
709        .build()
710        .unwrap()
711}
712
713#[fixture]
714pub fn binary_option() -> BinaryOption {
715    let raw_symbol = Symbol::new(
716        "0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
717    );
718    let activation = timestamp(2023, 11, 6, 20, 54, 7);
719    let expiration = timestamp(2024, 2, 23, 22, 59, 0);
720    let price_increment = Price::from("0.001");
721    let size_increment = Quantity::from("0.01");
722    BinaryOption::builder()
723        .instrument_id(InstrumentId::from("{raw_symbol}.POLYMARKET"))
724        .raw_symbol(raw_symbol)
725        .asset_class(AssetClass::Alternative)
726        .currency(Currency::USDC())
727        .activation_ns(UnixNanos::from(activation))
728        .expiration_ns(UnixNanos::from(expiration))
729        .price_precision(price_increment.precision)
730        .size_precision(size_increment.precision)
731        .price_increment(price_increment)
732        .size_increment(size_increment)
733        .ts_event(UnixNanos::default())
734        .ts_init(UnixNanos::default())
735        .build()
736        .unwrap()
737}
738
739#[fixture]
740pub fn tokenized_asset_aaplx() -> TokenizedAsset {
741    TokenizedAsset::builder()
742        .instrument_id(InstrumentId::from("AAPLx/USD.KRAKEN"))
743        .raw_symbol(Symbol::from("AAPLxUSD"))
744        .asset_class(AssetClass::Equity)
745        .base_currency(Currency::get_or_create_crypto("AAPLx"))
746        .quote_currency(Currency::from("USD"))
747        .price_precision(2)
748        .size_precision(4)
749        .price_increment(Price::from("0.01"))
750        .size_increment(Quantity::from("0.0001"))
751        .min_quantity(Quantity::from("0.0001"))
752        .ts_event(UnixNanos::default())
753        .ts_init(UnixNanos::default())
754        .build()
755        .unwrap()
756}