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}