1use ahash::AHashMap;
19use nautilus_execution::matching_engine::config::OrderMatchingEngineConfig;
20use nautilus_model::{
21 enums::{AccountType, BookType, OmsType},
22 identifiers::{AccountId, InstrumentId, TraderId, Venue},
23 types::{Currency, Money},
24};
25use rust_decimal::Decimal;
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30#[cfg_attr(
31 feature = "python",
32 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.sandbox", from_py_object)
33)]
34pub struct SandboxExecutionClientConfig {
35 pub trader_id: TraderId,
37 pub account_id: AccountId,
39 pub venue: Venue,
41 pub starting_balances: Vec<Money>,
43 pub base_currency: Option<Currency>,
45 pub oms_type: OmsType,
47 pub account_type: AccountType,
49 pub default_leverage: Decimal,
51 pub leverages: AHashMap<InstrumentId, Decimal>,
53 pub book_type: BookType,
55 pub frozen_account: bool,
57 pub bar_execution: bool,
59 pub trade_execution: bool,
61 pub reject_stop_orders: bool,
63 pub support_gtd_orders: bool,
65 pub support_contingent_orders: bool,
67 pub use_position_ids: bool,
69 pub use_random_ids: bool,
71 pub use_reduce_only: bool,
73}
74
75impl SandboxExecutionClientConfig {
76 #[must_use]
78 pub fn new(
79 trader_id: TraderId,
80 account_id: AccountId,
81 venue: Venue,
82 starting_balances: Vec<Money>,
83 ) -> Self {
84 Self {
85 trader_id,
86 account_id,
87 venue,
88 starting_balances,
89 base_currency: None,
90 oms_type: OmsType::Netting,
91 account_type: AccountType::Margin,
92 default_leverage: Decimal::ONE,
93 leverages: AHashMap::new(),
94 book_type: BookType::L1_MBP,
95 frozen_account: false,
96 bar_execution: true,
97 trade_execution: true,
98 reject_stop_orders: true,
99 support_gtd_orders: true,
100 support_contingent_orders: true,
101 use_position_ids: true,
102 use_random_ids: false,
103 use_reduce_only: true,
104 }
105 }
106
107 #[must_use]
109 pub fn to_matching_engine_config(&self) -> OrderMatchingEngineConfig {
110 OrderMatchingEngineConfig::new(
111 self.bar_execution,
112 false, self.trade_execution,
114 false, self.reject_stop_orders,
116 self.support_gtd_orders,
117 self.support_contingent_orders,
118 self.use_position_ids,
119 self.use_random_ids,
120 self.use_reduce_only,
121 false, )
123 }
124
125 #[must_use]
127 pub fn with_base_currency(mut self, currency: Currency) -> Self {
128 self.base_currency = Some(currency);
129 self
130 }
131
132 #[must_use]
134 pub fn with_oms_type(mut self, oms_type: OmsType) -> Self {
135 self.oms_type = oms_type;
136 self
137 }
138
139 #[must_use]
141 pub fn with_account_type(mut self, account_type: AccountType) -> Self {
142 self.account_type = account_type;
143 self
144 }
145
146 #[must_use]
148 pub fn with_default_leverage(mut self, leverage: Decimal) -> Self {
149 self.default_leverage = leverage;
150 self
151 }
152
153 #[must_use]
155 pub fn with_book_type(mut self, book_type: BookType) -> Self {
156 self.book_type = book_type;
157 self
158 }
159
160 #[must_use]
162 pub fn with_frozen_account(mut self, frozen: bool) -> Self {
163 self.frozen_account = frozen;
164 self
165 }
166
167 #[must_use]
169 pub fn with_bar_execution(mut self, enabled: bool) -> Self {
170 self.bar_execution = enabled;
171 self
172 }
173
174 #[must_use]
176 pub fn with_trade_execution(mut self, enabled: bool) -> Self {
177 self.trade_execution = enabled;
178 self
179 }
180}
181
182impl Default for SandboxExecutionClientConfig {
183 fn default() -> Self {
184 Self {
185 trader_id: TraderId::from("SANDBOX-001"),
186 account_id: AccountId::from("SANDBOX-001"),
187 venue: Venue::new("SANDBOX"),
188 starting_balances: Vec::new(),
189 base_currency: None,
190 oms_type: OmsType::Netting,
191 account_type: AccountType::Margin,
192 default_leverage: Decimal::ONE,
193 leverages: AHashMap::new(),
194 book_type: BookType::L1_MBP,
195 frozen_account: false,
196 bar_execution: true,
197 trade_execution: true,
198 reject_stop_orders: true,
199 support_gtd_orders: true,
200 support_contingent_orders: true,
201 use_position_ids: true,
202 use_random_ids: false,
203 use_reduce_only: true,
204 }
205 }
206}
207
208#[cfg(feature = "python")]
209mod pyo3_impl {
210 use nautilus_model::{
211 enums::{AccountType, BookType, OmsType},
212 identifiers::{AccountId, TraderId, Venue},
213 types::{Currency, Money},
214 };
215 use pyo3::prelude::*;
216 use rust_decimal::Decimal;
217
218 use super::SandboxExecutionClientConfig;
219
220 #[pymethods]
221 impl SandboxExecutionClientConfig {
222 #[new]
223 #[pyo3(signature = (venue, starting_balances, trader_id=None, account_id=None, base_currency=None, oms_type=None, account_type=None, default_leverage=None, book_type=None, frozen_account=false, bar_execution=true, trade_execution=true, reject_stop_orders=true, support_gtd_orders=true, support_contingent_orders=true, use_position_ids=true, use_random_ids=false, use_reduce_only=true))]
224 #[allow(clippy::too_many_arguments)]
225 fn py_new(
226 venue: Venue,
227 starting_balances: Vec<Money>,
228 trader_id: Option<TraderId>,
229 account_id: Option<AccountId>,
230 base_currency: Option<Currency>,
231 oms_type: Option<OmsType>,
232 account_type: Option<AccountType>,
233 default_leverage: Option<Decimal>,
234 book_type: Option<BookType>,
235 frozen_account: bool,
236 bar_execution: bool,
237 trade_execution: bool,
238 reject_stop_orders: bool,
239 support_gtd_orders: bool,
240 support_contingent_orders: bool,
241 use_position_ids: bool,
242 use_random_ids: bool,
243 use_reduce_only: bool,
244 ) -> Self {
245 let trader_id =
247 trader_id.unwrap_or_else(|| TraderId::from(format!("{venue}-001").as_str()));
248 let account_id = account_id
249 .unwrap_or_else(|| AccountId::from(format!("{venue}-SANDBOX-001").as_str()));
250
251 Self {
252 trader_id,
253 account_id,
254 venue,
255 starting_balances,
256 base_currency,
257 oms_type: oms_type.unwrap_or(OmsType::Netting),
258 account_type: account_type.unwrap_or(AccountType::Margin),
259 default_leverage: default_leverage.unwrap_or(Decimal::ONE),
260 leverages: ahash::AHashMap::new(),
261 book_type: book_type.unwrap_or(BookType::L1_MBP),
262 frozen_account,
263 bar_execution,
264 trade_execution,
265 reject_stop_orders,
266 support_gtd_orders,
267 support_contingent_orders,
268 use_position_ids,
269 use_random_ids,
270 use_reduce_only,
271 }
272 }
273
274 #[getter]
275 fn trader_id(&self) -> TraderId {
276 self.trader_id
277 }
278
279 #[getter]
280 fn account_id(&self) -> AccountId {
281 self.account_id
282 }
283
284 #[getter]
285 fn venue(&self) -> Venue {
286 self.venue
287 }
288
289 #[getter]
290 fn starting_balances(&self) -> Vec<Money> {
291 self.starting_balances.clone()
292 }
293
294 #[getter]
295 fn base_currency(&self) -> Option<Currency> {
296 self.base_currency
297 }
298
299 #[getter]
300 fn oms_type(&self) -> OmsType {
301 self.oms_type
302 }
303
304 #[getter]
305 fn account_type(&self) -> AccountType {
306 self.account_type
307 }
308
309 #[getter]
310 fn default_leverage(&self) -> Decimal {
311 self.default_leverage
312 }
313
314 #[getter]
315 fn book_type(&self) -> BookType {
316 self.book_type
317 }
318
319 #[getter]
320 fn frozen_account(&self) -> bool {
321 self.frozen_account
322 }
323
324 #[getter]
325 fn bar_execution(&self) -> bool {
326 self.bar_execution
327 }
328
329 #[getter]
330 fn trade_execution(&self) -> bool {
331 self.trade_execution
332 }
333
334 #[getter]
335 fn reject_stop_orders(&self) -> bool {
336 self.reject_stop_orders
337 }
338
339 #[getter]
340 fn support_gtd_orders(&self) -> bool {
341 self.support_gtd_orders
342 }
343
344 #[getter]
345 fn support_contingent_orders(&self) -> bool {
346 self.support_contingent_orders
347 }
348
349 #[getter]
350 fn use_position_ids(&self) -> bool {
351 self.use_position_ids
352 }
353
354 #[getter]
355 fn use_random_ids(&self) -> bool {
356 self.use_random_ids
357 }
358
359 #[getter]
360 fn use_reduce_only(&self) -> bool {
361 self.use_reduce_only
362 }
363 }
364}