a
This commit is contained in:
parent
b86abf4309
commit
d0d40b8f83
|
|
@ -751,9 +751,55 @@ class PositionManager:
|
||||||
|
|
||||||
# 获取当前所有持仓
|
# 获取当前所有持仓
|
||||||
positions = await self.client.get_open_positions()
|
positions = await self.client.get_open_positions()
|
||||||
|
binance_symbols = {p['symbol'] for p in positions}
|
||||||
|
active_symbols = set(self.active_positions.keys())
|
||||||
|
|
||||||
|
logger.info(f"币安持仓: {len(binance_symbols)} 个 ({', '.join(binance_symbols) if binance_symbols else '无'})")
|
||||||
|
logger.info(f"本地持仓记录: {len(active_symbols)} 个 ({', '.join(active_symbols) if active_symbols else '无'})")
|
||||||
|
|
||||||
|
# 为所有币安持仓启动监控(即使不在active_positions中,可能是手动开仓的)
|
||||||
for position in positions:
|
for position in positions:
|
||||||
symbol = position['symbol']
|
symbol = position['symbol']
|
||||||
if symbol not in self._monitor_tasks and symbol in self.active_positions:
|
if symbol not in self._monitor_tasks:
|
||||||
|
# 如果不在active_positions中,先创建记录
|
||||||
|
if symbol not in self.active_positions:
|
||||||
|
logger.warning(f"{symbol} 在币安有持仓但不在本地记录中,可能是手动开仓,尝试创建记录...")
|
||||||
|
# 这里会通过sync_positions_with_binance来处理,但先启动监控
|
||||||
|
try:
|
||||||
|
entry_price = position.get('entryPrice', 0)
|
||||||
|
position_amt = position['positionAmt']
|
||||||
|
quantity = abs(position_amt)
|
||||||
|
side = 'BUY' if position_amt > 0 else 'SELL'
|
||||||
|
|
||||||
|
# 创建临时记录用于监控
|
||||||
|
ticker = await self.client.get_ticker_24h(symbol)
|
||||||
|
current_price = ticker['price'] if ticker else entry_price
|
||||||
|
|
||||||
|
stop_loss_price = self.risk_manager.get_stop_loss_price(entry_price, side)
|
||||||
|
take_profit_price = self.risk_manager.get_take_profit_price(entry_price, side)
|
||||||
|
|
||||||
|
position_info = {
|
||||||
|
'symbol': symbol,
|
||||||
|
'side': side,
|
||||||
|
'quantity': quantity,
|
||||||
|
'entryPrice': entry_price,
|
||||||
|
'changePercent': 0,
|
||||||
|
'orderId': None,
|
||||||
|
'tradeId': None,
|
||||||
|
'stopLoss': stop_loss_price,
|
||||||
|
'takeProfit': take_profit_price,
|
||||||
|
'initialStopLoss': stop_loss_price,
|
||||||
|
'leverage': position.get('leverage', 10),
|
||||||
|
'entryReason': 'manual_entry_temp',
|
||||||
|
'atr': None,
|
||||||
|
'maxProfit': 0.0,
|
||||||
|
'trailingStopActivated': False
|
||||||
|
}
|
||||||
|
self.active_positions[symbol] = position_info
|
||||||
|
logger.info(f"{symbol} 已创建临时持仓记录用于监控")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{symbol} 创建临时持仓记录失败: {e}")
|
||||||
|
|
||||||
await self._start_position_monitoring(symbol)
|
await self._start_position_monitoring(symbol)
|
||||||
|
|
||||||
logger.info(f"已启动 {len(self._monitor_tasks)} 个持仓的实时监控")
|
logger.info(f"已启动 {len(self._monitor_tasks)} 个持仓的实时监控")
|
||||||
|
|
@ -952,20 +998,52 @@ class PositionManager:
|
||||||
|
|
||||||
# 检查止盈
|
# 检查止盈
|
||||||
if not should_close:
|
if not should_close:
|
||||||
take_profit = position_info['takeProfit']
|
take_profit = float(position_info['takeProfit'])
|
||||||
if position_info['side'] == 'BUY' and current_price >= take_profit:
|
# 计算止盈百分比(用于诊断)
|
||||||
should_close = True
|
if position_info['side'] == 'BUY':
|
||||||
exit_reason = 'take_profit'
|
take_profit_pct = ((take_profit - entry_price) / entry_price) * 100
|
||||||
logger.info(
|
else: # SELL
|
||||||
f"{symbol} [实时监控] 触发止盈: {current_price:.4f} >= {take_profit:.4f} "
|
take_profit_pct = ((entry_price - take_profit) / entry_price) * 100
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
|
||||||
|
# 每5%盈利记录一次诊断日志(帮助排查问题)
|
||||||
|
# 使用更宽松的条件,避免因为浮点数精度问题导致日志不输出
|
||||||
|
if pnl_percent >= 5.0:
|
||||||
|
# 每5%记录一次,但允许一些容差
|
||||||
|
should_log = (int(pnl_percent) % 5 == 0) or (pnl_percent >= 10.0 and pnl_percent < 10.5)
|
||||||
|
if should_log:
|
||||||
|
trigger_condition = current_price_float >= take_profit if position_info['side'] == 'BUY' else current_price_float <= take_profit
|
||||||
|
logger.warning(
|
||||||
|
f"{symbol} [实时监控] 诊断: 盈利{pnl_percent:.2f}% | "
|
||||||
|
f"当前价: {current_price_float:.4f} | "
|
||||||
|
f"入场价: {entry_price:.4f} | "
|
||||||
|
f"止盈价: {take_profit:.4f} ({take_profit_pct:.2f}%) | "
|
||||||
|
f"方向: {position_info['side']} | "
|
||||||
|
f"是否触发: {trigger_condition} | "
|
||||||
|
f"价格差: {abs(current_price_float - take_profit):.4f} | "
|
||||||
|
f"监控状态: {'运行中' if symbol in self._monitor_tasks else '未启动'}"
|
||||||
)
|
)
|
||||||
elif position_info['side'] == 'SELL' and current_price <= take_profit:
|
|
||||||
|
# 如果盈利超过止盈目标但未触发,记录警告
|
||||||
|
if pnl_percent > take_profit_pct and not trigger_condition:
|
||||||
|
logger.error(
|
||||||
|
f"{symbol} [实时监控] ⚠️ 异常: 盈利{pnl_percent:.2f}% > 止盈目标{take_profit_pct:.2f}%,但未触发平仓!"
|
||||||
|
)
|
||||||
|
price_diff = current_price_float - take_profit if position_info['side'] == 'BUY' else take_profit - current_price_float
|
||||||
|
logger.error(f" 当前价: {current_price_float:.4f}, 止盈价: {take_profit:.4f}, 价格差: {price_diff:.4f}")
|
||||||
|
|
||||||
|
if position_info['side'] == 'BUY' and current_price_float >= take_profit:
|
||||||
should_close = True
|
should_close = True
|
||||||
exit_reason = 'take_profit'
|
exit_reason = 'take_profit'
|
||||||
logger.info(
|
logger.info(
|
||||||
f"{symbol} [实时监控] 触发止盈: {current_price:.4f} <= {take_profit:.4f} "
|
f"{symbol} [实时监控] 触发止盈: {current_price_float:.4f} >= {take_profit:.4f} "
|
||||||
f"(盈亏: {pnl_percent:.2f}%)"
|
f"(盈亏: {pnl_percent:.2f}%, 止盈目标: {take_profit_pct:.2f}%)"
|
||||||
|
)
|
||||||
|
elif position_info['side'] == 'SELL' and current_price_float <= take_profit:
|
||||||
|
should_close = True
|
||||||
|
exit_reason = 'take_profit'
|
||||||
|
logger.info(
|
||||||
|
f"{symbol} [实时监控] 触发止盈: {current_price_float:.4f} <= {take_profit:.4f} "
|
||||||
|
f"(盈亏: {pnl_percent:.2f}%, 止盈目标: {take_profit_pct:.2f}%)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 如果触发止损止盈,执行平仓
|
# 如果触发止损止盈,执行平仓
|
||||||
|
|
@ -1010,3 +1088,93 @@ class PositionManager:
|
||||||
logger.info(f"{symbol} [自动平仓] ✓ 平仓成功完成")
|
logger.info(f"{symbol} [自动平仓] ✓ 平仓成功完成")
|
||||||
else:
|
else:
|
||||||
logger.error(f"{symbol} [自动平仓] ❌ 平仓失败")
|
logger.error(f"{symbol} [自动平仓] ❌ 平仓失败")
|
||||||
|
|
||||||
|
async def diagnose_position(self, symbol: str):
|
||||||
|
"""
|
||||||
|
诊断持仓状态(用于排查为什么没有自动平仓)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol: 交易对
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info(f"{symbol} [诊断] 开始诊断持仓状态...")
|
||||||
|
|
||||||
|
# 1. 检查是否在active_positions中
|
||||||
|
if symbol not in self.active_positions:
|
||||||
|
logger.warning(f"{symbol} [诊断] ❌ 不在本地持仓记录中 (active_positions)")
|
||||||
|
logger.warning(f" 可能原因: 手动开仓或系统重启后未同步")
|
||||||
|
logger.warning(f" 解决方案: 等待下次状态同步或手动触发同步")
|
||||||
|
else:
|
||||||
|
position_info = self.active_positions[symbol]
|
||||||
|
logger.info(f"{symbol} [诊断] ✓ 在本地持仓记录中")
|
||||||
|
logger.info(f" 入场价: {position_info['entryPrice']:.4f}")
|
||||||
|
logger.info(f" 方向: {position_info['side']}")
|
||||||
|
logger.info(f" 数量: {position_info['quantity']:.4f}")
|
||||||
|
logger.info(f" 止损价: {position_info['stopLoss']:.4f}")
|
||||||
|
logger.info(f" 止盈价: {position_info['takeProfit']:.4f}")
|
||||||
|
|
||||||
|
# 2. 检查WebSocket监控状态
|
||||||
|
if symbol in self._monitor_tasks:
|
||||||
|
task = self._monitor_tasks[symbol]
|
||||||
|
if task.done():
|
||||||
|
logger.warning(f"{symbol} [诊断] ⚠ WebSocket监控任务已结束")
|
||||||
|
try:
|
||||||
|
await task # 获取异常信息
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f" 任务异常: {e}")
|
||||||
|
else:
|
||||||
|
logger.info(f"{symbol} [诊断] ✓ WebSocket监控任务运行中")
|
||||||
|
else:
|
||||||
|
logger.warning(f"{symbol} [诊断] ❌ 没有WebSocket监控任务")
|
||||||
|
logger.warning(f" 可能原因: 监控未启动或已停止")
|
||||||
|
|
||||||
|
# 3. 获取币安实际持仓
|
||||||
|
positions = await self.client.get_open_positions()
|
||||||
|
binance_position = next((p for p in positions if p['symbol'] == symbol), None)
|
||||||
|
|
||||||
|
if not binance_position:
|
||||||
|
logger.warning(f"{symbol} [诊断] ❌ 币安账户中没有持仓")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"{symbol} [诊断] ✓ 币安账户中有持仓")
|
||||||
|
entry_price_binance = binance_position.get('entryPrice', 0)
|
||||||
|
mark_price = binance_position.get('markPrice', 0)
|
||||||
|
unrealized_pnl = binance_position.get('unRealizedProfit', 0)
|
||||||
|
|
||||||
|
logger.info(f" 币安入场价: {entry_price_binance:.4f}")
|
||||||
|
logger.info(f" 标记价格: {mark_price:.4f}")
|
||||||
|
logger.info(f" 未实现盈亏: {unrealized_pnl:.2f} USDT")
|
||||||
|
|
||||||
|
# 4. 计算实际盈亏
|
||||||
|
if symbol in self.active_positions:
|
||||||
|
position_info = self.active_positions[symbol]
|
||||||
|
entry_price = float(position_info['entryPrice'])
|
||||||
|
take_profit = float(position_info['takeProfit'])
|
||||||
|
|
||||||
|
if position_info['side'] == 'BUY':
|
||||||
|
pnl_percent = ((mark_price - entry_price) / entry_price) * 100
|
||||||
|
take_profit_pct = ((take_profit - entry_price) / entry_price) * 100
|
||||||
|
should_trigger = mark_price >= take_profit
|
||||||
|
else: # SELL
|
||||||
|
pnl_percent = ((entry_price - mark_price) / entry_price) * 100
|
||||||
|
take_profit_pct = ((entry_price - take_profit) / entry_price) * 100
|
||||||
|
should_trigger = mark_price <= take_profit
|
||||||
|
|
||||||
|
logger.info(f"{symbol} [诊断] 盈亏分析:")
|
||||||
|
logger.info(f" 当前盈亏: {pnl_percent:.2f}%")
|
||||||
|
logger.info(f" 止盈目标: {take_profit_pct:.2f}%")
|
||||||
|
logger.info(f" 当前价: {mark_price:.4f}")
|
||||||
|
logger.info(f" 止盈价: {take_profit:.4f}")
|
||||||
|
logger.info(f" 价格差: {abs(mark_price - take_profit):.4f}")
|
||||||
|
logger.info(f" 应该触发: {should_trigger}")
|
||||||
|
|
||||||
|
if pnl_percent > take_profit_pct and not should_trigger:
|
||||||
|
logger.error(f"{symbol} [诊断] ❌ 异常: 盈亏{pnl_percent:.2f}% > 止盈目标{take_profit_pct:.2f}%,但未触发平仓!")
|
||||||
|
logger.error(f" 可能原因: 浮点数精度问题或止盈价格计算错误")
|
||||||
|
|
||||||
|
logger.info(f"{symbol} [诊断] 诊断完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{symbol} [诊断] 诊断失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(f" 错误详情:\n{traceback.format_exc()}")
|
||||||
|
|
@ -164,6 +164,8 @@ class TradingStrategy:
|
||||||
# 3. 同步币安实际持仓状态与数据库(定期同步,确保状态一致)
|
# 3. 同步币安实际持仓状态与数据库(定期同步,确保状态一致)
|
||||||
try:
|
try:
|
||||||
await self.position_manager.sync_positions_with_binance()
|
await self.position_manager.sync_positions_with_binance()
|
||||||
|
# 同步后,确保所有持仓都有监控(包括手动开仓的)
|
||||||
|
await self.position_manager.start_all_position_monitoring()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"持仓状态同步失败: {e}")
|
logger.warning(f"持仓状态同步失败: {e}")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user