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