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, RequestBookSnapshot, RequestCustomData, RequestInstrument, RequestInstruments,
34    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, SubscribeIndexPrices, SubscribeInstrument, SubscribeInstrumentClose,
43    SubscribeInstrumentStatus, SubscribeInstruments, SubscribeMarkPrices, SubscribeQuotes,
44    SubscribeTrades,
45};
46pub use unsubscribe::{
47    UnsubscribeBars, UnsubscribeBookDeltas, UnsubscribeBookDepth10, UnsubscribeBookSnapshots,
48    UnsubscribeCustomData, UnsubscribeIndexPrices, UnsubscribeInstrument,
49    UnsubscribeInstrumentClose, UnsubscribeInstrumentStatus, UnsubscribeInstruments,
50    UnsubscribeMarkPrices, UnsubscribeQuotes, UnsubscribeTrades,
51};
52
53#[cfg(feature = "defi")]
54use crate::messages::defi::{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    DefiSubscribe(DefiSubscribeCommand),
64    #[cfg(feature = "defi")]
65    DefiUnsubscribe(DefiUnsubscribeCommand),
66}
67
68impl DataCommand {
69    /// Converts the command to a dyn Any trait object for messaging.
70    pub fn as_any(&self) -> &dyn Any {
71        self
72    }
73}
74
75#[derive(Clone, Debug)]
76pub enum SubscribeCommand {
77    Data(SubscribeCustomData),
78    Instrument(SubscribeInstrument),
79    Instruments(SubscribeInstruments),
80    BookDeltas(SubscribeBookDeltas),
81    BookDepth10(SubscribeBookDepth10),
82    BookSnapshots(SubscribeBookSnapshots),
83    Quotes(SubscribeQuotes),
84    Trades(SubscribeTrades),
85    Bars(SubscribeBars),
86    MarkPrices(SubscribeMarkPrices),
87    IndexPrices(SubscribeIndexPrices),
88    InstrumentStatus(SubscribeInstrumentStatus),
89    InstrumentClose(SubscribeInstrumentClose),
90}
91
92impl PartialEq for SubscribeCommand {
93    fn eq(&self, other: &Self) -> bool {
94        self.command_id() == other.command_id()
95    }
96}
97
98impl SubscribeCommand {
99    /// Converts the command to a dyn Any trait object for messaging.
100    pub fn as_any(&self) -> &dyn Any {
101        self
102    }
103
104    pub fn command_id(&self) -> UUID4 {
105        match self {
106            Self::Data(cmd) => cmd.command_id,
107            Self::Instrument(cmd) => cmd.command_id,
108            Self::Instruments(cmd) => cmd.command_id,
109            Self::BookDeltas(cmd) => cmd.command_id,
110            Self::BookDepth10(cmd) => cmd.command_id,
111            Self::BookSnapshots(cmd) => cmd.command_id,
112            Self::Quotes(cmd) => cmd.command_id,
113            Self::Trades(cmd) => cmd.command_id,
114            Self::Bars(cmd) => cmd.command_id,
115            Self::MarkPrices(cmd) => cmd.command_id,
116            Self::IndexPrices(cmd) => cmd.command_id,
117            Self::InstrumentStatus(cmd) => cmd.command_id,
118            Self::InstrumentClose(cmd) => cmd.command_id,
119        }
120    }
121
122    pub fn client_id(&self) -> Option<&ClientId> {
123        match self {
124            Self::Data(cmd) => cmd.client_id.as_ref(),
125            Self::Instrument(cmd) => cmd.client_id.as_ref(),
126            Self::Instruments(cmd) => cmd.client_id.as_ref(),
127            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
128            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
129            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
130            Self::Quotes(cmd) => cmd.client_id.as_ref(),
131            Self::Trades(cmd) => cmd.client_id.as_ref(),
132            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
133            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
134            Self::Bars(cmd) => cmd.client_id.as_ref(),
135            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
136            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
137        }
138    }
139
140    pub fn venue(&self) -> Option<&Venue> {
141        match self {
142            Self::Data(cmd) => cmd.venue.as_ref(),
143            Self::Instrument(cmd) => cmd.venue.as_ref(),
144            Self::Instruments(cmd) => Some(&cmd.venue),
145            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
146            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
147            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
148            Self::Quotes(cmd) => cmd.venue.as_ref(),
149            Self::Trades(cmd) => cmd.venue.as_ref(),
150            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
151            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
152            Self::Bars(cmd) => cmd.venue.as_ref(),
153            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
154            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
155        }
156    }
157
158    pub fn ts_init(&self) -> UnixNanos {
159        match self {
160            Self::Data(cmd) => cmd.ts_init,
161            Self::Instrument(cmd) => cmd.ts_init,
162            Self::Instruments(cmd) => cmd.ts_init,
163            Self::BookDeltas(cmd) => cmd.ts_init,
164            Self::BookDepth10(cmd) => cmd.ts_init,
165            Self::BookSnapshots(cmd) => cmd.ts_init,
166            Self::Quotes(cmd) => cmd.ts_init,
167            Self::Trades(cmd) => cmd.ts_init,
168            Self::MarkPrices(cmd) => cmd.ts_init,
169            Self::IndexPrices(cmd) => cmd.ts_init,
170            Self::Bars(cmd) => cmd.ts_init,
171            Self::InstrumentStatus(cmd) => cmd.ts_init,
172            Self::InstrumentClose(cmd) => cmd.ts_init,
173        }
174    }
175}
176
177#[derive(Clone, Debug)]
178pub enum UnsubscribeCommand {
179    Data(UnsubscribeCustomData),
180    Instrument(UnsubscribeInstrument),
181    Instruments(UnsubscribeInstruments),
182    BookDeltas(UnsubscribeBookDeltas),
183    BookDepth10(UnsubscribeBookDepth10),
184    BookSnapshots(UnsubscribeBookSnapshots),
185    Quotes(UnsubscribeQuotes),
186    Trades(UnsubscribeTrades),
187    Bars(UnsubscribeBars),
188    MarkPrices(UnsubscribeMarkPrices),
189    IndexPrices(UnsubscribeIndexPrices),
190    InstrumentStatus(UnsubscribeInstrumentStatus),
191    InstrumentClose(UnsubscribeInstrumentClose),
192}
193
194impl PartialEq for UnsubscribeCommand {
195    fn eq(&self, other: &Self) -> bool {
196        self.command_id() == other.command_id()
197    }
198}
199
200impl UnsubscribeCommand {
201    /// Converts the command to a dyn Any trait object for messaging.
202    pub fn as_any(&self) -> &dyn Any {
203        self
204    }
205
206    pub fn command_id(&self) -> UUID4 {
207        match self {
208            Self::Data(cmd) => cmd.command_id,
209            Self::Instrument(cmd) => cmd.command_id,
210            Self::Instruments(cmd) => cmd.command_id,
211            Self::BookDeltas(cmd) => cmd.command_id,
212            Self::BookDepth10(cmd) => cmd.command_id,
213            Self::BookSnapshots(cmd) => cmd.command_id,
214            Self::Quotes(cmd) => cmd.command_id,
215            Self::Trades(cmd) => cmd.command_id,
216            Self::Bars(cmd) => cmd.command_id,
217            Self::MarkPrices(cmd) => cmd.command_id,
218            Self::IndexPrices(cmd) => cmd.command_id,
219            Self::InstrumentStatus(cmd) => cmd.command_id,
220            Self::InstrumentClose(cmd) => cmd.command_id,
221        }
222    }
223
224    pub fn client_id(&self) -> Option<&ClientId> {
225        match self {
226            Self::Data(cmd) => cmd.client_id.as_ref(),
227            Self::Instrument(cmd) => cmd.client_id.as_ref(),
228            Self::Instruments(cmd) => cmd.client_id.as_ref(),
229            Self::BookDeltas(cmd) => cmd.client_id.as_ref(),
230            Self::BookDepth10(cmd) => cmd.client_id.as_ref(),
231            Self::BookSnapshots(cmd) => cmd.client_id.as_ref(),
232            Self::Quotes(cmd) => cmd.client_id.as_ref(),
233            Self::Trades(cmd) => cmd.client_id.as_ref(),
234            Self::Bars(cmd) => cmd.client_id.as_ref(),
235            Self::MarkPrices(cmd) => cmd.client_id.as_ref(),
236            Self::IndexPrices(cmd) => cmd.client_id.as_ref(),
237            Self::InstrumentStatus(cmd) => cmd.client_id.as_ref(),
238            Self::InstrumentClose(cmd) => cmd.client_id.as_ref(),
239        }
240    }
241
242    pub fn venue(&self) -> Option<&Venue> {
243        match self {
244            Self::Data(cmd) => cmd.venue.as_ref(),
245            Self::Instrument(cmd) => cmd.venue.as_ref(),
246            Self::Instruments(cmd) => Some(&cmd.venue),
247            Self::BookDeltas(cmd) => cmd.venue.as_ref(),
248            Self::BookDepth10(cmd) => cmd.venue.as_ref(),
249            Self::BookSnapshots(cmd) => cmd.venue.as_ref(),
250            Self::Quotes(cmd) => cmd.venue.as_ref(),
251            Self::Trades(cmd) => cmd.venue.as_ref(),
252            Self::Bars(cmd) => cmd.venue.as_ref(),
253            Self::MarkPrices(cmd) => cmd.venue.as_ref(),
254            Self::IndexPrices(cmd) => cmd.venue.as_ref(),
255            Self::InstrumentStatus(cmd) => cmd.venue.as_ref(),
256            Self::InstrumentClose(cmd) => cmd.venue.as_ref(),
257        }
258    }
259
260    pub fn ts_init(&self) -> UnixNanos {
261        match self {
262            Self::Data(cmd) => cmd.ts_init,
263            Self::Instrument(cmd) => cmd.ts_init,
264            Self::Instruments(cmd) => cmd.ts_init,
265            Self::BookDeltas(cmd) => cmd.ts_init,
266            Self::BookDepth10(cmd) => cmd.ts_init,
267            Self::BookSnapshots(cmd) => cmd.ts_init,
268            Self::Quotes(cmd) => cmd.ts_init,
269            Self::Trades(cmd) => cmd.ts_init,
270            Self::MarkPrices(cmd) => cmd.ts_init,
271            Self::IndexPrices(cmd) => cmd.ts_init,
272            Self::Bars(cmd) => cmd.ts_init,
273            Self::InstrumentStatus(cmd) => cmd.ts_init,
274            Self::InstrumentClose(cmd) => cmd.ts_init,
275        }
276    }
277}
278
279fn check_client_id_or_venue(client_id: &Option<ClientId>, venue: &Option<Venue>) {
280    assert!(
281        client_id.is_some() || venue.is_some(),
282        "Both `client_id` and `venue` were None"
283    );
284}
285
286#[derive(Clone, Debug)]
287pub enum RequestCommand {
288    Data(RequestCustomData),
289    Instrument(RequestInstrument),
290    Instruments(RequestInstruments),
291    BookSnapshot(RequestBookSnapshot),
292    Quotes(RequestQuotes),
293    Trades(RequestTrades),
294    Bars(RequestBars),
295}
296
297impl PartialEq for RequestCommand {
298    fn eq(&self, other: &Self) -> bool {
299        self.request_id() == other.request_id()
300    }
301}
302
303impl RequestCommand {
304    /// Converts the command to a dyn Any trait object for messaging.
305    pub fn as_any(&self) -> &dyn Any {
306        self
307    }
308
309    pub fn request_id(&self) -> &UUID4 {
310        match self {
311            Self::Data(cmd) => &cmd.request_id,
312            Self::Instrument(cmd) => &cmd.request_id,
313            Self::Instruments(cmd) => &cmd.request_id,
314            Self::BookSnapshot(cmd) => &cmd.request_id,
315            Self::Quotes(cmd) => &cmd.request_id,
316            Self::Trades(cmd) => &cmd.request_id,
317            Self::Bars(cmd) => &cmd.request_id,
318        }
319    }
320
321    pub fn client_id(&self) -> Option<&ClientId> {
322        match self {
323            Self::Data(cmd) => Some(&cmd.client_id),
324            Self::Instrument(cmd) => cmd.client_id.as_ref(),
325            Self::Instruments(cmd) => cmd.client_id.as_ref(),
326            Self::BookSnapshot(cmd) => cmd.client_id.as_ref(),
327            Self::Quotes(cmd) => cmd.client_id.as_ref(),
328            Self::Trades(cmd) => cmd.client_id.as_ref(),
329            Self::Bars(cmd) => cmd.client_id.as_ref(),
330        }
331    }
332
333    pub fn venue(&self) -> Option<&Venue> {
334        match self {
335            Self::Data(_) => None,
336            Self::Instrument(cmd) => Some(&cmd.instrument_id.venue),
337            Self::Instruments(cmd) => cmd.venue.as_ref(),
338            Self::BookSnapshot(cmd) => Some(&cmd.instrument_id.venue),
339            Self::Quotes(cmd) => Some(&cmd.instrument_id.venue),
340            Self::Trades(cmd) => Some(&cmd.instrument_id.venue),
341            // TODO: Extract the below somewhere
342            Self::Bars(cmd) => match &cmd.bar_type {
343                BarType::Standard { instrument_id, .. } => Some(&instrument_id.venue),
344                BarType::Composite { instrument_id, .. } => Some(&instrument_id.venue),
345            },
346        }
347    }
348
349    pub fn ts_init(&self) -> UnixNanos {
350        match self {
351            Self::Data(cmd) => cmd.ts_init,
352            Self::Instrument(cmd) => cmd.ts_init,
353            Self::Instruments(cmd) => cmd.ts_init,
354            Self::BookSnapshot(cmd) => cmd.ts_init,
355            Self::Quotes(cmd) => cmd.ts_init,
356            Self::Trades(cmd) => cmd.ts_init,
357            Self::Bars(cmd) => cmd.ts_init,
358        }
359    }
360}
361
362#[derive(Clone, Debug)]
363pub enum DataResponse {
364    Data(CustomDataResponse),
365    Instrument(Box<InstrumentResponse>),
366    Instruments(InstrumentsResponse),
367    Book(BookResponse),
368    Quotes(QuotesResponse),
369    Trades(TradesResponse),
370    Bars(BarsResponse),
371}
372
373impl DataResponse {
374    /// Converts the command to a dyn Any trait object for messaging.
375    pub fn as_any(&self) -> &dyn Any {
376        self
377    }
378
379    pub fn correlation_id(&self) -> &UUID4 {
380        match self {
381            Self::Data(resp) => &resp.correlation_id,
382            Self::Instrument(resp) => &resp.correlation_id,
383            Self::Instruments(resp) => &resp.correlation_id,
384            Self::Book(resp) => &resp.correlation_id,
385            Self::Quotes(resp) => &resp.correlation_id,
386            Self::Trades(resp) => &resp.correlation_id,
387            Self::Bars(resp) => &resp.correlation_id,
388        }
389    }
390}
391
392pub type Payload = Arc<dyn Any + Send + Sync>;