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