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}