Skip to main content

nautilus_common/python/
order_factory.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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//! Python bindings for the [`OrderFactory`].
17
18use std::{cell::RefCell, rc::Rc};
19
20use indexmap::IndexMap;
21use nautilus_core::{UnixNanos, python::to_pyvalue_err};
22use nautilus_model::{
23    enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType},
24    identifiers::{
25        ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, StrategyId, TraderId,
26    },
27    orders::{OrderAny, str_indexmap_to_ustr},
28    python::orders::order_any_to_pyobject,
29    types::{Price, Quantity},
30};
31use pyo3::{prelude::*, types::PyList};
32use rust_decimal::Decimal;
33use ustr::Ustr;
34
35use crate::{factories::OrderFactory, python::clock::PyClock};
36
37/// Wrapper providing shared access to [`OrderFactory`] from Python.
38#[allow(non_camel_case_types)]
39#[pyo3::pyclass(
40    module = "nautilus_trader.common",
41    name = "OrderFactory",
42    unsendable,
43    from_py_object
44)]
45#[pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.common")]
46#[derive(Debug, Clone)]
47pub struct PyOrderFactory(Rc<RefCell<OrderFactory>>);
48
49impl PyOrderFactory {
50    /// Creates a `PyOrderFactory` from an `Rc<RefCell<OrderFactory>>`.
51    #[must_use]
52    pub fn from_rc(rc: Rc<RefCell<OrderFactory>>) -> Self {
53        Self(rc)
54    }
55
56    /// Gets the inner `Rc<RefCell<OrderFactory>>` for use in Rust code.
57    #[must_use]
58    pub fn order_factory_rc(&self) -> Rc<RefCell<OrderFactory>> {
59        self.0.clone()
60    }
61}
62
63#[pymethods]
64#[pyo3_stub_gen::derive::gen_stub_pymethods]
65#[expect(
66    clippy::too_many_arguments,
67    reason = "factory methods mirror the Python order factory API"
68)]
69impl PyOrderFactory {
70    #[new]
71    #[pyo3(signature = (
72        trader_id,
73        strategy_id,
74        clock,
75        use_uuid_client_order_ids=false,
76        use_hyphens_in_client_order_ids=true
77    ))]
78    #[allow(
79        clippy::needless_pass_by_value,
80        reason = "PyO3 extracts pyclass constructor arguments by value"
81    )]
82    fn py_new(
83        trader_id: TraderId,
84        strategy_id: StrategyId,
85        clock: PyClock,
86        use_uuid_client_order_ids: bool,
87        use_hyphens_in_client_order_ids: bool,
88    ) -> Self {
89        Self(Rc::new(RefCell::new(OrderFactory::new(
90            trader_id,
91            strategy_id,
92            None,
93            None,
94            clock.clock_rc(),
95            use_uuid_client_order_ids,
96            use_hyphens_in_client_order_ids,
97        ))))
98    }
99
100    #[getter]
101    #[pyo3(name = "trader_id")]
102    fn py_trader_id(&self) -> TraderId {
103        self.0.borrow().trader_id()
104    }
105
106    #[getter]
107    #[pyo3(name = "strategy_id")]
108    fn py_strategy_id(&self) -> StrategyId {
109        self.0.borrow().strategy_id()
110    }
111
112    #[pyo3(name = "get_client_order_id_count")]
113    fn py_get_client_order_id_count(&self) -> usize {
114        self.0.borrow().client_order_id_count()
115    }
116
117    #[pyo3(name = "get_order_list_id_count")]
118    fn py_get_order_list_id_count(&self) -> usize {
119        self.0.borrow().order_list_id_count()
120    }
121
122    #[pyo3(name = "generate_client_order_id")]
123    fn py_generate_client_order_id(&mut self) -> ClientOrderId {
124        self.0.borrow_mut().generate_client_order_id()
125    }
126
127    #[pyo3(name = "generate_order_list_id")]
128    fn py_generate_order_list_id(&mut self) -> OrderListId {
129        self.0.borrow_mut().generate_order_list_id()
130    }
131
132    #[pyo3(name = "reset")]
133    fn py_reset(&mut self) {
134        self.0.borrow_mut().reset_factory();
135    }
136
137    #[pyo3(name = "market", signature = (
138        instrument_id,
139        order_side,
140        quantity,
141        time_in_force=None,
142        reduce_only=None,
143        quote_quantity=None,
144        exec_algorithm_id=None,
145        exec_algorithm_params=None,
146        tags=None,
147        client_order_id=None
148    ))]
149    fn py_market(
150        &mut self,
151        py: Python<'_>,
152        instrument_id: InstrumentId,
153        order_side: OrderSide,
154        quantity: Quantity,
155        time_in_force: Option<TimeInForce>,
156        reduce_only: Option<bool>,
157        quote_quantity: Option<bool>,
158        exec_algorithm_id: Option<ExecAlgorithmId>,
159        exec_algorithm_params: Option<IndexMap<String, String>>,
160        tags: Option<Vec<String>>,
161        client_order_id: Option<ClientOrderId>,
162    ) -> PyResult<Py<PyAny>> {
163        py_order_from_factory(py, || {
164            self.0.borrow_mut().try_market(
165                instrument_id,
166                order_side,
167                quantity,
168                time_in_force,
169                reduce_only,
170                quote_quantity,
171                exec_algorithm_id,
172                exec_algorithm_params.map(str_indexmap_to_ustr),
173                tags.map(strings_to_ustrs),
174                client_order_id,
175            )
176        })
177    }
178
179    #[pyo3(name = "limit", signature = (
180        instrument_id,
181        order_side,
182        quantity,
183        price,
184        time_in_force=None,
185        expire_time=None,
186        post_only=None,
187        reduce_only=None,
188        quote_quantity=None,
189        display_qty=None,
190        emulation_trigger=None,
191        trigger_instrument_id=None,
192        exec_algorithm_id=None,
193        exec_algorithm_params=None,
194        tags=None,
195        client_order_id=None
196    ))]
197    fn py_limit(
198        &mut self,
199        py: Python<'_>,
200        instrument_id: InstrumentId,
201        order_side: OrderSide,
202        quantity: Quantity,
203        price: Price,
204        time_in_force: Option<TimeInForce>,
205        expire_time: Option<u64>,
206        post_only: Option<bool>,
207        reduce_only: Option<bool>,
208        quote_quantity: Option<bool>,
209        display_qty: Option<Quantity>,
210        emulation_trigger: Option<TriggerType>,
211        trigger_instrument_id: Option<InstrumentId>,
212        exec_algorithm_id: Option<ExecAlgorithmId>,
213        exec_algorithm_params: Option<IndexMap<String, String>>,
214        tags: Option<Vec<String>>,
215        client_order_id: Option<ClientOrderId>,
216    ) -> PyResult<Py<PyAny>> {
217        py_order_from_factory(py, || {
218            self.0.borrow_mut().try_limit(
219                instrument_id,
220                order_side,
221                quantity,
222                price,
223                time_in_force,
224                expire_time.map(UnixNanos::from),
225                post_only,
226                reduce_only,
227                quote_quantity,
228                display_qty,
229                emulation_trigger,
230                trigger_instrument_id,
231                exec_algorithm_id,
232                exec_algorithm_params.map(str_indexmap_to_ustr),
233                tags.map(strings_to_ustrs),
234                client_order_id,
235            )
236        })
237    }
238
239    #[pyo3(name = "stop_market", signature = (
240        instrument_id,
241        order_side,
242        quantity,
243        trigger_price,
244        trigger_type=None,
245        time_in_force=None,
246        expire_time=None,
247        reduce_only=None,
248        quote_quantity=None,
249        display_qty=None,
250        emulation_trigger=None,
251        trigger_instrument_id=None,
252        exec_algorithm_id=None,
253        exec_algorithm_params=None,
254        tags=None,
255        client_order_id=None
256    ))]
257    fn py_stop_market(
258        &mut self,
259        py: Python<'_>,
260        instrument_id: InstrumentId,
261        order_side: OrderSide,
262        quantity: Quantity,
263        trigger_price: Price,
264        trigger_type: Option<TriggerType>,
265        time_in_force: Option<TimeInForce>,
266        expire_time: Option<u64>,
267        reduce_only: Option<bool>,
268        quote_quantity: Option<bool>,
269        display_qty: Option<Quantity>,
270        emulation_trigger: Option<TriggerType>,
271        trigger_instrument_id: Option<InstrumentId>,
272        exec_algorithm_id: Option<ExecAlgorithmId>,
273        exec_algorithm_params: Option<IndexMap<String, String>>,
274        tags: Option<Vec<String>>,
275        client_order_id: Option<ClientOrderId>,
276    ) -> PyResult<Py<PyAny>> {
277        py_order_from_factory(py, || {
278            self.0.borrow_mut().try_stop_market(
279                instrument_id,
280                order_side,
281                quantity,
282                trigger_price,
283                trigger_type,
284                time_in_force,
285                expire_time.map(UnixNanos::from),
286                reduce_only,
287                quote_quantity,
288                display_qty,
289                emulation_trigger,
290                trigger_instrument_id,
291                exec_algorithm_id,
292                exec_algorithm_params.map(str_indexmap_to_ustr),
293                tags.map(strings_to_ustrs),
294                client_order_id,
295            )
296        })
297    }
298
299    #[pyo3(name = "stop_limit", signature = (
300        instrument_id,
301        order_side,
302        quantity,
303        price,
304        trigger_price,
305        trigger_type=None,
306        time_in_force=None,
307        expire_time=None,
308        post_only=None,
309        reduce_only=None,
310        quote_quantity=None,
311        display_qty=None,
312        emulation_trigger=None,
313        trigger_instrument_id=None,
314        exec_algorithm_id=None,
315        exec_algorithm_params=None,
316        tags=None,
317        client_order_id=None
318    ))]
319    fn py_stop_limit(
320        &mut self,
321        py: Python<'_>,
322        instrument_id: InstrumentId,
323        order_side: OrderSide,
324        quantity: Quantity,
325        price: Price,
326        trigger_price: Price,
327        trigger_type: Option<TriggerType>,
328        time_in_force: Option<TimeInForce>,
329        expire_time: Option<u64>,
330        post_only: Option<bool>,
331        reduce_only: Option<bool>,
332        quote_quantity: Option<bool>,
333        display_qty: Option<Quantity>,
334        emulation_trigger: Option<TriggerType>,
335        trigger_instrument_id: Option<InstrumentId>,
336        exec_algorithm_id: Option<ExecAlgorithmId>,
337        exec_algorithm_params: Option<IndexMap<String, String>>,
338        tags: Option<Vec<String>>,
339        client_order_id: Option<ClientOrderId>,
340    ) -> PyResult<Py<PyAny>> {
341        py_order_from_factory(py, || {
342            self.0.borrow_mut().try_stop_limit(
343                instrument_id,
344                order_side,
345                quantity,
346                price,
347                trigger_price,
348                trigger_type,
349                time_in_force,
350                expire_time.map(UnixNanos::from),
351                post_only,
352                reduce_only,
353                quote_quantity,
354                display_qty,
355                emulation_trigger,
356                trigger_instrument_id,
357                exec_algorithm_id,
358                exec_algorithm_params.map(str_indexmap_to_ustr),
359                tags.map(strings_to_ustrs),
360                client_order_id,
361            )
362        })
363    }
364
365    #[pyo3(name = "market_to_limit", signature = (
366        instrument_id,
367        order_side,
368        quantity,
369        time_in_force=None,
370        expire_time=None,
371        reduce_only=None,
372        quote_quantity=None,
373        display_qty=None,
374        exec_algorithm_id=None,
375        exec_algorithm_params=None,
376        tags=None,
377        client_order_id=None
378    ))]
379    fn py_market_to_limit(
380        &mut self,
381        py: Python<'_>,
382        instrument_id: InstrumentId,
383        order_side: OrderSide,
384        quantity: Quantity,
385        time_in_force: Option<TimeInForce>,
386        expire_time: Option<u64>,
387        reduce_only: Option<bool>,
388        quote_quantity: Option<bool>,
389        display_qty: Option<Quantity>,
390        exec_algorithm_id: Option<ExecAlgorithmId>,
391        exec_algorithm_params: Option<IndexMap<String, String>>,
392        tags: Option<Vec<String>>,
393        client_order_id: Option<ClientOrderId>,
394    ) -> PyResult<Py<PyAny>> {
395        py_order_from_factory(py, || {
396            self.0.borrow_mut().try_market_to_limit(
397                instrument_id,
398                order_side,
399                quantity,
400                time_in_force,
401                expire_time.map(UnixNanos::from),
402                reduce_only,
403                quote_quantity,
404                display_qty,
405                exec_algorithm_id,
406                exec_algorithm_params.map(str_indexmap_to_ustr),
407                tags.map(strings_to_ustrs),
408                client_order_id,
409            )
410        })
411    }
412
413    #[pyo3(name = "market_if_touched", signature = (
414        instrument_id,
415        order_side,
416        quantity,
417        trigger_price,
418        trigger_type=None,
419        time_in_force=None,
420        expire_time=None,
421        reduce_only=None,
422        quote_quantity=None,
423        emulation_trigger=None,
424        trigger_instrument_id=None,
425        exec_algorithm_id=None,
426        exec_algorithm_params=None,
427        tags=None,
428        client_order_id=None
429    ))]
430    fn py_market_if_touched(
431        &mut self,
432        py: Python<'_>,
433        instrument_id: InstrumentId,
434        order_side: OrderSide,
435        quantity: Quantity,
436        trigger_price: Price,
437        trigger_type: Option<TriggerType>,
438        time_in_force: Option<TimeInForce>,
439        expire_time: Option<u64>,
440        reduce_only: Option<bool>,
441        quote_quantity: Option<bool>,
442        emulation_trigger: Option<TriggerType>,
443        trigger_instrument_id: Option<InstrumentId>,
444        exec_algorithm_id: Option<ExecAlgorithmId>,
445        exec_algorithm_params: Option<IndexMap<String, String>>,
446        tags: Option<Vec<String>>,
447        client_order_id: Option<ClientOrderId>,
448    ) -> PyResult<Py<PyAny>> {
449        py_order_from_factory(py, || {
450            self.0.borrow_mut().try_market_if_touched(
451                instrument_id,
452                order_side,
453                quantity,
454                trigger_price,
455                trigger_type,
456                time_in_force,
457                expire_time.map(UnixNanos::from),
458                reduce_only,
459                quote_quantity,
460                emulation_trigger,
461                trigger_instrument_id,
462                exec_algorithm_id,
463                exec_algorithm_params.map(str_indexmap_to_ustr),
464                tags.map(strings_to_ustrs),
465                client_order_id,
466            )
467        })
468    }
469
470    #[pyo3(name = "limit_if_touched", signature = (
471        instrument_id,
472        order_side,
473        quantity,
474        price,
475        trigger_price,
476        trigger_type=None,
477        time_in_force=None,
478        expire_time=None,
479        post_only=None,
480        reduce_only=None,
481        quote_quantity=None,
482        display_qty=None,
483        emulation_trigger=None,
484        trigger_instrument_id=None,
485        exec_algorithm_id=None,
486        exec_algorithm_params=None,
487        tags=None,
488        client_order_id=None
489    ))]
490    fn py_limit_if_touched(
491        &mut self,
492        py: Python<'_>,
493        instrument_id: InstrumentId,
494        order_side: OrderSide,
495        quantity: Quantity,
496        price: Price,
497        trigger_price: Price,
498        trigger_type: Option<TriggerType>,
499        time_in_force: Option<TimeInForce>,
500        expire_time: Option<u64>,
501        post_only: Option<bool>,
502        reduce_only: Option<bool>,
503        quote_quantity: Option<bool>,
504        display_qty: Option<Quantity>,
505        emulation_trigger: Option<TriggerType>,
506        trigger_instrument_id: Option<InstrumentId>,
507        exec_algorithm_id: Option<ExecAlgorithmId>,
508        exec_algorithm_params: Option<IndexMap<String, String>>,
509        tags: Option<Vec<String>>,
510        client_order_id: Option<ClientOrderId>,
511    ) -> PyResult<Py<PyAny>> {
512        py_order_from_factory(py, || {
513            self.0.borrow_mut().try_limit_if_touched(
514                instrument_id,
515                order_side,
516                quantity,
517                price,
518                trigger_price,
519                trigger_type,
520                time_in_force,
521                expire_time.map(UnixNanos::from),
522                post_only,
523                reduce_only,
524                quote_quantity,
525                display_qty,
526                emulation_trigger,
527                trigger_instrument_id,
528                exec_algorithm_id,
529                exec_algorithm_params.map(str_indexmap_to_ustr),
530                tags.map(strings_to_ustrs),
531                client_order_id,
532            )
533        })
534    }
535
536    #[pyo3(name = "trailing_stop_market", signature = (
537        instrument_id,
538        order_side,
539        quantity,
540        trailing_offset,
541        trailing_offset_type=None,
542        activation_price=None,
543        trigger_price=None,
544        trigger_type=None,
545        time_in_force=None,
546        expire_time=None,
547        reduce_only=None,
548        quote_quantity=None,
549        display_qty=None,
550        emulation_trigger=None,
551        trigger_instrument_id=None,
552        exec_algorithm_id=None,
553        exec_algorithm_params=None,
554        tags=None,
555        client_order_id=None
556    ))]
557    fn py_trailing_stop_market(
558        &mut self,
559        py: Python<'_>,
560        instrument_id: InstrumentId,
561        order_side: OrderSide,
562        quantity: Quantity,
563        trailing_offset: Decimal,
564        trailing_offset_type: Option<TrailingOffsetType>,
565        activation_price: Option<Price>,
566        trigger_price: Option<Price>,
567        trigger_type: Option<TriggerType>,
568        time_in_force: Option<TimeInForce>,
569        expire_time: Option<u64>,
570        reduce_only: Option<bool>,
571        quote_quantity: Option<bool>,
572        display_qty: Option<Quantity>,
573        emulation_trigger: Option<TriggerType>,
574        trigger_instrument_id: Option<InstrumentId>,
575        exec_algorithm_id: Option<ExecAlgorithmId>,
576        exec_algorithm_params: Option<IndexMap<String, String>>,
577        tags: Option<Vec<String>>,
578        client_order_id: Option<ClientOrderId>,
579    ) -> PyResult<Py<PyAny>> {
580        py_order_from_factory(py, || {
581            self.0.borrow_mut().try_trailing_stop_market(
582                instrument_id,
583                order_side,
584                quantity,
585                trailing_offset,
586                trailing_offset_type,
587                activation_price,
588                trigger_price,
589                trigger_type,
590                time_in_force,
591                expire_time.map(UnixNanos::from),
592                reduce_only,
593                quote_quantity,
594                display_qty,
595                emulation_trigger,
596                trigger_instrument_id,
597                exec_algorithm_id,
598                exec_algorithm_params.map(str_indexmap_to_ustr),
599                tags.map(strings_to_ustrs),
600                client_order_id,
601            )
602        })
603    }
604
605    #[pyo3(name = "trailing_stop_limit", signature = (
606        instrument_id,
607        order_side,
608        quantity,
609        price,
610        limit_offset,
611        trailing_offset,
612        trailing_offset_type=None,
613        activation_price=None,
614        trigger_price=None,
615        trigger_type=None,
616        time_in_force=None,
617        expire_time=None,
618        post_only=None,
619        reduce_only=None,
620        quote_quantity=None,
621        display_qty=None,
622        emulation_trigger=None,
623        trigger_instrument_id=None,
624        exec_algorithm_id=None,
625        exec_algorithm_params=None,
626        tags=None,
627        client_order_id=None
628    ))]
629    fn py_trailing_stop_limit(
630        &mut self,
631        py: Python<'_>,
632        instrument_id: InstrumentId,
633        order_side: OrderSide,
634        quantity: Quantity,
635        price: Option<Price>,
636        limit_offset: Decimal,
637        trailing_offset: Decimal,
638        trailing_offset_type: Option<TrailingOffsetType>,
639        activation_price: Option<Price>,
640        trigger_price: Option<Price>,
641        trigger_type: Option<TriggerType>,
642        time_in_force: Option<TimeInForce>,
643        expire_time: Option<u64>,
644        post_only: Option<bool>,
645        reduce_only: Option<bool>,
646        quote_quantity: Option<bool>,
647        display_qty: Option<Quantity>,
648        emulation_trigger: Option<TriggerType>,
649        trigger_instrument_id: Option<InstrumentId>,
650        exec_algorithm_id: Option<ExecAlgorithmId>,
651        exec_algorithm_params: Option<IndexMap<String, String>>,
652        tags: Option<Vec<String>>,
653        client_order_id: Option<ClientOrderId>,
654    ) -> PyResult<Py<PyAny>> {
655        py_order_from_factory(py, || {
656            self.0.borrow_mut().try_trailing_stop_limit(
657                instrument_id,
658                order_side,
659                quantity,
660                price,
661                limit_offset,
662                trailing_offset,
663                trailing_offset_type,
664                activation_price,
665                trigger_price,
666                trigger_type,
667                time_in_force,
668                expire_time.map(UnixNanos::from),
669                post_only,
670                reduce_only,
671                quote_quantity,
672                display_qty,
673                emulation_trigger,
674                trigger_instrument_id,
675                exec_algorithm_id,
676                exec_algorithm_params.map(str_indexmap_to_ustr),
677                tags.map(strings_to_ustrs),
678                client_order_id,
679            )
680        })
681    }
682
683    #[pyo3(name = "bracket", signature = (
684        instrument_id,
685        order_side,
686        quantity,
687        quote_quantity=false,
688        emulation_trigger=None,
689        trigger_instrument_id=None,
690        contingency_type=ContingencyType::Ouo,
691        entry_order_type=OrderType::Market,
692        entry_price=None,
693        entry_trigger_price=None,
694        expire_time=None,
695        time_in_force=TimeInForce::Gtc,
696        entry_post_only=false,
697        entry_exec_algorithm_id=None,
698        entry_exec_algorithm_params=None,
699        entry_tags=None,
700        entry_client_order_id=None,
701        tp_order_type=OrderType::Limit,
702        tp_price=None,
703        tp_trigger_price=None,
704        tp_trigger_type=TriggerType::Default,
705        tp_activation_price=None,
706        tp_trailing_offset=None,
707        tp_trailing_offset_type=TrailingOffsetType::Price,
708        tp_limit_offset=None,
709        tp_time_in_force=TimeInForce::Gtc,
710        tp_post_only=true,
711        tp_exec_algorithm_id=None,
712        tp_exec_algorithm_params=None,
713        tp_tags=None,
714        tp_client_order_id=None,
715        sl_order_type=OrderType::StopMarket,
716        sl_trigger_price=None,
717        sl_trigger_type=TriggerType::Default,
718        sl_activation_price=None,
719        sl_trailing_offset=None,
720        sl_trailing_offset_type=TrailingOffsetType::Price,
721        sl_time_in_force=TimeInForce::Gtc,
722        sl_exec_algorithm_id=None,
723        sl_exec_algorithm_params=None,
724        sl_tags=None,
725        sl_client_order_id=None
726    ))]
727    fn py_bracket(
728        &mut self,
729        py: Python<'_>,
730        instrument_id: InstrumentId,
731        order_side: OrderSide,
732        quantity: Quantity,
733        quote_quantity: bool,
734        emulation_trigger: Option<TriggerType>,
735        trigger_instrument_id: Option<InstrumentId>,
736        contingency_type: ContingencyType,
737        entry_order_type: OrderType,
738        entry_price: Option<Price>,
739        entry_trigger_price: Option<Price>,
740        expire_time: Option<u64>,
741        time_in_force: TimeInForce,
742        entry_post_only: bool,
743        entry_exec_algorithm_id: Option<ExecAlgorithmId>,
744        entry_exec_algorithm_params: Option<IndexMap<String, String>>,
745        entry_tags: Option<Vec<String>>,
746        entry_client_order_id: Option<ClientOrderId>,
747        tp_order_type: OrderType,
748        tp_price: Option<Price>,
749        tp_trigger_price: Option<Price>,
750        tp_trigger_type: TriggerType,
751        tp_activation_price: Option<Price>,
752        tp_trailing_offset: Option<Decimal>,
753        tp_trailing_offset_type: TrailingOffsetType,
754        tp_limit_offset: Option<Decimal>,
755        tp_time_in_force: TimeInForce,
756        tp_post_only: bool,
757        tp_exec_algorithm_id: Option<ExecAlgorithmId>,
758        tp_exec_algorithm_params: Option<IndexMap<String, String>>,
759        tp_tags: Option<Vec<String>>,
760        tp_client_order_id: Option<ClientOrderId>,
761        sl_order_type: OrderType,
762        sl_trigger_price: Option<Price>,
763        sl_trigger_type: TriggerType,
764        sl_activation_price: Option<Price>,
765        sl_trailing_offset: Option<Decimal>,
766        sl_trailing_offset_type: TrailingOffsetType,
767        sl_time_in_force: TimeInForce,
768        sl_exec_algorithm_id: Option<ExecAlgorithmId>,
769        sl_exec_algorithm_params: Option<IndexMap<String, String>>,
770        sl_tags: Option<Vec<String>>,
771        sl_client_order_id: Option<ClientOrderId>,
772    ) -> PyResult<Py<PyList>> {
773        let orders = py_orders_from_factory(|| {
774            self.0
775                .borrow_mut()
776                .try_bracket()
777                .instrument_id(instrument_id)
778                .order_side(order_side)
779                .quantity(quantity)
780                .quote_quantity(quote_quantity)
781                .maybe_emulation_trigger(emulation_trigger)
782                .maybe_trigger_instrument_id(trigger_instrument_id)
783                .contingency_type(contingency_type)
784                .entry_order_type(entry_order_type)
785                .maybe_entry_price(entry_price)
786                .maybe_entry_trigger_price(entry_trigger_price)
787                .maybe_expire_time(expire_time.map(UnixNanos::from))
788                .time_in_force(time_in_force)
789                .entry_post_only(entry_post_only)
790                .maybe_entry_exec_algorithm_id(entry_exec_algorithm_id)
791                .maybe_entry_exec_algorithm_params(
792                    entry_exec_algorithm_params.map(str_indexmap_to_ustr),
793                )
794                .maybe_entry_tags(entry_tags.map(strings_to_ustrs))
795                .maybe_entry_client_order_id(entry_client_order_id)
796                .tp_order_type(tp_order_type)
797                .maybe_tp_price(tp_price)
798                .maybe_tp_trigger_price(tp_trigger_price)
799                .tp_trigger_type(tp_trigger_type)
800                .maybe_tp_activation_price(tp_activation_price)
801                .maybe_tp_trailing_offset(tp_trailing_offset)
802                .tp_trailing_offset_type(tp_trailing_offset_type)
803                .maybe_tp_limit_offset(tp_limit_offset)
804                .tp_time_in_force(tp_time_in_force)
805                .tp_post_only(tp_post_only)
806                .maybe_tp_exec_algorithm_id(tp_exec_algorithm_id)
807                .maybe_tp_exec_algorithm_params(tp_exec_algorithm_params.map(str_indexmap_to_ustr))
808                .maybe_tp_tags(tp_tags.map(strings_to_ustrs))
809                .maybe_tp_client_order_id(tp_client_order_id)
810                .sl_order_type(sl_order_type)
811                .maybe_sl_trigger_price(sl_trigger_price)
812                .sl_trigger_type(sl_trigger_type)
813                .maybe_sl_activation_price(sl_activation_price)
814                .maybe_sl_trailing_offset(sl_trailing_offset)
815                .sl_trailing_offset_type(sl_trailing_offset_type)
816                .sl_time_in_force(sl_time_in_force)
817                .maybe_sl_exec_algorithm_id(sl_exec_algorithm_id)
818                .maybe_sl_exec_algorithm_params(sl_exec_algorithm_params.map(str_indexmap_to_ustr))
819                .maybe_sl_tags(sl_tags.map(strings_to_ustrs))
820                .maybe_sl_client_order_id(sl_client_order_id)
821                .call()
822        })?;
823        let py_orders = orders
824            .into_iter()
825            .map(|order| order_any_to_pyobject(py, order))
826            .collect::<PyResult<Vec<_>>>()?;
827        Ok(PyList::new(py, py_orders)?.unbind())
828    }
829}
830
831fn py_order_from_factory<F>(py: Python<'_>, create: F) -> PyResult<Py<PyAny>>
832where
833    F: FnOnce() -> anyhow::Result<OrderAny>,
834{
835    let order = create().map_err(to_pyvalue_err)?;
836    order_any_to_pyobject(py, order)
837}
838
839fn py_orders_from_factory<F>(create: F) -> PyResult<Vec<OrderAny>>
840where
841    F: FnOnce() -> anyhow::Result<Vec<OrderAny>>,
842{
843    create().map_err(to_pyvalue_err)
844}
845
846fn strings_to_ustrs(values: Vec<String>) -> Vec<Ustr> {
847    values
848        .into_iter()
849        .map(|value| Ustr::from(value.as_str()))
850        .collect()
851}