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;
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(service = "Symbology", name = "symbology", response = "SymbologySnapshot")]
23#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
24pub struct SymbologyRequest {}
25
26#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
27pub struct SymbologySnapshot {
28    #[serde(flatten)]
29    pub sequence: SequenceIdAndNumber,
30    pub products: BTreeMap<Product, ProductInfo>,
31    #[serde(default)]
32    pub product_aliases: BTreeMap<AliasKind, BTreeMap<String, Product>>,
33    pub options_series: BTreeMap<OptionsSeries, OptionsSeriesInfo>,
34    pub execution_info:
35        BTreeMap<TradableProduct, BTreeMap<ExecutionVenue, ExecutionInfo>>,
36}
37
38impl SymbologySnapshot {
39    pub fn exchange_symbols(
40        &self,
41        venue: &ExecutionVenue,
42    ) -> BTreeMap<TradableProduct, String> {
43        let mut map = BTreeMap::new();
44        for (symbol, infos) in &self.execution_info {
45            if let Some(exchange_symbol) =
46                infos.get(venue).and_then(|info| info.exchange_symbol.as_ref())
47            {
48                map.insert(symbol.clone(), exchange_symbol.clone());
49            }
50        }
51        map
52    }
53}
54
55#[skip_serializing_none]
56#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema)]
57pub struct SymbologyUpdate {
58    #[serde(flatten)]
59    pub sequence: SequenceIdAndNumber,
60    #[serde(default)]
61    pub products: Option<SnapshotOrUpdate<Product, ProductInfo>>,
62    #[serde(default)]
63    pub product_aliases:
64        Option<SnapshotOrUpdate<AliasKind, SnapshotOrUpdate<String, Product>>>,
65    #[serde(default)]
66    pub options_series: Option<SnapshotOrUpdate<OptionsSeries, OptionsSeriesInfo>>,
67    #[serde(default)]
68    pub execution_info: Option<
69        SnapshotOrUpdate<
70            TradableProduct,
71            SnapshotOrUpdate<ExecutionVenue, ExecutionInfo>,
72        >,
73    >,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
77#[serde(untagged)]
78pub enum SnapshotOrUpdate<K: Eq + Ord, V> {
79    Snapshot { snapshot: BTreeMap<K, V> },
80    Update { updates: Vec<(K, Option<V>)> },
81}
82
83impl<K: Eq + Ord, V> From<BTreeMap<K, V>> for SnapshotOrUpdate<K, V> {
84    fn from(map: BTreeMap<K, V>) -> Self {
85        SnapshotOrUpdate::Snapshot { snapshot: map }
86    }
87}
88
89impl<K: Eq + Ord, V> SnapshotOrUpdate<K, V> {
90    pub fn apply(self, map: &mut BTreeMap<K, V>) {
91        match self {
92            Self::Snapshot { snapshot } => {
93                *map = snapshot;
94            }
95            Self::Update { updates } => {
96                for (k, v) in updates {
97                    if let Some(v) = v {
98                        map.insert(k, v);
99                    } else {
100                        map.remove(&k);
101                    }
102                }
103            }
104        }
105    }
106}
107
108impl<K0: Eq + Ord, K1: Eq + Ord, V> From<BTreeMap<K0, BTreeMap<K1, V>>>
109    for SnapshotOrUpdate<K0, SnapshotOrUpdate<K1, V>>
110{
111    fn from(map: BTreeMap<K0, BTreeMap<K1, V>>) -> Self {
112        SnapshotOrUpdate::Snapshot {
113            snapshot: map
114                .into_iter()
115                .map(|(k, v)| (k, SnapshotOrUpdate::Snapshot { snapshot: v }))
116                .collect(),
117        }
118    }
119}
120
121impl<K0: Eq + Ord, K1: Eq + Ord, V> SnapshotOrUpdate<K0, SnapshotOrUpdate<K1, V>> {
122    pub fn apply2(self, map: &mut BTreeMap<K0, BTreeMap<K1, V>>) {
123        match self {
124            Self::Snapshot { snapshot } => {
125                map.clear();
126                for (k, t) in snapshot {
127                    match t {
128                        SnapshotOrUpdate::Snapshot { snapshot } => {
129                            map.insert(k, snapshot);
130                        }
131                        u @ SnapshotOrUpdate::Update { .. } => {
132                            let mut v = BTreeMap::new();
133                            u.apply(&mut v);
134                            map.insert(k, v);
135                        }
136                    }
137                }
138            }
139            Self::Update { updates } => {
140                for (k, t) in updates {
141                    match t {
142                        Some(SnapshotOrUpdate::Snapshot { snapshot }) => {
143                            map.insert(k, snapshot);
144                        }
145                        Some(u @ SnapshotOrUpdate::Update { .. }) => {
146                            let v = map.entry(k).or_default();
147                            u.apply(v);
148                        }
149                        None => {
150                            map.remove(&k);
151                        }
152                    }
153                }
154            }
155        }
156    }
157}
158
159#[grpc(package = "json.architect")]
160#[grpc(
161    service = "Symbology",
162    name = "subscribe_symbology",
163    response = "SymbologyUpdate",
164    server_streaming
165)]
166#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
167pub struct SubscribeSymbology {}
168
169#[grpc(package = "json.architect")]
170#[grpc(
171    service = "Symbology",
172    name = "upload_symbology",
173    response = "UploadSymbologyResponse"
174)]
175#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
176pub struct UploadSymbologyRequest {
177    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
178    pub products: BTreeMap<Product, ProductInfo>,
179    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
180    pub product_aliases: BTreeMap<AliasKind, BTreeMap<String, Product>>,
181    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
182    pub options_series: BTreeMap<OptionsSeries, OptionsSeriesInfo>,
183    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
184    pub execution_info:
185        BTreeMap<TradableProduct, BTreeMap<ExecutionVenue, ExecutionInfo>>,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
189pub struct UploadSymbologyResponse {}
190
191// One-shot RPC to the symbol store to make it expire symbols
192#[grpc(package = "json.architect")]
193#[grpc(
194    service = "Symbology",
195    name = "prune_expired_symbols",
196    response = "PruneExpiredSymbolsResponse"
197)]
198#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
199pub struct PruneExpiredSymbolsRequest {
200    /// If None then it will just use server current time;
201    /// otherwise, specify a unix timestamp in seconds
202    pub cutoff: Option<i64>,
203}
204
205impl PruneExpiredSymbolsRequest {
206    pub fn new(cutoff: Option<DateTime<Utc>>) -> Self {
207        Self { cutoff: cutoff.map(|dt| dt.timestamp()) }
208    }
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
212pub struct PruneExpiredSymbolsResponse {}