Kaynağa Gözat

grid trader improved

Lukas Goldschmidt 1 ay önce
ebeveyn
işleme
67f0ffbf38
1 değiştirilmiş dosya ile 48 ekleme ve 3 silme
  1. 48 3
      strategies/grid_trader.py

+ 48 - 3
strategies/grid_trader.py

@@ -20,6 +20,9 @@ class Strategy(Strategy):
         "order_size": {"type": "float", "default": 0.0, "min": 0.0},
         "inventory_cap_pct": {"type": "float", "default": 0.7, "min": 0.0, "max": 1.0},
         "recenter_pct": {"type": "float", "default": 0.05, "min": 0.0, "max": 0.5},
+        "recenter_atr_multiplier": {"type": "float", "default": 0.35, "min": 0.0, "max": 10.0},
+        "recenter_min_pct": {"type": "float", "default": 0.0025, "min": 0.0, "max": 0.5},
+        "recenter_max_pct": {"type": "float", "default": 0.03, "min": 0.0, "max": 0.5},
         "fee_rate": {"type": "float", "default": 0.0025, "min": 0.0, "max": 0.05},
         "trade_sides": {"type": "string", "default": "both"},
         "max_notional_per_order": {"type": "float", "default": 0.0, "min": 0.0},
@@ -166,6 +169,29 @@ class Strategy(Strategy):
         reason = f"1d={d1_trend} 4h={h4_trend} rev={max(d1_rev, h4_rev):.3f}"
         return active, reason
 
+    def _recenter_threshold_pct(self) -> float:
+        base_threshold = float(self.config.get("recenter_pct", 0.05) or 0.05)
+        atr_multiplier = float(self.config.get("recenter_atr_multiplier", 0.35) or 0.0)
+        min_threshold = float(self.config.get("recenter_min_pct", 0.0025) or 0.0)
+        max_threshold = float(self.config.get("recenter_max_pct", 0.03) or 1.0)
+
+        try:
+            tf = str(self.config.get("volatility_timeframe", "1h") or "1h")
+            regime = self.context.get_regime(self._base_symbol(), tf)
+            short_regime = self.context.get_regime(self._base_symbol(), "15m")
+            atr_pct = float((regime or {}).get("volatility", {}).get("atr_percent") or 0.0)
+            short_atr_pct = float((short_regime or {}).get("volatility", {}).get("atr_percent") or 0.0)
+            atr_pct = max(atr_pct, short_atr_pct)
+        except Exception:
+            atr_pct = 0.0
+
+        threshold = (atr_pct / 100.0) * atr_multiplier if atr_pct > 0 else base_threshold
+        threshold = max(threshold, min_threshold)
+        threshold = min(threshold, max_threshold)
+        self.state["recenter_pct_live"] = threshold
+        self.state["recenter_atr_percent"] = atr_pct
+        return threshold
+
     def _grid_step_pct(self) -> float:
         base_step = float(self.config.get("grid_step_pct", 0.012) or 0.012)
         tf = str(self.config.get("volatility_timeframe", "1h") or "1h")
@@ -325,6 +351,7 @@ class Strategy(Strategy):
         return max(amount, 0.0)
 
     def _place_grid(self, center: float) -> None:
+        center = self._maybe_refresh_center(center)
         mode = self._mode()
         levels = int(self.config.get("grid_levels", 6) or 6)
         step = self._grid_step_pct()
@@ -388,6 +415,7 @@ class Strategy(Strategy):
         self._set_grid_refresh_pause()
 
     def _place_side_grid(self, side: str, center: float, *, start_level: int = 1) -> None:
+        center = self._maybe_refresh_center(center)
         levels = int(self.config.get("grid_levels", 6) or 6)
         step = self._grid_step_pct()
         min_notional = float(self.context.minimum_order_value or 0.0)
@@ -457,6 +485,7 @@ class Strategy(Strategy):
         self._set_grid_refresh_pause()
 
     def _top_up_missing_levels(self, center: float, live_orders: list[dict]) -> None:
+        center = self._maybe_refresh_center(center)
         target_levels = int(self.config.get("grid_levels", 6) or 6)
         if target_levels <= 0:
             return
@@ -571,6 +600,7 @@ class Strategy(Strategy):
         return placed
 
     def _recenter_and_rebuild_from_fill(self, fill_price: float) -> None:
+        fill_price = self._maybe_refresh_center(fill_price)
         """Treat a fill as the new market anchor and rebuild the grid from there."""
         if fill_price <= 0:
             return
@@ -584,6 +614,21 @@ class Strategy(Strategy):
         self._place_grid(fill_price)
         self._set_grid_refresh_pause()
 
+    def _maybe_refresh_center(self, price: float) -> float:
+        if price <= 0:
+            return price
+        current = float(self.state.get("center_price") or 0.0)
+        if current <= 0:
+            self.state["center_price"] = price
+            return price
+        deviation = abs(price - current) / current if current else 0.0
+        threshold = self._recenter_threshold_pct()
+        if deviation >= threshold:
+            self._log(f"recenter anchor from {current} to {price} dev={deviation:.4f} threshold={threshold:.4f}")
+            self.state["center_price"] = price
+            return price
+        return current
+
     def _sync_open_orders_state(self) -> list[dict]:
         try:
             open_orders = self.context.get_open_orders()
@@ -765,11 +810,11 @@ class Strategy(Strategy):
         else:
             self.state["mismatch_ticks"] = 0
 
-        center = float(self.state.get("center_price") or price)
-        recenter_pct = float(self.config.get("recenter_pct", 0.05) or 0.05)
+        center = self._maybe_refresh_center(float(self.state.get("center_price") or price))
+        recenter_pct = self._recenter_threshold_pct()
         deviation = abs(price - center) / center if center else 0.0
         if mode == "active" and deviation >= recenter_pct and not self._grid_refresh_paused():
-            self._log(f"recenter needed at price={price} center={center} dev={deviation:.4f}")
+            self._log(f"recenter needed at price={price} center={center} dev={deviation:.4f} threshold={recenter_pct:.4f}")
             try:
                 self.context.cancel_all_orders()
             except Exception as exc: