From 0a9377f5acbc68e1a1b6943a7dd56c50abb06edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sat, 14 Feb 2026 19:59:57 +0800 Subject: [PATCH] 1 --- trading_system/position_manager.py | 48 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 847e3e2..f1e2003 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -1627,20 +1627,23 @@ class PositionManager: continue current_position = position_dict[symbol] - entry_price = position_info['entryPrice'] - quantity = position_info['quantity'] # 修复:获取quantity + # 统一转为 float,避免 Decimal 与 float 运算报错(DB/补建可能返回 Decimal) + entry_price = float(position_info['entryPrice']) + quantity = float(position_info['quantity']) # 获取当前标记价格 current_price = current_position.get('markPrice', 0) if current_price == 0: # 如果标记价格为0,尝试从ticker获取 ticker = await self.client.get_ticker_24h(symbol) if ticker: - current_price = ticker['price'] + current_price = float(ticker.get('price', 0) or 0) else: current_price = entry_price + else: + current_price = float(current_price) # 计算当前盈亏(基于保证金) - leverage = position_info.get('leverage', 10) + leverage = float(position_info.get('leverage', 10) or 10) position_value = entry_price * quantity margin = position_value / leverage if leverage > 0 else position_value @@ -1660,7 +1663,8 @@ class PositionManager: pnl_percent_price = ((entry_price - current_price) / entry_price) * 100 # 更新最大盈利(基于保证金) - if pnl_percent_margin > position_info.get('maxProfit', 0): + max_profit = float(position_info.get('maxProfit', 0) or 0) + if pnl_percent_margin > max_profit: position_info['maxProfit'] = pnl_percent_margin # 移动止损逻辑(盈利后保护利润,基于保证金) @@ -1716,7 +1720,8 @@ class PositionManager: # 计算新的止损价(基于剩余仓位) if position_info['side'] == 'BUY': new_stop_loss = entry_price + (remaining_pnl - protect_amount) / remaining_quantity - if new_stop_loss > position_info['stopLoss']: + current_sl = float(position_info['stopLoss']) if position_info.get('stopLoss') is not None else None + if current_sl is None or new_stop_loss > current_sl: position_info['stopLoss'] = new_stop_loss logger.info( f"{symbol} 移动止损更新(剩余仓位): {new_stop_loss:.4f} " @@ -1730,7 +1735,8 @@ class PositionManager: new_stop_loss = entry_price + (remaining_pnl - protect_amount) / remaining_quantity # 对于做空,止损价应该越来越高(更宽松),所以检查 new_stop_loss > 当前止损 # 同时,移动止损只应该在盈利时激活 - if new_stop_loss > position_info['stopLoss'] and remaining_pnl > 0: + current_sl = float(position_info['stopLoss']) if position_info.get('stopLoss') is not None else None + if current_sl is not None and new_stop_loss > current_sl and remaining_pnl > 0: position_info['stopLoss'] = new_stop_loss logger.info( f"{symbol} 移动止损更新(剩余仓位): {new_stop_loss:.4f} " @@ -1745,7 +1751,8 @@ class PositionManager: # 保护利润:当前盈亏 - 保护金额 = (止损价 - 开仓价) × 数量 # 所以:止损价 = 开仓价 + (当前盈亏 - 保护金额) / 数量 new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity - if new_stop_loss > position_info['stopLoss']: + current_sl = float(position_info['stopLoss']) if position_info.get('stopLoss') is not None else None + if current_sl is None or new_stop_loss > current_sl: position_info['stopLoss'] = new_stop_loss logger.info( f"{symbol} 移动止损更新: {new_stop_loss:.4f} " @@ -1759,7 +1766,8 @@ class PositionManager: new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity # 对于做空,止损价应该越来越高(更宽松),所以检查 new_stop_loss > 当前止损 # 同时,移动止损只应该在盈利时激活,不应该在亏损时把止损往下移 - if new_stop_loss > position_info['stopLoss'] and pnl_amount > 0: + current_sl = float(position_info['stopLoss']) if position_info.get('stopLoss') is not None else None + if current_sl is not None and new_stop_loss > current_sl and pnl_amount > 0: position_info['stopLoss'] = new_stop_loss logger.info( f"{symbol} 移动止损更新: {new_stop_loss:.4f} " @@ -1768,7 +1776,8 @@ class PositionManager: # 检查止损(使用更新后的止损价,基于保证金收益比) # ⚠️ 重要:止损检查应该在时间锁之前,止损必须立即执行 - stop_loss = position_info.get('stopLoss') + stop_loss_raw = position_info.get('stopLoss') + stop_loss = float(stop_loss_raw) if stop_loss_raw is not None else None should_close_due_to_sl = False exit_reason_sl = None @@ -1873,10 +1882,12 @@ class PositionManager: # 2) 分步止盈策略本身已提供利润保护(50%在1:1止盈,剩余保本) # 3) 交易所级别止盈单已提供保护 # 4) 及时止盈可以保护利润,避免价格回落 - take_profit_1 = position_info.get('takeProfit1') # 第一目标(盈亏比1:1) - take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标 + take_profit_1_raw = position_info.get('takeProfit1') # 第一目标(盈亏比1:1) + take_profit_1 = float(take_profit_1_raw) if take_profit_1_raw is not None else None + take_profit_2_raw = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标 + take_profit_2 = float(take_profit_2_raw) if take_profit_2_raw is not None else None partial_profit_taken = position_info.get('partialProfitTaken', False) - remaining_quantity = position_info.get('remainingQuantity', quantity) + remaining_quantity = float(position_info.get('remainingQuantity', quantity)) # 第一目标:TAKE_PROFIT_1_PERCENT 止盈(默认15%保证金),了结50%仓位 # ✅ 已移除时间锁限制,可以立即执行 @@ -2022,12 +2033,13 @@ class PositionManager: # 如果未部分止盈,但达到【第二目标】止盈价对应的收益比时,才全部平仓 # ⚠️ 修复:不再使用 TAKE_PROFIT_PERCENT(10%) 作为全平条件,否则会“刚赚一点就整仓止盈” # 改为使用 take_profit_2 价格对应的保证金收益%,与第一目标(20%) 取较大者,避免盈利过少 - take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) - if take_profit_2 is not None and margin and margin > 0: + take_profit_2_full = position_info.get('takeProfit2', position_info.get('takeProfit')) + take_profit_2_full = float(take_profit_2_full) if take_profit_2_full is not None else None + if take_profit_2_full is not None and margin and margin > 0: if position_info['side'] == 'BUY': - take_profit_2_amount = (take_profit_2 - entry_price) * quantity + take_profit_2_amount = (take_profit_2_full - entry_price) * quantity else: - take_profit_2_amount = (entry_price - take_profit_2) * quantity + take_profit_2_amount = (entry_price - take_profit_2_full) * quantity take_profit_2_pct_margin = (take_profit_2_amount / margin * 100) if margin > 0 else 0 # 至少要求达到第一目标对应的收益%(如 20%),避免过早全平 take_profit_1_pct = (config.TRADING_CONFIG.get('TAKE_PROFIT_1_PERCENT', 0.20) or 0.20) @@ -2039,7 +2051,7 @@ class PositionManager: logger.info( f"{symbol} 触发止盈(第二目标/保证金): " f"当前盈亏={pnl_percent_margin:.2f}% of margin >= 目标={min_pct_for_full_tp:.2f}% | " - f"当前价={current_price:.4f}, 第二目标价={take_profit_2:.4f}" + f"当前价={current_price:.4f}, 第二目标价={take_profit_2_full:.4f}" ) exit_reason = 'take_profit' # 更新数据库