Skip to main content

nautilus_common/messages/execution/
cancel.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::fmt::Display;
17
18use derive_builder::Builder;
19use nautilus_core::{Params, UUID4, UnixNanos};
20use nautilus_model::{
21    enums::OrderSide,
22    identifiers::{ClientId, ClientOrderId, InstrumentId, StrategyId, TraderId, VenueOrderId},
23};
24use serde::{Deserialize, Serialize};
25
26#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
27#[serde(tag = "type")]
28#[cfg_attr(
29    feature = "python",
30    pyo3::pyclass(module = "nautilus_trader.live", frozen, from_py_object)
31)]
32#[cfg_attr(
33    feature = "python",
34    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
35)]
36pub struct CancelOrder {
37    pub trader_id: TraderId,
38    pub client_id: Option<ClientId>,
39    pub strategy_id: StrategyId,
40    pub instrument_id: InstrumentId,
41    pub client_order_id: ClientOrderId,
42    pub venue_order_id: Option<VenueOrderId>,
43    pub command_id: UUID4,
44    pub ts_init: UnixNanos,
45    pub params: Option<Params>,
46    #[builder(default)]
47    pub correlation_id: Option<UUID4>,
48    #[builder(default)]
49    #[serde(default, skip_serializing_if = "Option::is_none")]
50    pub causation_id: Option<UUID4>,
51}
52
53impl CancelOrder {
54    /// Creates a new [`CancelOrder`] instance.
55    #[expect(clippy::too_many_arguments)]
56    #[must_use]
57    pub fn new(
58        trader_id: TraderId,
59        client_id: Option<ClientId>,
60        strategy_id: StrategyId,
61        instrument_id: InstrumentId,
62        client_order_id: ClientOrderId,
63        venue_order_id: Option<VenueOrderId>,
64        command_id: UUID4,
65        ts_init: UnixNanos,
66        params: Option<Params>,
67        correlation_id: Option<UUID4>,
68    ) -> Self {
69        Self {
70            trader_id,
71            client_id,
72            strategy_id,
73            instrument_id,
74            client_order_id,
75            venue_order_id,
76            command_id,
77            ts_init,
78            params,
79            correlation_id,
80            causation_id: None,
81        }
82    }
83}
84
85impl Display for CancelOrder {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(
88            f,
89            "CancelOrder(instrument_id={}, client_order_id={}, venue_order_id={:?})",
90            self.instrument_id, self.client_order_id, self.venue_order_id,
91        )
92    }
93}
94
95#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
96#[serde(tag = "type")]
97#[cfg_attr(
98    feature = "python",
99    pyo3::pyclass(module = "nautilus_trader.live", frozen, from_py_object)
100)]
101#[cfg_attr(
102    feature = "python",
103    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
104)]
105pub struct CancelAllOrders {
106    pub trader_id: TraderId,
107    pub client_id: Option<ClientId>,
108    pub strategy_id: StrategyId,
109    pub instrument_id: InstrumentId,
110    #[serde(with = "nautilus_model::enums::serde_option_order_side")]
111    pub order_side: Option<OrderSide>,
112    pub command_id: UUID4,
113    pub ts_init: UnixNanos,
114    pub params: Option<Params>,
115    #[builder(default)]
116    pub correlation_id: Option<UUID4>,
117    #[builder(default)]
118    #[serde(default, skip_serializing_if = "Option::is_none")]
119    pub causation_id: Option<UUID4>,
120}
121
122impl CancelAllOrders {
123    /// Creates a new [`CancelAllOrders`] instance.
124    #[expect(clippy::too_many_arguments)]
125    #[must_use]
126    pub fn new(
127        trader_id: TraderId,
128        client_id: Option<ClientId>,
129        strategy_id: StrategyId,
130        instrument_id: InstrumentId,
131        order_side: Option<OrderSide>,
132        command_id: UUID4,
133        ts_init: UnixNanos,
134        params: Option<Params>,
135        correlation_id: Option<UUID4>,
136    ) -> Self {
137        Self {
138            trader_id,
139            client_id,
140            strategy_id,
141            instrument_id,
142            order_side,
143            command_id,
144            ts_init,
145            params,
146            correlation_id,
147            causation_id: None,
148        }
149    }
150}
151
152impl Display for CancelAllOrders {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        let order_side = self
155            .order_side
156            .as_ref()
157            .map_or("NO_ORDER_SIDE", AsRef::as_ref);
158        write!(
159            f,
160            "CancelAllOrders(instrument_id={}, order_side={})",
161            self.instrument_id, order_side,
162        )
163    }
164}
165
166#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
167#[serde(tag = "type")]
168#[cfg_attr(
169    feature = "python",
170    pyo3::pyclass(module = "nautilus_trader.live", frozen, from_py_object)
171)]
172#[cfg_attr(
173    feature = "python",
174    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.live")
175)]
176pub struct BatchCancelOrders {
177    pub trader_id: TraderId,
178    pub client_id: Option<ClientId>,
179    pub strategy_id: StrategyId,
180    pub instrument_id: InstrumentId,
181    pub cancels: Vec<CancelOrder>,
182    pub command_id: UUID4,
183    pub ts_init: UnixNanos,
184    pub params: Option<Params>,
185    #[builder(default)]
186    pub correlation_id: Option<UUID4>,
187    #[builder(default)]
188    #[serde(default, skip_serializing_if = "Option::is_none")]
189    pub causation_id: Option<UUID4>,
190}
191
192impl BatchCancelOrders {
193    /// Creates a new [`BatchCancelOrders`] instance.
194    #[expect(clippy::too_many_arguments)]
195    #[must_use]
196    pub fn new(
197        trader_id: TraderId,
198        client_id: Option<ClientId>,
199        strategy_id: StrategyId,
200        instrument_id: InstrumentId,
201        cancels: Vec<CancelOrder>,
202        command_id: UUID4,
203        ts_init: UnixNanos,
204        params: Option<Params>,
205        correlation_id: Option<UUID4>,
206    ) -> Self {
207        Self {
208            trader_id,
209            client_id,
210            strategy_id,
211            instrument_id,
212            cancels,
213            command_id,
214            ts_init,
215            params,
216            correlation_id,
217            causation_id: None,
218        }
219    }
220}
221
222impl Display for BatchCancelOrders {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        write!(
225            f,
226            "BatchCancelOrders(instrument_id={}, cancels={})",
227            self.instrument_id,
228            self.cancels.len(),
229        )
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use rstest::rstest;
236
237    use super::*;
238
239    #[rstest]
240    #[case(Some(OrderSide::Buy), "BUY")]
241    #[case(None, "NO_ORDER_SIDE")]
242    fn test_cancel_all_orders_display(
243        #[case] order_side: Option<OrderSide>,
244        #[case] expected_order_side: &str,
245    ) {
246        let command = CancelAllOrders::new(
247            TraderId::from("TRADER-001"),
248            None,
249            StrategyId::from("S-001"),
250            InstrumentId::from("AUD/USD.SIM"),
251            order_side,
252            UUID4::new(),
253            UnixNanos::default(),
254            None,
255            None,
256        );
257
258        assert_eq!(
259            command.to_string(),
260            format!("CancelAllOrders(instrument_id=AUD/USD.SIM, order_side={expected_order_side})")
261        );
262    }
263
264    #[rstest]
265    fn test_batch_cancel_orders_display() {
266        let cancel = CancelOrder::new(
267            TraderId::from("TRADER-001"),
268            None,
269            StrategyId::from("S-001"),
270            InstrumentId::from("AUD/USD.SIM"),
271            ClientOrderId::from("O-001"),
272            None,
273            UUID4::new(),
274            UnixNanos::default(),
275            None,
276            None,
277        );
278        let command = BatchCancelOrders::new(
279            TraderId::from("TRADER-001"),
280            None,
281            StrategyId::from("S-001"),
282            InstrumentId::from("AUD/USD.SIM"),
283            vec![cancel.clone(), cancel],
284            UUID4::new(),
285            UnixNanos::default(),
286            None,
287            None,
288        );
289
290        assert_eq!(
291            command.to_string(),
292            "BatchCancelOrders(instrument_id=AUD/USD.SIM, cancels=2)"
293        );
294    }
295}