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