Skip to content

Commit 80ca06c

Browse files
committed
fix(strategy): use prefixed order IDs to avoid entry/exit collisions
- Add "entry_" and "exit_" prefixes to order IDs in position.orders to prevent collisions between entry and exit orders. - Update all logic for adding, removing, and accessing orders to use the new prefixed keys. - Adjust commission handling and order deletion to account for prefixed IDs.
1 parent 36c56ff commit 80ca06c

File tree

1 file changed

+42
-16
lines changed

1 file changed

+42
-16
lines changed

src/pynecore/lib/strategy/__init__.py

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,9 @@ def _fill_order(self, order: Order, price: float, h: float, l: float):
385385
# Commission summ
386386
self.open_commission -= closed_trade.commission
387387

388-
# We realize later if it is cash per order
389-
if commission_type == _commission.cash_per_contract:
388+
# We realize later if it is cash per order or cash per contract
389+
if (commission_type == _commission.cash_per_contract or
390+
commission_type == _commission.cash_per_order):
390391
closed_trade_size += abs(size)
391392
else:
392393
commission = abs(size) * commission_value
@@ -458,7 +459,8 @@ def _fill_order(self, order: Order, price: float, h: float, l: float):
458459
self.open_trades = open_trades
459460
if delete:
460461
if order.exit_id is not None:
461-
self.orders.pop(order.exit_id, None)
462+
# Exit orders are stored with "exit_" prefix
463+
self.orders.pop("exit_" + order.exit_id, None)
462464

463465
if commission_type == _commission.cash_per_order:
464466
# Realize commission
@@ -521,7 +523,13 @@ def _fill_order(self, order: Order, price: float, h: float, l: float):
521523
# Commission summ
522524
self.open_commission += commission
523525

524-
del self.orders[order.order_id]
526+
# Remove the order using the actual key (which includes the prefix)
527+
# The order_id in the Order object doesn't have the prefix,
528+
# so we need to find and delete by iterating
529+
for key, stored_order in list(self.orders.items()):
530+
if stored_order is order:
531+
del self.orders[key]
532+
break
525533

526534
# If position has just closed
527535
if not self.open_trades:
@@ -562,7 +570,11 @@ def fill_order(self, order: Order, price: float, h: float, l: float) -> bool:
562570
# Modify the original order to open a position in the new direction
563571
order.size = new_size
564572
assert order.order_id is not None
565-
self.orders[order.order_id] = order
573+
# Store with appropriate prefix based on order type
574+
if order.order_type == _order_type_entry:
575+
self.orders["entry_" + order.order_id] = order
576+
else:
577+
self.orders["exit_" + order.order_id] = order
566578
self._fill_order(order, price, h, l)
567579
return True
568580

@@ -811,10 +823,17 @@ def cancel(id: str):
811823
if lib._lib_semaphore:
812824
return
813825

826+
assert lib._script is not None and lib._script.position is not None
827+
# Try to cancel both entry and exit orders with the given ID
828+
# since we don't know which type it is
829+
# noinspection PyProtectedMember
830+
try:
831+
del lib._script.position.orders["entry_" + id]
832+
except KeyError:
833+
pass
834+
# noinspection PyProtectedMember
814835
try:
815-
assert lib._script is not None and lib._script.position is not None
816-
# noinspection PyProtectedMember
817-
del lib._script.position.orders[id]
836+
del lib._script.position.orders["exit_" + id]
818837
except KeyError:
819838
pass
820839

@@ -873,7 +892,8 @@ def close(id: str, comment: str | NA[str] = NA(str), qty: float | NA[float] = NA
873892
comment=None if isinstance(comment, NA) else comment,
874893
alert_message=None if isinstance(alert_message, NA) else alert_message)
875894

876-
position.orders[exit_id] = order
895+
# Use "exit_" prefix to avoid ID collision with entry orders
896+
position.orders["exit_" + exit_id] = order
877897
if immediately:
878898
round_to_mintick = lib.math.round_to_mintick
879899
position.fill_order(order,
@@ -904,7 +924,8 @@ def close_all(comment: str | NA[str] = NA(str), alert_message: str | NA[str] = N
904924
order = Order(None, -position.size, exit_id=exit_id, order_type=_order_type_close,
905925
comment=comment, alert_message=alert_message)
906926

907-
position.orders[exit_id] = order
927+
# Use "exit_" prefix to avoid ID collision with entry orders
928+
position.orders["exit_" + exit_id] = order
908929
if immediately:
909930
round_to_mintick = lib.math.round_to_mintick
910931
position.fill_order(order,
@@ -1050,7 +1071,8 @@ def entry(id: str, direction: direction.Direction, qty: int | float | NA[float]
10501071

10511072
order = Order(id, size, order_type=_order_type_entry, limit=limit, stop=stop, oca_name=oca_name,
10521073
oca_type=oca_type, comment=comment, alert_message=alert_message)
1053-
script.position.orders[id] = order
1074+
# Use "entry_" prefix to avoid ID collision with exit orders
1075+
script.position.orders["entry_" + id] = order
10541076

10551077

10561078
# noinspection PyShadowingBuiltins,PyProtectedMember,PyShadowingNames,PyUnusedLocal
@@ -1134,14 +1156,18 @@ def _exit():
11341156
if not isinstance(trail_price, NA):
11351157
trail_price = _price_round(trail_price, direction)
11361158

1137-
position.orders[id] = Order(from_entry, size, exit_id=id, order_type=_order_type_close,
1138-
limit=limit, stop=stop,
1139-
trail_price=trail_price, trail_offset=trail_offset,
1140-
oca_name=oca_name, comment=comment, alert_message=alert_message)
1159+
# Use "exit_" prefix to avoid ID collision with entry orders
1160+
position.orders["exit_" + id] = Order(
1161+
from_entry, size, exit_id=id, order_type=_order_type_close,
1162+
limit=limit, stop=stop,
1163+
trail_price=trail_price, trail_offset=trail_offset,
1164+
oca_name=oca_name, comment=comment, alert_message=alert_message
1165+
)
11411166

11421167
# Find direction and size
11431168
if from_entry:
1144-
entry_order = position.orders.get(from_entry, None)
1169+
# Entry orders are stored with "entry_" prefix
1170+
entry_order = position.orders.get("entry_" + from_entry, None)
11451171

11461172
# Find open trade if no entry order found
11471173
if not entry_order:

0 commit comments

Comments
 (0)