Skip to main content

nautilus_plugin/surfaces/commands/
modify.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
16//! Modify-order command and its boundary-owned handle.
17//!
18//! The plug-in constructs a [`ModifyOrderCommand`], wraps it in a
19//! [`ModifyOrderHandle`], and hands the host a `*const ModifyOrderHandle`
20//! via [`HostVTable::modify_order`](crate::host::HostVTable::modify_order).
21//! The host derefs the handle once and routes the borrowed command into
22//! the calling strategy's modify path. The plug-in owns the box and frees
23//! it when the call returns.
24
25#![allow(unsafe_code)]
26
27use std::ops::Deref;
28
29use nautilus_core::Params;
30use nautilus_model::{
31    identifiers::{ClientId, ClientOrderId},
32    types::{Price, Quantity},
33};
34
35/// Modify-order command. Mirrors the arguments to `Strategy::modify_order`.
36#[repr(C)]
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ModifyOrderCommand {
39    /// The client order identifier of the order to modify.
40    pub client_order_id: ClientOrderId,
41
42    /// New order quantity.
43    pub quantity: Option<Quantity>,
44
45    /// New limit price.
46    pub price: Option<Price>,
47
48    /// New trigger price.
49    pub trigger_price: Option<Price>,
50
51    /// Optional client routing identifier.
52    pub client_id: Option<ClientId>,
53
54    /// Optional venue-specific parameters.
55    pub params: Option<Params>,
56}
57
58impl ModifyOrderCommand {
59    /// Creates a new [`ModifyOrderCommand`] instance.
60    #[must_use]
61    pub const fn new(
62        client_order_id: ClientOrderId,
63        quantity: Option<Quantity>,
64        price: Option<Price>,
65        trigger_price: Option<Price>,
66        client_id: Option<ClientId>,
67        params: Option<Params>,
68    ) -> Self {
69        Self {
70            client_order_id,
71            quantity,
72            price,
73            trigger_price,
74            client_id,
75            params,
76        }
77    }
78}
79
80/// Boundary-owned wrapper that lets [`ModifyOrderCommand`] cross the cdylib
81/// FFI boundary by reference.
82///
83/// The plug-in constructs an instance, hands a
84/// `*const ModifyOrderHandle` to the host for the duration of the
85/// `modify_order` call, and drops the handle when the call returns. The
86/// host only borrows the handle and never owns it.
87#[repr(C)]
88#[derive(Debug, Clone)]
89pub struct ModifyOrderHandle(Box<ModifyOrderCommand>);
90
91impl ModifyOrderHandle {
92    /// Wraps `command` in a boundary-owned handle.
93    #[must_use]
94    pub fn new(command: ModifyOrderCommand) -> Self {
95        Self(Box::new(command))
96    }
97
98    /// Returns a reference to the wrapped command.
99    #[must_use]
100    pub fn command(&self) -> &ModifyOrderCommand {
101        &self.0
102    }
103
104    /// Consumes the wrapper and returns the inner command.
105    #[must_use]
106    pub fn into_inner(self) -> ModifyOrderCommand {
107        *self.0
108    }
109}
110
111impl Deref for ModifyOrderHandle {
112    type Target = ModifyOrderCommand;
113
114    fn deref(&self) -> &Self::Target {
115        &self.0
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use nautilus_model::identifiers::ClientOrderId;
122    use rstest::rstest;
123
124    use super::*;
125
126    #[rstest]
127    fn modify_order_handle_round_trips_command() {
128        let cmd = ModifyOrderCommand::new(
129            ClientOrderId::from("O-1"),
130            Some(Quantity::from("2.5")),
131            Some(Price::from("100.00")),
132            None,
133            None,
134            None,
135        );
136        let handle = ModifyOrderHandle::new(cmd.clone());
137        assert_eq!(handle.command(), &cmd);
138        assert_eq!(&*handle, &cmd);
139        assert_eq!(handle.into_inner(), cmd);
140    }
141}