Skip to main content

rustrade_instrument/instrument/
mod.rs

1use crate::{
2    Underlying,
3    asset::Asset,
4    instrument::{
5        kind::{
6            InstrumentKind, future::FutureContract, option::OptionContract,
7            perpetual::PerpetualContract,
8        },
9        market_data::{MarketDataInstrument, kind::MarketDataInstrumentKind},
10        name::{InstrumentNameExchange, InstrumentNameInternal},
11        quote::InstrumentQuoteAsset,
12        spec::{InstrumentSpec, InstrumentSpecQuantity, OrderQuantityUnits},
13    },
14};
15use derive_more::{Constructor, Display};
16use serde::{Deserialize, Serialize};
17use std::fmt::Formatter;
18
19/// Defines an [`Instrument`]s [`InstrumentKind`] (eg/ Spot, Perpetual, etc).
20pub mod kind;
21
22/// Defines the [`InstrumentNameExchange`] and [`InstrumentNameExchange`] types, used as
23/// `SmolStr` identifiers for an [`Instrument`].
24pub mod name;
25
26/// Defines the [`InstrumentSpec`], including specifications for an [`Instrument`]s
27/// price, quantity and notional value.
28///
29/// eg/ `InstrumentSpecPrice.tick_size`, `OrderQuantityUnits`, etc.
30pub mod spec;
31
32/// Defines a simplified [`MarketDataInstrument`], with only the necessary data to subscribe to
33/// market data feeds.
34pub mod market_data;
35
36/// Defines the [`InstrumentQuoteAsset`] (underlying base or quote) for an [`Instrument`].
37pub mod quote;
38
39/// Unique identifier for an `Instrument` traded on an execution.
40///
41/// Used to key data events in a memory efficient way.
42#[derive(
43    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Display,
44)]
45pub struct InstrumentId(pub u64);
46
47#[derive(
48    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Constructor,
49)]
50pub struct InstrumentIndex(pub usize);
51
52impl InstrumentIndex {
53    pub fn index(&self) -> usize {
54        self.0
55    }
56}
57
58impl std::fmt::Display for InstrumentIndex {
59    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
60        write!(f, "InstrumentIndex({})", self.0)
61    }
62}
63
64/// Comprehensive Instrument model, containing all the data required to subscribe to market data
65/// and generate correct orders.
66#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
67pub struct Instrument<ExchangeKey, AssetKey> {
68    pub exchange: ExchangeKey,
69    pub name_internal: InstrumentNameInternal,
70    pub name_exchange: InstrumentNameExchange,
71    pub underlying: Underlying<AssetKey>,
72    pub quote: InstrumentQuoteAsset,
73    #[serde(alias = "instrument_kind")]
74    pub kind: InstrumentKind<AssetKey>,
75    pub spec: Option<InstrumentSpec<AssetKey>>,
76}
77
78impl<ExchangeKey, AssetKey> Instrument<ExchangeKey, AssetKey> {
79    /// Construct a new `Instrument` with the provided data.
80    ///
81    /// This constructor assumes the [`InstrumentNameInternal`] can be constructed in the default
82    /// style via the [`InstrumentNameInternal::new_from_exchange`] constructor.
83    pub fn new<NameInternal, NameExchange>(
84        exchange: ExchangeKey,
85        name_internal: NameInternal,
86        name_exchange: NameExchange,
87        underlying: Underlying<AssetKey>,
88        quote: InstrumentQuoteAsset,
89        kind: InstrumentKind<AssetKey>,
90        spec: Option<InstrumentSpec<AssetKey>>,
91    ) -> Self
92    where
93        NameInternal: Into<InstrumentNameInternal>,
94        NameExchange: Into<InstrumentNameExchange>,
95    {
96        Self {
97            exchange,
98            name_internal: name_internal.into(),
99            name_exchange: name_exchange.into(),
100            quote,
101            underlying,
102            kind,
103            spec,
104        }
105    }
106
107    /// Construct a new `Spot` `Instrument` with the provided data.
108    ///
109    /// This constructor assumes the [`InstrumentNameInternal`] can be constructed in the default
110    /// style via the [`InstrumentNameInternal::new_from_exchange`] constructor.
111    pub fn spot<NameInternal, NameExchange>(
112        exchange: ExchangeKey,
113        name_internal: NameInternal,
114        name_exchange: NameExchange,
115        underlying: Underlying<AssetKey>,
116        spec: Option<InstrumentSpec<AssetKey>>,
117    ) -> Self
118    where
119        NameInternal: Into<InstrumentNameInternal>,
120        NameExchange: Into<InstrumentNameExchange>,
121    {
122        Self {
123            exchange,
124            name_internal: name_internal.into(),
125            name_exchange: name_exchange.into(),
126            quote: InstrumentQuoteAsset::UnderlyingQuote,
127            underlying,
128            kind: InstrumentKind::Spot,
129            spec,
130        }
131    }
132
133    /// Map this Instruments `ExchangeKey` to a new key.
134    pub fn map_exchange_key<NewExchangeKey>(
135        self,
136        exchange: NewExchangeKey,
137    ) -> Instrument<NewExchangeKey, AssetKey> {
138        let Instrument {
139            exchange: _,
140            name_internal,
141            name_exchange,
142            underlying,
143            quote,
144            kind,
145            spec,
146        } = self;
147
148        Instrument {
149            exchange,
150            name_internal,
151            name_exchange,
152            underlying,
153            quote,
154            kind,
155            spec,
156        }
157    }
158
159    /// Map this Instruments `AssetKey` to a new key, using the provided lookup closure.
160    pub fn map_asset_key_with_lookup<FnFindAsset, NewAssetKey, Error>(
161        self,
162        find_asset: FnFindAsset,
163    ) -> Result<Instrument<ExchangeKey, NewAssetKey>, Error>
164    where
165        FnFindAsset: Fn(&AssetKey) -> Result<NewAssetKey, Error>,
166    {
167        let Instrument {
168            exchange,
169            name_internal,
170            name_exchange,
171            underlying,
172            quote,
173            kind,
174            spec,
175        } = self;
176
177        let base_new_key = find_asset(&underlying.base)?;
178        let quote_new_key = find_asset(&underlying.quote)?;
179
180        let kind = match kind {
181            InstrumentKind::Spot => InstrumentKind::Spot,
182            InstrumentKind::Perpetual(contract) => InstrumentKind::Perpetual(PerpetualContract {
183                contract_size: contract.contract_size,
184                settlement_asset: find_asset(&contract.settlement_asset)?,
185            }),
186            InstrumentKind::Future(contract) => InstrumentKind::Future(FutureContract {
187                contract_size: contract.contract_size,
188                settlement_asset: find_asset(&contract.settlement_asset)?,
189                expiry: contract.expiry,
190            }),
191            InstrumentKind::Option(contract) => InstrumentKind::Option(OptionContract {
192                contract_size: contract.contract_size,
193                settlement_asset: find_asset(&contract.settlement_asset)?,
194                kind: contract.kind,
195                exercise: contract.exercise,
196                expiry: contract.expiry,
197                strike: contract.strike,
198            }),
199        };
200
201        let spec = match spec {
202            Some(spec) => {
203                let InstrumentSpec {
204                    price,
205                    quantity:
206                        InstrumentSpecQuantity {
207                            unit,
208                            min,
209                            increment,
210                        },
211                    notional,
212                } = spec;
213
214                let unit = match unit {
215                    OrderQuantityUnits::Asset(asset) => {
216                        OrderQuantityUnits::Asset(find_asset(&asset)?)
217                    }
218                    OrderQuantityUnits::Contract => OrderQuantityUnits::Contract,
219                    OrderQuantityUnits::Quote => OrderQuantityUnits::Quote,
220                };
221
222                Some(InstrumentSpec {
223                    price,
224                    quantity: InstrumentSpecQuantity {
225                        unit,
226                        min,
227                        increment,
228                    },
229                    notional,
230                })
231            }
232            None => None,
233        };
234
235        Ok(Instrument {
236            exchange,
237            name_internal,
238            name_exchange,
239            underlying: Underlying::new(base_new_key, quote_new_key),
240            quote,
241            kind,
242            spec,
243        })
244    }
245}
246
247impl<ExchangeKey> From<&Instrument<ExchangeKey, Asset>> for MarketDataInstrument {
248    fn from(value: &Instrument<ExchangeKey, Asset>) -> Self {
249        Self {
250            base: value.underlying.base.name_internal.clone(),
251            quote: value.underlying.quote.name_internal.clone(),
252            kind: MarketDataInstrumentKind::from(&value.kind),
253        }
254    }
255}