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")]
28pub struct CancelOrder {
29    pub trader_id: TraderId,
30    pub client_id: Option<ClientId>,
31    pub strategy_id: StrategyId,
32    pub instrument_id: InstrumentId,
33    pub client_order_id: ClientOrderId,
34    pub venue_order_id: Option<VenueOrderId>,
35    pub command_id: UUID4,
36    pub ts_init: UnixNanos,
37    pub params: Option<Params>,
38    #[builder(default)]
39    pub correlation_id: Option<UUID4>,
40    #[builder(default)]
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub causation_id: Option<UUID4>,
43}
44
45impl CancelOrder {
46    /// Creates a new [`CancelOrder`] instance.
47    #[expect(clippy::too_many_arguments)]
48    #[must_use]
49    pub fn new(
50        trader_id: TraderId,
51        client_id: Option<ClientId>,
52        strategy_id: StrategyId,
53        instrument_id: InstrumentId,
54        client_order_id: ClientOrderId,
55        venue_order_id: Option<VenueOrderId>,
56        command_id: UUID4,
57        ts_init: UnixNanos,
58        params: Option<Params>,
59        correlation_id: Option<UUID4>,
60    ) -> Self {
61        Self {
62            trader_id,
63            client_id,
64            strategy_id,
65            instrument_id,
66            client_order_id,
67            venue_order_id,
68            command_id,
69            ts_init,
70            params,
71            correlation_id,
72            causation_id: None,
73        }
74    }
75}
76
77impl Display for CancelOrder {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        write!(
80            f,
81            "CancelOrder(instrument_id={}, client_order_id={}, venue_order_id={:?})",
82            self.instrument_id, self.client_order_id, self.venue_order_id,
83        )
84    }
85}
86
87#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
88#[serde(tag = "type")]
89pub struct CancelAllOrders {
90    pub trader_id: TraderId,
91    pub client_id: Option<ClientId>,
92    pub strategy_id: StrategyId,
93    pub instrument_id: InstrumentId,
94    #[serde(with = "nautilus_model::enums::serde_option_order_side")]
95    pub order_side: Option<OrderSide>,
96    pub command_id: UUID4,
97    pub ts_init: UnixNanos,
98    pub params: Option<Params>,
99    #[builder(default)]
100    pub correlation_id: Option<UUID4>,
101    #[builder(default)]
102    #[serde(default, skip_serializing_if = "Option::is_none")]
103    pub causation_id: Option<UUID4>,
104}
105
106impl CancelAllOrders {
107    /// Creates a new [`CancelAllOrders`] instance.
108    #[expect(clippy::too_many_arguments)]
109    #[must_use]
110    pub fn new(
111        trader_id: TraderId,
112        client_id: Option<ClientId>,
113        strategy_id: StrategyId,
114        instrument_id: InstrumentId,
115        order_side: Option<OrderSide>,
116        command_id: UUID4,
117        ts_init: UnixNanos,
118        params: Option<Params>,
119        correlation_id: Option<UUID4>,
120    ) -> Self {
121        Self {
122            trader_id,
123            client_id,
124            strategy_id,
125            instrument_id,
126            order_side,
127            command_id,
128            ts_init,
129            params,
130            correlation_id,
131            causation_id: None,
132        }
133    }
134}
135
136impl Display for CancelAllOrders {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        let order_side = self
139            .order_side
140            .as_ref()
141            .map_or("NO_ORDER_SIDE", AsRef::as_ref);
142        write!(
143            f,
144            "CancelAllOrders(instrument_id={}, order_side={})",
145            self.instrument_id, order_side,
146        )
147    }
148}
149
150#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
151#[serde(tag = "type")]
152pub struct BatchCancelOrders {
153    pub trader_id: TraderId,
154    pub client_id: Option<ClientId>,
155    pub strategy_id: StrategyId,
156    pub instrument_id: InstrumentId,
157    pub cancels: Vec<CancelOrder>,
158    pub command_id: UUID4,
159    pub ts_init: UnixNanos,
160    pub params: Option<Params>,
161    #[builder(default)]
162    pub correlation_id: Option<UUID4>,
163    #[builder(default)]
164    #[serde(default, skip_serializing_if = "Option::is_none")]
165    pub causation_id: Option<UUID4>,
166}
167
168impl BatchCancelOrders {
169    /// Creates a new [`BatchCancelOrders`] instance.
170    #[expect(clippy::too_many_arguments)]
171    #[must_use]
172    pub fn new(
173        trader_id: TraderId,
174        client_id: Option<ClientId>,
175        strategy_id: StrategyId,
176        instrument_id: InstrumentId,
177        cancels: Vec<CancelOrder>,
178        command_id: UUID4,
179        ts_init: UnixNanos,
180        params: Option<Params>,
181        correlation_id: Option<UUID4>,
182    ) -> Self {
183        Self {
184            trader_id,
185            client_id,
186            strategy_id,
187            instrument_id,
188            cancels,
189            command_id,
190            ts_init,
191            params,
192            correlation_id,
193            causation_id: None,
194        }
195    }
196}
197
198impl Display for BatchCancelOrders {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(
201            f,
202            "BatchCancelOrders(instrument_id={}, cancels=TBD)",
203            self.instrument_id,
204        )
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use rstest::rstest;
211
212    use super::*;
213
214    #[rstest]
215    #[case(Some(OrderSide::Buy), "BUY")]
216    #[case(None, "NO_ORDER_SIDE")]
217    fn test_cancel_all_orders_display(
218        #[case] order_side: Option<OrderSide>,
219        #[case] expected_order_side: &str,
220    ) {
221        let command = CancelAllOrders::new(
222            TraderId::from("TRADER-001"),
223            None,
224            StrategyId::from("S-001"),
225            InstrumentId::from("AUD/USD.SIM"),
226            order_side,
227            UUID4::new(),
228            UnixNanos::default(),
229            None,
230            None,
231        );
232
233        assert_eq!(
234            command.to_string(),
235            format!("CancelAllOrders(instrument_id=AUD/USD.SIM, order_side={expected_order_side})")
236        );
237    }
238}