use chrono::{TimeZone, Utc};
use nautilus_core::UnixNanos;
use rstest::*;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use ustr::Ustr;
use super::{
CryptoOption, betting::BettingInstrument, binary_option::BinaryOption, cfd::Cfd,
commodity::Commodity, futures_spread::FuturesSpread, index_instrument::IndexInstrument,
option_spread::OptionSpread, perpetual_contract::PerpetualContract,
synthetic::SyntheticInstrument, tokenized_asset::TokenizedAsset,
};
use crate::{
enums::{AssetClass, OptionKind},
identifiers::{InstrumentId, Symbol, Venue},
instruments::{
CryptoFuture, CryptoPerpetual, CurrencyPair, Equity, FuturesContract, OptionContract,
},
types::{Currency, Money, Price, Quantity},
};
impl Default for SyntheticInstrument {
fn default() -> Self {
let btc_binance = InstrumentId::from("BTC.BINANCE");
let ltc_binance = InstrumentId::from("LTC.BINANCE");
let formula = "(BTC.BINANCE + LTC.BINANCE) / 2.0";
Self::new(
Symbol::new("BTC-LTC"),
2,
vec![btc_binance, ltc_binance],
formula,
0.into(),
0.into(),
)
}
}
#[fixture]
pub fn crypto_future_btcusdt(
#[default(2)] price_precision: u8,
#[default(6)] size_precision: u8,
#[default(Price::from("0.01"))] price_increment: Price,
#[default(Quantity::from("0.000001"))] size_increment: Quantity,
) -> CryptoFuture {
let activation = Utc.with_ymd_and_hms(2014, 4, 8, 0, 0, 0).unwrap();
let expiration = Utc.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
CryptoFuture::new(
InstrumentId::from("ETHUSDT-123.BINANCE"),
Symbol::from("BTCUSDT"),
Currency::from("BTC"),
Currency::from("USDT"),
Currency::from("USDT"),
false,
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
price_precision,
size_precision,
price_increment,
size_increment,
None,
None,
Some(Quantity::from("9000.0")),
Some(Quantity::from("0.000001")),
None,
Some(Money::new(10.00, Currency::from("USDT"))),
Some(Price::from("1000000.00")),
Some(Price::from("0.01")),
None,
None,
None,
None,
None, 0.into(),
0.into(),
)
}
#[fixture]
pub fn ethbtc_quanto(
#[default(5)] price_precision: u8,
#[default(3)] size_precision: u8,
#[default(Price::from("0.00001"))] price_increment: Price,
#[default(Quantity::from("0.001"))] size_increment: Quantity,
) -> CryptoFuture {
let activation = Utc.with_ymd_and_hms(2014, 4, 8, 0, 0, 0).unwrap();
let expiration = Utc.with_ymd_and_hms(2014, 7, 8, 0, 0, 0).unwrap();
CryptoFuture::new(
InstrumentId::from("ETHBTC-123.BINANCE"),
Symbol::from("ETHBTC"),
Currency::from("ETH"),
Currency::from("BTC"),
Currency::from("BTC"),
false,
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
price_precision,
size_precision,
price_increment,
size_increment,
None,
None,
Some(Quantity::from("9000.0")),
Some(Quantity::from("0.001")),
None,
Some(Money::new(1.0, Currency::from("BTC"))),
Some(Price::from("1.0")),
Some(Price::from("0.00001")),
None,
None,
None,
None,
None, 0.into(),
0.into(),
)
}
#[fixture]
pub fn xbtusd_inverse_perp(
#[default(1)] price_precision: u8,
#[default(0)] size_precision: u8,
#[default(Price::from("0.5"))] price_increment: Price,
#[default(Quantity::from("1"))] size_increment: Quantity,
) -> CryptoPerpetual {
CryptoPerpetual::new(
InstrumentId::from("XBTUSD-PERP.BITMEX"),
Symbol::from("XBTUSD"),
Currency::BTC(), Currency::USD(), Currency::BTC(), true, price_precision,
size_precision,
price_increment,
size_increment,
None, Some(Quantity::from("1")), None, None, Some(Money::from("10000000 USD")), Some(Money::from("1 USD")), Some(Price::from("10000000")), Some(Price::from("0.01")), Some(dec!(0.01)), Some(dec!(0.0035)), Some(dec!(-0.00025)), Some(dec!(0.00075)), None, UnixNanos::default(), UnixNanos::default(), )
}
#[fixture]
pub fn crypto_option_btc_deribit(
#[default(3)] price_precision: u8,
#[default(1)] size_precision: u8,
#[default(Price::from("0.001"))] price_increment: Price,
#[default(Quantity::from("0.1"))] size_increment: Quantity,
) -> CryptoOption {
let activation = UnixNanos::from(1_671_696_002_000_000_000);
let expiration = UnixNanos::from(1_673_596_800_000_000_000);
CryptoOption::new(
InstrumentId::from("BTC-13JAN23-16000-P.DERIBIT"),
Symbol::from("BTC-13JAN23-16000-P"),
Currency::from("BTC"),
Currency::from("USD"),
Currency::from("BTC"),
false,
OptionKind::Put,
Price::from("16000.000"),
activation,
expiration,
price_precision,
size_precision,
price_increment,
size_increment,
Some(Quantity::from(1)),
Some(Quantity::from(1)),
Some(Quantity::from("9000.0")),
Some(Quantity::from("0.1")),
None,
Some(Money::new(10.00, Currency::from("USD"))),
None,
None,
None,
None,
Some(dec!(0.0003)),
Some(dec!(0.0003)),
None, 0.into(),
0.into(),
)
}
#[fixture]
pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
CryptoPerpetual::new(
InstrumentId::from("ETHUSDT-PERP.BINANCE"),
Symbol::from("ETHUSDT"),
Currency::from("ETH"),
Currency::from("USDT"),
Currency::from("USDT"),
false,
2,
3,
Price::from("0.01"),
Quantity::from("0.001"),
None,
None,
Some(Quantity::from("10000.0")),
Some(Quantity::from("0.001")),
None,
Some(Money::new(10.00, Currency::from("USDT"))),
Some(Price::from("15000.00")),
Some(Price::from("1.0")),
Some(dec!(1.0)),
Some(dec!(0.35)),
Some(dec!(0.0002)),
Some(dec!(0.0004)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn xbtusd_bitmex() -> CryptoPerpetual {
CryptoPerpetual::new(
InstrumentId::from("BTCUSDT.BITMEX"),
Symbol::from("XBTUSD"),
Currency::BTC(),
Currency::USD(),
Currency::BTC(),
true,
1,
0,
Price::from("0.5"),
Quantity::from("1"),
None,
None,
None,
None,
Some(Money::from("10000000 USD")),
Some(Money::from("1 USD")),
Some(Price::from("10000000")),
Some(Price::from("0.01")),
Some(dec!(0.01)),
Some(dec!(0.0035)),
Some(dec!(-0.00025)),
Some(dec!(0.00075)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn ethusdt_bitmex() -> CryptoPerpetual {
CryptoPerpetual::new(
InstrumentId::from("ETHUSD.BITMEX"),
Symbol::from("ETHUSD"),
Currency::ETH(),
Currency::USD(),
Currency::ETH(),
true,
2,
0,
Price::from("0.05"),
Quantity::from("1"),
None,
None,
None,
None,
None,
None,
Some(Price::from("10000000")),
Some(Price::from("0.01")),
Some(dec!(0.01)),
Some(dec!(0.0035)),
Some(dec!(-0.00025)),
Some(dec!(0.00075)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn currency_pair_btcusdt() -> CurrencyPair {
CurrencyPair::new(
InstrumentId::from("BTCUSDT.BINANCE"),
Symbol::from("BTCUSDT"),
Currency::from("BTC"),
Currency::from("USDT"),
2,
6,
Price::from("0.01"),
Quantity::from("0.000001"),
None,
None,
Some(Quantity::from("9000")),
Some(Quantity::from("0.000001")),
None,
None,
Some(Price::from("1000000")),
Some(Price::from("0.01")),
Some(dec!(0.001)),
Some(dec!(0.001)),
Some(dec!(0.001)),
Some(dec!(0.001)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn currency_pair_ethusdt() -> CurrencyPair {
CurrencyPair::new(
InstrumentId::from("ETHUSDT.BINANCE"),
Symbol::from("ETHUSDT"),
Currency::from("ETH"),
Currency::from("USDT"),
2,
5,
Price::from("0.01"),
Quantity::from("0.00001"),
None,
None,
Some(Quantity::from("9000")),
Some(Quantity::from("0.00001")),
None,
None,
Some(Price::from("1000000")),
Some(Price::from("0.01")),
Some(dec!(0.01)),
Some(dec!(0.0035)),
Some(dec!(0.0001)),
Some(dec!(0.0001)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[must_use]
pub fn default_fx_ccy(symbol: Symbol, venue: Option<Venue>) -> CurrencyPair {
let target_venue = venue.unwrap_or(Venue::from("SIM"));
let instrument_id = InstrumentId::new(symbol, target_venue);
let base_currency = symbol.as_str().split('/').next().unwrap();
let quote_currency = symbol.as_str().split('/').next_back().unwrap();
let price_precision = if quote_currency == "JPY" { 3 } else { 5 };
let price_increment = Price::new(1.0 / 10.0f64.powi(price_precision as i32), price_precision);
CurrencyPair::new(
instrument_id,
symbol,
Currency::from(base_currency),
Currency::from(quote_currency),
price_precision,
0,
price_increment,
Quantity::from("1"),
None,
Some(Quantity::from("1000")),
Some(Quantity::from("1000000")),
Some(Quantity::from("100")),
None,
None,
None,
None,
Some(dec!(0.03)),
Some(dec!(0.03)),
Some(dec!(0.00002)),
Some(dec!(0.00002)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn audusd_sim() -> CurrencyPair {
default_fx_ccy(Symbol::from("AUD/USD"), Some(Venue::from("SIM")))
}
#[fixture]
pub fn gbpusd_sim() -> CurrencyPair {
default_fx_ccy(Symbol::from("GBP/USD"), Some(Venue::from("SIM")))
}
#[fixture]
pub fn usdjpy_idealpro() -> CurrencyPair {
default_fx_ccy(Symbol::from("USD/JPY"), Some(Venue::from("IDEALPRO")))
}
#[fixture]
pub fn equity_aapl() -> Equity {
Equity::new(
InstrumentId::from("AAPL.XNAS"),
Symbol::from("AAPL"),
Some(Ustr::from("US0378331005")),
Currency::from("USD"),
2,
Price::from("0.01"),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
pub fn equity_aapl_itch() -> Equity {
Equity::new(
InstrumentId::from("AAPL.XNAS"),
Symbol::from("AAPL"),
Some(Ustr::from("US0378331005")),
Currency::from("USD"),
4,
Price::from("0.0001"),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
UnixNanos::default(),
UnixNanos::default(),
)
}
pub fn futures_contract_es(
activation: Option<UnixNanos>,
expiration: Option<UnixNanos>,
) -> FuturesContract {
let activation = activation.unwrap_or(UnixNanos::from(
Utc.with_ymd_and_hms(2021, 9, 10, 0, 0, 0)
.unwrap()
.timestamp_nanos_opt()
.unwrap() as u64,
));
let expiration = expiration.unwrap_or(UnixNanos::from(
Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0)
.unwrap()
.timestamp_nanos_opt()
.unwrap() as u64,
));
FuturesContract::new(
InstrumentId::from("ESZ21.GLBX"),
Symbol::from("ESZ21"),
AssetClass::Index,
Some(Ustr::from("XCME")),
Ustr::from("ES"),
activation,
expiration,
Currency::USD(),
2,
Price::from("0.01"),
Quantity::from(1),
Quantity::from(1),
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn futures_spread_es() -> FuturesSpread {
let activation = Utc.with_ymd_and_hms(2022, 6, 21, 13, 30, 0).unwrap();
let expiration = Utc.with_ymd_and_hms(2024, 6, 21, 13, 30, 0).unwrap();
FuturesSpread::new(
InstrumentId::from("ESM4-ESU4.GLBX"),
Symbol::from("ESM4-ESU4"),
AssetClass::Index,
Some(Ustr::from("XCME")),
Ustr::from("ES"),
Ustr::from("EQ"),
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
Currency::USD(),
2,
Price::from("0.01"),
Quantity::from(1),
Quantity::from(1),
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn option_contract_appl() -> OptionContract {
let activation = Utc.with_ymd_and_hms(2021, 9, 17, 0, 0, 0).unwrap();
let expiration = Utc.with_ymd_and_hms(2021, 12, 17, 0, 0, 0).unwrap();
OptionContract::new(
InstrumentId::from("AAPL211217C00150000.OPRA"),
Symbol::from("AAPL211217C00150000"),
AssetClass::Equity,
Some(Ustr::from("GMNI")),
Ustr::from("AAPL"),
OptionKind::Call,
Price::from("149.0"),
Currency::USD(),
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
2,
Price::from("0.01"),
Quantity::from(1),
Quantity::from(1),
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn option_spread() -> OptionSpread {
let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
OptionSpread::new(
InstrumentId::from("UD:U$: GN 2534559.GLBX"),
Symbol::from("UD:U$: GN 2534559"),
AssetClass::FX,
Some(Ustr::from("XCME")),
Ustr::from("SR3"),
Ustr::from("GN"),
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
Currency::USD(),
2,
Price::from("0.01"),
Quantity::from(1),
Quantity::from(1),
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn betting() -> BettingInstrument {
let raw_symbol = Symbol::new("1-123456789");
let id = InstrumentId::from(format!("{raw_symbol}.BETFAIR"));
let event_type_id = 6423;
let event_type_name = Ustr::from("American Football");
let competition_id = 12282733;
let competition_name = Ustr::from("NFL");
let event_id = 29678534;
let event_name = Ustr::from("NFL");
let event_country_code = Ustr::from("GB");
let event_open_date = UnixNanos::from(
Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
.unwrap()
.timestamp_nanos_opt()
.unwrap() as u64,
);
let betting_type = Ustr::from("ODDS");
let market_id = Ustr::from("1-123456789");
let market_name = Ustr::from("AFC Conference Winner");
let market_type = Ustr::from("SPECIAL");
let market_start_time = UnixNanos::from(
Utc.with_ymd_and_hms(2022, 2, 7, 23, 30, 0)
.unwrap()
.timestamp_nanos_opt()
.unwrap() as u64,
);
let selection_id = 50214;
let selection_name = Ustr::from("Kansas City Chiefs");
let selection_handicap = 0.0;
let currency = Currency::GBP();
let price_increment = Price::from("0.01");
let size_increment = Quantity::from("0.01");
let max_quantity = Some(Quantity::from("1000"));
let min_quantity = Some(Quantity::from("1"));
let max_notional = Some(Money::from("10000 GBP"));
let min_notional = Some(Money::from("10 GBP"));
let max_price = Some(Price::from("100.00"));
let min_price = Some(Price::from("1.00"));
let margin_init = Some(Decimal::from(1));
let margin_maint = Some(Decimal::from(1));
let maker_fee = Some(Decimal::from(0));
let taker_fee = Some(Decimal::from(0));
let ts_event = UnixNanos::default();
let ts_init = UnixNanos::default();
BettingInstrument::new(
id,
raw_symbol,
event_type_id,
event_type_name,
competition_id,
competition_name,
event_id,
event_name,
event_country_code,
event_open_date,
betting_type,
market_id,
market_name,
market_type,
market_start_time,
selection_id,
selection_name,
selection_handicap,
currency,
price_increment.precision,
size_increment.precision,
price_increment,
size_increment,
max_quantity,
min_quantity,
max_notional,
min_notional,
max_price,
min_price,
margin_init,
margin_maint,
maker_fee,
taker_fee,
None, ts_event,
ts_init,
)
}
#[fixture]
pub fn commodity_gold() -> Commodity {
Commodity::new(
InstrumentId::from("GOLD.COMEX"),
Symbol::from("GOLD"),
AssetClass::Commodity,
Currency::from("USD"),
2,
0,
Price::from("0.01"),
Quantity::from("1"),
Some(Quantity::from("1")),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn index_instrument_spx() -> IndexInstrument {
IndexInstrument::new(
InstrumentId::from("SPX.INDEX"),
Symbol::from("SPX"),
Currency::from("USD"),
2,
0,
Price::from("0.01"),
Quantity::from("1"),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn cfd_gold() -> Cfd {
Cfd::new(
InstrumentId::from("GOLD-CFD.SIM"),
Symbol::from("GOLD-CFD"),
AssetClass::Commodity,
None,
Currency::from("USD"),
2,
0,
Price::from("0.01"),
Quantity::from("1"),
Some(Quantity::from("1")),
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn perpetual_contract_eurusd() -> PerpetualContract {
PerpetualContract::new(
InstrumentId::from("EURUSD-PERP.AX"),
Symbol::from("EURUSD-PERP"),
Ustr::from("EURUSD"),
AssetClass::FX,
Some(Currency::from("EUR")),
Currency::from("USD"),
Currency::from("USD"),
false,
5,
0,
Price::from("0.00001"),
Quantity::from("1"),
None,
None,
None,
None,
None,
None,
None,
None,
Some(dec!(0.03)),
Some(dec!(0.03)),
Some(dec!(0.00002)),
Some(dec!(0.00002)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn binary_option() -> BinaryOption {
let raw_symbol = Symbol::new(
"0x12a0cb60174abc437bf1178367c72d11f069e1a3add20b148fb0ab4279b772b2-92544998123698303655208967887569360731013655782348975589292031774495159624905",
);
let activation = Utc.with_ymd_and_hms(2023, 11, 6, 20, 54, 7).unwrap();
let expiration = Utc.with_ymd_and_hms(2024, 2, 23, 22, 59, 0).unwrap();
let price_increment = Price::from("0.001");
let size_increment = Quantity::from("0.01");
BinaryOption::new(
InstrumentId::from("{raw_symbol}.POLYMARKET"),
raw_symbol,
AssetClass::Alternative,
Currency::USDC(),
UnixNanos::from(activation.timestamp_nanos_opt().unwrap() as u64),
UnixNanos::from(expiration.timestamp_nanos_opt().unwrap() as u64),
price_increment.precision,
size_increment.precision,
price_increment,
size_increment,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None, UnixNanos::default(),
UnixNanos::default(),
)
}
#[fixture]
pub fn tokenized_asset_aaplx() -> TokenizedAsset {
TokenizedAsset::new(
InstrumentId::from("AAPLx/USD.KRAKEN"),
Symbol::from("AAPLxUSD"),
AssetClass::Equity,
Currency::get_or_create_crypto("AAPLx"),
Currency::from("USD"),
None,
2,
4,
Price::from("0.01"),
Quantity::from("0.0001"),
None,
None,
None,
Some(Quantity::from("0.0001")),
None,
None,
None,
None,
None,
None,
Some(dec!(-0.0002)),
Some(dec!(0.001)),
None, UnixNanos::default(),
UnixNanos::default(),
)
}