nautilus_common/messages/data/
mod.rs

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