|
@@ -322,7 +322,7 @@ class Strategy(Strategy):
|
|
|
self.state["order_ids"] = order_ids
|
|
self.state["order_ids"] = order_ids
|
|
|
self.state["last_action"] = "seeded grid"
|
|
self.state["last_action"] = "seeded grid"
|
|
|
|
|
|
|
|
- def _place_side_grid(self, side: str, center: float) -> None:
|
|
|
|
|
|
|
+ def _place_side_grid(self, side: str, center: float, *, start_level: int = 1) -> None:
|
|
|
levels = int(self.config.get("grid_levels", 6) or 6)
|
|
levels = int(self.config.get("grid_levels", 6) or 6)
|
|
|
step = self._grid_step_pct()
|
|
step = self._grid_step_pct()
|
|
|
min_notional = float(self.context.minimum_order_value or 0.0)
|
|
min_notional = float(self.context.minimum_order_value or 0.0)
|
|
@@ -356,7 +356,7 @@ class Strategy(Strategy):
|
|
|
f"prepare side {side}: market={market} center={center} levels={side_levels} amount={amount:.6g} min_notional={min_notional} existing_ids={order_ids}"
|
|
f"prepare side {side}: market={market} center={center} levels={side_levels} amount={amount:.6g} min_notional={min_notional} existing_ids={order_ids}"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
- for i in range(1, levels + 1):
|
|
|
|
|
|
|
+ for i in range(start_level, levels + 1):
|
|
|
price = round(center * (1 - (step * i)) if side == "buy" else center * (1 + (step * i)), 8)
|
|
price = round(center * (1 - (step * i)) if side == "buy" else center * (1 + (step * i)), 8)
|
|
|
min_size = (min_notional / price) if price > 0 else 0.0
|
|
min_size = (min_notional / price) if price > 0 else 0.0
|
|
|
if i > side_levels or amount < min_size:
|
|
if i > side_levels or amount < min_size:
|
|
@@ -390,6 +390,21 @@ class Strategy(Strategy):
|
|
|
self.state["order_ids"] = order_ids
|
|
self.state["order_ids"] = order_ids
|
|
|
self._log(f"side {side} placement complete: tracked_ids={order_ids}")
|
|
self._log(f"side {side} placement complete: tracked_ids={order_ids}")
|
|
|
|
|
|
|
|
|
|
+ def _top_up_missing_levels(self, center: float, live_orders: list[dict]) -> None:
|
|
|
|
|
+ target_levels = int(self.config.get("grid_levels", 6) or 6)
|
|
|
|
|
+ if target_levels <= 0:
|
|
|
|
|
+ return
|
|
|
|
|
+ for side in ("buy", "sell"):
|
|
|
|
|
+ count = 0
|
|
|
|
|
+ for order in live_orders:
|
|
|
|
|
+ if not isinstance(order, dict):
|
|
|
|
|
+ continue
|
|
|
|
|
+ if str(order.get("side") or "").lower() == side:
|
|
|
|
|
+ count += 1
|
|
|
|
|
+ if 0 < count < target_levels:
|
|
|
|
|
+ self._log(f"top up side {side}: have {count}, want {target_levels}")
|
|
|
|
|
+ self._place_side_grid(side, center, start_level=count + 1)
|
|
|
|
|
+
|
|
|
def _cancel_obsolete_side_orders(self, open_orders: list[dict], desired_sides: set[str]) -> list[str]:
|
|
def _cancel_obsolete_side_orders(self, open_orders: list[dict], desired_sides: set[str]) -> list[str]:
|
|
|
removed: list[str] = []
|
|
removed: list[str] = []
|
|
|
for order in open_orders:
|
|
for order in open_orders:
|
|
@@ -544,6 +559,10 @@ class Strategy(Strategy):
|
|
|
self.state["last_action"] = f"added {','.join(missing_sides)} side(s)"
|
|
self.state["last_action"] = f"added {','.join(missing_sides)} side(s)"
|
|
|
return {"action": "add_side", "price": price, "side": ",".join(missing_sides)}
|
|
return {"action": "add_side", "price": price, "side": ",".join(missing_sides)}
|
|
|
|
|
|
|
|
|
|
+ if live_orders and self.state.get("center_price"):
|
|
|
|
|
+ self._top_up_missing_levels(float(self.state.get("center_price") or price), live_orders)
|
|
|
|
|
+ live_orders = self._sync_open_orders_state()
|
|
|
|
|
+
|
|
|
if not self.state.get("seeded") or not self.state.get("center_price"):
|
|
if not self.state.get("seeded") or not self.state.get("center_price"):
|
|
|
self.state["center_price"] = price
|
|
self.state["center_price"] = price
|
|
|
self._place_grid(price)
|
|
self._place_grid(price)
|