Procházet zdrojové kódy

Refine Hermes grid-trend switching

Lukas Goldschmidt před 3 týdny
rodič
revize
9afada544c
1 změnil soubory, kde provedl 36 přidání a 1 odebrání
  1. 36 1
      src/hermes_mcp/decision_engine.py

+ 36 - 1
src/hermes_mcp/decision_engine.py

@@ -713,11 +713,23 @@ def _grid_switch_tradeoff(*,
     trend: dict[str, Any] | None,
 ) -> dict[str, Any]:
     inventory_state = _inventory_state_label(wallet_state.get("inventory_state"))
+    supervision = current_primary.get("supervision") if isinstance(current_primary.get("supervision"), dict) else {}
     open_order_count = int(current_primary.get("open_order_count") or 0)
     if not open_order_count:
         state = current_primary.get("state") if isinstance(current_primary.get("state"), dict) else {}
         open_order_count = int(state.get("open_order_count") or len(state.get("orders") or []) or 0)
 
+    adverse_side = str(supervision.get("adverse_side") or "unknown")
+    adverse_count = int(supervision.get("adverse_side_open_order_count") or 0)
+    adverse_notional = float(supervision.get("adverse_side_open_order_notional_quote") or 0.0)
+    adverse_distance = _safe_float(supervision.get("adverse_side_nearest_distance_pct"))
+    base_order_notional = 1.0
+    config = current_primary.get("config") if isinstance(current_primary.get("config"), dict) else {}
+    for candidate in (config.get("order_notional_quote"), config.get("max_order_notional_quote")):
+        candidate_value = _safe_float(candidate)
+        if candidate_value and candidate_value > base_order_notional:
+            base_order_notional = candidate_value
+
     trend_score = float(trend.get("score") or 0.0) if trend else 0.0
     breakout_score = float(breakout.get("score") or 0.0)
     levels = float(grid_pressure.get("levels") or 0.0)
@@ -726,6 +738,7 @@ def _grid_switch_tradeoff(*,
     persistent = bool(breakout.get("persistent"))
     trend_ready = trend_score > 0.45 and directional_micro_clear
 
+    stay_cost = 0.0
     switch_benefit = 0.0
     if persistent:
         switch_benefit += 0.28
@@ -738,7 +751,15 @@ def _grid_switch_tradeoff(*,
     switch_benefit += min(trend_score, 2.5) * 0.18
     switch_benefit += min(breakout_score, 5.0) * 0.04
 
-    stay_cost = 0.0
+    if adverse_side in {"buy", "sell"} and adverse_count > 0:
+        adverse_notional_ratio = adverse_notional / max(base_order_notional, 1.0)
+        switch_benefit += min(adverse_count, 8) * 0.02
+        if adverse_distance is not None and adverse_distance <= 1.25:
+            switch_benefit += 0.08
+        stay_cost += min(adverse_notional_ratio, 4.0) * 0.07
+    else:
+        adverse_notional_ratio = 0.0
+
     if inventory_state == "balanced":
         stay_cost += 0.06
     elif inventory_state in {"base_heavy", "quote_heavy"}:
@@ -755,6 +776,8 @@ def _grid_switch_tradeoff(*,
         stay_cost += 0.18
     if not persistent:
         stay_cost += 0.12
+    if adverse_notional_ratio >= 1.0:
+        stay_cost += 0.08
 
     margin = round(switch_benefit - stay_cost, 4)
     should_switch = persistent and trend_ready and margin > 0.0
@@ -771,6 +794,10 @@ def _grid_switch_tradeoff(*,
         "open_order_count": open_order_count,
         "near_fill": near_fill,
         "fill_fights": fill_fights,
+        "adverse_side": adverse_side,
+        "adverse_side_open_order_count": adverse_count,
+        "adverse_side_open_order_notional_quote": round(adverse_notional, 4),
+        "adverse_side_nearest_distance_pct": round(adverse_distance, 4) if adverse_distance is not None else None,
         "inventory_state": inventory_state,
     }
 
@@ -895,6 +922,10 @@ def _decide_for_grid(*,
             action = "replace_with_trend_follower"
             target_strategy = trend["strategy_id"]
             mode = "act"
+            if switch_tradeoff.get("adverse_side_open_order_count", 0) > 0:
+                reasons.append(
+                    f"{switch_tradeoff.get('adverse_side')} ladder is exposed near market"
+                )
             if directional_inventory:
                 reasons.append("inventory posture can be absorbed by the directional handoff")
             reasons.append(
@@ -917,6 +948,10 @@ def _decide_for_grid(*,
             reasons.append(
                 f"breakout is persistent, but staying in grid still looks cheaper than switching (benefit {switch_tradeoff['switch_benefit']:.2f} vs cost {switch_tradeoff['stay_cost']:.2f})"
             )
+            if switch_tradeoff.get("adverse_side_open_order_count", 0) > 0:
+                reasons.append(
+                    f"{switch_tradeoff.get('adverse_side')} ladder exposure is not yet costly enough to justify the handoff"
+                )
             if grid_fill.get("near_fill") and fill_fights_breakout:
                 reasons.append("nearby opposing fill is only a warning here, not enough on its own to justify the handoff")
         else: