diff --git a/MALKHUT/malkhut/planner/action_menu.py b/MALKHUT/malkhut/planner/action_menu.py index 82f3388..4c9b9e5 100644 --- a/MALKHUT/malkhut/planner/action_menu.py +++ b/MALKHUT/malkhut/planner/action_menu.py @@ -98,6 +98,7 @@ def build_our_actions( price_ticks_from_best=offset, qty_fraction=0.25, ttl_ms=params.passive_ttl_ms, cancel_order_id=oo.client_order_id, post_only=intent.prefer_maker, reduce_only=intent.reduce_only, + metadata={"requote": True, "requote_offset": offset}, )) # Passive quote placements diff --git a/MALKHUT/malkhut/training/dsl.py b/MALKHUT/malkhut/training/dsl.py index f68aeeb..886c0c2 100644 --- a/MALKHUT/malkhut/training/dsl.py +++ b/MALKHUT/malkhut/training/dsl.py @@ -706,10 +706,25 @@ def _primitive_to_action(primitive: ActionPrimitive, state: MarketWorldState) -> return FulfilmentAction(ActionKind.NOOP, None, None, 0, 0.0, 0) elif at == ActionType.REQUOTE: + # Requote: cancel existing order and place a new one at target offset. + # The CANCEL_REPLACE action handles both cancel + place in a single step. + # This is distinct from QUOTE (place new) and CHASE (place + wait + auto-cancel). + if state.open_orders: + oo = state.open_orders[0] + return FulfilmentAction( + kind=ActionKind.CANCEL_REPLACE, side=primitive.side, order_type=OrderType.LIMIT, + price_ticks_from_best=primitive.offset_ticks, qty_fraction=primitive.size_fraction, + ttl_ms=0, # Replaced order has no TTL (stays until next cancel/requote) + post_only=True, + cancel_order_id=oo.client_order_id, + metadata={"requote": True, "requote_offset": primitive.offset_ticks}, + ) + # No existing order — fall back to PLACE return FulfilmentAction( kind=ActionKind.PLACE, side=primitive.side, order_type=OrderType.LIMIT, price_ticks_from_best=primitive.offset_ticks, qty_fraction=primitive.size_fraction, - ttl_ms=int(primitive.duration_s * 1000), post_only=True, + ttl_ms=0, post_only=True, + metadata={"requote": True, "requote_offset": primitive.offset_ticks, "fallback": True}, ) elif at == ActionType.CHASE: