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
19pub mod kind;
21
22pub mod name;
25
26pub mod spec;
31
32pub mod market_data;
35
36pub mod quote;
38
39#[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#[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 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 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 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 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}