nautilus_common/messages/data/
mod.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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    pub fn correlation_id(&self) -> Option<UUID4> {
184        match self {
185            Self::Data(cmd) => cmd.correlation_id,
186            Self::Instrument(cmd) => cmd.correlation_id,
187            Self::Instruments(cmd) => cmd.correlation_id,
188            Self::BookDeltas(cmd) => cmd.correlation_id,
189            Self::BookDepth10(cmd) => cmd.correlation_id,
190            Self::BookSnapshots(cmd) => cmd.correlation_id,
191            Self::Quotes(cmd) => cmd.correlation_id,
192            Self::Trades(cmd) => cmd.correlation_id,
193            Self::MarkPrices(cmd) => cmd.correlation_id,
194            Self::IndexPrices(cmd) => cmd.correlation_id,
195            Self::FundingRates(cmd) => cmd.correlation_id,
196            Self::Bars(cmd) => cmd.correlation_id,
197            Self::InstrumentStatus(cmd) => cmd.correlation_id,
198            Self::InstrumentClose(cmd) => cmd.correlation_id,
199        }
200    }
201}
202
203#[derive(Clone, Debug)]
204pub enum UnsubscribeCommand {
205    Data(UnsubscribeCustomData),
206    Instrument(UnsubscribeInstrument),
207    Instruments(UnsubscribeInstruments),
208    BookDeltas(UnsubscribeBookDeltas),
209    BookDepth10(UnsubscribeBookDepth10),
210    BookSnapshots(UnsubscribeBookSnapshots),
211    Quotes(UnsubscribeQuotes),
212    Trades(UnsubscribeTrades),
213    Bars(UnsubscribeBars),
214    MarkPrices(UnsubscribeMarkPrices),
215    IndexPrices(UnsubscribeIndexPrices),
216    FundingRates(UnsubscribeFundingRates),
217    InstrumentStatus(UnsubscribeInstrumentStatus),
218    InstrumentClose(UnsubscribeInstrumentClose),
219}
220
221impl PartialEq for UnsubscribeCommand {
222    fn eq(&self, other: &Self) -> bool {
223        self.command_id() == other.command_id()
224    }
225}
226
227impl UnsubscribeCommand {
228    /// Converts the command to a dyn Any trait object for messaging.
229    pub fn as_any(&self) -> &dyn Any {
230        self
231    }
232
233    pub fn command_id(&self) -> UUID4 {
234        match self {
235            Self::Data(cmd) => cmd.command_id,
236            Self::Instrument(cmd) => cmd.command_id,
237            Self::Instruments(cmd) => cmd.command_id,
238            Self::BookDeltas(cmd) => cmd.command_id,
239            Self::BookDepth10(cmd) => cmd.command_id,
240            Self::BookSnapshots(cmd) => cmd.command_id,
241            Self::Quotes(cmd) => cmd.command_id,
242            Self::Trades(cmd) => cmd.command_id,
243            Self::Bars(cmd) => cmd.command_id,
244            Self::MarkPrices(cmd) => cmd.command_id,
245            Self::IndexPrices(cmd) => cmd.command_id,
246            Self::FundingRates(cmd) => cmd.command_id,
247            Self::InstrumentStatus(cmd) => cmd.command_id,
248            Self::InstrumentClose(cmd) => cmd.command_id,
249        }
250    }
251
252    pub fn client_id(&self) -> Option<&ClientId> {
253        match self {
254            Self::Data(cmd) => cmd.client_id.as_ref(),
255            Self::Instrument(cmd) => cmd.client_id.as_ref(),
256            Self::Instruments(cmd) => cmd.client_id.as_ref(),
257            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
258            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
259            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
260            Self::Quotes(cmd) => cmd.client_id.as_ref(),
261            Self::Trades(cmd) => cmd.client_id.as_ref(),
262            Self::Bars(cmd) => cmd.client_id.as_ref(),
263            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
264            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
265            Self::FundingRates(cmd) => cmd.client_id.as_ref(),
266            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
267            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
268        }
269    }
270
271    pub fn venue(&self) -> Option<&Venue> {
272        match self {
273            Self::Data(cmd) => cmd.venue.as_ref(),
274            Self::Instrument(cmd) => cmd.venue.as_ref(),
275            Self::Instruments(cmd) => Some(&cmd.venue),
276            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
277            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
278            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
279            Self::Quotes(cmd) => cmd.venue.as_ref(),
280            Self::Trades(cmd) => cmd.venue.as_ref(),
281            Self::Bars(cmd) => cmd.venue.as_ref(),
282            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
283            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
284            Self::FundingRates(cmd) => cmd.venue.as_ref(),
285            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
286            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
287        }
288    }
289
290    pub fn ts_init(&self) -> UnixNanos {
291        match self {
292            Self::Data(cmd) => cmd.ts_init,
293            Self::Instrument(cmd) => cmd.ts_init,
294            Self::Instruments(cmd) => cmd.ts_init,
295            Self::BookDeltas(cmd) => cmd.ts_init,
296            Self::BookDepth10(cmd) => cmd.ts_init,
297            Self::BookSnapshots(cmd) => cmd.ts_init,
298            Self::Quotes(cmd) => cmd.ts_init,
299            Self::Trades(cmd) => cmd.ts_init,
300            Self::MarkPrices(cmd) => cmd.ts_init,
301            Self::IndexPrices(cmd) => cmd.ts_init,
302            Self::FundingRates(cmd) => cmd.ts_init,
303            Self::Bars(cmd) => cmd.ts_init,
304            Self::InstrumentStatus(cmd) => cmd.ts_init,
305            Self::InstrumentClose(cmd) => cmd.ts_init,
306        }
307    }
308
309    pub fn correlation_id(&self) -> Option<UUID4> {
310        match self {
311            Self::Data(cmd) => cmd.correlation_id,
312            Self::Instrument(cmd) => cmd.correlation_id,
313            Self::Instruments(cmd) => cmd.correlation_id,
314            Self::BookDeltas(cmd) => cmd.correlation_id,
315            Self::BookDepth10(cmd) => cmd.correlation_id,
316            Self::BookSnapshots(cmd) => cmd.correlation_id,
317            Self::Quotes(cmd) => cmd.correlation_id,
318            Self::Trades(cmd) => cmd.correlation_id,
319            Self::MarkPrices(cmd) => cmd.correlation_id,
320            Self::IndexPrices(cmd) => cmd.correlation_id,
321            Self::FundingRates(cmd) => cmd.correlation_id,
322            Self::Bars(cmd) => cmd.correlation_id,
323            Self::InstrumentStatus(cmd) => cmd.correlation_id,
324            Self::InstrumentClose(cmd) => cmd.correlation_id,
325        }
326    }
327}
328
329fn check_client_id_or_venue(client_id: &Option<ClientId>, venue: &Option<Venue>) {
330    assert!(
331        client_id.is_some() || venue.is_some(),
332        "Both `client_id` and `venue` were None"
333    );
334}
335
336#[derive(Clone, Debug)]
337pub enum RequestCommand {
338    Data(RequestCustomData),
339    Instrument(RequestInstrument),
340    Instruments(RequestInstruments),
341    BookSnapshot(RequestBookSnapshot),
342    BookDepth(RequestBookDepth),
343    Quotes(RequestQuotes),
344    Trades(RequestTrades),
345    Bars(RequestBars),
346}
347
348impl PartialEq for RequestCommand {
349    fn eq(&self, other: &Self) -> bool {
350        self.request_id() == other.request_id()
351    }
352}
353
354impl RequestCommand {
355    /// Converts the command to a dyn Any trait object for messaging.
356    pub fn as_any(&self) -> &dyn Any {
357        self
358    }
359
360    pub fn request_id(&self) -> &UUID4 {
361        match self {
362            Self::Data(cmd) => &cmd.request_id,
363            Self::Instrument(cmd) => &cmd.request_id,
364            Self::Instruments(cmd) => &cmd.request_id,
365            Self::BookSnapshot(cmd) => &cmd.request_id,
366            Self::BookDepth(cmd) => &cmd.request_id,
367            Self::Quotes(cmd) => &cmd.request_id,
368            Self::Trades(cmd) => &cmd.request_id,
369            Self::Bars(cmd) => &cmd.request_id,
370        }
371    }
372
373    pub fn client_id(&self) -> Option<&ClientId> {
374        match self {
375            Self::Data(cmd) => Some(&cmd.client_id),
376            Self::Instrument(cmd) => cmd.client_id.as_ref(),
377            Self::Instruments(cmd) => cmd.client_id.as_ref(),
378            Self::BookSnapshot(cmd) => cmd.client_id.as_ref(),
379            Self::BookDepth(cmd) => cmd.client_id.as_ref(),
380            Self::Quotes(cmd) => cmd.client_id.as_ref(),
381            Self::Trades(cmd) => cmd.client_id.as_ref(),
382            Self::Bars(cmd) => cmd.client_id.as_ref(),
383        }
384    }
385
386    pub fn venue(&self) -> Option<&Venue> {
387        match self {
388            Self::Data(_) => None,
389            Self::Instrument(cmd) => Some(&cmd.instrument_id.venue),
390            Self::Instruments(cmd) => cmd.venue.as_ref(),
391            Self::BookSnapshot(cmd) => Some(&cmd.instrument_id.venue),
392            Self::BookDepth(cmd) => Some(&cmd.instrument_id.venue),
393            Self::Quotes(cmd) => Some(&cmd.instrument_id.venue),
394            Self::Trades(cmd) => Some(&cmd.instrument_id.venue),
395            // TODO: Extract the below somewhere
396            Self::Bars(cmd) => match &cmd.bar_type {
397                BarType::Standard { instrument_id, .. } => Some(&instrument_id.venue),
398                BarType::Composite { instrument_id, .. } => Some(&instrument_id.venue),
399            },
400        }
401    }
402
403    pub fn ts_init(&self) -> UnixNanos {
404        match self {
405            Self::Data(cmd) => cmd.ts_init,
406            Self::Instrument(cmd) => cmd.ts_init,
407            Self::Instruments(cmd) => cmd.ts_init,
408            Self::BookSnapshot(cmd) => cmd.ts_init,
409            Self::BookDepth(cmd) => cmd.ts_init,
410            Self::Quotes(cmd) => cmd.ts_init,
411            Self::Trades(cmd) => cmd.ts_init,
412            Self::Bars(cmd) => cmd.ts_init,
413        }
414    }
415}
416
417#[derive(Clone, Debug)]
418pub enum DataResponse {
419    Data(CustomDataResponse),
420    Instrument(Box<InstrumentResponse>),
421    Instruments(InstrumentsResponse),
422    Book(BookResponse),
423    Quotes(QuotesResponse),
424    Trades(TradesResponse),
425    Bars(BarsResponse),
426}
427
428impl DataResponse {
429    /// Converts the command to a dyn Any trait object for messaging.
430    pub fn as_any(&self) -> &dyn Any {
431        self
432    }
433
434    pub fn correlation_id(&self) -> &UUID4 {
435        match self {
436            Self::Data(resp) => &resp.correlation_id,
437            Self::Instrument(resp) => &resp.correlation_id,
438            Self::Instruments(resp) => &resp.correlation_id,
439            Self::Book(resp) => &resp.correlation_id,
440            Self::Quotes(resp) => &resp.correlation_id,
441            Self::Trades(resp) => &resp.correlation_id,
442            Self::Bars(resp) => &resp.correlation_id,
443        }
444    }
445}
446
447pub type Payload = Arc<dyn Any + Send + Sync>;