Skip to content

Commit 68e4ae2

Browse files
committed
Add use_uuid_client_order_ids config option
1 parent c1833a0 commit 68e4ae2

File tree

8 files changed

+53
-31
lines changed

8 files changed

+53
-31
lines changed

RELEASES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ Released on 16th March 2025 (UTC).
44

55
### Enhancements
66
- Added `CryptoOption` instrument, supporting inverse and fractional sizes
7-
- Added catalog consolidation functions of several parquet files into one (#2421), thanks @faysou
87
- Added `Cache.prices(...)` to return a map of latest price per instrument for a price type
8+
- Added `use_uuid_client_order_ids` config option for `StrategyConfig`
9+
- Added catalog consolidation functions of several parquet files into one (#2421), thanks @faysou
910
- Added FDUSD (First Digital USD) crypto `Currency` constant
1011
- Added initial leverage, `margin_mode` and `position_mode` config options for Bybit (#2441), thanks @sunlei
1112
- Updated parquet catalog in Rust with recent features (#2442), thanks @faysou

nautilus_trader/common/factories.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ cdef class OrderFactory:
5757
"""The order factories trader ID.\n\n:returns: `TraderId`"""
5858
cdef readonly StrategyId strategy_id
5959
"""The order factories trading strategy ID.\n\n:returns: `StrategyId`"""
60+
cdef readonly bint use_uuid_client_order_ids
61+
"""If UUID4's should be used for client order ID values.\n\n:returns: `bool`"""
6062

6163
cpdef void set_client_order_id_count(self, int count)
6264
cpdef void set_order_list_id_count(self, int count)

nautilus_trader/common/factories.pyx

+22-28
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,9 @@ cdef class OrderFactory:
6363
The clock for the factory.
6464
cache : CacheFacade, optional
6565
The cache facade for the order factory.
66-
initial_order_id_count : int, optional
67-
The initial order ID count for the factory.
68-
initial_order_list_id_count : int, optional
69-
The initial order list ID count for the factory.
70-
71-
Raises
72-
------
73-
ValueError
74-
If `initial_order_id_count` is negative (< 0).
75-
ValueError
76-
If `initial_order_list_id_count` is negative (< 0).
66+
use_uuid_client_order_ids : bool, default False
67+
If UUID4's should be used for client order ID values.
68+
7769
"""
7870

7971
def __init__(
@@ -82,25 +74,23 @@ cdef class OrderFactory:
8274
StrategyId strategy_id not None,
8375
Clock clock not None,
8476
CacheFacade cache: CacheFacade | None = None,
85-
int initial_order_id_count=0,
86-
int initial_order_list_id_count=0,
87-
):
77+
bint use_uuid_client_order_ids = False,
78+
) -> None:
8879
self._clock = clock
8980
self._cache = cache
9081
self.trader_id = trader_id
9182
self.strategy_id = strategy_id
83+
self.use_uuid_client_order_ids = use_uuid_client_order_ids
9284

9385
self._order_id_generator = ClientOrderIdGenerator(
9486
trader_id=trader_id,
9587
strategy_id=strategy_id,
9688
clock=clock,
97-
initial_count=initial_order_id_count,
9889
)
9990
self._order_list_id_generator = OrderListIdGenerator(
10091
trader_id=trader_id,
10192
strategy_id=strategy_id,
10293
clock=clock,
103-
initial_count=initial_order_list_id_count,
10494
)
10595

10696
cpdef void set_client_order_id_count(self, int count):
@@ -146,7 +136,11 @@ cdef class OrderFactory:
146136
ClientOrderId
147137
148138
"""
139+
if self.use_uuid_client_order_ids:
140+
return ClientOrderId(UUID4().value)
141+
149142
cdef ClientOrderId client_order_id = self._order_id_generator.generate()
143+
150144
if self._cache is not None:
151145
while self._cache.order(client_order_id) is not None:
152146
client_order_id = self._order_id_generator.generate()
@@ -263,7 +257,7 @@ cdef class OrderFactory:
263257
264258
"""
265259
if client_order_id is None:
266-
client_order_id = self._order_id_generator.generate()
260+
client_order_id = self.generate_client_order_id()
267261
return MarketOrder(
268262
trader_id=self.trader_id,
269263
strategy_id=self.strategy_id,
@@ -362,7 +356,7 @@ cdef class OrderFactory:
362356
363357
"""
364358
if client_order_id is None:
365-
client_order_id = self._order_id_generator.generate()
359+
client_order_id = self.generate_client_order_id()
366360
return LimitOrder(
367361
trader_id=self.trader_id,
368362
strategy_id=self.strategy_id,
@@ -466,7 +460,7 @@ cdef class OrderFactory:
466460
467461
"""
468462
if client_order_id is None:
469-
client_order_id = self._order_id_generator.generate()
463+
client_order_id = self.generate_client_order_id()
470464
return StopMarketOrder(
471465
trader_id=self.trader_id,
472466
strategy_id=self.strategy_id,
@@ -580,7 +574,7 @@ cdef class OrderFactory:
580574
581575
"""
582576
if client_order_id is None:
583-
client_order_id = self._order_id_generator.generate()
577+
client_order_id = self.generate_client_order_id()
584578
return StopLimitOrder(
585579
trader_id=self.trader_id,
586580
strategy_id=self.strategy_id,
@@ -670,7 +664,7 @@ cdef class OrderFactory:
670664
671665
"""
672666
if client_order_id is None:
673-
client_order_id = self._order_id_generator.generate()
667+
client_order_id = self.generate_client_order_id()
674668
return MarketToLimitOrder(
675669
trader_id=self.trader_id,
676670
strategy_id=self.strategy_id,
@@ -769,7 +763,7 @@ cdef class OrderFactory:
769763
770764
"""
771765
if client_order_id is None:
772-
client_order_id = self._order_id_generator.generate()
766+
client_order_id = self.generate_client_order_id()
773767
return MarketIfTouchedOrder(
774768
trader_id=self.trader_id,
775769
strategy_id=self.strategy_id,
@@ -883,7 +877,7 @@ cdef class OrderFactory:
883877
884878
"""
885879
if client_order_id is None:
886-
client_order_id = self._order_id_generator.generate()
880+
client_order_id = self.generate_client_order_id()
887881
return LimitIfTouchedOrder(
888882
trader_id=self.trader_id,
889883
strategy_id=self.strategy_id,
@@ -996,7 +990,7 @@ cdef class OrderFactory:
996990
997991
"""
998992
if client_order_id is None:
999-
client_order_id = self._order_id_generator.generate()
993+
client_order_id = self.generate_client_order_id()
1000994
return TrailingStopMarketOrder(
1001995
trader_id=self.trader_id,
1002996
strategy_id=self.strategy_id,
@@ -1125,7 +1119,7 @@ cdef class OrderFactory:
11251119
11261120
"""
11271121
if client_order_id is None:
1128-
client_order_id = self._order_id_generator.generate()
1122+
client_order_id = self.generate_client_order_id()
11291123
return TrailingStopLimitOrder(
11301124
trader_id=self.trader_id,
11311125
strategy_id=self.strategy_id,
@@ -1284,11 +1278,11 @@ cdef class OrderFactory:
12841278
cdef OrderListId order_list_id = self._order_list_id_generator.generate()
12851279

12861280
if entry_client_order_id is None:
1287-
entry_client_order_id = self._order_id_generator.generate()
1281+
entry_client_order_id = self.generate_client_order_id()
12881282
if sl_client_order_id is None:
1289-
sl_client_order_id = self._order_id_generator.generate()
1283+
sl_client_order_id = self.generate_client_order_id()
12901284
if tp_client_order_id is None:
1291-
tp_client_order_id = self._order_id_generator.generate()
1285+
tp_client_order_id = self.generate_client_order_id()
12921286

12931287
########################################################################
12941288
# ENTRY ORDER

nautilus_trader/trading/config.py

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class StrategyConfig(NautilusConfig, kw_only=True, frozen=True):
3939
order_id_tag : str, optional
4040
The unique order ID tag for the strategy. Must be unique
4141
amongst all running strategies for a particular trader ID.
42+
use_uuid_client_order_ids : bool, default False
43+
If UUID4's should be used for client order ID values.
4244
oms_type : OmsType, optional
4345
The order management system type for the strategy. This will determine
4446
how the `ExecutionEngine` handles position IDs.
@@ -61,6 +63,7 @@ class StrategyConfig(NautilusConfig, kw_only=True, frozen=True):
6163

6264
strategy_id: StrategyId | None = None
6365
order_id_tag: str | None = None
66+
use_uuid_client_order_ids: bool = False
6467
oms_type: str | None = None
6568
external_order_claims: list[InstrumentId] | None = None
6669
manage_contingent_orders: bool = False

nautilus_trader/trading/strategy.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ cdef class Strategy(Actor):
6767
"""The order factory for the strategy.\n\n:returns: `OrderFactory`"""
6868
cdef readonly str order_id_tag
6969
"""The order ID tag for the strategy.\n\n:returns: `str`"""
70+
cdef readonly bint use_uuid_client_order_ids
71+
"""If UUID4's should be used for client order ID values.\n\n:returns: `bool`"""
7072
cdef readonly OmsType oms_type
7173
"""The order management system for the strategy.\n\n:returns: `OmsType`"""
7274
cdef readonly list external_order_claims

nautilus_trader/trading/strategy.pyx

+2
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ cdef class Strategy(Actor):
144144
component_id = type(self).__name__ if config.strategy_id is None else config.strategy_id
145145
self.id = StrategyId(f"{component_id}-{config.order_id_tag}")
146146
self.order_id_tag = str(config.order_id_tag)
147+
self.use_uuid_client_order_ids = config.use_uuid_client_order_ids
147148
self._log = Logger(name=component_id)
148149

149150
oms_type = config.oms_type or OmsType.UNSPECIFIED
@@ -287,6 +288,7 @@ cdef class Strategy(Actor):
287288
strategy_id=self.id,
288289
clock=clock,
289290
cache=cache,
291+
use_uuid_client_order_ids=self.use_uuid_client_order_ids
290292
)
291293

292294
self._manager = OrderManager(

tests/unit_tests/common/test_factories.py

+16
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,22 @@ def test_generate_client_order_id(self):
4646
# Assert
4747
assert result == ClientOrderId("O-19700101-000000-000-001-1")
4848

49+
def test_generate_uuid_client_order_id(self):
50+
# Arrange
51+
order_factory = OrderFactory(
52+
trader_id=self.trader_id,
53+
strategy_id=self.strategy_id,
54+
clock=TestClock(),
55+
use_uuid_client_order_ids=True,
56+
)
57+
58+
# Act
59+
result = order_factory.generate_client_order_id()
60+
61+
# Assert
62+
assert order_factory.use_uuid_client_order_ids
63+
assert len(result.value) == 36
64+
4965
def test_generate_order_list_id(self):
5066
# Arrange, Act
5167
result = self.order_factory.generate_order_list_id()

tests/unit_tests/trading/test_strategy.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ def test_strategy_to_importable_config_with_no_specific_config(self) -> None:
190190
assert result.config == {
191191
"strategy_id": None,
192192
"order_id_tag": None,
193+
"use_uuid_client_order_ids": False,
193194
"oms_type": None,
194195
"external_order_claims": None,
195196
"manage_contingent_orders": False,
@@ -219,9 +220,10 @@ def test_strategy_to_importable_config(self) -> None:
219220
assert result.strategy_path == "nautilus_trader.trading.strategy:Strategy"
220221
assert result.config_path == "nautilus_trader.trading.config:StrategyConfig"
221222
assert result.config == {
222-
"oms_type": None,
223-
"order_id_tag": "001",
224223
"strategy_id": "ALPHA-01",
224+
"order_id_tag": "001",
225+
"use_uuid_client_order_ids": False,
226+
"oms_type": None,
225227
"external_order_claims": ["ETHUSDT-PERP.DYDX"],
226228
"manage_contingent_orders": True,
227229
"manage_gtd_expiry": True,

0 commit comments

Comments
 (0)