diff --git a/user_data/strategies/grid_manager.py b/user_data/strategies/grid_manager.py index 01c169d..0bceffc 100644 --- a/user_data/strategies/grid_manager.py +++ b/user_data/strategies/grid_manager.py @@ -57,6 +57,69 @@ class OrderFill: reason: str = "" # 成交原因(entry/add/exit 等) +class GridMonitor: + """ + GridManager 自监测机制 - 判断是否需要止盈或止损 + + 简单策略: + - 止盈:持仓利润达到 target_profit_pct(如 2%) + - 止损:持仓亏损达到 stop_loss_pct(如 -5%) + """ + + def __init__(self, + target_profit_pct: float = 0.02, # 止盈目标 2% + stop_loss_pct: float = -0.05): # 止损阈值 -5% + self.target_profit_pct = target_profit_pct + self.stop_loss_pct = stop_loss_pct + + def should_take_profit(self, current_price: float, avg_entry_price: float) -> bool: + """ + 判断是否应该止盈 + + Args: + current_price: 当前价格 + avg_entry_price: 平均建仓价 + + Returns: + True 表示应该止盈 + """ + if avg_entry_price <= 0: + return False + + profit_pct = (current_price - avg_entry_price) / avg_entry_price + return profit_pct >= self.target_profit_pct + + def should_stop_loss(self, current_price: float, avg_entry_price: float) -> bool: + """ + 判断是否应该止损 + + Args: + current_price: 当前价格 + avg_entry_price: 平均建仓价 + + Returns: + True 表示应该止损 + """ + if avg_entry_price <= 0: + return False + + loss_pct = (current_price - avg_entry_price) / avg_entry_price + return loss_pct <= self.stop_loss_pct + + def get_exit_reason(self, current_price: float, avg_entry_price: float) -> Optional[str]: + """ + 判断退出原因(止盈或止损) + + Returns: + "take_profit", "stop_loss" 或 None + """ + if self.should_take_profit(current_price, avg_entry_price): + return "take_profit" + elif self.should_stop_loss(current_price, avg_entry_price): + return "stop_loss" + return None + + class GridManager: """ 网格交易管理器 - 核心思想:维护网格的填充/排空状态 @@ -141,6 +204,14 @@ class GridManager: # 控制标志:是否已经从 Trade 对象一次性初始化过网格状态(第一次有持仓时) self._grid_initialized_from_trade = False + # ✅ 自监测机制 - 止盈/止损判断 + self.monitor = GridMonitor( + target_profit_pct=0.02, # 止盈目标: 2% + stop_loss_pct=-0.05 # 止损阈值: -5% + ) + self.is_completed = False # GridManager 是否完结 + self.completion_reason: Optional[str] = None # 完结原因 (take_profit / stop_loss) + def update_state(self, current_price: float, candle_index: int) -> None: """ 更新网格对象的当前状态 @@ -169,6 +240,17 @@ class GridManager: f"持仓: {self.total_quantity:.6f}, 平均价: {self.avg_entry_price:.2f}, " f"网格状态: {filled_count} FILLED / {empty_count} EMPTY", file=sys.stderr, flush=True) + + # ✅ 自监测:检查是否需要止盈/止损 + if not self.is_completed and self.total_quantity > 0: + exit_reason = self.monitor.get_exit_reason(current_price, self.avg_entry_price) + if exit_reason: + self.is_completed = True + self.completion_reason = exit_reason + profit_pct = (current_price - self.avg_entry_price) / self.avg_entry_price * 100 + print(f"[GridManager] {self.pair} 自监测触发✅ - 原因: {exit_reason}, " + f"利润: {profit_pct:.2f}%, 当前价: {current_price:.2f}", + file=sys.stderr, flush=True) def apply_adjustment(self, adjustment: PositionRecord) -> None: """ @@ -269,6 +351,19 @@ class GridManager: if self.current_price is None: return None + # ✅ 自监测止盈/止损:如果已经变为完结,返回 EXIT 信号 + if self.is_completed: + if self.total_quantity > 0: + # 全部平仓 + return PositionRecord( + level_index=0, + price=self.current_price, + quantity=self.total_quantity, + type=AdjustmentType.EXIT, + timestamp=self.candle_index + ) + return None + # 找到当前价格所在的网格点 current_grid_price = self._round_to_grid(self.current_price)