nautilus_execution/client/mod.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2025 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//! Execution client implementations for trading venue connectivity.
17
18use std::{
19 fmt::Debug,
20 ops::{Deref, DerefMut},
21};
22
23use async_trait::async_trait;
24use nautilus_common::messages::execution::{
25 BatchCancelOrders, CancelAllOrders, CancelOrder, ModifyOrder, QueryAccount, QueryOrder,
26 SubmitOrder, SubmitOrderList,
27};
28use nautilus_core::UnixNanos;
29use nautilus_model::{
30 accounts::AccountAny,
31 enums::OmsType,
32 identifiers::{AccountId, ClientId, Venue},
33 types::{AccountBalance, MarginBalance},
34};
35
36pub mod base;
37
38/// Defines the interface for an execution client managing order operations.
39///
40/// # Thread safety
41///
42/// Client instances are not intended to be sent across threads. The `?Send` bound
43/// allows implementations to hold non-Send state for any Python interop.
44#[async_trait(?Send)]
45pub trait ExecutionClient {
46 fn is_connected(&self) -> bool;
47 fn client_id(&self) -> ClientId;
48 fn account_id(&self) -> AccountId;
49 fn venue(&self) -> Venue;
50 fn oms_type(&self) -> OmsType;
51 fn get_account(&self) -> Option<AccountAny>;
52
53 /// Generates and publishes the account state event.
54 ///
55 /// # Errors
56 ///
57 /// Returns an error if generating the account state fails.
58 fn generate_account_state(
59 &self,
60 balances: Vec<AccountBalance>,
61 margins: Vec<MarginBalance>,
62 reported: bool,
63 ts_event: UnixNanos,
64 ) -> anyhow::Result<()>;
65
66 /// Starts the execution client.
67 ///
68 /// # Errors
69 ///
70 /// Returns an error if the client fails to start.
71 fn start(&mut self) -> anyhow::Result<()>;
72
73 /// Stops the execution client.
74 ///
75 /// # Errors
76 ///
77 /// Returns an error if the client fails to stop.
78 fn stop(&mut self) -> anyhow::Result<()>;
79
80 /// Connects the client to the execution venue.
81 ///
82 /// # Errors
83 ///
84 /// Returns an error if connection fails.
85 async fn connect(&mut self) -> anyhow::Result<()> {
86 Ok(())
87 }
88
89 /// Disconnects the client from the execution venue.
90 ///
91 /// # Errors
92 ///
93 /// Returns an error if disconnection fails.
94 async fn disconnect(&mut self) -> anyhow::Result<()> {
95 Ok(())
96 }
97
98 /// Submits a single order command to the execution venue.
99 ///
100 /// # Errors
101 ///
102 /// Returns an error if submission fails.
103 fn submit_order(&self, cmd: &SubmitOrder) -> anyhow::Result<()> {
104 log_not_implemented(cmd);
105 Ok(())
106 }
107
108 /// Submits a list of orders to the execution venue.
109 ///
110 /// # Errors
111 ///
112 /// Returns an error if submission fails.
113 fn submit_order_list(&self, cmd: &SubmitOrderList) -> anyhow::Result<()> {
114 log_not_implemented(cmd);
115 Ok(())
116 }
117
118 /// Modifies an existing order.
119 ///
120 /// # Errors
121 ///
122 /// Returns an error if modification fails.
123 fn modify_order(&self, cmd: &ModifyOrder) -> anyhow::Result<()> {
124 log_not_implemented(cmd);
125 Ok(())
126 }
127
128 /// Cancels a specific order.
129 ///
130 /// # Errors
131 ///
132 /// Returns an error if cancellation fails.
133 fn cancel_order(&self, cmd: &CancelOrder) -> anyhow::Result<()> {
134 log_not_implemented(cmd);
135 Ok(())
136 }
137
138 /// Cancels all orders.
139 ///
140 /// # Errors
141 ///
142 /// Returns an error if cancellation fails.
143 fn cancel_all_orders(&self, cmd: &CancelAllOrders) -> anyhow::Result<()> {
144 log_not_implemented(cmd);
145 Ok(())
146 }
147
148 /// Cancels a batch of orders.
149 ///
150 /// # Errors
151 ///
152 /// Returns an error if batch cancellation fails.
153 fn batch_cancel_orders(&self, cmd: &BatchCancelOrders) -> anyhow::Result<()> {
154 log_not_implemented(cmd);
155 Ok(())
156 }
157
158 /// Queries the status of an account.
159 ///
160 /// # Errors
161 ///
162 /// Returns an error if the query fails.
163 fn query_account(&self, cmd: &QueryAccount) -> anyhow::Result<()> {
164 log_not_implemented(cmd);
165 Ok(())
166 }
167
168 /// Queries the status of an order.
169 ///
170 /// # Errors
171 ///
172 /// Returns an error if the query fails.
173 fn query_order(&self, cmd: &QueryOrder) -> anyhow::Result<()> {
174 log_not_implemented(cmd);
175 Ok(())
176 }
177}
178
179#[inline(always)]
180fn log_not_implemented<T: Debug>(cmd: &T) {
181 log::warn!("{cmd:?} – handler not implemented");
182}
183
184/// Wraps an [`ExecutionClient`], managing its lifecycle and providing access to the client.
185pub struct ExecutionClientAdapter {
186 pub(crate) client: Box<dyn ExecutionClient>,
187 pub client_id: ClientId,
188 pub venue: Venue,
189 pub account_id: AccountId,
190 pub oms_type: OmsType,
191}
192
193impl Deref for ExecutionClientAdapter {
194 type Target = Box<dyn ExecutionClient>;
195
196 fn deref(&self) -> &Self::Target {
197 &self.client
198 }
199}
200
201impl DerefMut for ExecutionClientAdapter {
202 fn deref_mut(&mut self) -> &mut Self::Target {
203 &mut self.client
204 }
205}
206
207impl Debug for ExecutionClientAdapter {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 f.debug_struct(stringify!(ExecutionClientAdapter))
210 .field("client_id", &self.client_id)
211 .field("venue", &self.venue)
212 .field("account_id", &self.account_id)
213 .field("oms_type", &self.oms_type)
214 .finish()
215 }
216}
217
218impl ExecutionClientAdapter {
219 /// Creates a new [`ExecutionClientAdapter`] with the given client.
220 #[must_use]
221 pub fn new(client: Box<dyn ExecutionClient>) -> Self {
222 let client_id = client.client_id();
223 let venue = client.venue();
224 let account_id = client.account_id();
225 let oms_type = client.oms_type();
226
227 Self {
228 client,
229 client_id,
230 venue,
231 account_id,
232 oms_type,
233 }
234 }
235
236 /// Connects the execution client to the venue.
237 ///
238 /// # Errors
239 ///
240 /// Returns an error if connection fails.
241 pub async fn connect(&mut self) -> anyhow::Result<()> {
242 self.client.connect().await
243 }
244
245 /// Disconnects the execution client from the venue.
246 ///
247 /// # Errors
248 ///
249 /// Returns an error if disconnection fails.
250 pub async fn disconnect(&mut self) -> anyhow::Result<()> {
251 self.client.disconnect().await
252 }
253}