architect_api/
typed_message.rs

1#[cfg(feature = "netidx")]
2use super::*;
3#[cfg(feature = "netidx")]
4use derive::{FromInner, FromValue, TryIntoAnyInner};
5use enumflags2::bitflags;
6#[cfg(feature = "netidx")]
7use enumflags2::BitFlags;
8#[cfg(feature = "netidx")]
9use netidx_derive::Pack;
10#[cfg(feature = "netidx")]
11use schemars::JsonSchema;
12use schemars::JsonSchema_repr;
13#[cfg(feature = "netidx")]
14use serde::{Deserialize, Serialize};
15use serde_json::json;
16
17/// TypedMessage is a wrapper enum for component messages, for all components that
18/// this version of Architect is compiled with and supports.  This lets components
19/// define and operate over their own independent message types while still allowing 
20/// cross-component communication.
21///
22/// Architect installations are mutually intelligible to the extent of TypedMessage
23/// variants they share in common.
24///
25/// TypedMessage should follow sensible rules for versioning and cross-
26/// compatibility, such as explicit tagging of variants, and avoiding breaking 
27/// changes to the component message types.
28#[cfg(feature = "netidx")]
29#[derive(Debug, Clone, Pack, FromValue, Serialize, Deserialize, FromInner, TryIntoAnyInner, JsonSchema)]
30#[transitive(B2C2Cpty <-> Orderflow)]
31#[transitive(BinanceCpty <-> Orderflow)]
32#[transitive(BinanceCpty <-> Folio)]
33#[transitive(CboeDigitalCpty <-> Folio)]
34#[transitive(CoinbaseCpty <-> Folio)]
35#[transitive(CoinbaseCpty <-> Orderflow)]
36#[transitive(CoinbasePrimeCpty <-> Folio)]
37#[transitive(CoinbasePrimeCpty <-> Orderflow)]
38#[transitive(CqgCpty <-> Orderflow)]
39#[transitive(CqgCpty <-> Folio)]
40#[transitive(CumberlandCpty <-> Orderflow)]
41#[transitive(CumberlandCpty <-> Folio)]
42#[transitive(DeribitCpty <-> Folio)]
43#[transitive(DeribitCpty <-> Orderflow)]
44#[transitive(FalconXCpty <-> Folio)]
45#[transitive(FalconXCpty <-> Orderflow)]
46#[transitive(GalaxyCpty <-> Orderflow)]
47#[transitive(KalshiCpty <-> Folio)]
48#[transitive(KalshiCpty <-> Orderflow)]
49#[transitive(KrakenCpty <-> Folio)]
50#[transitive(KrakenCpty <-> Orderflow)]
51#[transitive(OkxCpty <-> Folio)]
52#[transitive(OkxCpty <-> Orderflow)]
53#[transitive(MockCpty <-> Folio)]
54#[transitive(MockCpty <-> Orderflow)]
55#[transitive(PaperCpty <-> Folio)]
56#[transitive(PaperCpty <-> Orderflow)]
57#[transitive(ExternalCpty <-> Folio)]
58#[transitive(ExternalCpty <-> Orderflow)]
59#[transitive(WintermuteCpty <-> Folio)]
60#[transitive(WintermuteCpty <-> Orderflow)]
61#[transitive(Orderflow <-> Oms)]
62#[transitive(Orderflow <- Algo)]
63#[transitive(Algo <-> TwapAlgo <- Orderflow)]
64#[transitive(Algo <-> ChaserAlgo <- Orderflow)]
65#[transitive(Algo <-> SmartOrderRouterAlgo)]
66#[transitive(Algo <-> MarketMakerAlgo <- Orderflow)]
67#[transitive(Algo <-> PovAlgo <- Orderflow)]
68#[transitive(TradingActivity <- Orderflow)]
69#[transitive(Algo <-> SpreaderAlgo <- Orderflow)]
70#[rustfmt::skip]
71pub enum TypedMessage {
72    #[pack(tag(  0))] SystemControl(system_control::SystemControlMessage),
73    #[pack(tag(  1))] Symbology(symbology::SymbologyUpdate),
74    #[pack(tag(  3))] Orderflow(orderflow::OrderflowMessage),
75    #[pack(tag(  4))] Oms(oms::OmsMessage),
76    #[pack(tag(  5))] Algo(algo::AlgoMessage),
77    #[pack(tag(  6))] Folio(folio::FolioMessage),
78    #[pack(tag( 10))] ChannelControl(channel_control::ChannelControlMessage),
79    #[pack(tag( 98))] ExternalCpty(cpty::generic_external::ExternalCptyMessage),
80    #[pack(tag( 99))] MockCpty(cpty::mock::MockCptyMessage),
81    #[pack(tag(100))] CoinbaseCpty(cpty::coinbase::CoinbaseMessage),
82    #[pack(tag(101))] B2C2Cpty(cpty::b2c2::B2C2Message),
83    #[pack(tag(103))] KrakenCpty(cpty::kraken::KrakenMessage),
84    #[pack(tag(104))] DeribitCpty(cpty::deribit::DeribitMessage),
85    #[pack(tag(105))] WintermuteCpty(cpty::wintermute::WintermuteMessage),
86    #[pack(tag(106))] FalconXCpty(cpty::falconx::FalconXMessage),
87    #[pack(tag(107))] CoinbasePrimeCpty(cpty::coinbase_prime::CoinbasePrimeMessage),
88    #[pack(tag(108))] GalaxyCpty(cpty::galaxy::GalaxyMessage),
89    #[pack(tag(109))] CumberlandCpty(cpty::cumberland::CumberlandMessage),
90    #[pack(tag(110))] CboeDigitalCpty(cpty::cboe_digital::CboeDigitalMessage),
91    #[pack(tag(111))] BinanceCpty(cpty::binance::BinanceMessage),
92    #[pack(tag(112))] CqgCpty(cpty::cqg::CqgMessage),
93    #[pack(tag(113))] OkxCpty(cpty::okx::OkxMessage),
94    #[pack(tag(114))] KalshiCpty(cpty::kalshi::KalshiMessage),
95    #[pack(tag(115))] PaperCpty(cpty::paper::PaperCptyMessage),
96    #[pack(tag(200))] TwapAlgo(algo::twap::TwapAlgoMessage),
97    #[pack(tag(201))] SmartOrderRouterAlgo(algo::smart_order_router::SmartOrderRouterMessage),
98    #[pack(tag(202))] MarketMakerAlgo(algo::mm::MMAlgoMessage),
99    #[pack(tag(203))] PovAlgo(algo::pov::PovAlgoMessage),
100    #[pack(tag(204))] ChaserAlgo(algo::chaser::ChaserAlgoMessage),
101    #[pack(tag(205))] TradingActivity(trading_activity::TradingActivityMessage),
102    #[pack(tag(206))] TakeAndChaseAlgo(algo::take_and_chase::TakeAndChaseAlgoMessage),
103    #[pack(tag(207))] QuoteOneSideAlgo(algo::quote_one_side::QuoteOneSideAlgoMessage),
104    #[pack(tag(208))] SpreaderAlgo(algo::spreader::SpreaderAlgoMessage)
105}
106
107#[cfg(feature = "netidx")]
108impl TypedMessage {
109    pub fn is_system_control(&self) -> bool {
110        matches!(self, TypedMessage::SystemControl(..))
111    }
112
113    pub fn downcast<T>(self) -> Option<T>
114    where
115        TypedMessage: TryInto<MaybeSplit<TypedMessage, T>>,
116    {
117        if let Ok((_, downcasted)) =
118            TryInto::<MaybeSplit<TypedMessage, T>>::try_into(self).map(MaybeSplit::parts)
119        {
120            Some(downcasted)
121        } else {
122            None
123        }
124    }
125
126    pub fn topics(&self) -> BitFlags<MessageTopic> {
127        match self {
128            TypedMessage::Orderflow(_) => MessageTopic::Orderflow.into(),
129            // CR alee: would be easier to determine in common+ext data format
130            TypedMessage::Oms(om) => {
131                use oms::OmsMessage;
132                match om {
133                    OmsMessage::Order(..)
134                    | OmsMessage::OrderUpdate(..)
135                    | OmsMessage::Cancel(..)
136                    | OmsMessage::CancelAll(..)
137                    | OmsMessage::Reject(..)
138                    | OmsMessage::Ack(..)
139                    | OmsMessage::Fill(..)
140                    | OmsMessage::FillWarning(..)
141                    | OmsMessage::Out(..) => MessageTopic::Orderflow.into(),
142                    _ => BitFlags::empty(),
143                }
144            }
145            _ => BitFlags::empty(),
146        }
147    }
148}
149
150#[bitflags]
151#[repr(u64)]
152#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, JsonSchema_repr)]
153pub enum MessageTopic {
154    Orderflow,
155    ExternalOrderflow,
156}
157
158pub enum MaybeSplit<A, B> {
159    Just(B),
160    Split(A, B),
161}
162
163impl<A, B> MaybeSplit<A, B> {
164    pub fn parts(self) -> (Option<A>, B) {
165        match self {
166            MaybeSplit::Just(b) => (None, b),
167            MaybeSplit::Split(a, b) => (Some(a), b),
168        }
169    }
170}
171
172#[cfg(test)]
173mod test {
174    use super::*;
175    use crate::{
176        orderflow::{OrderBuilder, OrderId, OrderSource},
177        symbology::MarketId,
178        Dir,
179    };
180    use anyhow::Result;
181    use rust_decimal::Decimal;
182
183    #[test]
184    fn test_try_into_any_variant() -> Result<()> {
185        use crate::orderflow::OrderflowMessage;
186        let m = TypedMessage::Orderflow(OrderflowMessage::Order(
187            OrderBuilder::new(
188                OrderId::nil(123),
189                OrderSource::API,
190                MarketId::try_from("BTC Crypto/USD*COINBASE/DIRECT")?,
191            )
192            .limit(Dir::Buy, Decimal::new(100, 0), Decimal::new(1, 0), false)
193            .build()?,
194        ));
195        let m2: std::result::Result<MaybeSplit<TypedMessage, oms::OmsMessage>, _> =
196            m.try_into();
197        assert_eq!(m2.is_ok(), true);
198        Ok(())
199    }
200}