Replace a leg of an active OrderGroup¶
OrderGroup is a passive state machine. After the strategy fires
replaceOrder(oldId, newOrder) through its executor and the
executor surfaces the replace event, the group needs three things:
- The new exchange order id stitched onto the same leg slot so the policy state machine (OneSided cancel-on-fill, AllOrNothing revert-on-failure) keeps tracking the right leg.
- A way to route a later replace / fill event back from the exchange id to the leg, since the id has changed.
- A no-op on a late-replace race when the leg has already filled between the replace request and the ack landing.
API¶
Late-replace race¶
When the configured replace ack latency knob is non-zero
(see how-to: replace-in-flight), the executor surfaces the replace
async. If the leg fills inside the latency window, the simulator
emits REPLACE_REJECTED and the original order's fill stands.
The OrderGroup mirrors this: calling recordReplaceAccepted on a
leg that is already Filled or Cancelled is a no-op — the leg's
order id is not overwritten, otherwise downstream bookkeeping
would point at a phantom id.
The recommended pattern in the strategy:
def on_replace_event(self, ev):
leg = self.group.find_leg_by_order_id(ev.original_order_id)
if leg is None:
return # not a grouped order
if ev.kind == "accepted":
self.group.record_replace_accepted(leg, ev.new_order_id)
else:
self.group.record_replace_rejected(leg)
Policy interaction¶
A replace ack does not change the leg's LegState. A leg in
Submitted stays Submitted, a PartiallyFilled leg stays
PartiallyFilled with its accumulated fill quantity intact. The
OneSided "cancel the other leg on first fill" and AllOrNothing
"revert all filled legs on any failure" policies continue to fire
against the leg slot, using whatever exchange id the leg currently
holds.