architect_api/symbology/
protocol.rs

1use super::*;
2use crate::SequenceIdAndNumber;
3use chrono::{DateTime, Utc};
4use derive::grpc;
5use schemars::JsonSchema;
6use serde::{Deserialize, Serialize};
7use serde_with::skip_serializing_none;
8use std::collections::{BTreeMap, BTreeSet};
9
10/// List all symbols
11#[grpc(package = "json.architect")]
12#[grpc(service = "Symbology", name = "symbols", response = "SymbolsResponse")]
13#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
14pub struct SymbolsRequest {}
15
16#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
17pub struct SymbolsResponse {
18    pub symbols: Vec<String>,
19}
20
21#[grpc(package = "json.architect")]
22#[grpc(
23    service = "Symbology",
24    name = "execution_info",
25    response = "ExecutionInfoResponse"
26)]
27#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
28pub struct ExecutionInfoRequest {
29    pub symbol: String,
30    pub execution_venue: Option<ExecutionVenue>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
34pub struct ExecutionInfoResponse {
35    pub execution_info: BTreeMap<ExecutionVenue, ExecutionInfo>,
36}
37
38#[grpc(package = "json.architect")]
39#[grpc(
40    service = "Symbology",
41    name = "futures_series",
42    response = "FuturesSeriesResponse"
43)]
44#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
45pub struct FuturesSeriesRequest {
46    pub series_symbol: String,
47    #[serde(default)]
48    pub include_expired: bool,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
52pub struct FuturesSeriesResponse {
53    pub futures: Vec<Product>,
54}
55
56#[grpc(package = "json.architect")]
57#[grpc(service = "Symbology", name = "symbology", response = "SymbologySnapshot")]
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
59pub struct SymbologyRequest {}
60
61#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
62pub struct SymbologySnapshot {
63    #[serde(flatten)]
64    pub sequence: SequenceIdAndNumber,
65    pub products: BTreeMap<Product, ProductInfo>,
66    #[serde(default)]
67    pub product_aliases: BTreeMap<AliasKind, BTreeMap<String, Product>>,
68    #[serde(default)]
69    pub product_catalog: BTreeMap<ExecutionVenue, BTreeMap<String, ProductCatalogInfo>>,
70    pub options_series: BTreeMap<OptionsSeries, OptionsSeriesInfo>,
71    pub execution_info:
72        BTreeMap<TradableProduct, BTreeMap<ExecutionVenue, ExecutionInfo>>,
73}
74
75impl SymbologySnapshot {
76    pub fn exchange_symbols(
77        &self,
78        venue: &ExecutionVenue,
79    ) -> BTreeMap<TradableProduct, String> {
80        let mut map = BTreeMap::new();
81        for (symbol, infos) in &self.execution_info {
82            if let Some(exchange_symbol) =
83                infos.get(venue).and_then(|info| info.exchange_symbol.as_ref())
84            {
85                map.insert(symbol.clone(), exchange_symbol.clone());
86            }
87        }
88        map
89    }
90
91    pub fn exchange_symbol(
92        &self,
93        venue: &str,
94        symbol: &TradableProduct,
95    ) -> Option<&String> {
96        self.execution_info.get(symbol).and_then(|infos| {
97            infos.get(venue).and_then(|info| info.exchange_symbol.as_ref())
98        })
99    }
100
101    pub fn filter_venue(mut self, venue: &ExecutionVenue) -> Self {
102        let mut out = Self::default();
103        let mut products = BTreeSet::default();
104        for (symbol, infos) in self.execution_info {
105            for (venue_key, info) in infos {
106                if &venue_key != venue {
107                    continue;
108                }
109                out.execution_info
110                    .entry(symbol.clone())
111                    .or_default()
112                    .insert(venue.clone(), info);
113                products.insert(symbol.base());
114                if let Some(quote) = symbol.quote() {
115                    products.insert(quote);
116                }
117            }
118        }
119        for (alias_kind, alias_map) in self.product_aliases {
120            for (alias, product) in alias_map {
121                if products.contains(&product) {
122                    out.product_aliases
123                        .entry(alias_kind)
124                        .or_default()
125                        .insert(alias, product);
126                }
127            }
128        }
129        for product in products {
130            if let Some(info) = self.products.remove(&product) {
131                out.products.insert(product, info);
132            }
133        }
134        out
135    }
136}
137
138#[skip_serializing_none]
139#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
140pub struct SymbologyUpdate {
141    #[serde(flatten)]
142    pub sequence: SequenceIdAndNumber,
143    #[serde(default)]
144    pub products: Option<SnapshotOrUpdate<Product, ProductInfo>>,
145    #[serde(default)]
146    pub product_aliases:
147        Option<SnapshotOrUpdate<AliasKind, SnapshotOrUpdate<String, Product>>>,
148    #[serde(default)]
149    pub product_catalog: Option<
150        SnapshotOrUpdate<ExecutionVenue, SnapshotOrUpdate<String, ProductCatalogInfo>>,
151    >,
152    #[serde(default)]
153    pub options_series: Option<SnapshotOrUpdate<OptionsSeries, OptionsSeriesInfo>>,
154    #[serde(default)]
155    pub execution_info: Option<
156        SnapshotOrUpdate<
157            TradableProduct,
158            SnapshotOrUpdate<ExecutionVenue, ExecutionInfo>,
159        >,
160    >,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
164#[serde(untagged)]
165pub enum SnapshotOrUpdate<K: Eq + Ord, V> {
166    Snapshot { snapshot: BTreeMap<K, V> },
167    Update { updates: Vec<(K, Option<V>)> },
168}
169
170impl<K: Eq + Ord, V> From<BTreeMap<K, V>> for SnapshotOrUpdate<K, V> {
171    fn from(map: BTreeMap<K, V>) -> Self {
172        SnapshotOrUpdate::Snapshot { snapshot: map }
173    }
174}
175
176impl<K: Eq + Ord, V> SnapshotOrUpdate<K, V> {
177    pub fn apply(self, map: &mut BTreeMap<K, V>) {
178        match self {
179            Self::Snapshot { snapshot } => {
180                *map = snapshot;
181            }
182            Self::Update { updates } => {
183                for (k, v) in updates {
184                    if let Some(v) = v {
185                        map.insert(k, v);
186                    } else {
187                        map.remove(&k);
188                    }
189                }
190            }
191        }
192    }
193}
194
195impl<K0: Eq + Ord, K1: Eq + Ord, V> From<BTreeMap<K0, BTreeMap<K1, V>>>
196    for SnapshotOrUpdate<K0, SnapshotOrUpdate<K1, V>>
197{
198    fn from(map: BTreeMap<K0, BTreeMap<K1, V>>) -> Self {
199        SnapshotOrUpdate::Snapshot {
200            snapshot: map
201                .into_iter()
202                .map(|(k, v)| (k, SnapshotOrUpdate::Snapshot { snapshot: v }))
203                .collect(),
204        }
205    }
206}
207
208impl<K0: Eq + Ord, K1: Eq + Ord, V> SnapshotOrUpdate<K0, SnapshotOrUpdate<K1, V>> {
209    pub fn apply2(self, map: &mut BTreeMap<K0, BTreeMap<K1, V>>) {
210        match self {
211            Self::Snapshot { snapshot } => {
212                map.clear();
213                for (k, t) in snapshot {
214                    match t {
215                        SnapshotOrUpdate::Snapshot { snapshot } => {
216                            map.insert(k, snapshot);
217                        }
218                        u @ SnapshotOrUpdate::Update { .. } => {
219                            let mut v = BTreeMap::new();
220                            u.apply(&mut v);
221                            map.insert(k, v);
222                        }
223                    }
224                }
225            }
226            Self::Update { updates } => {
227                for (k, t) in updates {
228                    match t {
229                        Some(SnapshotOrUpdate::Snapshot { snapshot }) => {
230                            map.insert(k, snapshot);
231                        }
232                        Some(u @ SnapshotOrUpdate::Update { .. }) => {
233                            let v = map.entry(k).or_default();
234                            u.apply(v);
235                        }
236                        None => {
237                            map.remove(&k);
238                        }
239                    }
240                }
241            }
242        }
243    }
244}
245
246#[grpc(package = "json.architect")]
247#[grpc(
248    service = "Symbology",
249    name = "subscribe_symbology",
250    response = "SymbologyUpdate",
251    server_streaming
252)]
253#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
254pub struct SubscribeSymbology {}
255
256#[grpc(package = "json.architect")]
257#[grpc(
258    service = "Symbology",
259    name = "upload_symbology",
260    response = "UploadSymbologyResponse"
261)]
262#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
263pub struct UploadSymbologyRequest {
264    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
265    pub products: BTreeMap<Product, ProductInfo>,
266    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
267    pub product_aliases: BTreeMap<AliasKind, BTreeMap<String, Product>>,
268    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
269    pub options_series: BTreeMap<OptionsSeries, OptionsSeriesInfo>,
270    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
271    pub execution_info:
272        BTreeMap<TradableProduct, BTreeMap<ExecutionVenue, ExecutionInfo>>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
276pub struct UploadSymbologyResponse {}
277
278// One-shot RPC to the symbol store to make it expire symbols
279#[grpc(package = "json.architect")]
280#[grpc(
281    service = "Symbology",
282    name = "prune_expired_symbols",
283    response = "PruneExpiredSymbolsResponse"
284)]
285#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
286pub struct PruneExpiredSymbolsRequest {
287    /// If None then it will just use server current time;
288    /// otherwise, specify a unix timestamp in seconds
289    pub cutoff: Option<i64>,
290}
291
292impl PruneExpiredSymbolsRequest {
293    pub fn new(cutoff: Option<DateTime<Utc>>) -> Self {
294        Self { cutoff: cutoff.map(|dt| dt.timestamp()) }
295    }
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
299pub struct PruneExpiredSymbolsResponse {}
300
301#[grpc(package = "json.architect")]
302#[grpc(
303    service = "Symbology",
304    name = "upload_product_catalog",
305    response = "UploadProductCatalogResponse"
306)]
307#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
308pub struct UploadProductCatalogRequest {
309    pub exchange: ExecutionVenue,
310    pub product_catalog: Vec<ProductCatalogInfo>,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
314pub struct UploadProductCatalogResponse {}
315
316#[grpc(package = "json.architect")]
317#[grpc(
318    service = "Symbology",
319    name = "download_product_catalog",
320    response = "DownloadProductCatalogResponse"
321)]
322#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
323pub struct DownloadProductCatalogRequest {
324    pub exchange: ExecutionVenue,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
328pub struct DownloadProductCatalogResponse {
329    pub product_catalog: Vec<ProductCatalogInfo>,
330}