nautilus_common/messages/data/
mod.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Data specific messages such as subscriptions and requests.
17
18use std::{any::Any, sync::Arc};
19
20use nautilus_core::{UUID4, UnixNanos};
21use nautilus_model::{
22    data::BarType,
23    identifiers::{ClientId, Venue},
24};
25
26pub mod request;
27pub mod response;
28pub mod subscribe;
29pub mod unsubscribe;
30
31// Re-exports
32pub use request::{
33    RequestBars, RequestBookDepth, RequestBookSnapshot, RequestCustomData, RequestInstrument,
34    RequestInstruments, RequestQuotes, RequestTrades,
35};
36pub use response::{
37    BarsResponse, BookResponse, CustomDataResponse, InstrumentResponse, InstrumentsResponse,
38    QuotesResponse, TradesResponse,
39};
40pub use subscribe::{
41    SubscribeBars, SubscribeBookDeltas, SubscribeBookDepth10, SubscribeBookSnapshots,
42    SubscribeCustomData, SubscribeFundingRates, SubscribeIndexPrices, SubscribeInstrument,
43    SubscribeInstrumentClose, SubscribeInstrumentStatus, SubscribeInstruments, SubscribeMarkPrices,
44    SubscribeQuotes, SubscribeTrades,
45};
46pub use unsubscribe::{
47    UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth10, UnsubscribeBookSnapshots,
48    UnsubscribeCustomData, UnsubscribeFundingRates, UnsubscribeIndexPrices, UnsubscribeInstrument,
49    UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus, UnsubscribeInstruments,
50    UnsubscribeMarkPrices, UnsubscribeQuotes, UnsubscribeTrades,
51};
52
53#[cfg(feature = "defi")]
54use crate::messages::defi::{DefiRequestCommand, DefiSubscribeCommand, DefiUnsubscribeCommand};
55
56#[non_exhaustive]
57#[derive(Clone, Debug, PartialEq)]
58pub enum DataCommand {
59    Request(RequestCommand),
60    Subscribe(SubscribeCommand),
61    Unsubscribe(UnsubscribeCommand),
62    #[cfg(feature = "defi")]
63    DefiRequest(DefiRequestCommand),
64    #[cfg(feature = "defi")]
65    DefiSubscribe(DefiSubscribeCommand),
66    #[cfg(feature = "defi")]
67    DefiUnsubscribe(DefiUnsubscribeCommand),
68}
69
70impl DataCommand {
71    /// Converts the command to a dyn Any trait object for messaging.
72    pub fn as_any(&self) -> &dyn Any {
73        self
74    }
75}
76
77#[derive(Clone, Debug)]
78pub enum SubscribeCommand {
79    Data(SubscribeCustomData),
80    Instrument(SubscribeInstrument),
81    Instruments(SubscribeInstruments),
82    BookDeltas(SubscribeBookDeltas),
83    BookDepth10(SubscribeBookDepth10),
84    BookSnapshots(SubscribeBookSnapshots),
85    Quotes(SubscribeQuotes),
86    Trades(SubscribeTrades),
87    Bars(SubscribeBars),
88    MarkPrices(SubscribeMarkPrices),
89    IndexPrices(SubscribeIndexPrices),
90    FundingRates(SubscribeFundingRates),
91    InstrumentStatus(SubscribeInstrumentStatus),
92    InstrumentClose(SubscribeInstrumentClose),
93}
94
95impl PartialEq for SubscribeCommand {
96    fn eq(&self, other: &Self) -> bool {
97        self.command_id() == other.command_id()
98    }
99}
100
101impl SubscribeCommand {
102    /// Converts the command to a dyn Any trait object for messaging.
103    pub fn as_any(&self) -> &dyn Any {
104        self
105    }
106
107    pub fn command_id(&self) -> UUID4 {
108        match self {
109            Self::Data(cmd) => cmd.command_id,
110            Self::Instrument(cmd) => cmd.command_id,
111            Self::Instruments(cmd) => cmd.command_id,
112            Self::BookDeltas(cmd) => cmd.command_id,
113            Self::BookDepth10(cmd) => cmd.command_id,
114            Self::BookSnapshots(cmd) => cmd.command_id,
115            Self::Quotes(cmd) => cmd.command_id,
116            Self::Trades(cmd) => cmd.command_id,
117            Self::Bars(cmd) => cmd.command_id,
118            Self::MarkPrices(cmd) => cmd.command_id,
119            Self::IndexPrices(cmd) => cmd.command_id,
120            Self::FundingRates(cmd) => cmd.command_id,
121            Self::InstrumentStatus(cmd) => cmd.command_id,
122            Self::InstrumentClose(cmd) => cmd.command_id,
123        }
124    }
125
126    pub fn client_id(&self) -> Option<&ClientId> {
127        match self {
128            Self::Data(cmd) => cmd.client_id.as_ref(),
129            Self::Instrument(cmd) => cmd.client_id.as_ref(),
130            Self::Instruments(cmd) => cmd.client_id.as_ref(),
131            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
132            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
133            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
134            Self::Quotes(cmd) => cmd.client_id.as_ref(),
135            Self::Trades(cmd) => cmd.client_id.as_ref(),
136            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
137            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
138            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
139            Self::Bars(cmd) => cmd.client_id.as_ref(),
140            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
141            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
142        }
143    }
144
145    pub fn venue(&self) -> Option<&Venue> {
146        match self {
147            Self::Data(cmd) => cmd.venue.as_ref(),
148            Self::Instrument(cmd) => cmd.venue.as_ref(),
149            Self::Instruments(cmd) => Some(&cmd.venue),
150            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
151            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
152            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
153            Self::Quotes(cmd) => cmd.venue.as_ref(),
154            Self::Trades(cmd) => cmd.venue.as_ref(),
155            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
156            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
157            Self::FundingRates(cmd) => cmd.venue.as_ref(),
158            Self::Bars(cmd) => cmd.venue.as_ref(),
159            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
160            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
161        }
162    }
163
164    pub fn ts_init(&self) -> UnixNanos {
165        match self {
166            Self::Data(cmd) => cmd.ts_init,
167            Self::Instrument(cmd) => cmd.ts_init,
168            Self::Instruments(cmd) => cmd.ts_init,
169            Self::BookDeltas(cmd) => cmd.ts_init,
170            Self::BookDepth10(cmd) => cmd.ts_init,
171            Self::BookSnapshots(cmd) => cmd.ts_init,
172            Self::Quotes(cmd) => cmd.ts_init,
173            Self::Trades(cmd) => cmd.ts_init,
174            Self::MarkPrices(cmd) => cmd.ts_init,
175            Self::IndexPrices(cmd) => cmd.ts_init,
176            Self::FundingRates(cmd) => cmd.ts_init,
177            Self::Bars(cmd) => cmd.ts_init,
178            Self::InstrumentStatus(cmd) => cmd.ts_init,
179            Self::InstrumentClose(cmd) => cmd.ts_init,
180        }
181    }
182}
183
184#[derive(Clone, Debug)]
185pub enum UnsubscribeCommand {
186    Data(UnsubscribeCustomData),
187    Instrument(UnsubscribeInstrument),
188    Instruments(UnsubscribeInstruments),
189    BookDeltas(UnsubscribeBookDeltas),
190    BookDepth10(UnsubscribeBookDepth10),
191    BookSnapshots(UnsubscribeBookSnapshots),
192    Quotes(UnsubscribeQuotes),
193    Trades(UnsubscribeTrades),
194    Bars(UnsubscribeBars),
195    MarkPrices(UnsubscribeMarkPrices),
196    IndexPrices(UnsubscribeIndexPrices),
197    FundingRates(UnsubscribeFundingRates),
198    InstrumentStatus(UnsubscribeInstrumentStatus),
199    InstrumentClose(UnsubscribeInstrumentClose),
200}
201
202impl PartialEq for UnsubscribeCommand {
203    fn eq(&self, other: &Self) -> bool {
204        self.command_id() == other.command_id()
205    }
206}
207
208impl UnsubscribeCommand {
209    /// Converts the command to a dyn Any trait object for messaging.
210    pub fn as_any(&self) -> &dyn Any {
211        self
212    }
213
214    pub fn command_id(&self) -> UUID4 {
215        match self {
216            Self::Data(cmd) => cmd.command_id,
217            Self::Instrument(cmd) => cmd.command_id,
218            Self::Instruments(cmd) => cmd.command_id,
219            Self::BookDeltas(cmd) => cmd.command_id,
220            Self::BookDepth10(cmd) => cmd.command_id,
221            Self::BookSnapshots(cmd) => cmd.command_id,
222            Self::Quotes(cmd) => cmd.command_id,
223            Self::Trades(cmd) => cmd.command_id,
224            Self::Bars(cmd) => cmd.command_id,
225            Self::MarkPrices(cmd) => cmd.command_id,
226            Self::IndexPrices(cmd) => cmd.command_id,
227            Self::FundingRates(cmd) => cmd.command_id,
228            Self::InstrumentStatus(cmd) => cmd.command_id,
229            Self::InstrumentClose(cmd) => cmd.command_id,
230        }
231    }
232
233    pub fn client_id(&self) -> Option<&ClientId> {
234        match self {
235            Self::Data(cmd) => cmd.client_id.as_ref(),
236            Self::Instrument(cmd) => cmd.client_id.as_ref(),
237            Self::Instruments(cmd) => cmd.client_id.as_ref(),
238            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
239            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
240            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
241            Self::Quotes(cmd) => cmd.client_id.as_ref(),
242            Self::Trades(cmd) => cmd.client_id.as_ref(),
243            Self::Bars(cmd) => cmd.client_id.as_ref(),
244            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
245            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
246            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
247            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
248            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
249        }
250    }
251
252    pub fn venue(&self) -> Option<&Venue> {
253        match self {
254            Self::Data(cmd) => cmd.venue.as_ref(),
255            Self::Instrument(cmd) => cmd.venue.as_ref(),
256            Self::Instruments(cmd) => Some(&cmd.venue),
257            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
258            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
259            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
260            Self::Quotes(cmd) => cmd.venue.as_ref(),
261            Self::Trades(cmd) => cmd.venue.as_ref(),
262            Self::Bars(cmd) => cmd.venue.as_ref(),
263            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
264            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
265            Self::FundingRates(cmd) => cmd.venue.as_ref(),
266            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
267            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
268        }
269    }
270
271    pub fn ts_init(&self) -> UnixNanos {
272        match self {
273            Self::Data(cmd) => cmd.ts_init,
274            Self::Instrument(cmd) => cmd.ts_init,
275            Self::Instruments(cmd) => cmd.ts_init,
276            Self::BookDeltas(cmd) => cmd.ts_init,
277            Self::BookDepth10(cmd) => cmd.ts_init,
278            Self::BookSnapshots(cmd) => cmd.ts_init,
279            Self::Quotes(cmd) => cmd.ts_init,
280            Self::Trades(cmd) => cmd.ts_init,
281            Self::MarkPrices(cmd) => cmd.ts_init,
282            Self::IndexPrices(cmd) => cmd.ts_init,
283            Self::FundingRates(cmd) => cmd.ts_init,
284            Self::Bars(cmd) => cmd.ts_init,
285            Self::InstrumentStatus(cmd) => cmd.ts_init,
286            Self::InstrumentClose(cmd) => cmd.ts_init,
287        }
288    }
289}
290
291fn check_client_id_or_venue(client_id: &Option<ClientId>, venue: &Option<Venue>) {
292    assert!(
293        client_id.is_some() || venue.is_some(),
294        "Both `client_id` and `venue` were None"
295    );
296}
297
298#[derive(Clone, Debug)]
299pub enum RequestCommand {
300    Data(RequestCustomData),
301    Instrument(RequestInstrument),
302    Instruments(RequestInstruments),
303    BookSnapshot(RequestBookSnapshot),
304    BookDepth(RequestBookDepth),
305    Quotes(RequestQuotes),
306    Trades(RequestTrades),
307    Bars(RequestBars),
308}
309
310impl PartialEq for RequestCommand {
311    fn eq(&self, other: &Self) -> bool {
312        self.request_id() == other.request_id()
313    }
314}
315
316impl RequestCommand {
317    /// Converts the command to a dyn Any trait object for messaging.
318    pub fn as_any(&self) -> &dyn Any {
319        self
320    }
321
322    pub fn request_id(&self) -> &UUID4 {
323        match self {
324            Self::Data(cmd) => &cmd.request_id,
325            Self::Instrument(cmd) => &cmd.request_id,
326            Self::Instruments(cmd) => &cmd.request_id,
327            Self::BookSnapshot(cmd) => &cmd.request_id,
328            Self::BookDepth(cmd) => &cmd.request_id,
329            Self::Quotes(cmd) => &cmd.request_id,
330            Self::Trades(cmd) => &cmd.request_id,
331            Self::Bars(cmd) => &cmd.request_id,
332        }
333    }
334
335    pub fn client_id(&self) -> Option<&ClientId> {
336        match self {
337            Self::Data(cmd) => Some(&cmd.client_id),
338            Self::Instrument(cmd) => cmd.client_id.as_ref(),
339            Self::Instruments(cmd) => cmd.client_id.as_ref(),
340            Self::BookSnapshot(cmd) => cmd.client_id.as_ref(),
341            Self::BookDepth(cmd) => cmd.client_id.as_ref(),
342            Self::Quotes(cmd) => cmd.client_id.as_ref(),
343            Self::Trades(cmd) => cmd.client_id.as_ref(),
344            Self::Bars(cmd) => cmd.client_id.as_ref(),
345        }
346    }
347
348    pub fn venue(&self) -> Option<&Venue> {
349        match self {
350            Self::Data(_) => None,
351            Self::Instrument(cmd) => Some(&cmd.instrument_id.venue),
352            Self::Instruments(cmd) => cmd.venue.as_ref(),
353            Self::BookSnapshot(cmd) => Some(&cmd.instrument_id.venue),
354            Self::BookDepth(cmd) => Some(&cmd.instrument_id.venue),
355            Self::Quotes(cmd) => Some(&cmd.instrument_id.venue),
356            Self::Trades(cmd) => Some(&cmd.instrument_id.venue),
357            // TODO: Extract the below somewhere
358            Self::Bars(cmd) => match &cmd.bar_type {
359                BarType::Standard { instrument_id, .. } => Some(&instrument_id.venue),
360                BarType::Composite { instrument_id, .. } => Some(&instrument_id.venue),
361            },
362        }
363    }
364
365    pub fn ts_init(&self) -> UnixNanos {
366        match self {
367            Self::Data(cmd) => cmd.ts_init,
368            Self::Instrument(cmd) => cmd.ts_init,
369            Self::Instruments(cmd) => cmd.ts_init,
370            Self::BookSnapshot(cmd) => cmd.ts_init,
371            Self::BookDepth(cmd) => cmd.ts_init,
372            Self::Quotes(cmd) => cmd.ts_init,
373            Self::Trades(cmd) => cmd.ts_init,
374            Self::Bars(cmd) => cmd.ts_init,
375        }
376    }
377}
378
379#[derive(Clone, Debug)]
380pub enum DataResponse {
381    Data(CustomDataResponse),
382    Instrument(Box<InstrumentResponse>),
383    Instruments(InstrumentsResponse),
384    Book(BookResponse),
385    Quotes(QuotesResponse),
386    Trades(TradesResponse),
387    Bars(BarsResponse),
388}
389
390impl DataResponse {
391    /// Converts the command to a dyn Any trait object for messaging.
392    pub fn as_any(&self) -> &dyn Any {
393        self
394    }
395
396    pub fn correlation_id(&self) -> &UUID4 {
397        match self {
398            Self::Data(resp) => &resp.correlation_id,
399            Self::Instrument(resp) => &resp.correlation_id,
400            Self::Instruments(resp) => &resp.correlation_id,
401            Self::Book(resp) => &resp.correlation_id,
402            Self::Quotes(resp) => &resp.correlation_id,
403            Self::Trades(resp) => &resp.correlation_id,
404            Self::Bars(resp) => &resp.correlation_id,
405        }
406    }
407}
408
409pub type Payload = Arc<dyn Any + Send + Sync>;