nautilus_model/python/events/order/
initialized.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
16use indexmap::IndexMap;
17use nautilus_core::{python::serialization::from_dict_pyo3, UnixNanos, UUID4};
18use pyo3::{
19    basic::CompareOp,
20    prelude::*,
21    types::{PyDict, PyList},
22};
23use rust_decimal::Decimal;
24use ustr::Ustr;
25
26use crate::{
27    enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType},
28    events::OrderInitialized,
29    identifiers::{
30        ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, StrategyId, TraderId,
31    },
32    orders::base::str_indexmap_to_ustr,
33    types::{Price, Quantity},
34};
35
36#[pymethods]
37impl OrderInitialized {
38    #[allow(clippy::too_many_arguments)]
39    #[new]
40    #[pyo3(signature = (trader_id, strategy_id, instrument_id, client_order_id, order_side, order_type, quantity, time_in_force, post_only, reduce_only, quote_quantity, reconciliation, event_id, ts_event, ts_init, price=None, trigger_price=None, trigger_type=None, limit_offset=None, trailing_offset=None, trailing_offset_type=None, expire_time=None, display_qty=None, emulation_trigger=None, trigger_instrument_id=None, contingency_type=None, order_list_id=None, linked_order_ids=None, parent_order_id=None, exec_algorithm_id=None, exec_algorithm_params=None, exec_spawn_id=None, tags=None))]
41    fn py_new(
42        trader_id: TraderId,
43        strategy_id: StrategyId,
44        instrument_id: InstrumentId,
45        client_order_id: ClientOrderId,
46        order_side: OrderSide,
47        order_type: OrderType,
48        quantity: Quantity,
49        time_in_force: TimeInForce,
50        post_only: bool,
51        reduce_only: bool,
52        quote_quantity: bool,
53        reconciliation: bool,
54        event_id: UUID4,
55        ts_event: u64,
56        ts_init: u64,
57        price: Option<Price>,
58        trigger_price: Option<Price>,
59        trigger_type: Option<TriggerType>,
60        limit_offset: Option<Decimal>,
61        trailing_offset: Option<Decimal>,
62        trailing_offset_type: Option<TrailingOffsetType>,
63        expire_time: Option<u64>,
64        display_qty: Option<Quantity>,
65        emulation_trigger: Option<TriggerType>,
66        trigger_instrument_id: Option<InstrumentId>,
67        contingency_type: Option<ContingencyType>,
68        order_list_id: Option<OrderListId>,
69        linked_order_ids: Option<Vec<ClientOrderId>>,
70        parent_order_id: Option<ClientOrderId>,
71        exec_algorithm_id: Option<ExecAlgorithmId>,
72        exec_algorithm_params: Option<IndexMap<String, String>>,
73        exec_spawn_id: Option<ClientOrderId>,
74        tags: Option<Vec<String>>,
75    ) -> Self {
76        Self::new(
77            trader_id,
78            strategy_id,
79            instrument_id,
80            client_order_id,
81            order_side,
82            order_type,
83            quantity,
84            time_in_force,
85            post_only,
86            reduce_only,
87            quote_quantity,
88            reconciliation,
89            event_id,
90            ts_event.into(),
91            ts_init.into(),
92            price,
93            trigger_price,
94            trigger_type,
95            limit_offset,
96            trailing_offset,
97            trailing_offset_type,
98            expire_time.map(UnixNanos::from),
99            display_qty,
100            emulation_trigger,
101            trigger_instrument_id,
102            contingency_type,
103            order_list_id,
104            linked_order_ids,
105            parent_order_id,
106            exec_algorithm_id,
107            exec_algorithm_params.map(str_indexmap_to_ustr),
108            exec_spawn_id,
109            tags.map(|vec| vec.iter().map(|s| Ustr::from(s)).collect()),
110        )
111    }
112
113    fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
114        match op {
115            CompareOp::Eq => self.eq(other).into_py(py),
116            CompareOp::Ne => self.ne(other).into_py(py),
117            _ => py.NotImplemented(),
118        }
119    }
120
121    fn __repr__(&self) -> String {
122        format!("{:?}", self)
123    }
124
125    fn __str__(&self) -> String {
126        self.to_string()
127    }
128
129    #[getter]
130    #[pyo3(name = "order_type")]
131    fn py_order_type(&self) -> OrderType {
132        self.order_type
133    }
134
135    #[staticmethod]
136    #[pyo3(name = "from_dict")]
137    fn py_from_dict(py: Python<'_>, values: Py<PyDict>) -> PyResult<Self> {
138        from_dict_pyo3(py, values)
139    }
140
141    #[pyo3(name = "to_dict")]
142    fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
143        let dict = PyDict::new(py);
144        dict.set_item("type", stringify!(OrderInitiliazed))?;
145        dict.set_item("trader_id", self.trader_id.to_string())?;
146        dict.set_item("strategy_id", self.strategy_id.to_string())?;
147        dict.set_item("instrument_id", self.instrument_id.to_string())?;
148        dict.set_item("client_order_id", self.client_order_id.to_string())?;
149        dict.set_item("order_side", self.order_side.to_string())?;
150        dict.set_item("order_type", self.order_type.to_string())?;
151        dict.set_item("quantity", self.quantity.to_string())?;
152        dict.set_item("time_in_force", self.time_in_force.to_string())?;
153        dict.set_item("post_only", self.post_only)?;
154        dict.set_item("reduce_only", self.reduce_only)?;
155        dict.set_item("quote_quantity", self.quote_quantity)?;
156        dict.set_item("reconciliation", self.reconciliation)?;
157        // TODO remove options as in legacy cython only
158        let options = PyDict::new(py);
159        if self.order_type == OrderType::StopMarket {
160            options.set_item("trigger_type", self.trigger_type.map(|x| x.to_string()))?;
161            options.set_item("trigger_price", self.trigger_price.map(|x| x.to_string()))?;
162            options.set_item("expire_time_ns", self.expire_time.map(|x| x.to_string()))?;
163        }
164        dict.set_item("options", options)?;
165        dict.set_item("event_id", self.event_id.to_string())?;
166        dict.set_item("ts_event", self.ts_event.as_u64())?;
167        dict.set_item("ts_init", self.ts_init.as_u64())?;
168        match self.price {
169            Some(price) => dict.set_item("price", price.to_string())?,
170            None => dict.set_item("price", py.None())?,
171        }
172        match self.trigger_price {
173            Some(trigger_price) => dict.set_item("trigger_price", trigger_price.to_string())?,
174            None => dict.set_item("trigger_price", py.None())?,
175        }
176        match self.trigger_type {
177            Some(trigger_type) => dict.set_item("trigger_type", trigger_type.to_string())?,
178            None => dict.set_item("trigger_type", py.None())?,
179        }
180        match self.limit_offset {
181            Some(limit_offset) => dict.set_item("limit_offset", limit_offset.to_string())?,
182            None => dict.set_item("limit_offset", py.None())?,
183        }
184        match self.trailing_offset {
185            Some(trailing_offset) => {
186                dict.set_item("trailing_offset", trailing_offset.to_string())?;
187            }
188            None => dict.set_item("trailing_offset", py.None())?,
189        }
190        match self.trailing_offset_type {
191            Some(trailing_offset_type) => {
192                dict.set_item("trailing_offset_type", trailing_offset_type.to_string())?;
193            }
194            None => dict.set_item("trailing_offset_type", py.None())?,
195        }
196        match self.expire_time {
197            Some(expire_time) => dict.set_item("expire_time", expire_time.as_u64())?,
198            None => dict.set_item("expire_time", py.None())?,
199        }
200        match self.display_qty {
201            Some(display_qty) => dict.set_item("display_qty", display_qty.to_string())?,
202            None => dict.set_item("display_qty", py.None())?,
203        }
204        match self.emulation_trigger {
205            Some(emulation_trigger) => {
206                dict.set_item("emulation_trigger", emulation_trigger.to_string())?;
207            }
208            None => dict.set_item("emulation_trigger", py.None())?,
209        }
210        match self.trigger_instrument_id {
211            Some(trigger_instrument_id) => {
212                dict.set_item("trigger_instrument_id", trigger_instrument_id.to_string())?;
213            }
214            None => dict.set_item("trigger_instrument_id", py.None())?,
215        }
216        match self.contingency_type {
217            Some(contingency_type) => {
218                dict.set_item("contingency_type", contingency_type.to_string())?;
219            }
220            None => dict.set_item("contingency_type", py.None())?,
221        }
222        match self.order_list_id {
223            Some(order_list_id) => dict.set_item("order_list_id", order_list_id.to_string())?,
224            None => dict.set_item("order_list_id", py.None())?,
225        }
226        match &self.linked_order_ids {
227            Some(linked_order_ids) => {
228                let py_linked_order_ids = PyList::empty(py);
229                for linked_order_id in linked_order_ids {
230                    py_linked_order_ids.append(linked_order_id.to_string())?;
231                }
232                dict.set_item("linked_order_ids", py_linked_order_ids)?;
233            }
234            None => dict.set_item("linked_order_ids", py.None())?,
235        }
236        match self.parent_order_id {
237            Some(parent_order_id) => {
238                dict.set_item("parent_order_id", parent_order_id.to_string())?;
239            }
240            None => dict.set_item("parent_order_id", py.None())?,
241        }
242        match self.exec_algorithm_id {
243            Some(exec_algorithm_id) => {
244                dict.set_item("exec_algorithm_id", exec_algorithm_id.to_string())?;
245            }
246            None => dict.set_item("exec_algorithm_id", py.None())?,
247        }
248        match &self.exec_algorithm_params {
249            Some(exec_algorithm_params) => {
250                let py_exec_algorithm_params = PyDict::new(py);
251                for (key, value) in exec_algorithm_params {
252                    py_exec_algorithm_params.set_item(key.to_string(), value.to_string())?;
253                }
254                dict.set_item("exec_algorithm_params", py_exec_algorithm_params)?;
255            }
256            None => dict.set_item("exec_algorithm_params", py.None())?,
257        }
258        match self.exec_spawn_id {
259            Some(exec_spawn_id) => dict.set_item("exec_spawn_id", exec_spawn_id.to_string())?,
260            None => dict.set_item("exec_spawn_id", py.None())?,
261        }
262        match &self.tags {
263            Some(tags) => dict.set_item(
264                "tags",
265                tags.iter().map(|x| x.to_string()).collect::<Vec<String>>(),
266            )?,
267            None => dict.set_item("tags", py.None())?,
268        }
269        Ok(dict.into())
270    }
271}