use crate::{
Underlying,
asset::Asset,
instrument::{
kind::{
InstrumentKind, future::FutureContract, option::OptionContract,
perpetual::PerpetualContract,
},
market_data::{MarketDataInstrument, kind::MarketDataInstrumentKind},
name::{InstrumentNameExchange, InstrumentNameInternal},
quote::InstrumentQuoteAsset,
spec::{InstrumentSpec, InstrumentSpecQuantity, OrderQuantityUnits},
},
};
use derive_more::{Constructor, Display};
use serde::{Deserialize, Serialize};
use std::fmt::Formatter;
pub mod kind;
pub mod name;
pub mod spec;
pub mod market_data;
pub mod quote;
#[derive(
Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Display,
)]
pub struct InstrumentId(pub u64);
#[derive(
Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Constructor,
)]
pub struct InstrumentIndex(pub usize);
impl InstrumentIndex {
pub fn index(&self) -> usize {
self.0
}
}
impl std::fmt::Display for InstrumentIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "InstrumentIndex({})", self.0)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
pub struct Instrument<ExchangeKey, AssetKey> {
pub exchange: ExchangeKey,
pub name_internal: InstrumentNameInternal,
pub name_exchange: InstrumentNameExchange,
pub underlying: Underlying<AssetKey>,
pub quote: InstrumentQuoteAsset,
#[serde(alias = "instrument_kind")]
pub kind: InstrumentKind<AssetKey>,
pub spec: Option<InstrumentSpec<AssetKey>>,
}
impl<ExchangeKey, AssetKey> Instrument<ExchangeKey, AssetKey> {
pub fn new<NameInternal, NameExchange>(
exchange: ExchangeKey,
name_internal: NameInternal,
name_exchange: NameExchange,
underlying: Underlying<AssetKey>,
quote: InstrumentQuoteAsset,
kind: InstrumentKind<AssetKey>,
spec: Option<InstrumentSpec<AssetKey>>,
) -> Self
where
NameInternal: Into<InstrumentNameInternal>,
NameExchange: Into<InstrumentNameExchange>,
{
Self {
exchange,
name_internal: name_internal.into(),
name_exchange: name_exchange.into(),
quote,
underlying,
kind,
spec,
}
}
pub fn spot<NameInternal, NameExchange>(
exchange: ExchangeKey,
name_internal: NameInternal,
name_exchange: NameExchange,
underlying: Underlying<AssetKey>,
spec: Option<InstrumentSpec<AssetKey>>,
) -> Self
where
NameInternal: Into<InstrumentNameInternal>,
NameExchange: Into<InstrumentNameExchange>,
{
Self {
exchange,
name_internal: name_internal.into(),
name_exchange: name_exchange.into(),
quote: InstrumentQuoteAsset::UnderlyingQuote,
underlying,
kind: InstrumentKind::Spot,
spec,
}
}
pub fn map_exchange_key<NewExchangeKey>(
self,
exchange: NewExchangeKey,
) -> Instrument<NewExchangeKey, AssetKey> {
let Instrument {
exchange: _,
name_internal,
name_exchange,
underlying,
quote,
kind,
spec,
} = self;
Instrument {
exchange,
name_internal,
name_exchange,
underlying,
quote,
kind,
spec,
}
}
pub fn map_asset_key_with_lookup<FnFindAsset, NewAssetKey, Error>(
self,
find_asset: FnFindAsset,
) -> Result<Instrument<ExchangeKey, NewAssetKey>, Error>
where
FnFindAsset: Fn(&AssetKey) -> Result<NewAssetKey, Error>,
{
let Instrument {
exchange,
name_internal,
name_exchange,
underlying,
quote,
kind,
spec,
} = self;
let base_new_key = find_asset(&underlying.base)?;
let quote_new_key = find_asset(&underlying.quote)?;
let kind = match kind {
InstrumentKind::Spot => InstrumentKind::Spot,
InstrumentKind::Perpetual(contract) => InstrumentKind::Perpetual(PerpetualContract {
contract_size: contract.contract_size,
settlement_asset: find_asset(&contract.settlement_asset)?,
}),
InstrumentKind::Future(contract) => InstrumentKind::Future(FutureContract {
contract_size: contract.contract_size,
settlement_asset: find_asset(&contract.settlement_asset)?,
expiry: contract.expiry,
}),
InstrumentKind::Option(contract) => InstrumentKind::Option(OptionContract {
contract_size: contract.contract_size,
settlement_asset: find_asset(&contract.settlement_asset)?,
kind: contract.kind,
exercise: contract.exercise,
expiry: contract.expiry,
strike: contract.strike,
}),
};
let spec = match spec {
Some(spec) => {
let InstrumentSpec {
price,
quantity:
InstrumentSpecQuantity {
unit,
min,
increment,
},
notional,
} = spec;
let unit = match unit {
OrderQuantityUnits::Asset(asset) => {
OrderQuantityUnits::Asset(find_asset(&asset)?)
}
OrderQuantityUnits::Contract => OrderQuantityUnits::Contract,
OrderQuantityUnits::Quote => OrderQuantityUnits::Quote,
};
Some(InstrumentSpec {
price,
quantity: InstrumentSpecQuantity {
unit,
min,
increment,
},
notional,
})
}
None => None,
};
Ok(Instrument {
exchange,
name_internal,
name_exchange,
underlying: Underlying::new(base_new_key, quote_new_key),
quote,
kind,
spec,
})
}
}
impl<ExchangeKey> From<&Instrument<ExchangeKey, Asset>> for MarketDataInstrument {
fn from(value: &Instrument<ExchangeKey, Asset>) -> Self {
Self {
base: value.underlying.base.name_internal.clone(),
quote: value.underlying.quote.name_internal.clone(),
kind: MarketDataInstrumentKind::from(&value.kind),
}
}
}