Skip to main content

nautilus_common/messages/data/
unsubscribe.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
16use std::num::NonZeroUsize;
17
18use nautilus_core::{Params, UUID4, UnixNanos};
19use nautilus_model::{
20    data::{BarType, DataType},
21    identifiers::{ClientId, InstrumentId, OptionSeriesId, Venue},
22};
23use serde::{Deserialize, Serialize};
24
25use super::{
26    IntoBarTypes, IntoInstrumentIds, check_client_id_or_venue,
27    identifiers::{
28        deserialize_bar_types, deserialize_instrument_ids, single_bar_type, single_instrument_id,
29    },
30};
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
33#[cfg_attr(
34    feature = "python",
35    pyo3::pyclass(module = "nautilus_trader.live", frozen)
36)]
37#[cfg_attr(
38    feature = "python",
39    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
40)]
41pub struct UnsubscribeCustomData {
42    pub client_id: Option<ClientId>,
43    pub venue: Option<Venue>,
44    pub data_type: DataType,
45    pub command_id: UUID4,
46    pub ts_init: UnixNanos,
47    pub correlation_id: Option<UUID4>,
48    pub params: Option<Params>,
49}
50
51impl UnsubscribeCustomData {
52    /// Creates a new [`UnsubscribeCustomData`] instance.
53    pub fn new(
54        client_id: Option<ClientId>,
55        venue: Option<Venue>,
56        data_type: DataType,
57        command_id: UUID4,
58        ts_init: UnixNanos,
59        correlation_id: Option<UUID4>,
60        params: Option<Params>,
61    ) -> Self {
62        check_client_id_or_venue(&client_id, &venue);
63        Self {
64            client_id,
65            venue,
66            data_type,
67            command_id,
68            ts_init,
69            correlation_id,
70            params,
71        }
72    }
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize)]
76#[cfg_attr(
77    feature = "python",
78    pyo3::pyclass(module = "nautilus_trader.live", frozen)
79)]
80#[cfg_attr(
81    feature = "python",
82    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
83)]
84pub struct UnsubscribeInstrument {
85    #[serde(deserialize_with = "deserialize_instrument_ids")]
86    pub instrument_ids: Vec<InstrumentId>,
87    pub client_id: Option<ClientId>,
88    pub venue: Option<Venue>,
89    pub command_id: UUID4,
90    pub ts_init: UnixNanos,
91    pub correlation_id: Option<UUID4>,
92    pub params: Option<Params>,
93}
94
95impl UnsubscribeInstrument {
96    /// Creates a new [`UnsubscribeInstrument`] instance.
97    ///
98    /// Duplicate IDs are removed in first-occurrence order.
99    ///
100    /// # Panics
101    ///
102    /// Panics if:
103    /// - The identifier list is empty.
104    /// - Both client and venue are absent.
105    pub fn new(
106        instrument_ids: impl IntoInstrumentIds,
107        client_id: Option<ClientId>,
108        venue: Option<Venue>,
109        command_id: UUID4,
110        ts_init: UnixNanos,
111        correlation_id: Option<UUID4>,
112        params: Option<Params>,
113    ) -> Self {
114        let instrument_ids = instrument_ids
115            .into_instrument_ids()
116            .expect("invalid instrument identifier list");
117        check_client_id_or_venue(&client_id, &venue);
118        Self {
119            instrument_ids,
120            client_id,
121            venue,
122            command_id,
123            ts_init,
124            correlation_id,
125            params,
126        }
127    }
128
129    /// Creates a command for the supplied IDs with the same routing and identity.
130    ///
131    /// # Panics
132    ///
133    /// Panics if the identifier list is empty.
134    #[must_use]
135    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
136        let instrument_ids = instrument_ids
137            .into_instrument_ids()
138            .expect("invalid instrument identifier list");
139
140        Self {
141            instrument_ids,
142            client_id: self.client_id,
143            venue: self.venue,
144            command_id: self.command_id,
145            ts_init: self.ts_init,
146            correlation_id: self.correlation_id,
147            params: self.params.clone(),
148        }
149    }
150
151    /// Returns the instrument ID for a single-instrument adapter path.
152    ///
153    /// # Errors
154    ///
155    /// Returns an error if the command does not contain exactly one instrument.
156    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
157        single_instrument_id(&self.instrument_ids, "subscription")
158    }
159}
160
161#[derive(Clone, Debug, Serialize, Deserialize)]
162#[cfg_attr(
163    feature = "python",
164    pyo3::pyclass(module = "nautilus_trader.live", frozen)
165)]
166#[cfg_attr(
167    feature = "python",
168    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
169)]
170pub struct UnsubscribeInstruments {
171    pub client_id: Option<ClientId>,
172    pub venue: Venue,
173    pub command_id: UUID4,
174    pub ts_init: UnixNanos,
175    pub correlation_id: Option<UUID4>,
176    pub params: Option<Params>,
177}
178
179impl UnsubscribeInstruments {
180    /// Creates a new [`UnsubscribeInstruments`] instance.
181    pub fn new(
182        client_id: Option<ClientId>,
183        venue: Venue,
184        command_id: UUID4,
185        ts_init: UnixNanos,
186        correlation_id: Option<UUID4>,
187        params: Option<Params>,
188    ) -> Self {
189        Self {
190            client_id,
191            venue,
192            command_id,
193            ts_init,
194            correlation_id,
195            params,
196        }
197    }
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize)]
201#[cfg_attr(
202    feature = "python",
203    pyo3::pyclass(module = "nautilus_trader.live", frozen)
204)]
205#[cfg_attr(
206    feature = "python",
207    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
208)]
209pub struct UnsubscribeBookDeltas {
210    #[serde(deserialize_with = "deserialize_instrument_ids")]
211    pub instrument_ids: Vec<InstrumentId>,
212    pub client_id: Option<ClientId>,
213    pub venue: Option<Venue>,
214    pub command_id: UUID4,
215    pub ts_init: UnixNanos,
216    pub correlation_id: Option<UUID4>,
217    pub params: Option<Params>,
218}
219
220impl UnsubscribeBookDeltas {
221    /// Creates a new [`UnsubscribeBookDeltas`] instance.
222    ///
223    /// Duplicate IDs are removed in first-occurrence order.
224    ///
225    /// # Panics
226    ///
227    /// Panics if:
228    /// - The identifier list is empty.
229    /// - Both client and venue are absent.
230    pub fn new(
231        instrument_ids: impl IntoInstrumentIds,
232        client_id: Option<ClientId>,
233        venue: Option<Venue>,
234        command_id: UUID4,
235        ts_init: UnixNanos,
236        correlation_id: Option<UUID4>,
237        params: Option<Params>,
238    ) -> Self {
239        let instrument_ids = instrument_ids
240            .into_instrument_ids()
241            .expect("invalid instrument identifier list");
242        check_client_id_or_venue(&client_id, &venue);
243        Self {
244            instrument_ids,
245            client_id,
246            venue,
247            command_id,
248            ts_init,
249            correlation_id,
250            params,
251        }
252    }
253
254    /// Creates a command for the supplied IDs with the same routing and identity.
255    ///
256    /// # Panics
257    ///
258    /// Panics if the identifier list is empty.
259    #[must_use]
260    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
261        let instrument_ids = instrument_ids
262            .into_instrument_ids()
263            .expect("invalid instrument identifier list");
264
265        Self {
266            instrument_ids,
267            client_id: self.client_id,
268            venue: self.venue,
269            command_id: self.command_id,
270            ts_init: self.ts_init,
271            correlation_id: self.correlation_id,
272            params: self.params.clone(),
273        }
274    }
275
276    /// Returns the instrument ID for a single-instrument adapter path.
277    ///
278    /// # Errors
279    ///
280    /// Returns an error if the command does not contain exactly one instrument.
281    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
282        single_instrument_id(&self.instrument_ids, "subscription")
283    }
284}
285
286#[derive(Clone, Debug, Serialize, Deserialize)]
287#[cfg_attr(
288    feature = "python",
289    pyo3::pyclass(module = "nautilus_trader.live", frozen)
290)]
291#[cfg_attr(
292    feature = "python",
293    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
294)]
295pub struct UnsubscribeBookDepth {
296    #[serde(deserialize_with = "deserialize_instrument_ids")]
297    pub instrument_ids: Vec<InstrumentId>,
298    pub client_id: Option<ClientId>,
299    pub venue: Option<Venue>,
300    pub command_id: UUID4,
301    pub ts_init: UnixNanos,
302    pub correlation_id: Option<UUID4>,
303    pub params: Option<Params>,
304}
305
306impl UnsubscribeBookDepth {
307    /// Creates a new [`UnsubscribeBookDepth`] instance.
308    ///
309    /// Duplicate IDs are removed in first-occurrence order.
310    ///
311    /// # Panics
312    ///
313    /// Panics if:
314    /// - The identifier list is empty.
315    /// - Both client and venue are absent.
316    pub fn new(
317        instrument_ids: impl IntoInstrumentIds,
318        client_id: Option<ClientId>,
319        venue: Option<Venue>,
320        command_id: UUID4,
321        ts_init: UnixNanos,
322        correlation_id: Option<UUID4>,
323        params: Option<Params>,
324    ) -> Self {
325        let instrument_ids = instrument_ids
326            .into_instrument_ids()
327            .expect("invalid instrument identifier list");
328        check_client_id_or_venue(&client_id, &venue);
329        Self {
330            instrument_ids,
331            client_id,
332            venue,
333            command_id,
334            ts_init,
335            correlation_id,
336            params,
337        }
338    }
339
340    /// Creates a command for the supplied IDs with the same routing and identity.
341    ///
342    /// # Panics
343    ///
344    /// Panics if the identifier list is empty.
345    #[must_use]
346    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
347        let instrument_ids = instrument_ids
348            .into_instrument_ids()
349            .expect("invalid instrument identifier list");
350
351        Self {
352            instrument_ids,
353            client_id: self.client_id,
354            venue: self.venue,
355            command_id: self.command_id,
356            ts_init: self.ts_init,
357            correlation_id: self.correlation_id,
358            params: self.params.clone(),
359        }
360    }
361
362    /// Returns the instrument ID for a single-instrument adapter path.
363    ///
364    /// # Errors
365    ///
366    /// Returns an error if the command does not contain exactly one instrument.
367    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
368        single_instrument_id(&self.instrument_ids, "subscription")
369    }
370}
371
372#[derive(Clone, Debug, Serialize, Deserialize)]
373pub struct UnsubscribeBookSnapshots {
374    #[serde(deserialize_with = "deserialize_instrument_ids")]
375    pub instrument_ids: Vec<InstrumentId>,
376    pub interval_ms: NonZeroUsize,
377    pub client_id: Option<ClientId>,
378    pub venue: Option<Venue>,
379    pub command_id: UUID4,
380    pub ts_init: UnixNanos,
381    pub correlation_id: Option<UUID4>,
382    pub params: Option<Params>,
383}
384
385impl UnsubscribeBookSnapshots {
386    /// Creates a new [`UnsubscribeBookSnapshots`] instance.
387    ///
388    /// Duplicate IDs are removed in first-occurrence order.
389    ///
390    /// # Panics
391    ///
392    /// Panics if:
393    /// - The identifier list is empty.
394    /// - Both client and venue are absent.
395    #[expect(clippy::too_many_arguments)]
396    pub fn new(
397        instrument_ids: impl IntoInstrumentIds,
398        interval_ms: NonZeroUsize,
399        client_id: Option<ClientId>,
400        venue: Option<Venue>,
401        command_id: UUID4,
402        ts_init: UnixNanos,
403        correlation_id: Option<UUID4>,
404        params: Option<Params>,
405    ) -> Self {
406        let instrument_ids = instrument_ids
407            .into_instrument_ids()
408            .expect("invalid instrument identifier list");
409        check_client_id_or_venue(&client_id, &venue);
410        Self {
411            instrument_ids,
412            interval_ms,
413            client_id,
414            venue,
415            command_id,
416            ts_init,
417            correlation_id,
418            params,
419        }
420    }
421
422    /// Creates a command for the supplied IDs with the same routing and identity.
423    ///
424    /// # Panics
425    ///
426    /// Panics if the identifier list is empty.
427    #[must_use]
428    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
429        let instrument_ids = instrument_ids
430            .into_instrument_ids()
431            .expect("invalid instrument identifier list");
432
433        Self {
434            instrument_ids,
435            interval_ms: self.interval_ms,
436            client_id: self.client_id,
437            venue: self.venue,
438            command_id: self.command_id,
439            ts_init: self.ts_init,
440            correlation_id: self.correlation_id,
441            params: self.params.clone(),
442        }
443    }
444
445    /// Returns the instrument ID for a single-instrument adapter path.
446    ///
447    /// # Errors
448    ///
449    /// Returns an error if the command does not contain exactly one instrument.
450    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
451        single_instrument_id(&self.instrument_ids, "subscription")
452    }
453}
454
455#[derive(Clone, Debug, Serialize, Deserialize)]
456#[cfg_attr(
457    feature = "python",
458    pyo3::pyclass(module = "nautilus_trader.live", frozen)
459)]
460#[cfg_attr(
461    feature = "python",
462    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
463)]
464pub struct UnsubscribeQuotes {
465    #[serde(deserialize_with = "deserialize_instrument_ids")]
466    pub instrument_ids: Vec<InstrumentId>,
467    pub client_id: Option<ClientId>,
468    pub venue: Option<Venue>,
469    pub command_id: UUID4,
470    pub ts_init: UnixNanos,
471    pub correlation_id: Option<UUID4>,
472    pub params: Option<Params>,
473}
474
475impl UnsubscribeQuotes {
476    /// Creates a new [`UnsubscribeQuotes`] instance.
477    ///
478    /// Duplicate IDs are removed in first-occurrence order.
479    ///
480    /// # Panics
481    ///
482    /// Panics if:
483    /// - The identifier list is empty.
484    /// - Both client and venue are absent.
485    pub fn new(
486        instrument_ids: impl IntoInstrumentIds,
487        client_id: Option<ClientId>,
488        venue: Option<Venue>,
489        command_id: UUID4,
490        ts_init: UnixNanos,
491        correlation_id: Option<UUID4>,
492        params: Option<Params>,
493    ) -> Self {
494        let instrument_ids = instrument_ids
495            .into_instrument_ids()
496            .expect("invalid instrument identifier list");
497        check_client_id_or_venue(&client_id, &venue);
498        Self {
499            instrument_ids,
500            client_id,
501            venue,
502            command_id,
503            ts_init,
504            correlation_id,
505            params,
506        }
507    }
508
509    /// Creates a command for the supplied IDs with the same routing and identity.
510    ///
511    /// # Panics
512    ///
513    /// Panics if the identifier list is empty.
514    #[must_use]
515    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
516        let instrument_ids = instrument_ids
517            .into_instrument_ids()
518            .expect("invalid instrument identifier list");
519
520        Self {
521            instrument_ids,
522            client_id: self.client_id,
523            venue: self.venue,
524            command_id: self.command_id,
525            ts_init: self.ts_init,
526            correlation_id: self.correlation_id,
527            params: self.params.clone(),
528        }
529    }
530
531    /// Returns the instrument ID for a single-instrument adapter path.
532    ///
533    /// # Errors
534    ///
535    /// Returns an error if the command does not contain exactly one instrument.
536    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
537        single_instrument_id(&self.instrument_ids, "subscription")
538    }
539}
540
541#[derive(Clone, Debug, Serialize, Deserialize)]
542#[cfg_attr(
543    feature = "python",
544    pyo3::pyclass(module = "nautilus_trader.live", frozen)
545)]
546#[cfg_attr(
547    feature = "python",
548    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
549)]
550pub struct UnsubscribeTrades {
551    #[serde(deserialize_with = "deserialize_instrument_ids")]
552    pub instrument_ids: Vec<InstrumentId>,
553    pub client_id: Option<ClientId>,
554    pub venue: Option<Venue>,
555    pub command_id: UUID4,
556    pub ts_init: UnixNanos,
557    pub correlation_id: Option<UUID4>,
558    pub params: Option<Params>,
559}
560
561impl UnsubscribeTrades {
562    /// Creates a new [`UnsubscribeTrades`] instance.
563    ///
564    /// Duplicate IDs are removed in first-occurrence order.
565    ///
566    /// # Panics
567    ///
568    /// Panics if:
569    /// - The identifier list is empty.
570    /// - Both client and venue are absent.
571    pub fn new(
572        instrument_ids: impl IntoInstrumentIds,
573        client_id: Option<ClientId>,
574        venue: Option<Venue>,
575        command_id: UUID4,
576        ts_init: UnixNanos,
577        correlation_id: Option<UUID4>,
578        params: Option<Params>,
579    ) -> Self {
580        let instrument_ids = instrument_ids
581            .into_instrument_ids()
582            .expect("invalid instrument identifier list");
583        check_client_id_or_venue(&client_id, &venue);
584        Self {
585            instrument_ids,
586            client_id,
587            venue,
588            command_id,
589            ts_init,
590            correlation_id,
591            params,
592        }
593    }
594
595    /// Creates a command for the supplied IDs with the same routing and identity.
596    ///
597    /// # Panics
598    ///
599    /// Panics if the identifier list is empty.
600    #[must_use]
601    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
602        let instrument_ids = instrument_ids
603            .into_instrument_ids()
604            .expect("invalid instrument identifier list");
605
606        Self {
607            instrument_ids,
608            client_id: self.client_id,
609            venue: self.venue,
610            command_id: self.command_id,
611            ts_init: self.ts_init,
612            correlation_id: self.correlation_id,
613            params: self.params.clone(),
614        }
615    }
616
617    /// Returns the instrument ID for a single-instrument adapter path.
618    ///
619    /// # Errors
620    ///
621    /// Returns an error if the command does not contain exactly one instrument.
622    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
623        single_instrument_id(&self.instrument_ids, "subscription")
624    }
625}
626
627#[derive(Clone, Debug, Serialize, Deserialize)]
628#[cfg_attr(
629    feature = "python",
630    pyo3::pyclass(module = "nautilus_trader.live", frozen)
631)]
632#[cfg_attr(
633    feature = "python",
634    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
635)]
636pub struct UnsubscribeBars {
637    #[serde(deserialize_with = "deserialize_bar_types")]
638    pub bar_types: Vec<BarType>,
639    pub client_id: Option<ClientId>,
640    pub venue: Option<Venue>,
641    pub command_id: UUID4,
642    pub ts_init: UnixNanos,
643    pub correlation_id: Option<UUID4>,
644    pub params: Option<Params>,
645}
646
647impl UnsubscribeBars {
648    /// Creates a new [`UnsubscribeBars`] instance.
649    ///
650    /// Duplicate bar types are removed in first-occurrence order.
651    ///
652    /// # Panics
653    ///
654    /// Panics if:
655    /// - The bar type list is empty.
656    /// - Both client and venue are absent.
657    pub fn new(
658        bar_types: impl IntoBarTypes,
659        client_id: Option<ClientId>,
660        venue: Option<Venue>,
661        command_id: UUID4,
662        ts_init: UnixNanos,
663        correlation_id: Option<UUID4>,
664        params: Option<Params>,
665    ) -> Self {
666        let bar_types = bar_types.into_bar_types().expect("invalid bar type list");
667        check_client_id_or_venue(&client_id, &venue);
668        Self {
669            bar_types,
670            client_id,
671            venue,
672            command_id,
673            ts_init,
674            correlation_id,
675            params,
676        }
677    }
678
679    /// Creates a command for the supplied bar types with the same routing and identity.
680    ///
681    /// # Panics
682    ///
683    /// Panics if the bar type list is empty.
684    #[must_use]
685    pub fn with_bar_types(&self, bar_types: impl IntoBarTypes) -> Self {
686        let bar_types = bar_types.into_bar_types().expect("invalid bar type list");
687
688        Self {
689            bar_types,
690            client_id: self.client_id,
691            venue: self.venue,
692            command_id: self.command_id,
693            ts_init: self.ts_init,
694            correlation_id: self.correlation_id,
695            params: self.params.clone(),
696        }
697    }
698
699    /// Returns the bar type for a single-bar-type adapter path.
700    ///
701    /// # Errors
702    ///
703    /// Returns an error if the command does not contain exactly one bar type.
704    pub fn single_bar_type(&self) -> anyhow::Result<BarType> {
705        single_bar_type(&self.bar_types, "subscription")
706    }
707}
708
709#[derive(Clone, Debug, Serialize, Deserialize)]
710#[cfg_attr(
711    feature = "python",
712    pyo3::pyclass(module = "nautilus_trader.live", frozen)
713)]
714#[cfg_attr(
715    feature = "python",
716    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
717)]
718pub struct UnsubscribeMarkPrices {
719    #[serde(deserialize_with = "deserialize_instrument_ids")]
720    pub instrument_ids: Vec<InstrumentId>,
721    pub client_id: Option<ClientId>,
722    pub venue: Option<Venue>,
723    pub command_id: UUID4,
724    pub ts_init: UnixNanos,
725    pub correlation_id: Option<UUID4>,
726    pub params: Option<Params>,
727}
728
729impl UnsubscribeMarkPrices {
730    /// Creates a new [`UnsubscribeMarkPrices`] instance.
731    ///
732    /// Duplicate IDs are removed in first-occurrence order.
733    ///
734    /// # Panics
735    ///
736    /// Panics if:
737    /// - The identifier list is empty.
738    /// - Both client and venue are absent.
739    pub fn new(
740        instrument_ids: impl IntoInstrumentIds,
741        client_id: Option<ClientId>,
742        venue: Option<Venue>,
743        command_id: UUID4,
744        ts_init: UnixNanos,
745        correlation_id: Option<UUID4>,
746        params: Option<Params>,
747    ) -> Self {
748        let instrument_ids = instrument_ids
749            .into_instrument_ids()
750            .expect("invalid instrument identifier list");
751        check_client_id_or_venue(&client_id, &venue);
752        Self {
753            instrument_ids,
754            client_id,
755            venue,
756            command_id,
757            ts_init,
758            correlation_id,
759            params,
760        }
761    }
762
763    /// Creates a command for the supplied IDs with the same routing and identity.
764    ///
765    /// # Panics
766    ///
767    /// Panics if the identifier list is empty.
768    #[must_use]
769    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
770        let instrument_ids = instrument_ids
771            .into_instrument_ids()
772            .expect("invalid instrument identifier list");
773
774        Self {
775            instrument_ids,
776            client_id: self.client_id,
777            venue: self.venue,
778            command_id: self.command_id,
779            ts_init: self.ts_init,
780            correlation_id: self.correlation_id,
781            params: self.params.clone(),
782        }
783    }
784
785    /// Returns the instrument ID for a single-instrument adapter path.
786    ///
787    /// # Errors
788    ///
789    /// Returns an error if the command does not contain exactly one instrument.
790    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
791        single_instrument_id(&self.instrument_ids, "subscription")
792    }
793}
794
795#[derive(Clone, Debug, Serialize, Deserialize)]
796#[cfg_attr(
797    feature = "python",
798    pyo3::pyclass(module = "nautilus_trader.live", frozen)
799)]
800#[cfg_attr(
801    feature = "python",
802    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
803)]
804pub struct UnsubscribeIndexPrices {
805    #[serde(deserialize_with = "deserialize_instrument_ids")]
806    pub instrument_ids: Vec<InstrumentId>,
807    pub client_id: Option<ClientId>,
808    pub venue: Option<Venue>,
809    pub command_id: UUID4,
810    pub ts_init: UnixNanos,
811    pub correlation_id: Option<UUID4>,
812    pub params: Option<Params>,
813}
814
815impl UnsubscribeIndexPrices {
816    /// Creates a new [`UnsubscribeIndexPrices`] instance.
817    ///
818    /// Duplicate IDs are removed in first-occurrence order.
819    ///
820    /// # Panics
821    ///
822    /// Panics if:
823    /// - The identifier list is empty.
824    /// - Both client and venue are absent.
825    pub fn new(
826        instrument_ids: impl IntoInstrumentIds,
827        client_id: Option<ClientId>,
828        venue: Option<Venue>,
829        command_id: UUID4,
830        ts_init: UnixNanos,
831        correlation_id: Option<UUID4>,
832        params: Option<Params>,
833    ) -> Self {
834        let instrument_ids = instrument_ids
835            .into_instrument_ids()
836            .expect("invalid instrument identifier list");
837        check_client_id_or_venue(&client_id, &venue);
838        Self {
839            instrument_ids,
840            client_id,
841            venue,
842            command_id,
843            ts_init,
844            correlation_id,
845            params,
846        }
847    }
848
849    /// Creates a command for the supplied IDs with the same routing and identity.
850    ///
851    /// # Panics
852    ///
853    /// Panics if the identifier list is empty.
854    #[must_use]
855    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
856        let instrument_ids = instrument_ids
857            .into_instrument_ids()
858            .expect("invalid instrument identifier list");
859
860        Self {
861            instrument_ids,
862            client_id: self.client_id,
863            venue: self.venue,
864            command_id: self.command_id,
865            ts_init: self.ts_init,
866            correlation_id: self.correlation_id,
867            params: self.params.clone(),
868        }
869    }
870
871    /// Returns the instrument ID for a single-instrument adapter path.
872    ///
873    /// # Errors
874    ///
875    /// Returns an error if the command does not contain exactly one instrument.
876    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
877        single_instrument_id(&self.instrument_ids, "subscription")
878    }
879}
880
881#[derive(Clone, Debug, Serialize, Deserialize)]
882#[cfg_attr(
883    feature = "python",
884    pyo3::pyclass(module = "nautilus_trader.live", frozen)
885)]
886#[cfg_attr(
887    feature = "python",
888    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
889)]
890pub struct UnsubscribeFundingRates {
891    #[serde(deserialize_with = "deserialize_instrument_ids")]
892    pub instrument_ids: Vec<InstrumentId>,
893    pub client_id: Option<ClientId>,
894    pub venue: Option<Venue>,
895    pub command_id: UUID4,
896    pub ts_init: UnixNanos,
897    pub correlation_id: Option<UUID4>,
898    pub params: Option<Params>,
899}
900
901impl UnsubscribeFundingRates {
902    /// Creates a new [`UnsubscribeFundingRates`] instance.
903    ///
904    /// Duplicate IDs are removed in first-occurrence order.
905    ///
906    /// # Panics
907    ///
908    /// Panics if:
909    /// - The identifier list is empty.
910    /// - Both client and venue are absent.
911    pub fn new(
912        instrument_ids: impl IntoInstrumentIds,
913        client_id: Option<ClientId>,
914        venue: Option<Venue>,
915        command_id: UUID4,
916        ts_init: UnixNanos,
917        correlation_id: Option<UUID4>,
918        params: Option<Params>,
919    ) -> Self {
920        let instrument_ids = instrument_ids
921            .into_instrument_ids()
922            .expect("invalid instrument identifier list");
923        check_client_id_or_venue(&client_id, &venue);
924        Self {
925            instrument_ids,
926            client_id,
927            venue,
928            command_id,
929            ts_init,
930            correlation_id,
931            params,
932        }
933    }
934
935    /// Creates a command for the supplied IDs with the same routing and identity.
936    ///
937    /// # Panics
938    ///
939    /// Panics if the identifier list is empty.
940    #[must_use]
941    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
942        let instrument_ids = instrument_ids
943            .into_instrument_ids()
944            .expect("invalid instrument identifier list");
945
946        Self {
947            instrument_ids,
948            client_id: self.client_id,
949            venue: self.venue,
950            command_id: self.command_id,
951            ts_init: self.ts_init,
952            correlation_id: self.correlation_id,
953            params: self.params.clone(),
954        }
955    }
956
957    /// Returns the instrument ID for a single-instrument adapter path.
958    ///
959    /// # Errors
960    ///
961    /// Returns an error if the command does not contain exactly one instrument.
962    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
963        single_instrument_id(&self.instrument_ids, "subscription")
964    }
965}
966
967#[derive(Clone, Debug, Serialize, Deserialize)]
968#[cfg_attr(
969    feature = "python",
970    pyo3::pyclass(module = "nautilus_trader.live", frozen)
971)]
972#[cfg_attr(
973    feature = "python",
974    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
975)]
976pub struct UnsubscribeInstrumentStatus {
977    #[serde(deserialize_with = "deserialize_instrument_ids")]
978    pub instrument_ids: Vec<InstrumentId>,
979    pub client_id: Option<ClientId>,
980    pub venue: Option<Venue>,
981    pub command_id: UUID4,
982    pub ts_init: UnixNanos,
983    pub correlation_id: Option<UUID4>,
984    pub params: Option<Params>,
985}
986
987impl UnsubscribeInstrumentStatus {
988    /// Creates a new [`UnsubscribeInstrumentStatus`] instance.
989    ///
990    /// Duplicate IDs are removed in first-occurrence order.
991    ///
992    /// # Panics
993    ///
994    /// Panics if:
995    /// - The identifier list is empty.
996    /// - Both client and venue are absent.
997    pub fn new(
998        instrument_ids: impl IntoInstrumentIds,
999        client_id: Option<ClientId>,
1000        venue: Option<Venue>,
1001        command_id: UUID4,
1002        ts_init: UnixNanos,
1003        correlation_id: Option<UUID4>,
1004        params: Option<Params>,
1005    ) -> Self {
1006        let instrument_ids = instrument_ids
1007            .into_instrument_ids()
1008            .expect("invalid instrument identifier list");
1009        check_client_id_or_venue(&client_id, &venue);
1010        Self {
1011            instrument_ids,
1012            client_id,
1013            venue,
1014            command_id,
1015            ts_init,
1016            correlation_id,
1017            params,
1018        }
1019    }
1020
1021    /// Creates a command for the supplied IDs with the same routing and identity.
1022    ///
1023    /// # Panics
1024    ///
1025    /// Panics if the identifier list is empty.
1026    #[must_use]
1027    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
1028        let instrument_ids = instrument_ids
1029            .into_instrument_ids()
1030            .expect("invalid instrument identifier list");
1031
1032        Self {
1033            instrument_ids,
1034            client_id: self.client_id,
1035            venue: self.venue,
1036            command_id: self.command_id,
1037            ts_init: self.ts_init,
1038            correlation_id: self.correlation_id,
1039            params: self.params.clone(),
1040        }
1041    }
1042
1043    /// Returns the instrument ID for a single-instrument adapter path.
1044    ///
1045    /// # Errors
1046    ///
1047    /// Returns an error if the command does not contain exactly one instrument.
1048    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
1049        single_instrument_id(&self.instrument_ids, "subscription")
1050    }
1051}
1052
1053#[derive(Clone, Debug, Serialize, Deserialize)]
1054#[cfg_attr(
1055    feature = "python",
1056    pyo3::pyclass(module = "nautilus_trader.live", frozen)
1057)]
1058#[cfg_attr(
1059    feature = "python",
1060    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
1061)]
1062pub struct UnsubscribeOptionGreeks {
1063    #[serde(deserialize_with = "deserialize_instrument_ids")]
1064    pub instrument_ids: Vec<InstrumentId>,
1065    pub client_id: Option<ClientId>,
1066    pub venue: Option<Venue>,
1067    pub command_id: UUID4,
1068    pub ts_init: UnixNanos,
1069    pub correlation_id: Option<UUID4>,
1070    pub params: Option<Params>,
1071}
1072
1073impl UnsubscribeOptionGreeks {
1074    /// Creates a new [`UnsubscribeOptionGreeks`] instance.
1075    ///
1076    /// Duplicate IDs are removed in first-occurrence order.
1077    ///
1078    /// # Panics
1079    ///
1080    /// Panics if:
1081    /// - The identifier list is empty.
1082    /// - Both client and venue are absent.
1083    pub fn new(
1084        instrument_ids: impl IntoInstrumentIds,
1085        client_id: Option<ClientId>,
1086        venue: Option<Venue>,
1087        command_id: UUID4,
1088        ts_init: UnixNanos,
1089        correlation_id: Option<UUID4>,
1090        params: Option<Params>,
1091    ) -> Self {
1092        let instrument_ids = instrument_ids
1093            .into_instrument_ids()
1094            .expect("invalid instrument identifier list");
1095        check_client_id_or_venue(&client_id, &venue);
1096        Self {
1097            instrument_ids,
1098            client_id,
1099            venue,
1100            command_id,
1101            ts_init,
1102            correlation_id,
1103            params,
1104        }
1105    }
1106
1107    /// Creates a command for the supplied IDs with the same routing and identity.
1108    ///
1109    /// # Panics
1110    ///
1111    /// Panics if the identifier list is empty.
1112    #[must_use]
1113    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
1114        let instrument_ids = instrument_ids
1115            .into_instrument_ids()
1116            .expect("invalid instrument identifier list");
1117
1118        Self {
1119            instrument_ids,
1120            client_id: self.client_id,
1121            venue: self.venue,
1122            command_id: self.command_id,
1123            ts_init: self.ts_init,
1124            correlation_id: self.correlation_id,
1125            params: self.params.clone(),
1126        }
1127    }
1128
1129    /// Returns the instrument ID for a single-instrument adapter path.
1130    ///
1131    /// # Errors
1132    ///
1133    /// Returns an error if the command does not contain exactly one instrument.
1134    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
1135        single_instrument_id(&self.instrument_ids, "subscription")
1136    }
1137}
1138
1139#[derive(Clone, Debug, Serialize, Deserialize)]
1140#[cfg_attr(
1141    feature = "python",
1142    pyo3::pyclass(module = "nautilus_trader.live", frozen)
1143)]
1144#[cfg_attr(
1145    feature = "python",
1146    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
1147)]
1148pub struct UnsubscribeInstrumentClose {
1149    #[serde(deserialize_with = "deserialize_instrument_ids")]
1150    pub instrument_ids: Vec<InstrumentId>,
1151    pub client_id: Option<ClientId>,
1152    pub venue: Option<Venue>,
1153    pub command_id: UUID4,
1154    pub ts_init: UnixNanos,
1155    pub correlation_id: Option<UUID4>,
1156    pub params: Option<Params>,
1157}
1158
1159impl UnsubscribeInstrumentClose {
1160    /// Creates a new [`UnsubscribeInstrumentClose`] instance.
1161    ///
1162    /// Duplicate IDs are removed in first-occurrence order.
1163    ///
1164    /// # Panics
1165    ///
1166    /// Panics if:
1167    /// - The identifier list is empty.
1168    /// - Both client and venue are absent.
1169    pub fn new(
1170        instrument_ids: impl IntoInstrumentIds,
1171        client_id: Option<ClientId>,
1172        venue: Option<Venue>,
1173        command_id: UUID4,
1174        ts_init: UnixNanos,
1175        correlation_id: Option<UUID4>,
1176        params: Option<Params>,
1177    ) -> Self {
1178        let instrument_ids = instrument_ids
1179            .into_instrument_ids()
1180            .expect("invalid instrument identifier list");
1181        check_client_id_or_venue(&client_id, &venue);
1182        Self {
1183            instrument_ids,
1184            client_id,
1185            venue,
1186            command_id,
1187            ts_init,
1188            correlation_id,
1189            params,
1190        }
1191    }
1192
1193    /// Creates a command for the supplied IDs with the same routing and identity.
1194    ///
1195    /// # Panics
1196    ///
1197    /// Panics if the identifier list is empty.
1198    #[must_use]
1199    pub fn with_instrument_ids(&self, instrument_ids: impl IntoInstrumentIds) -> Self {
1200        let instrument_ids = instrument_ids
1201            .into_instrument_ids()
1202            .expect("invalid instrument identifier list");
1203
1204        Self {
1205            instrument_ids,
1206            client_id: self.client_id,
1207            venue: self.venue,
1208            command_id: self.command_id,
1209            ts_init: self.ts_init,
1210            correlation_id: self.correlation_id,
1211            params: self.params.clone(),
1212        }
1213    }
1214
1215    /// Returns the instrument ID for a single-instrument adapter path.
1216    ///
1217    /// # Errors
1218    ///
1219    /// Returns an error if the command does not contain exactly one instrument.
1220    pub fn single_instrument_id(&self) -> anyhow::Result<InstrumentId> {
1221        single_instrument_id(&self.instrument_ids, "subscription")
1222    }
1223}
1224
1225#[derive(Clone, Debug, Serialize, Deserialize)]
1226pub struct UnsubscribeOptionChain {
1227    pub series_id: OptionSeriesId,
1228    pub command_id: UUID4,
1229    pub ts_init: UnixNanos,
1230    pub client_id: Option<ClientId>,
1231    pub venue: Option<Venue>,
1232    #[serde(default)]
1233    pub params: Option<Params>,
1234}
1235
1236impl UnsubscribeOptionChain {
1237    /// Creates a new [`UnsubscribeOptionChain`] instance.
1238    pub fn new(
1239        series_id: OptionSeriesId,
1240        command_id: UUID4,
1241        ts_init: UnixNanos,
1242        client_id: Option<ClientId>,
1243        venue: Option<Venue>,
1244    ) -> Self {
1245        check_client_id_or_venue(&client_id, &venue);
1246        Self {
1247            series_id,
1248            command_id,
1249            ts_init,
1250            client_id,
1251            venue,
1252            params: None,
1253        }
1254    }
1255}