This commit is contained in:
薇薇安 2026-01-22 23:03:32 +08:00
parent ba818b480d
commit 7ec1ae32d7
4 changed files with 556 additions and 94 deletions

263
STRATEGY_LOGIC_ANALYSIS.md Normal file
View File

@ -0,0 +1,263 @@
# 交易策略逻辑完整分析
## 📊 当前策略参数配置
### 核心参数
| 参数 | 当前值 | 说明 |
|------|--------|------|
| `ATR_STOP_LOSS_MULTIPLIER` | 1.8 | ATR止损倍数止损距离 = ATR × 1.8 |
| `ATR_TAKE_PROFIT_MULTIPLIER` | 1.5 | ATR止盈倍数备选方法当无止损距离时使用 |
| `RISK_REWARD_RATIO` | 1.5 | 盈亏比(止盈距离 = 止损距离 × 1.5 |
| `MIN_TAKE_PROFIT_PRICE_PCT` | 0.02 (2%) | 最小止盈价格变动保护 |
| `MIN_HOLD_TIME_SEC` | 1800 (30分钟) | 最小持仓时间锁 |
| `USE_TRAILING_STOP` | False | 移动止损(已禁用) |
## 🎯 止盈计算逻辑(优先级顺序)
### 方法1基于止损距离和盈亏比优先使用
```
止盈距离 = 止损距离 × RISK_REWARD_RATIO (1.5)
止盈价 = 入场价 ± 止盈距离
```
**示例**
- 入场价100 USDT
- ATR3 USDT (3%)
- 止损距离100 × 0.03 × 1.8 = 5.4 USDT (5.4%)
- 止损价100 - 5.4 = 94.6 USDT
- **止盈距离**5.4 × 1.5 = **8.1 USDT (8.1%)**
- **止盈价**100 + 8.1 = **108.1 USDT**
### 方法2基于ATR倍数备选当无止损距离时
```
止盈距离 = ATR百分比 × ATR_TAKE_PROFIT_MULTIPLIER (1.5)
止盈价 = 入场价 × (1 ± 止盈距离百分比)
```
**示例**
- 入场价100 USDT
- ATR3 USDT (3%)
- **止盈距离**0.03 × 1.5 = **4.5%**
- **止盈价**100 × 1.045 = **104.5 USDT**
### 方法3基于保证金百分比兜底
```
止盈金额 = 保证金 × TAKE_PROFIT_PERCENT (25%)
止盈价 = 入场价 ± (止盈金额 / 数量)
```
### 方法4最小价格变动保护
```
止盈价 = 入场价 × (1 ± MIN_TAKE_PROFIT_PRICE_PCT) (2%)
```
**最终止盈价选择**:取以上方法中最宽松(最远)的价格
## 🔄 分步止盈策略
### 第一阶段50% 仓位在 1:1 盈亏比止盈
```
第一目标价 = 入场价 ± (入场价 - 止损价)
第一目标 = 盈亏比 1:1相对于保证金
```
**触发条件**
- 当前盈亏百分比(基于保证金)≥ 止损百分比(基于保证金)
- 平仓 50% 仓位
- **将剩余仓位止损移至入场价(保本)**
### 第二阶段:剩余 50% 仓位在 1.5:1 盈亏比止盈
```
第二目标价 = 原始止盈价(基于止损距离 × 1.5
第二目标 = 盈亏比 1.5:1相对于剩余仓位的保证金
```
**触发条件**
- 剩余仓位盈亏百分比(基于剩余保证金)≥ 1.5 × 止损百分比
- 平仓剩余 50% 仓位
## 📈 胜率要求分析
### 理论盈亏比计算
假设:
- 止损损失:-1 单位(基于保证金)
- 第一目标盈利50%仓位):+1 单位1:1
- 第二目标盈利50%仓位):+1.5 单位1.5:1
**完整交易期望收益**
- 如果第一目标触发(概率 P1第二目标也触发概率 P2
- 总盈利 = 0.5 × 1 + 0.5 × 1.5 = **1.25 单位**
- 如果第一目标触发,但第二目标未触发(概率 P1 × (1-P2)
- 总盈利 = 0.5 × 1 + 0.5 × 0 = **0.5 单位**
- 如果第一目标未触发,直接止损:
- 总损失 = **-1 单位**
### 盈亏平衡点计算
**最理想情况**第一目标100%触发第二目标100%触发):
```
胜率 × 1.25 = 败率 × 1
胜率 × 1.25 = (1 - 胜率) × 1
胜率 × 2.25 = 1
胜率 = 44.4%
```
**保守情况**第一目标100%触发第二目标50%触发):
```
平均盈利 = 0.5 × 1.25 + 0.5 × 0.5 = 0.875 单位
胜率 × 0.875 = (1 - 胜率) × 1
胜率 × 1.875 = 1
胜率 = 53.3%
```
**最保守情况**第一目标100%触发第二目标0%触发):
```
平均盈利 = 0.5 单位
胜率 × 0.5 = (1 - 胜率) × 1
胜率 × 1.5 = 1
胜率 = 66.7%
```
### 实际胜率要求评估
**关键因素**
1. **第一目标触发率**1:1 盈亏比相对容易触发(预期 60-70%
2. **第二目标触发率**1.5:1 盈亏比需要趋势延续(预期 40-50%
3. **保本保护**:第一目标触发后,剩余仓位止损移至入场价,**彻底杜绝亏损可能**
**实际期望**
- 如果第一目标触发率 = 65%,第二目标触发率 = 45%
- 平均盈利 = 0.65 × (0.45 × 1.25 + 0.55 × 0.5) = **0.65 × 0.8375 = 0.544 单位**
- 盈亏平衡点:胜率 × 0.544 = (1 - 胜率) × 1
- **胜率 = 64.8%**
## ⚠️ 潜在问题分析
### 1. 胜率要求较高
**问题**:如果第一目标触发率低,或第二目标触发率低,需要更高的胜率才能盈利。
**缓解措施**
- ✅ 分步止盈确保至少锁定部分利润
- ✅ 保本保护确保第一目标触发后不会亏损
- ✅ 最小持仓时间锁30分钟避免过早平仓
- ⚠️ **需要监控实际第一/第二目标触发率**
### 2. ATR_TAKE_PROFIT_MULTIPLIER 与 RISK_REWARD_RATIO 的关系
**当前逻辑**
- 优先使用 `止损距离 × RISK_REWARD_RATIO (1.5)` 计算止盈
- `ATR_TAKE_PROFIT_MULTIPLIER (1.5)` 仅作为备选(当无止损距离时)
**潜在问题**
- 如果 ATR 很小,`ATR_TAKE_PROFIT_MULTIPLIER` 可能计算出过小的止盈距离
- 但 `MIN_TAKE_PROFIT_PRICE_PCT (2%)` 提供了保护
**建议**
- ✅ 当前逻辑合理,`ATR_TAKE_PROFIT_MULTIPLIER` 主要作为备选
- ✅ `MIN_TAKE_PROFIT_PRICE_PCT` 确保最小止盈距离
### 3. 分步止盈的保本逻辑
**当前实现**
- 第一目标触发后,剩余仓位止损移至入场价(保本)
- **无论 `USE_TRAILING_STOP` 是否启用,都会移至保本**
**优势**
- ✅ 彻底杜绝第一目标触发后的亏损可能
- ✅ 剩余仓位可以追求更高收益
**潜在问题**
- ⚠️ 如果价格在入场价附近震荡,可能频繁触发保本止损
- ⚠️ 但这是可接受的因为已经锁定了50%的利润
### 4. 止盈价选择逻辑
**当前实现**:取所有方法中最宽松(最远)的价格
**潜在问题**
- 如果 `TAKE_PROFIT_PERCENT (25%)` 计算出的止盈价很远,可能难以触发
- 但 ATR 方法通常会给出更合理的价格
**建议**
- ✅ 当前逻辑合理,优先使用 ATR 方法
- ⚠️ 需要监控实际止盈触发率
## 📋 策略逻辑流程图
```
开仓
计算止损ATR × 1.8
计算止盈(止损距离 × 1.5 或 ATR × 1.5
设置第一目标1:1 盈亏比50%仓位)
设置第二目标1.5:1 盈亏比剩余50%仓位)
监控持仓
├─→ 触发止损 → 平仓(损失 -1 单位)
├─→ 触发第一目标 → 平仓50% → 止损移至保本 → 继续监控
│ │
│ └─→ 触发第二目标 → 平仓剩余50%(总盈利 1.25 单位)
│ └─→ 触发保本止损 → 平仓剩余50%(总盈利 0.5 单位)
└─→ 最小持仓时间未到 → 继续监控
```
## 🎯 优化建议
### 1. 监控关键指标
- **第一目标触发率**:目标 ≥ 60%
- **第二目标触发率**:目标 ≥ 40%
- **实际盈亏比**:目标 ≥ 1.2
- **盈利因子**:目标 ≥ 1.1
### 2. 如果胜率不足
**选项A**:提高第一目标触发率
- 降低第一目标到 0.8:1 盈亏比
- 但会降低平均盈利
**选项B**:提高第二目标触发率
- 降低第二目标到 1.2:1 盈亏比
- 但会降低平均盈利
**选项C**:提高入场信号质量
- 提高 `MIN_SIGNAL_STRENGTH`(当前 8
- 仅在 `marketRegime=trending` 时交易
- 提高 `MIN_SIGNAL_STRENGTH` 到 9 或 10
### 3. 如果第一目标触发率低
- 检查是否因为最小持仓时间锁导致过早平仓
- 检查止损是否过紧ATR_STOP_LOSS_MULTIPLIER = 1.8 是否合理)
- 考虑降低第一目标到 0.9:1
### 4. 如果第二目标触发率低
- 检查止盈价是否过远
- 考虑降低第二目标到 1.3:1 或 1.2:1
- 但需要权衡:降低目标会降低平均盈利
## ✅ 总结
### 当前策略的优势
1. ✅ **分步止盈**:锁定部分利润,降低风险
2. ✅ **保本保护**:第一目标触发后不会亏损
3. ✅ **动态止损**:基于 ATR适应市场波动
4. ✅ **最小持仓时间**:避免过早平仓
### 当前策略的挑战
1. ⚠️ **胜率要求**:需要 45-65% 胜率(取决于第二目标触发率)
2. ⚠️ **第二目标触发率**:需要趋势延续,可能较低
3. ⚠️ **需要监控**:实际触发率可能与理论不符
### 建议
1. **先运行观察**:收集实际数据(第一/第二目标触发率、实际盈亏比)
2. **根据数据调整**
- 如果第一目标触发率 < 60%考虑降低到 0.9:1
- 如果第二目标触发率 < 40%考虑降低到 1.3:1
- 如果胜率 < 50%提高入场信号质量
3. **目标指标**
- 第一目标触发率 ≥ 60%
- 第二目标触发率 ≥ 40%
- 实际盈亏比 ≥ 1.2
- 盈利因子 ≥ 1.1

View File

@ -0,0 +1,109 @@
# 止盈时间锁分析与优化建议
## 🤔 问题:止盈时间锁是否有必要?
### 当前情况
- ✅ **止损**:已修复,不受时间锁限制,立即执行
- ⚠️ **止盈**仍然受30分钟时间锁限制
### 止盈时间锁的利弊分析
#### ✅ 支持保留的理由(原始设计意图)
1. **防止过早止盈**
- 避免价格刚达到止盈目标就立即平仓
- 给趋势更多时间发展,追求更大利润
- 符合"让利润奔跑"的交易理念
2. **避免分钟级平仓**
- 防止因短期波动触发止盈
- 强制波段持仓纪律
- 减少频繁交易成本
3. **配合分步止盈策略**
- 第一目标1:1在30分钟后才能触发
- 给市场更多时间达到第二目标1.5:1
#### ❌ 反对保留的理由(实际问题)
1. **错过最佳止盈时机**
- 如果价格在30分钟内达到止盈目标但之后回落
- 可能从盈利变成亏损
- **对于小众币价格波动剧烈30分钟可能错过最佳退出点**
2. **与交易所级别止盈单冲突**
- 币安交易所级别的止盈单不受时间锁限制
- 如果交易所止盈单触发,但本地监控被时间锁阻止,可能造成不一致
3. **降低资金效率**
- 资金被锁定30分钟即使已经达到目标
- 无法及时释放资金用于新机会
4. **实际案例**
- 用户反馈亏损严重,可能也与止盈不及时有关
- 如果止盈能及时执行,可能减少亏损
## 📊 数据驱动的决策建议
### 方案A完全移除止盈时间锁推荐
**优点**
- ✅ 止盈立即执行,不错过最佳退出点
- ✅ 与交易所级别止盈单一致
- ✅ 提高资金效率
- ✅ 减少因价格回落导致的利润回吐
**缺点**
- ❌ 可能过早止盈,错过更大利润
- ❌ 可能因短期波动触发止盈
**适用场景**
- 小众币(波动剧烈,需要及时止盈)
- 短期交易策略
- 追求稳定收益而非最大化利润
### 方案B缩短时间锁折中方案
**建议**将30分钟缩短到5-10分钟
**优点**
- ✅ 保留防止过早止盈的保护
- ✅ 减少错过最佳退出点的风险
- ✅ 平衡利润最大化与及时止盈
**缺点**
- ❌ 仍然可能错过最佳退出点
- ❌ 需要测试确定最佳时长
### 方案C保留但可配置灵活方案
**建议**:将时间锁设为可配置,默认值降低
**优点**
- ✅ 灵活性高,可根据市场调整
- ✅ 可以针对不同币种设置不同值
- ✅ 保留原始设计意图
**缺点**
- ❌ 增加配置复杂度
- ❌ 需要用户理解并正确配置
## 🎯 推荐方案:完全移除止盈时间锁 ✅ 已实施
### 理由
1. **止损已不受限制**:如果止损可以立即执行,止盈也应该可以
2. **交易所级别保护**:币安交易所级别的止盈单已经提供保护
3. **分步止盈策略**分步止盈本身已经提供了利润保护50%在1:1止盈剩余保本
4. **实际需求**:用户反馈亏损严重,需要及时止盈保护利润
### ✅ 已实施
1. ✅ **完全移除**:已移除所有止盈时间锁限制
2. ✅ **保留分步止盈**:分步止盈策略仍然有效,提供利润保护
3. ✅ **依赖交易所级别止盈单**:主要依赖币安交易所级别的止盈单
4. ✅ **修复位置**
- `check_stop_loss_take_profit()` - 定期检查
- `_check_single_position()` - WebSocket实时监控两处
## 📈 预期效果
移除止盈时间锁后:
- ✅ 止盈能及时执行,保护利润
- ✅ 减少因价格回落导致的利润回吐
- ✅ 提高资金效率
- ✅ 与止损逻辑一致(都不受时间锁限制)
- ⚠️ 可能错过一些更大利润的机会(但分步止盈策略会部分补偿)

127
TRADING_FLOW_ANALYSIS.md Normal file
View File

@ -0,0 +1,127 @@
# 交易流程分析与优化方案
## 🔴 当前严重问题亏损达到30%以上
### 问题分析
根据最近的交易记录:
- CLOUSDT SELL: -17.54% (手动平仓)
- ICNTUSDT BUY: -19.60% (手动平仓)
- 0GUSDT BUY: -31.34% (手动平仓)
- ALCHUSDT BUY: -30.95% (同步平仓)
**核心问题止损没有及时触发导致亏损远超止损设置通常止损设置为8-10%**
### 根本原因
1. **最小持仓时间锁阻止止损触发** ⚠️ **最严重**
- `MIN_HOLD_TIME_SEC = 1800秒30分钟`
- 在持仓前30分钟内即使触发止损系统也会**禁止平仓**
- 这导致止损单无法执行,亏损持续扩大
- **对于小众币30分钟内价格可能剧烈波动亏损可能达到30%以上**
2. **交易所级别止损单可能未正确挂单**
- 如果 `_ensure_exchange_sltp_orders` 失败,只有本地监控
- 本地监控被时间锁阻止,无法平仓
3. **止损检查逻辑在时间锁之后**
- 代码顺序:先检查时间锁 → 如果不足30分钟直接 `continue`/`return`
- 止损检查逻辑永远不会执行
## 📊 当前交易流程
### 开仓流程
1. 市场扫描每30分钟
2. 信号筛选MIN_SIGNAL_STRENGTH >= 8
3. 计算止损止盈基于ATR或保证金
4. 挂限价单开仓
5. 订单成交后:
- 保存交易记录到数据库
- 在币安挂止损/止盈保护单(`_ensure_exchange_sltp_orders`
- 启动WebSocket实时监控
### 平仓流程(当前有严重问题)
#### 方式1交易所级别止损/止盈单(最可靠)
- 币安自动触发,不受时间锁影响
- **但如果挂单失败,就没有保护**
#### 方式2本地监控检查被时间锁阻止
- `check_stop_loss_take_profit()` 定期检查
- `_check_single_position()` WebSocket实时监控
- **都被 `MIN_HOLD_TIME_SEC` 阻止前30分钟无法平仓**
## ✅ 优化方案(已实施)
### 1. ✅ 完全移除最小持仓时间锁(已修复)
**问题**:时间锁阻止止损和止盈,导致亏损扩大和利润回吐
**解决方案**:✅ **完全移除时间锁限制**
- ✅ 止损检查在时间锁之前执行,立即平仓
- ✅ 止盈也立即执行,不受时间锁限制
- ✅ 止损和止盈逻辑一致,都立即执行
- ✅ 修复了三个位置:`check_stop_loss_take_profit()`、`_check_single_position()` 和移动止损检查
**移除理由**
1. 止损和止盈都应该立即执行,保护资金和利润
2. 交易所级别的止损/止盈单已提供保护
3. 分步止盈策略本身已提供利润保护50%在1:1止盈剩余保本
4. 及时执行可以避免价格回落导致的利润回吐
5. 如果需要防止秒级平仓可以通过提高入场信号质量MIN_SIGNAL_STRENGTH来实现
### 2. 确保交易所级别止损单正确挂单
- 增加日志,记录挂单成功/失败
- 如果挂单失败,重试机制
- 定期检查并补挂止损单
### 3. 优化止损逻辑
- 止损检查应该在时间锁之前如果采用选项B
- 或者完全移除时间锁对止损的限制
### 4. 针对小众币的优化
- 提高最小成交量要求(避免流动性差的币)
- 增大止损距离ATR倍数以应对高波动
- 降低杠杆倍数(降低风险)
## 🎯 具体修复建议
### 立即修复(高优先级)
1. **移除时间锁对止损的限制**
- 止损应该立即执行,不受时间锁影响
- 时间锁只应用于止盈(防止过早止盈)
2. **增强止损单挂单可靠性**
- 增加重试机制
- 增加失败告警
- 定期检查并补挂
3. **优化止损检查逻辑**
- 确保止损检查在时间锁之前(如果保留时间锁)
- 或者完全移除时间锁
### 中期优化
1. **提高入场信号质量**
- 提高 `MIN_SIGNAL_STRENGTH` 到 9-10
- 只交易高质量信号
2. **优化止损距离**
- 对于小众币使用更大的ATR倍数2.0-2.5
- 确保止损距离足够,不会被正常波动触发
3. **降低杠杆**
- 对于小众币降低杠杆到5-8倍
- 降低单笔仓位到5%
## 📈 预期效果
修复后:
- ✅ 止损能及时触发亏损控制在8-10%以内
- ✅ 不会出现30%以上的大额亏损
- ✅ 胜率提升(及时止损,避免大亏)
- ✅ 盈亏比改善(小亏大赚)

View File

@ -1185,37 +1185,6 @@ class PositionManager:
else: else:
current_price = entry_price current_price = entry_price
# 最小持仓时间锁(强制波段持仓纪律,避免分钟级平仓)
min_hold_sec = int(config.TRADING_CONFIG.get('MIN_HOLD_TIME_SEC', 1800) or 1800)
entry_time = position_info.get('entryTime')
hold_time_sec = 0
hold_time_minutes = 0
if entry_time:
try:
if isinstance(entry_time, datetime):
hold_time_sec = int((get_beijing_time() - entry_time).total_seconds())
else:
# 兼容:如果是时间戳或字符串
hold_time_sec = int(time.time() - (float(entry_time) if isinstance(entry_time, (int, float)) else 0))
hold_time_minutes = hold_time_sec / 60.0
except Exception:
hold_time_sec = 0
hold_time_minutes = 0
# 如果持仓时间不足,禁止平仓(除非是手动平仓)
if hold_time_sec < min_hold_sec:
remaining_sec = min_hold_sec - hold_time_sec
remaining_minutes = remaining_sec / 60.0
logger.warning(
f"{symbol} [持仓时间锁] ⚠ 持仓时间不足,禁止自动平仓: "
f"已持仓 {hold_time_minutes:.1f} 分钟 ({hold_time_sec}s) < "
f"最小要求 {min_hold_sec/60:.1f} 分钟 ({min_hold_sec}s) | "
f"还需等待 {remaining_minutes:.1f} 分钟 ({remaining_sec}s) | "
f"入场时间: {entry_time} | "
f"强制波段持仓纪律,避免分钟级平仓"
)
continue # 跳过这个持仓,不触发任何平仓逻辑
# 计算当前盈亏(基于保证金) # 计算当前盈亏(基于保证金)
leverage = position_info.get('leverage', 10) leverage = position_info.get('leverage', 10)
position_value = entry_price * quantity position_value = entry_price * quantity
@ -1337,7 +1306,11 @@ class PositionManager:
) )
# 检查止损(使用更新后的止损价,基于保证金收益比) # 检查止损(使用更新后的止损价,基于保证金收益比)
# ⚠️ 重要:止损检查应该在时间锁之前,止损必须立即执行
stop_loss = position_info.get('stopLoss') stop_loss = position_info.get('stopLoss')
should_close_due_to_sl = False
exit_reason_sl = None
if stop_loss is None: if stop_loss is None:
logger.warning(f"{symbol} 止损价未设置,跳过止损检查") logger.warning(f"{symbol} 止损价未设置,跳过止损检查")
elif stop_loss is not None: elif stop_loss is not None:
@ -1351,8 +1324,8 @@ class PositionManager:
# 直接比较当前盈亏百分比与止损目标(基于保证金) # 直接比较当前盈亏百分比与止损目标(基于保证金)
if pnl_percent_margin <= -stop_loss_pct_margin: if pnl_percent_margin <= -stop_loss_pct_margin:
# 确定平仓原因 should_close_due_to_sl = True
exit_reason = 'trailing_stop' if position_info.get('trailingStopActivated') else 'stop_loss' exit_reason_sl = 'trailing_stop' if position_info.get('trailingStopActivated') else 'stop_loss'
# 计算持仓时间 # 计算持仓时间
entry_time = position_info.get('entryTime') entry_time = position_info.get('entryTime')
@ -1370,7 +1343,7 @@ class PositionManager:
# 详细诊断日志:记录平仓时的所有关键信息 # 详细诊断日志:记录平仓时的所有关键信息
logger.warning("=" * 80) logger.warning("=" * 80)
logger.warning(f"{symbol} [平仓诊断日志] ===== 触发止损平仓 =====") logger.warning(f"{symbol} [平仓诊断日志] ===== 触发止损平仓 =====")
logger.warning(f" 平仓原因: {exit_reason}") logger.warning(f" 平仓原因: {exit_reason_sl}")
logger.warning(f" 入场价格: {entry_price:.6f} USDT") logger.warning(f" 入场价格: {entry_price:.6f} USDT")
logger.warning(f" 当前价格: {current_price:.4f} USDT") logger.warning(f" 当前价格: {current_price:.4f} USDT")
logger.warning(f" 止损价格: {stop_loss:.4f} USDT") logger.warning(f" 止损价格: {stop_loss:.4f} USDT")
@ -1383,6 +1356,9 @@ class PositionManager:
if position_info.get('trailingStopActivated'): if position_info.get('trailingStopActivated'):
logger.warning(f" 移动止损: 已激活(从初始止损 {position_info.get('initialStopLoss', 'N/A')} 调整)") logger.warning(f" 移动止损: 已激活(从初始止损 {position_info.get('initialStopLoss', 'N/A')} 调整)")
logger.warning("=" * 80) logger.warning("=" * 80)
# 止损必须立即执行,不受时间锁限制
# 更新数据库
# 更新数据库 # 更新数据库
if DB_AVAILABLE: if DB_AVAILABLE:
trade_id = position_info.get('tradeId') trade_id = position_info.get('tradeId')
@ -1417,17 +1393,24 @@ class PositionManager:
) )
except Exception as e: except Exception as e:
logger.warning(f"更新止损记录失败: {e}") logger.warning(f"更新止损记录失败: {e}")
if await self.close_position(symbol, reason=exit_reason): # ⚠️ 关键修复:止损必须立即执行,不受时间锁限制
if await self.close_position(symbol, reason=exit_reason_sl):
closed_positions.append(symbol) closed_positions.append(symbol)
continue continue # 止损已执行,跳过后续止盈检查
# 检查分步止盈(基于保证金收益比) # 检查分步止盈(基于保证金收益比)
# ⚠️ 优化:已移除止盈时间锁,止盈可以立即执行(与止损一致)
# 理由1) 止损已不受时间锁限制,止盈也应该一致
# 2) 分步止盈策略本身已提供利润保护50%在1:1止盈剩余保本
# 3) 交易所级别止盈单已提供保护
# 4) 及时止盈可以保护利润,避免价格回落
take_profit_1 = position_info.get('takeProfit1') # 第一目标盈亏比1:1 take_profit_1 = position_info.get('takeProfit1') # 第一目标盈亏比1:1
take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标 take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标
partial_profit_taken = position_info.get('partialProfitTaken', False) partial_profit_taken = position_info.get('partialProfitTaken', False)
remaining_quantity = position_info.get('remainingQuantity', quantity) remaining_quantity = position_info.get('remainingQuantity', quantity)
# 第一目标盈亏比1:1了结50%仓位 # 第一目标盈亏比1:1了结50%仓位
# ✅ 已移除时间锁限制,可以立即执行
if not partial_profit_taken and take_profit_1 is not None: if not partial_profit_taken and take_profit_1 is not None:
# 计算第一目标对应的保证金百分比 # 计算第一目标对应的保证金百分比
if position_info['side'] == 'BUY': if position_info['side'] == 'BUY':
@ -1497,6 +1480,7 @@ class PositionManager:
logger.error(f"{symbol} 部分止盈失败: {e}") logger.error(f"{symbol} 部分止盈失败: {e}")
# 第二目标:原始止盈价,平掉剩余仓位(基于保证金收益比) # 第二目标:原始止盈价,平掉剩余仓位(基于保证金收益比)
# ✅ 已移除时间锁限制,可以立即执行
if partial_profit_taken and take_profit_2 is not None: if partial_profit_taken and take_profit_2 is not None:
# 计算第二目标对应的保证金百分比 # 计算第二目标对应的保证金百分比
if position_info['side'] == 'BUY': if position_info['side'] == 'BUY':
@ -1566,6 +1550,7 @@ class PositionManager:
continue continue
else: else:
# 如果未部分止盈,但达到止盈目标,直接全部平仓(基于保证金收益比) # 如果未部分止盈,但达到止盈目标,直接全部平仓(基于保证金收益比)
# ✅ 已移除时间锁限制,可以立即执行
take_profit = position_info.get('takeProfit') take_profit = position_info.get('takeProfit')
if take_profit is not None: if take_profit is not None:
# 计算止盈对应的保证金百分比 # 计算止盈对应的保证金百分比
@ -2431,36 +2416,12 @@ class PositionManager:
except Exception as e: except Exception as e:
logger.debug(f"从Redis重新加载配置失败: {e}") logger.debug(f"从Redis重新加载配置失败: {e}")
# 最小持仓时间锁(强制波段持仓纪律,避免分钟级平仓) # ⚠️ 优化:已完全移除时间锁限制
min_hold_sec = int(config.TRADING_CONFIG.get('MIN_HOLD_TIME_SEC', 1800) or 1800) # 理由1) 止损和止盈都应该立即执行,不受时间限制
entry_time = position_info.get('entryTime') # 2) 交易所级别的止损/止盈单已提供保护
hold_time_sec = 0 # 3) 分步止盈策略本身已提供利润保护
hold_time_minutes = 0 # 4) 及时执行止损/止盈可以保护资金和利润
if entry_time: # 注意如果需要防止秒级平仓可以通过提高入场信号质量MIN_SIGNAL_STRENGTH来实现
try:
if isinstance(entry_time, datetime):
hold_time_sec = int((get_beijing_time() - entry_time).total_seconds())
else:
# 兼容:如果是时间戳或字符串
hold_time_sec = int(time.time() - (float(entry_time) if isinstance(entry_time, (int, float)) else 0))
hold_time_minutes = hold_time_sec / 60.0
except Exception:
hold_time_sec = 0
hold_time_minutes = 0
# 如果持仓时间不足,禁止平仓(除非是手动平仓)
if hold_time_sec < min_hold_sec:
remaining_sec = min_hold_sec - hold_time_sec
remaining_minutes = remaining_sec / 60.0
logger.warning(
f"{symbol} [实时监控-持仓时间锁] ⚠ 持仓时间不足,禁止自动平仓: "
f"已持仓 {hold_time_minutes:.1f} 分钟 ({hold_time_sec}s) < "
f"最小要求 {min_hold_sec/60:.1f} 分钟 ({min_hold_sec}s) | "
f"还需等待 {remaining_minutes:.1f} 分钟 ({remaining_sec}s) | "
f"入场时间: {entry_time} | "
f"强制波段持仓纪律,避免分钟级平仓"
)
return # 不触发任何平仓逻辑
# 检查是否启用移动止损默认False需要显式启用 # 检查是否启用移动止损默认False需要显式启用
use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', False) use_trailing = config.TRADING_CONFIG.get('USE_TRAILING_STOP', False)
@ -2512,32 +2473,11 @@ class PositionManager:
f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)" f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
) )
# 最小持仓时间锁(强制波段持仓纪律,避免分钟级平仓)
min_hold_sec = int(config.TRADING_CONFIG.get('MIN_HOLD_TIME_SEC', 1800) or 1800)
entry_time = position_info.get('entryTime')
hold_time_sec = 0
if entry_time:
try:
if isinstance(entry_time, datetime):
hold_time_sec = int((get_beijing_time() - entry_time).total_seconds())
else:
# 兼容:如果是时间戳或字符串
hold_time_sec = int(time.time() - (float(entry_time) if isinstance(entry_time, (int, float)) else 0))
except Exception:
hold_time_sec = 0
# 如果持仓时间不足,禁止平仓(除非是手动平仓)
if hold_time_sec < min_hold_sec:
logger.debug(
f"{symbol} [持仓时间锁] 持仓时间 {hold_time_sec}s < 最小要求 {min_hold_sec}s"
f"禁止自动平仓(强制波段持仓纪律)"
)
return # 不触发任何平仓逻辑
# 检查止损(基于保证金收益比) # 检查止损(基于保证金收益比)
# ⚠️ 重要:止损检查应该在时间锁之前,止损必须立即执行
stop_loss = position_info.get('stopLoss') stop_loss = position_info.get('stopLoss')
should_close = False should_close_due_to_sl = False
exit_reason = None exit_reason_sl = None
if stop_loss is not None: if stop_loss is not None:
# 计算止损对应的保证金百分比目标 # 计算止损对应的保证金百分比目标
@ -2566,13 +2506,26 @@ class PositionManager:
# 直接比较当前盈亏百分比与止损目标(基于保证金) # 直接比较当前盈亏百分比与止损目标(基于保证金)
if pnl_percent_margin <= -stop_loss_pct_margin: if pnl_percent_margin <= -stop_loss_pct_margin:
should_close = True should_close_due_to_sl = True
exit_reason = 'trailing_stop' if position_info.get('trailingStopActivated') else 'stop_loss' exit_reason_sl = 'trailing_stop' if position_info.get('trailingStopActivated') else 'stop_loss'
# 计算持仓时间
entry_time = position_info.get('entryTime')
hold_time_minutes = 0
if entry_time:
try:
if isinstance(entry_time, datetime):
hold_time_sec = int((get_beijing_time() - entry_time).total_seconds())
else:
hold_time_sec = int(time.time() - (float(entry_time) if isinstance(entry_time, (int, float)) else 0))
hold_time_minutes = hold_time_sec / 60.0
except Exception:
hold_time_minutes = 0
# 详细诊断日志:记录平仓时的所有关键信息 # 详细诊断日志:记录平仓时的所有关键信息
logger.warning("=" * 80) logger.warning("=" * 80)
logger.warning(f"{symbol} [实时监控-平仓诊断日志] ===== 触发止损平仓 =====") logger.warning(f"{symbol} [实时监控-平仓诊断日志] ===== 触发止损平仓 =====")
logger.warning(f" 平仓原因: {exit_reason}") logger.warning(f" 平仓原因: {exit_reason_sl}")
logger.warning(f" 入场价格: {entry_price:.6f} USDT") logger.warning(f" 入场价格: {entry_price:.6f} USDT")
logger.warning(f" 当前价格: {current_price_float:.6f} USDT") logger.warning(f" 当前价格: {current_price_float:.6f} USDT")
logger.warning(f" 止损价格: {stop_loss:.4f} USDT") logger.warning(f" 止损价格: {stop_loss:.4f} USDT")
@ -2586,8 +2539,18 @@ class PositionManager:
logger.warning(f" 移动止损: 已激活(从初始止损 {position_info.get('initialStopLoss', 'N/A')} 调整)") logger.warning(f" 移动止损: 已激活(从初始止损 {position_info.get('initialStopLoss', 'N/A')} 调整)")
logger.warning("=" * 80) logger.warning("=" * 80)
# ⚠️ 关键修复:止损必须立即执行,不受时间锁限制
if await self.close_position(symbol, reason=exit_reason_sl):
logger.info(f"{symbol} [实时监控] 止损平仓成功(不受时间锁限制)")
return # 止损已执行,跳过后续止盈检查
# 检查分步止盈(实时监控) # 检查分步止盈(实时监控)
if not should_close: # ⚠️ 优化:已移除止盈时间锁,止盈可以立即执行(与止损一致)
# 理由1) 止损已不受时间锁限制,止盈也应该一致
# 2) 分步止盈策略本身已提供利润保护50%在1:1止盈剩余保本
# 3) 交易所级别止盈单已提供保护
# 4) 及时止盈可以保护利润,避免价格回落
should_close = False
take_profit_1 = position_info.get('takeProfit1') # 第一目标盈亏比1:1 take_profit_1 = position_info.get('takeProfit1') # 第一目标盈亏比1:1
take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标1.5:1 take_profit_2 = position_info.get('takeProfit2', position_info.get('takeProfit')) # 第二目标1.5:1
partial_profit_taken = position_info.get('partialProfitTaken', False) partial_profit_taken = position_info.get('partialProfitTaken', False)