barter_execution/
map.rs

1use crate::error::KeyError;
2use barter_instrument::{
3    Keyed,
4    asset::{AssetIndex, name::AssetNameExchange},
5    exchange::{ExchangeId, ExchangeIndex},
6    index::{IndexedInstruments, error::IndexError},
7    instrument::{InstrumentIndex, name::InstrumentNameExchange},
8};
9use barter_integration::collection::{FnvIndexMap, FnvIndexSet};
10use fnv::FnvHashMap;
11
12/// Indexed instrument map used to associate the internal Barter representation of instruments and
13/// assets with the [`ExecutionClient`](super::client::ExecutionClient) representation.
14///
15/// Similarly, when the execution manager received an [`AccountEvent`](super::AccountEvent)
16/// from the execution API, it needs to determine the internal representation of the associated
17/// assets and instruments.
18///
19/// eg/ `InstrumentNameExchange("XBT-USDT")` <--> `InstrumentIndex(1)` <br>
20/// eg/ `AssetNameExchange("XBT")` <--> `AssetIndex(1)`
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ExecutionInstrumentMap {
23    pub exchange: Keyed<ExchangeIndex, ExchangeId>,
24    pub assets: FnvIndexSet<AssetNameExchange>,
25    pub instruments: FnvIndexSet<InstrumentNameExchange>,
26    pub asset_names: FnvHashMap<AssetNameExchange, AssetIndex>,
27    pub instrument_names: FnvHashMap<InstrumentNameExchange, InstrumentIndex>,
28}
29
30impl ExecutionInstrumentMap {
31    /// Construct a new [`Self`] using the provided indexed assets and instruments.
32    pub fn new(
33        exchange: Keyed<ExchangeIndex, ExchangeId>,
34        assets: FnvIndexMap<AssetIndex, AssetNameExchange>,
35        instruments: FnvIndexMap<InstrumentIndex, InstrumentNameExchange>,
36    ) -> Self {
37        Self {
38            exchange,
39            asset_names: assets
40                .iter()
41                .map(|(key, value)| (value.clone(), *key))
42                .collect(),
43            instrument_names: instruments
44                .iter()
45                .map(|(key, value)| (value.clone(), *key))
46                .collect(),
47            assets: assets.into_values().collect(),
48            instruments: instruments.into_values().collect(),
49        }
50    }
51
52    pub fn exchange_assets(&self) -> impl Iterator<Item = &AssetNameExchange> {
53        self.assets.iter()
54    }
55
56    pub fn exchange_instruments(&self) -> impl Iterator<Item = &InstrumentNameExchange> {
57        self.instruments.iter()
58    }
59
60    pub fn find_exchange_id(&self, exchange: ExchangeIndex) -> Result<ExchangeId, KeyError> {
61        if self.exchange.key == exchange {
62            Ok(self.exchange.value)
63        } else {
64            Err(KeyError::ExchangeId(format!(
65                "ExecutionInstrumentMap does not contain {exchange}"
66            )))
67        }
68    }
69
70    pub fn find_exchange_index(&self, exchange: ExchangeId) -> Result<ExchangeIndex, IndexError> {
71        if self.exchange.value == exchange {
72            Ok(self.exchange.key)
73        } else {
74            Err(IndexError::ExchangeIndex(format!(
75                "ExecutionInstrumentMap does not contain {exchange}"
76            )))
77        }
78    }
79
80    pub fn find_asset_name_exchange(
81        &self,
82        asset: AssetIndex,
83    ) -> Result<&AssetNameExchange, KeyError> {
84        self.assets.get_index(asset.index()).ok_or_else(|| {
85            KeyError::AssetKey(format!("ExecutionInstrumentMap does not contain: {asset}"))
86        })
87    }
88
89    pub fn find_asset_index(&self, asset: &AssetNameExchange) -> Result<AssetIndex, IndexError> {
90        self.asset_names.get(asset).copied().ok_or_else(|| {
91            IndexError::AssetIndex(format!("ExecutionInstrumentMap does not contain: {asset}"))
92        })
93    }
94
95    pub fn find_instrument_name_exchange(
96        &self,
97        instrument: InstrumentIndex,
98    ) -> Result<&InstrumentNameExchange, KeyError> {
99        self.instruments
100            .get_index(instrument.index())
101            .ok_or_else(|| {
102                KeyError::InstrumentKey(format!(
103                    "ExecutionInstrumentMap does not contain: {instrument}"
104                ))
105            })
106    }
107
108    pub fn find_instrument_index(
109        &self,
110        instrument: &InstrumentNameExchange,
111    ) -> Result<InstrumentIndex, IndexError> {
112        self.instrument_names
113            .get(instrument)
114            .copied()
115            .ok_or_else(|| {
116                IndexError::InstrumentIndex(format!(
117                    "ExecutionInstrumentMap does not contain: {instrument}"
118                ))
119            })
120    }
121}
122
123pub fn generate_execution_instrument_map(
124    instruments: &IndexedInstruments,
125    exchange: ExchangeId,
126) -> Result<ExecutionInstrumentMap, IndexError> {
127    let exchange_index = instruments
128        .exchanges()
129        .iter()
130        .find_map(|keyed_exchange| (keyed_exchange.value == exchange).then_some(keyed_exchange.key))
131        .ok_or_else(|| {
132            IndexError::ExchangeIndex(format!(
133                "IndexedInstrument does not contain index for: {exchange}"
134            ))
135        })?;
136
137    Ok(ExecutionInstrumentMap::new(
138        Keyed::new(exchange_index, exchange),
139        instruments
140            .assets()
141            .iter()
142            .filter_map(|asset| {
143                (asset.value.exchange == exchange)
144                    .then_some((asset.key, asset.value.asset.name_exchange.clone()))
145            })
146            .collect(),
147        instruments
148            .instruments()
149            .iter()
150            .filter_map(|instrument| {
151                (instrument.value.exchange.value == exchange)
152                    .then_some((instrument.key, instrument.value.name_exchange.clone()))
153            })
154            .collect(),
155    ))
156}