129 lines
5.5 KiB
Markdown
129 lines
5.5 KiB
Markdown
# 止损立即平仓修复说明
|
||
|
||
## 🔍 问题描述
|
||
|
||
**时间范围**:23点后到早上
|
||
**症状**:系统检测到价格已触发止损价,但只记录错误日志,**没有执行平仓操作**,导致亏损持续扩大。
|
||
|
||
### 错误日志示例
|
||
```
|
||
NOMUSDT ⚠️ 当前价格(0.00944000)已触发止损价(0.00977746),无法挂止损单,应该立即平仓!
|
||
ZROUSDT ⚠️ 当前价格(2.02533560)已触发止损价(2.02531200),无法挂止损单,应该立即平仓!
|
||
WCTUSDT ⚠️ 当前价格(0.08786000)已触发止损价(0.08963080),无法挂止损单,应该立即平仓!
|
||
```
|
||
|
||
## 🎯 根本原因
|
||
|
||
在 `trading_system/position_manager.py` 的 `_ensure_exchange_sltp_orders()` 方法中:
|
||
|
||
1. **挂单逻辑死锁**:当 `current_price` 已经低于 `stop_loss_price`(做多时),币安 API 会拒绝 `STOP_MARKET` 订单,返回 `Order would immediately trigger`(错误代码 -2021)。
|
||
|
||
2. **只报警不执行**:代码检测到了这个情况并打印了警告日志,但**只设置了 `sl_order = None`,没有触发市价平仓**。
|
||
|
||
3. **依赖WebSocket延迟**:代码注释说"依赖WebSocket监控立即平仓",但WebSocket监控可能有延迟,在深夜价格剧烈波动时,无法及时止损。
|
||
|
||
## ✅ 修复方案
|
||
|
||
### 修复位置
|
||
`trading_system/position_manager.py` 的 `_ensure_exchange_sltp_orders()` 方法
|
||
|
||
### 修复内容
|
||
|
||
#### 1. 止损价触发时立即平仓(第1199-1223行)
|
||
|
||
**修复前**:
|
||
```python
|
||
if current_price_val <= stop_loss_val:
|
||
logger.error(f"{symbol} ⚠️ 当前价格(...)已触发止损价(...),无法挂止损单,应该立即平仓!")
|
||
logger.error(f" 建议: 立即手动平仓或等待WebSocket监控触发平仓")
|
||
sl_order = None # ❌ 只设置None,没有执行平仓
|
||
```
|
||
|
||
**修复后**:
|
||
```python
|
||
if current_price_val <= stop_loss_val:
|
||
logger.error(f"{symbol} ⚠️ 当前价格({current_price_val:.8f})已触发止损价({stop_loss_val:.8f}),无法挂止损单,立即执行市价平仓保护!")
|
||
logger.error(f" 入场价: {entry_price_val:.8f if entry_price_val else 'N/A'}")
|
||
# ✅ 立即执行市价平仓
|
||
await self.close_position(symbol, reason='stop_loss')
|
||
return # 直接返回,不再尝试挂单
|
||
```
|
||
|
||
#### 2. 止盈价触发时立即平仓(第1272-1288行)
|
||
|
||
**新增逻辑**:在挂止盈单前,也检查价格是否已经达到止盈价,如果达到则立即执行市价平仓。
|
||
|
||
```python
|
||
# 在挂止盈单前,检查当前价格是否已经触发止盈
|
||
if current_price and take_profit:
|
||
try:
|
||
current_price_val = float(current_price)
|
||
take_profit_val = float(take_profit)
|
||
|
||
# 检查是否已经触发止盈
|
||
triggered_tp = False
|
||
if side == "BUY" and current_price_val >= take_profit_val:
|
||
triggered_tp = True
|
||
elif side == "SELL" and current_price_val <= take_profit_val:
|
||
triggered_tp = True
|
||
|
||
if triggered_tp:
|
||
logger.info(f"{symbol} 🎯 当前价格({current_price_val:.8f})已达到止盈价({take_profit_val:.8f}),立即执行市价止盈!")
|
||
await self.close_position(symbol, reason='take_profit')
|
||
return
|
||
except Exception as e:
|
||
logger.debug(f"{symbol} 检查止盈触发条件时出错: {e}")
|
||
```
|
||
|
||
## 📊 修复效果
|
||
|
||
### 修复前
|
||
- ❌ 检测到止损触发 → 只记录错误日志 → 等待WebSocket监控 → **可能延迟或失败**
|
||
- ❌ 价格继续下跌 → 亏损扩大 → 直到下次扫描才可能止损
|
||
|
||
### 修复后
|
||
- ✅ 检测到止损触发 → **立即执行市价平仓** → 止损保护立即生效
|
||
- ✅ 价格继续下跌 → **已平仓,不再亏损**
|
||
|
||
## 🔄 触发场景
|
||
|
||
这个修复会在以下场景生效:
|
||
|
||
1. **开仓后立即检查**:在 `_ensure_exchange_sltp_orders()` 被调用时(开仓后立即执行)
|
||
2. **系统重启后同步**:如果系统重启,同步持仓时会调用 `_ensure_exchange_sltp_orders()` 补挂保护单
|
||
3. **定期检查**:`check_stop_loss_take_profit()` 方法会定期检查(通过扫描间隔)
|
||
|
||
## ⚠️ 注意事项
|
||
|
||
1. **市价平仓**:修复使用 `close_position()` 方法执行市价平仓,可能会有轻微滑点,但能确保及时止损。
|
||
|
||
2. **扫描间隔影响**:如果扫描间隔较长(如1小时),在间隔期间价格暴跌穿透止损线,等到下次扫描时,`_ensure_exchange_sltp_orders()` 会被调用(例如系统重启后),此时会立即平仓。
|
||
|
||
3. **WebSocket监控**:WebSocket监控仍然有效,作为第二层保护。但修复后,即使WebSocket延迟,也能通过价格检查立即平仓。
|
||
|
||
## 🚀 部署建议
|
||
|
||
1. **重启交易进程**:修复后需要重启所有 `trading_system` 进程才能生效。
|
||
```bash
|
||
supervisorctl restart auto_sys_acc1 auto_sys_acc2 auto_sys_acc3 ...
|
||
```
|
||
|
||
2. **验证修复**:查看日志,确认当价格触发止损时,会看到:
|
||
```
|
||
{symbol} ⚠️ 当前价格(...)已触发止损价(...),无法挂止损单,立即执行市价平仓保护!
|
||
{symbol} [平仓] 开始平仓操作 (原因: stop_loss)
|
||
{symbol} [平仓] ✓ 平仓订单已提交
|
||
```
|
||
|
||
3. **监控效果**:观察后续交易,确认深夜价格波动时能及时止损,不再出现"只报警不平仓"的情况。
|
||
|
||
## 📝 相关文件
|
||
|
||
- `trading_system/position_manager.py`:主要修复文件
|
||
- `_ensure_exchange_sltp_orders()` 方法(第1101-1320行)
|
||
- `close_position()` 方法(第669-769行)
|
||
|
||
## ✅ 修复完成时间
|
||
|
||
2026-01-25
|