a
This commit is contained in:
parent
5d0b0a9974
commit
ff22683da2
215
OPTIMIZATION_SUMMARY.md
Normal file
215
OPTIMIZATION_SUMMARY.md
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# 交易系统优化总结
|
||||
|
||||
## 优化完成时间
|
||||
2026-01-17
|
||||
|
||||
## 一、已完成的优化
|
||||
|
||||
### ✅ 1. 调整止损止盈参数(第一步:最容易见效)
|
||||
|
||||
**修改内容**:
|
||||
- `STOP_LOSS_PERCENT`: 0.08 → **0.10** (10%)
|
||||
- `TAKE_PROFIT_PERCENT`: 0.15 → **0.20** (20%)
|
||||
- `TRAILING_STOP_ACTIVATION`: 0.01 → **0.05** (5%)
|
||||
- `TRAILING_STOP_PROTECT`: 0.01 → **0.03** (3%)
|
||||
|
||||
**修改文件**:
|
||||
- `trading_system/config.py`
|
||||
- `backend/config_manager.py`
|
||||
- `backend/database/init.sql`
|
||||
|
||||
**预期效果**:
|
||||
- 止损更宽松,避免被小幅波动触发
|
||||
- 止盈提高,提高盈亏比(从1:1.875提升到1:2)
|
||||
- 移动止损激活更晚,给趋势更多空间
|
||||
- 保护利润更合理,避免过早退出
|
||||
|
||||
### ✅ 2. 简化策略:移除均值回归,只保留趋势跟踪(第二步)
|
||||
|
||||
**修改内容**:
|
||||
- 移除震荡市场(ranging)的均值回归策略
|
||||
- 移除RSI、布林带等震荡指标
|
||||
- 只使用趋势跟踪指标:
|
||||
- MACD金叉/死叉(权重5)
|
||||
- EMA20上穿/下穿EMA50(权重4)
|
||||
- 价格在EMA20之上/下(权重3)
|
||||
- 4H趋势确认(权重2)
|
||||
|
||||
**修改文件**:
|
||||
- `trading_system/strategy.py` - `_analyze_trade_signal` 方法
|
||||
|
||||
**预期效果**:
|
||||
- 避免信号冲突(RSI超买但MACD死叉等)
|
||||
- 策略逻辑更清晰,更容易维护
|
||||
- 减少频繁开仓又平仓的情况
|
||||
|
||||
### ✅ 3. 优化4H趋势判断:使用多指标投票机制(第三步)
|
||||
|
||||
**修改内容**:
|
||||
- 新增 `_judge_trend_4h` 方法
|
||||
- 使用多指标投票:
|
||||
- 价格 vs EMA20
|
||||
- EMA20 vs EMA50
|
||||
- MACD histogram
|
||||
- 至少需要2个指标确认才判断趋势方向
|
||||
|
||||
**修改文件**:
|
||||
- `trading_system/strategy.py` - 新增 `_judge_trend_4h` 方法
|
||||
|
||||
**预期效果**:
|
||||
- 避免单一指标误导(如只用EMA20)
|
||||
- 在震荡行情中减少频繁切换方向
|
||||
- 提高趋势判断的准确性
|
||||
|
||||
### ✅ 4. 添加交易日志与统计功能(第四步)
|
||||
|
||||
**修改内容**:
|
||||
- 数据库表添加字段:
|
||||
- `strategy_type` VARCHAR(50) - 策略类型
|
||||
- `duration_minutes` INT - 持仓持续时间(分钟)
|
||||
- 更新 `Trade.update_exit` 方法,支持新字段
|
||||
- 在开仓时记录 `entryTime` 和 `strategyType`
|
||||
- 在平仓时计算并记录 `duration_minutes`
|
||||
|
||||
**修改文件**:
|
||||
- `backend/database/add_trade_statistics.sql` - 新增SQL脚本
|
||||
- `backend/database/models.py` - 更新 `update_exit` 方法
|
||||
- `trading_system/position_manager.py` - 记录和计算统计信息
|
||||
|
||||
**预期效果**:
|
||||
- 可以统计不同策略类型的表现
|
||||
- 可以分析持仓持续时间
|
||||
- 为后续优化提供数据支持
|
||||
|
||||
## 二、配置参数对比
|
||||
|
||||
### 优化前
|
||||
```python
|
||||
STOP_LOSS_PERCENT = 0.08 # 8%
|
||||
TAKE_PROFIT_PERCENT = 0.15 # 15%
|
||||
TRAILING_STOP_ACTIVATION = 0.01 # 1%
|
||||
TRAILING_STOP_PROTECT = 0.01 # 1%
|
||||
MIN_SIGNAL_STRENGTH = 3 # 信号强度阈值较低
|
||||
```
|
||||
|
||||
### 优化后
|
||||
```python
|
||||
STOP_LOSS_PERCENT = 0.10 # 10% ✅
|
||||
TAKE_PROFIT_PERCENT = 0.20 # 20% ✅
|
||||
TRAILING_STOP_ACTIVATION = 0.05 # 5% ✅
|
||||
TRAILING_STOP_PROTECT = 0.03 # 3% ✅
|
||||
MIN_SIGNAL_STRENGTH = 7 # 信号强度阈值提高 ✅
|
||||
```
|
||||
|
||||
## 三、策略逻辑对比
|
||||
|
||||
### 优化前
|
||||
- **双策略**:均值回归(震荡市场)+ 趋势跟踪(趋势市场)
|
||||
- **多指标**:RSI、MACD、布林带、均线系统
|
||||
- **4H趋势判断**:只用EMA20
|
||||
- **信号冲突**:可能出现RSI超买但MACD死叉的情况
|
||||
|
||||
### 优化后
|
||||
- **单策略**:只做趋势跟踪
|
||||
- **趋势指标**:MACD、EMA系统
|
||||
- **4H趋势判断**:多指标投票(价格、EMA20、EMA50、MACD)
|
||||
- **信号清晰**:避免冲突,逻辑更简单
|
||||
|
||||
## 四、数据库变更
|
||||
|
||||
### 新增字段
|
||||
```sql
|
||||
ALTER TABLE `trades`
|
||||
ADD COLUMN `strategy_type` VARCHAR(50) COMMENT '策略类型: trend_following, mean_reversion',
|
||||
ADD COLUMN `duration_minutes` INT COMMENT '持仓持续时间(分钟)';
|
||||
```
|
||||
|
||||
### 执行方式
|
||||
运行SQL脚本:
|
||||
```bash
|
||||
mysql -u your_user -p auto_trade_sys < backend/database/add_trade_statistics.sql
|
||||
```
|
||||
|
||||
## 五、使用建议
|
||||
|
||||
### 1. 数据库更新
|
||||
如果数据库已存在,需要执行SQL脚本添加新字段:
|
||||
```bash
|
||||
cd /path/to/project
|
||||
mysql -u your_user -p auto_trade_sys < backend/database/add_trade_statistics.sql
|
||||
```
|
||||
|
||||
### 2. 重启服务
|
||||
修改配置后,需要重启交易系统以应用新配置:
|
||||
```bash
|
||||
# 重启交易系统
|
||||
cd trading_system
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 3. 监控效果
|
||||
- 观察止损止盈触发频率是否降低
|
||||
- 观察信号质量是否提高(减少无效交易)
|
||||
- 观察4H趋势判断是否更稳定
|
||||
- 查看交易统计,分析策略表现
|
||||
|
||||
## 六、后续优化建议
|
||||
|
||||
### 短期(1-2周)
|
||||
1. **观察数据**:收集至少1周的交易数据
|
||||
2. **分析统计**:查看胜率、盈亏比、持仓时间等
|
||||
3. **微调参数**:根据实际表现调整止损止盈参数
|
||||
|
||||
### 中期(1个月)
|
||||
1. **策略回测**:添加历史数据回测功能
|
||||
2. **参数优化**:使用回测数据优化参数
|
||||
3. **性能优化**:优化WebSocket连接(使用组合流)
|
||||
|
||||
### 长期(3个月+)
|
||||
1. **多策略支持**:如果趋势跟踪效果好,可以考虑重新引入均值回归
|
||||
2. **机器学习**:使用历史数据训练模型,优化信号强度计算
|
||||
3. **风险模型**:根据市场波动率动态调整止损止盈
|
||||
|
||||
## 七、注意事项
|
||||
|
||||
1. **数据库迁移**:如果已有交易数据,新字段会为NULL,不影响现有数据
|
||||
2. **配置同步**:确保前端、后端、数据库配置一致
|
||||
3. **测试环境**:建议先在测试网环境验证优化效果
|
||||
4. **逐步调整**:不要一次性调整所有参数,建议逐步优化
|
||||
|
||||
## 八、预期效果
|
||||
|
||||
### 短期(1周内)
|
||||
- ✅ 止损触发频率降低(参数更宽松)
|
||||
- ✅ 止盈目标提高(盈亏比改善)
|
||||
- ✅ 信号质量提高(只做趋势跟踪)
|
||||
- ✅ 4H趋势判断更稳定(多指标投票)
|
||||
|
||||
### 中期(1个月内)
|
||||
- ✅ 交易统计数据积累
|
||||
- ✅ 可以分析策略表现
|
||||
- ✅ 根据数据优化参数
|
||||
|
||||
### 长期(3个月+)
|
||||
- ✅ 策略表现稳定
|
||||
- ✅ 参数优化完成
|
||||
- ✅ 可以引入更多策略
|
||||
|
||||
## 九、总结
|
||||
|
||||
本次优化遵循"先做对,再做好"的原则:
|
||||
1. ✅ **简化策略**:移除冲突的均值回归策略
|
||||
2. ✅ **优化参数**:调整止损止盈,提高盈亏比
|
||||
3. ✅ **改进判断**:使用多指标投票,提高准确性
|
||||
4. ✅ **添加统计**:为后续优化提供数据支持
|
||||
|
||||
**核心改进**:
|
||||
- 策略更简单、更清晰
|
||||
- 参数更合理、更稳定
|
||||
- 判断更准确、更可靠
|
||||
- 数据更完整、更有用
|
||||
|
||||
**下一步**:
|
||||
- 观察实际运行效果
|
||||
- 收集交易统计数据
|
||||
- 根据数据进一步优化
|
||||
428
STRATEGY_ANALYSIS.md
Normal file
428
STRATEGY_ANALYSIS.md
Normal file
|
|
@ -0,0 +1,428 @@
|
|||
# 交易策略与盈亏平仓处理分析报告
|
||||
|
||||
## 一、当前策略架构
|
||||
|
||||
### 1.1 策略核心逻辑
|
||||
|
||||
#### 多周期共振策略
|
||||
- **4H周期定方向**:使用EMA20判断大趋势(up/down/neutral)
|
||||
- **1H周期找信号**:使用RSI、MACD、布林带、均线系统
|
||||
- **15min周期入场**:精细点位选择
|
||||
|
||||
#### 市场状态识别
|
||||
- **震荡市场(ranging)**:均值回归策略
|
||||
- RSI超卖(<30)→ 做多信号
|
||||
- RSI超买(>70)→ 做空信号
|
||||
- 布林带下轨 → 做多信号
|
||||
- 布林带上轨 → 做空信号
|
||||
|
||||
- **趋势市场(trending)**:趋势跟踪策略
|
||||
- MACD金叉 → 做多信号
|
||||
- MACD死叉 → 做空信号
|
||||
- 价格在均线之上(EMA20 > EMA50)→ 做多信号
|
||||
- 价格在均线之下(EMA20 < EMA50)→ 做空信号
|
||||
|
||||
#### 信号强度计算
|
||||
- **基础分数**:
|
||||
- RSI超买/超卖:+4分
|
||||
- 布林带触及:+3分
|
||||
- MACD金叉/死叉:+3分
|
||||
- 均线系统:+2分
|
||||
- 多指标确认(≥2个):+2分
|
||||
- 4H周期共振:+2分
|
||||
|
||||
- **扣分项**:
|
||||
- 逆4H趋势:-3分
|
||||
- 信号强度不足:拒绝交易
|
||||
|
||||
- **交易阈值**:信号强度 ≥ 7(可配置 `MIN_SIGNAL_STRENGTH`)
|
||||
|
||||
#### 开仓条件检查流程
|
||||
```
|
||||
1. 市场扫描 → 找出涨跌幅最大的前N个交易对
|
||||
2. 技术指标分析 → 计算信号强度和方向
|
||||
3. 风险检查:
|
||||
- 最小涨跌幅阈值(MIN_CHANGE_PERCENT)
|
||||
- 是否已有持仓(避免重复开仓)
|
||||
- 成交量确认(MIN_VOLUME_24H)
|
||||
4. 仓位计算:
|
||||
- 单笔仓位限制(MAX_POSITION_PERCENT)
|
||||
- 总仓位限制(MAX_TOTAL_POSITION_PERCENT)
|
||||
- 最小保证金要求(MIN_MARGIN_USDT)
|
||||
5. 动态杠杆计算(根据信号强度)
|
||||
6. 开仓执行
|
||||
```
|
||||
|
||||
### 1.2 风险管理
|
||||
|
||||
#### 仓位管理
|
||||
- **单笔仓位**:账户余额的 1%-5%(可配置)
|
||||
- **总仓位上限**:账户余额的 30%(可配置)
|
||||
- **最小保证金**:0.5 USDT(可配置)
|
||||
- **动态杠杆**:10x-20x(根据信号强度调整)
|
||||
|
||||
#### 止损止盈机制
|
||||
|
||||
**止损计算(混合策略)**:
|
||||
1. **基于保证金的止损**:
|
||||
- 止损金额 = 保证金 × STOP_LOSS_PERCENT(默认8%)
|
||||
- 止损价 = 入场价 ± (止损金额 / 数量)
|
||||
|
||||
2. **基于价格百分比的保护**:
|
||||
- 最小价格变动 = 入场价 × MIN_STOP_LOSS_PRICE_PCT(默认2%)
|
||||
- 最终止损价 = max(基于保证金的止损, 基于价格的止损) [取更宽松的]
|
||||
|
||||
3. **技术止损(可选)**:
|
||||
- 使用支撑/阻力位、布林带
|
||||
- 如果技术止损更紧,使用技术止损
|
||||
|
||||
**止盈计算(混合策略)**:
|
||||
1. **基于保证金的止盈**:
|
||||
- 止盈金额 = 保证金 × TAKE_PROFIT_PERCENT(默认15%)
|
||||
- 止盈价 = 入场价 ± (止盈金额 / 数量)
|
||||
|
||||
2. **基于价格百分比的保护**:
|
||||
- 最小价格变动 = 入场价 × MIN_TAKE_PROFIT_PRICE_PCT(默认3%)
|
||||
- 最终止盈价 = max(基于保证金的止盈, 基于价格的止盈) [取更宽松的]
|
||||
|
||||
**分步止盈策略**:
|
||||
- **第一目标**:盈亏比 1:1,平仓 50%
|
||||
- **第二目标**:原始止盈价,平仓剩余 50%
|
||||
- 部分止盈后,止损移至成本价(保本)
|
||||
|
||||
**移动止损(Trailing Stop)**:
|
||||
- **激活条件**:盈利达到保证金的 1%(TRAILING_STOP_ACTIVATION)
|
||||
- **保护机制**:止损价跟随价格移动,保护保证金的 1%利润(TRAILING_STOP_PROTECT)
|
||||
|
||||
### 1.3 监控机制
|
||||
|
||||
#### 实时监控(WebSocket)
|
||||
- 每个持仓独立WebSocket连接
|
||||
- 实时接收价格更新
|
||||
- 立即检查止损止盈条件
|
||||
- 自动重连机制(最多5次)
|
||||
|
||||
#### 定时检查(备用)
|
||||
- 扫描间隔:可配置(默认3600秒)
|
||||
- 持仓同步:每5分钟(POSITION_SYNC_INTERVAL)
|
||||
- 作为WebSocket的备用方案
|
||||
|
||||
## 二、发现的问题
|
||||
|
||||
### 2.1 代码问题
|
||||
|
||||
#### 问题1:部分止盈逻辑重复
|
||||
**位置**:`position_manager.py` 第829-851行
|
||||
```python
|
||||
# 代码重复:做多和做空的处理逻辑几乎相同,但写了两遍
|
||||
# 第一段(第808-828行):处理做多
|
||||
# 第二段(第829-851行):处理做空,但逻辑相同
|
||||
```
|
||||
|
||||
**影响**:代码冗余,维护困难,容易出错
|
||||
|
||||
#### 问题2:止损检查逻辑重复
|
||||
**位置**:`position_manager.py` 第744-782行
|
||||
```python
|
||||
# 第744行:检查止损价是否设置
|
||||
# 第747行:再次检查止损价(重复)
|
||||
```
|
||||
|
||||
**影响**:代码冗余,逻辑不清晰
|
||||
|
||||
#### 问题3:移动止损计算可能有问题
|
||||
**位置**:`position_manager.py` 第718-740行
|
||||
```python
|
||||
# 移动止损的计算方式:
|
||||
# new_stop_loss = entry_price + (pnl_amount - protect_amount) / quantity
|
||||
# 这个公式可能不正确,因为:
|
||||
# 1. pnl_amount 是当前总盈亏,但应该基于剩余仓位
|
||||
# 2. 如果已经部分止盈,应该基于剩余仓位计算
|
||||
```
|
||||
|
||||
**影响**:移动止损可能不准确
|
||||
|
||||
#### 问题4:分步止盈后剩余仓位计算
|
||||
**位置**:`position_manager.py` 第854-894行
|
||||
```python
|
||||
# 第二目标止盈时,使用 remaining_quantity 计算盈亏
|
||||
# 但 pnl_amount 仍然是基于原始 quantity 计算的
|
||||
# 这可能导致盈亏计算不准确
|
||||
```
|
||||
|
||||
**影响**:盈亏统计可能不准确
|
||||
|
||||
### 2.2 策略问题
|
||||
|
||||
#### 问题1:信号强度计算不够清晰
|
||||
- 多个指标同时触发时,分数可能过高
|
||||
- 没有考虑指标之间的冲突(如RSI超买但MACD死叉)
|
||||
- 4H趋势判断过于简单(只用EMA20)
|
||||
|
||||
#### 问题2:缺少策略回测
|
||||
- 没有历史数据回测功能
|
||||
- 无法验证策略有效性
|
||||
- 无法优化参数
|
||||
|
||||
#### 问题3:缺少交易统计
|
||||
- 没有胜率统计
|
||||
- 没有盈亏比统计
|
||||
- 没有按策略类型统计(均值回归 vs 趋势跟踪)
|
||||
|
||||
#### 问题4:移动止损激活条件可能太早
|
||||
- 激活条件:盈利1% of margin
|
||||
- 保护金额:1% of margin
|
||||
- 这意味着一旦激活,就保护1%利润,但可能被正常波动触发
|
||||
|
||||
### 2.3 性能问题
|
||||
|
||||
#### 问题1:WebSocket连接过多
|
||||
- 每个持仓一个WebSocket连接
|
||||
- 如果有10个持仓,就有10个连接
|
||||
- 可能达到币安连接数限制
|
||||
|
||||
#### 问题2:定时检查与WebSocket重复
|
||||
- WebSocket实时监控 + 定时检查
|
||||
- 可能造成重复计算
|
||||
|
||||
## 三、优化建议
|
||||
|
||||
### 3.1 代码优化
|
||||
|
||||
#### 建议1:重构部分止盈逻辑
|
||||
```python
|
||||
# 统一处理做多和做空
|
||||
async def _partial_take_profit(self, symbol: str, position_info: Dict, target_pct: float):
|
||||
"""统一的部分止盈逻辑"""
|
||||
side = position_info['side']
|
||||
quantity = position_info['quantity']
|
||||
partial_quantity = quantity * 0.5
|
||||
|
||||
# 确定平仓方向
|
||||
close_side = 'SELL' if side == 'BUY' else 'BUY'
|
||||
|
||||
# 执行部分平仓
|
||||
partial_order = await self.client.place_order(
|
||||
symbol=symbol,
|
||||
side=close_side,
|
||||
quantity=partial_quantity,
|
||||
order_type='MARKET'
|
||||
)
|
||||
|
||||
if partial_order:
|
||||
position_info['partialProfitTaken'] = True
|
||||
position_info['remainingQuantity'] = quantity - partial_quantity
|
||||
position_info['stopLoss'] = position_info['entryPrice'] # 移至成本价
|
||||
position_info['trailingStopActivated'] = True
|
||||
```
|
||||
|
||||
#### 建议2:修复移动止损计算
|
||||
```python
|
||||
# 修复移动止损计算,基于剩余仓位
|
||||
if position_info.get('partialProfitTaken', False):
|
||||
remaining_quantity = position_info.get('remainingQuantity', quantity)
|
||||
remaining_margin = (entry_price * remaining_quantity) / leverage
|
||||
protect_amount = remaining_margin * trailing_protect
|
||||
# 基于剩余仓位计算盈亏
|
||||
if side == 'BUY':
|
||||
remaining_pnl = (current_price - entry_price) * remaining_quantity
|
||||
else:
|
||||
remaining_pnl = (entry_price - current_price) * remaining_quantity
|
||||
|
||||
# 计算新的止损价
|
||||
if side == 'BUY':
|
||||
new_stop_loss = entry_price + (remaining_pnl - protect_amount) / remaining_quantity
|
||||
else:
|
||||
new_stop_loss = entry_price - (remaining_pnl - protect_amount) / remaining_quantity
|
||||
else:
|
||||
# 未部分止盈,使用原始逻辑
|
||||
...
|
||||
```
|
||||
|
||||
#### 建议3:统一止损止盈检查
|
||||
```python
|
||||
async def _check_sl_tp(self, symbol: str, current_price: float) -> Optional[str]:
|
||||
"""
|
||||
统一的止损止盈检查
|
||||
返回:None(不触发)或 'stop_loss'/'take_profit'/'trailing_stop'
|
||||
"""
|
||||
position_info = self.active_positions.get(symbol)
|
||||
if not position_info:
|
||||
return None
|
||||
|
||||
# 计算盈亏
|
||||
pnl_data = self._calculate_pnl(position_info, current_price)
|
||||
|
||||
# 检查止损
|
||||
if self._check_stop_loss(position_info, pnl_data):
|
||||
return 'stop_loss'
|
||||
|
||||
# 检查移动止损
|
||||
if self._check_trailing_stop(position_info, pnl_data):
|
||||
return 'trailing_stop'
|
||||
|
||||
# 检查止盈
|
||||
if self._check_take_profit(position_info, pnl_data):
|
||||
return 'take_profit'
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### 3.2 策略优化
|
||||
|
||||
#### 建议1:改进信号强度计算
|
||||
```python
|
||||
# 添加指标冲突检测
|
||||
def _check_indicator_conflict(self, indicators: Dict) -> int:
|
||||
"""检测指标冲突,返回冲突分数"""
|
||||
conflict_score = 0
|
||||
|
||||
# RSI与MACD冲突
|
||||
if indicators.get('rsi', 50) > 70 and indicators.get('macd_histogram', 0) < 0:
|
||||
conflict_score += 2 # RSI超买但MACD死叉
|
||||
|
||||
if indicators.get('rsi', 50) < 30 and indicators.get('macd_histogram', 0) > 0:
|
||||
conflict_score += 2 # RSI超卖但MACD金叉
|
||||
|
||||
return conflict_score
|
||||
|
||||
# 在信号强度计算中减去冲突分数
|
||||
signal_strength -= conflict_score
|
||||
```
|
||||
|
||||
#### 建议2:改进4H趋势判断
|
||||
```python
|
||||
# 使用多个指标判断4H趋势
|
||||
def _judge_trend_4h(self, symbol_info: Dict) -> str:
|
||||
"""使用多个指标判断4H趋势"""
|
||||
price_4h = symbol_info.get('price_4h')
|
||||
ema20_4h = symbol_info.get('ema20_4h')
|
||||
ema50_4h = symbol_info.get('ema50_4h')
|
||||
macd_4h = symbol_info.get('macd_4h')
|
||||
|
||||
signals = []
|
||||
|
||||
# EMA20 vs 价格
|
||||
if price_4h > ema20_4h:
|
||||
signals.append('up')
|
||||
elif price_4h < ema20_4h:
|
||||
signals.append('down')
|
||||
|
||||
# EMA20 vs EMA50
|
||||
if ema20_4h > ema50_4h:
|
||||
signals.append('up')
|
||||
elif ema20_4h < ema50_4h:
|
||||
signals.append('down')
|
||||
|
||||
# MACD
|
||||
if macd_4h and macd_4h.get('histogram', 0) > 0:
|
||||
signals.append('up')
|
||||
elif macd_4h and macd_4h.get('histogram', 0) < 0:
|
||||
signals.append('down')
|
||||
|
||||
# 多数投票
|
||||
up_count = signals.count('up')
|
||||
down_count = signals.count('down')
|
||||
|
||||
if up_count > down_count:
|
||||
return 'up'
|
||||
elif down_count > up_count:
|
||||
return 'down'
|
||||
else:
|
||||
return 'neutral'
|
||||
```
|
||||
|
||||
#### 建议3:调整移动止损参数
|
||||
```python
|
||||
# 建议的移动止损参数
|
||||
TRAILING_STOP_ACTIVATION = 0.05 # 盈利5% of margin后激活(更保守)
|
||||
TRAILING_STOP_PROTECT = 0.03 # 保护3% of margin利润(更合理)
|
||||
```
|
||||
|
||||
### 3.3 性能优化
|
||||
|
||||
#### 建议1:使用组合流(Combined Stream)
|
||||
```python
|
||||
# 使用币安组合流,一个连接监控多个交易对
|
||||
# wss://fstream.binance.com/stream?streams=btcusdt@ticker/ethusdt@ticker/...
|
||||
ws_url = f"wss://fstream.binance.com/stream?streams={'/'.join([f'{s.lower()}@ticker' for s in symbols])}"
|
||||
```
|
||||
|
||||
#### 建议2:减少定时检查频率
|
||||
```python
|
||||
# 如果WebSocket正常工作,减少定时检查频率
|
||||
# 或者只在WebSocket断开时启用定时检查
|
||||
if not self._websocket_connected:
|
||||
await self.check_stop_loss_take_profit()
|
||||
```
|
||||
|
||||
### 3.4 功能增强
|
||||
|
||||
#### 建议1:添加策略回测
|
||||
```python
|
||||
class StrategyBacktester:
|
||||
"""策略回测器"""
|
||||
async def backtest(self, start_date: str, end_date: str, initial_balance: float):
|
||||
"""回测策略"""
|
||||
# 加载历史数据
|
||||
# 模拟交易
|
||||
# 计算收益、胜率、最大回撤等
|
||||
pass
|
||||
```
|
||||
|
||||
#### 建议2:添加交易统计
|
||||
```python
|
||||
class TradeStatistics:
|
||||
"""交易统计"""
|
||||
def get_statistics(self, start_date: str, end_date: str):
|
||||
"""获取交易统计"""
|
||||
return {
|
||||
'total_trades': 0,
|
||||
'win_rate': 0.0,
|
||||
'profit_factor': 0.0,
|
||||
'avg_win': 0.0,
|
||||
'avg_loss': 0.0,
|
||||
'max_drawdown': 0.0,
|
||||
'by_strategy': {
|
||||
'mean_reversion': {...},
|
||||
'trend_following': {...}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 四、优先级建议
|
||||
|
||||
### 高优先级(立即修复)
|
||||
1. ✅ **修复部分止盈逻辑重复** - 代码质量问题
|
||||
2. ✅ **修复移动止损计算** - 可能影响盈亏
|
||||
3. ✅ **修复分步止盈后盈亏计算** - 统计准确性
|
||||
|
||||
### 中优先级(近期优化)
|
||||
4. ⚠️ **改进信号强度计算** - 提高策略质量
|
||||
5. ⚠️ **改进4H趋势判断** - 提高策略质量
|
||||
6. ⚠️ **调整移动止损参数** - 优化风险控制
|
||||
|
||||
### 低优先级(长期优化)
|
||||
7. 📝 **添加策略回测** - 功能增强
|
||||
8. 📝 **添加交易统计** - 功能增强
|
||||
9. 📝 **优化WebSocket连接** - 性能优化
|
||||
|
||||
## 五、总结
|
||||
|
||||
当前策略架构整体合理,采用了多周期共振、技术指标确认、风险管理等成熟方法。但存在一些代码质量和逻辑问题需要修复。
|
||||
|
||||
**核心优势**:
|
||||
- 多周期共振策略,提高胜率
|
||||
- 基于保证金的止损止盈,更合理
|
||||
- WebSocket实时监控,响应及时
|
||||
- 分步止盈策略,平衡风险收益
|
||||
|
||||
**主要问题**:
|
||||
- 代码重复和逻辑错误
|
||||
- 移动止损计算可能不准确
|
||||
- 缺少策略验证和统计
|
||||
|
||||
**建议行动**:
|
||||
1. 先修复高优先级问题(代码质量)
|
||||
2. 然后优化策略逻辑(提高胜率)
|
||||
3. 最后添加回测和统计功能(长期优化)
|
||||
|
|
@ -119,8 +119,8 @@ class ConfigManager:
|
|||
'TOP_N_SYMBOLS': self.get('TOP_N_SYMBOLS', 10),
|
||||
|
||||
# 风险控制
|
||||
'STOP_LOSS_PERCENT': self.get('STOP_LOSS_PERCENT', 0.08), # 默认8%(更宽松,避免被正常波动触发)
|
||||
'TAKE_PROFIT_PERCENT': self.get('TAKE_PROFIT_PERCENT', 0.15), # 默认15%(给趋势更多空间)
|
||||
'STOP_LOSS_PERCENT': self.get('STOP_LOSS_PERCENT', 0.10), # 默认10%(更宽松,避免被小幅波动触发)
|
||||
'TAKE_PROFIT_PERCENT': self.get('TAKE_PROFIT_PERCENT', 0.20), # 默认20%(提高盈亏比)
|
||||
'MIN_STOP_LOSS_PRICE_PCT': self.get('MIN_STOP_LOSS_PRICE_PCT', 0.02), # 默认2%
|
||||
'MIN_TAKE_PROFIT_PRICE_PCT': self.get('MIN_TAKE_PROFIT_PRICE_PCT', 0.03), # 默认3%
|
||||
|
||||
|
|
@ -143,8 +143,8 @@ class ConfigManager:
|
|||
'USE_DYNAMIC_LEVERAGE': self.get('USE_DYNAMIC_LEVERAGE', True),
|
||||
'MAX_LEVERAGE': self.get('MAX_LEVERAGE', 20),
|
||||
'USE_TRAILING_STOP': self.get('USE_TRAILING_STOP', True),
|
||||
'TRAILING_STOP_ACTIVATION': self.get('TRAILING_STOP_ACTIVATION', 0.01),
|
||||
'TRAILING_STOP_PROTECT': self.get('TRAILING_STOP_PROTECT', 0.01),
|
||||
'TRAILING_STOP_ACTIVATION': self.get('TRAILING_STOP_ACTIVATION', 0.05), # 默认5%(避免过早触发)
|
||||
'TRAILING_STOP_PROTECT': self.get('TRAILING_STOP_PROTECT', 0.03), # 默认3%(更合理)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
11
backend/database/add_trade_statistics.sql
Normal file
11
backend/database/add_trade_statistics.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-- 添加交易统计字段
|
||||
-- 用于记录策略类型和持仓持续时间
|
||||
|
||||
ALTER TABLE `trades`
|
||||
ADD COLUMN IF NOT EXISTS `strategy_type` VARCHAR(50) COMMENT '策略类型: trend_following, mean_reversion' AFTER `exit_reason`,
|
||||
ADD COLUMN IF NOT EXISTS `duration_minutes` INT COMMENT '持仓持续时间(分钟)' AFTER `strategy_type`;
|
||||
|
||||
-- 添加索引以便统计查询
|
||||
ALTER TABLE `trades`
|
||||
ADD INDEX IF NOT EXISTS `idx_strategy_type` (`strategy_type`),
|
||||
ADD INDEX IF NOT EXISTS `idx_exit_reason` (`exit_reason`);
|
||||
|
|
@ -141,8 +141,8 @@ INSERT INTO `trading_config` (`config_key`, `config_value`, `config_type`, `cate
|
|||
('MAX_SCAN_SYMBOLS', '500', 'number', 'scan', '扫描的最大交易对数量(0表示扫描所有,建议100-500)'),
|
||||
|
||||
-- 风险控制
|
||||
('STOP_LOSS_PERCENT', '0.08', 'number', 'risk', '止损:8%(相对于保证金,更宽松避免被正常波动触发)'),
|
||||
('TAKE_PROFIT_PERCENT', '0.15', 'number', 'risk', '止盈:15%(相对于保证金,给趋势更多空间)'),
|
||||
('STOP_LOSS_PERCENT', '0.10', 'number', 'risk', '止损:10%(相对于保证金,更宽松避免被小幅波动触发)'),
|
||||
('TAKE_PROFIT_PERCENT', '0.20', 'number', 'risk', '止盈:20%(相对于保证金,提高盈亏比)'),
|
||||
('MIN_STOP_LOSS_PRICE_PCT', '0.02', 'number', 'risk', '最小止损价格变动:2%(防止止损过紧)'),
|
||||
('MIN_TAKE_PROFIT_PRICE_PCT', '0.03', 'number', 'risk', '最小止盈价格变动:3%(防止止盈过紧)'),
|
||||
|
||||
|
|
@ -163,8 +163,8 @@ INSERT INTO `trading_config` (`config_key`, `config_value`, `config_type`, `cate
|
|||
('USE_DYNAMIC_LEVERAGE', 'true', 'boolean', 'strategy', '是否启用动态杠杆(根据信号强度调整杠杆倍数)'),
|
||||
('MAX_LEVERAGE', '20', 'number', 'strategy', '最大杠杆倍数(动态杠杆上限)'),
|
||||
('USE_TRAILING_STOP', 'true', 'boolean', 'strategy', '是否使用移动止损'),
|
||||
('TRAILING_STOP_ACTIVATION', '0.01', 'number', 'strategy', '移动止损激活阈值(盈利1%后激活)'),
|
||||
('TRAILING_STOP_PROTECT', '0.01', 'number', 'strategy', '移动止损保护利润(保护1%利润)'),
|
||||
('TRAILING_STOP_ACTIVATION', '0.05', 'number', 'strategy', '移动止损激活阈值(盈利5%后激活,避免过早触发)'),
|
||||
('TRAILING_STOP_PROTECT', '0.03', 'number', 'strategy', '移动止损保护利润(保护3%利润,更合理)'),
|
||||
|
||||
-- 持仓同步
|
||||
('POSITION_SYNC_INTERVAL', '300', 'number', 'scan', '持仓状态同步间隔(秒),默认5分钟,用于同步币安实际持仓与数据库状态'),
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class Trade:
|
|||
return db.execute_one("SELECT LAST_INSERT_ID() as id")['id']
|
||||
|
||||
@staticmethod
|
||||
def update_exit(trade_id, exit_price, exit_reason, pnl, pnl_percent, exit_order_id=None):
|
||||
def update_exit(trade_id, exit_price, exit_reason, pnl, pnl_percent, exit_order_id=None, strategy_type=None, duration_minutes=None):
|
||||
"""更新平仓信息(使用北京时间)
|
||||
|
||||
Args:
|
||||
|
|
@ -141,12 +141,23 @@ class Trade:
|
|||
f"跳过更新 exit_order_id,只更新其他字段"
|
||||
)
|
||||
# 只更新其他字段,不更新 exit_order_id
|
||||
update_fields = [
|
||||
"exit_price = %s", "exit_time = %s",
|
||||
"exit_reason = %s", "pnl = %s", "pnl_percent = %s", "status = 'closed'"
|
||||
]
|
||||
update_values = [exit_price, exit_time, exit_reason, pnl, pnl_percent]
|
||||
|
||||
if strategy_type is not None:
|
||||
update_fields.append("strategy_type = %s")
|
||||
update_values.append(strategy_type)
|
||||
if duration_minutes is not None:
|
||||
update_fields.append("duration_minutes = %s")
|
||||
update_values.append(duration_minutes)
|
||||
|
||||
update_values.append(trade_id)
|
||||
db.execute_update(
|
||||
"""UPDATE trades
|
||||
SET exit_price = %s, exit_time = %s,
|
||||
exit_reason = %s, pnl = %s, pnl_percent = %s, status = 'closed'
|
||||
WHERE id = %s""",
|
||||
(exit_price, exit_time, exit_reason, pnl, pnl_percent, trade_id)
|
||||
f"UPDATE trades SET {', '.join(update_fields)} WHERE id = %s",
|
||||
tuple(update_values)
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
|
|
@ -155,13 +166,24 @@ class Trade:
|
|||
|
||||
# 正常更新(包括 exit_order_id)
|
||||
try:
|
||||
update_fields = [
|
||||
"exit_price = %s", "exit_time = %s",
|
||||
"exit_reason = %s", "pnl = %s", "pnl_percent = %s", "status = 'closed'",
|
||||
"exit_order_id = %s"
|
||||
]
|
||||
update_values = [exit_price, exit_time, exit_reason, pnl, pnl_percent, exit_order_id]
|
||||
|
||||
if strategy_type is not None:
|
||||
update_fields.append("strategy_type = %s")
|
||||
update_values.append(strategy_type)
|
||||
if duration_minutes is not None:
|
||||
update_fields.append("duration_minutes = %s")
|
||||
update_values.append(duration_minutes)
|
||||
|
||||
update_values.append(trade_id)
|
||||
db.execute_update(
|
||||
"""UPDATE trades
|
||||
SET exit_price = %s, exit_time = %s,
|
||||
exit_reason = %s, pnl = %s, pnl_percent = %s, status = 'closed',
|
||||
exit_order_id = %s
|
||||
WHERE id = %s""",
|
||||
(exit_price, exit_time, exit_reason, pnl, pnl_percent, exit_order_id, trade_id)
|
||||
f"UPDATE trades SET {', '.join(update_fields)} WHERE id = %s",
|
||||
tuple(update_values)
|
||||
)
|
||||
except Exception as e:
|
||||
# 如果更新失败(可能是唯一约束冲突),尝试不更新 exit_order_id
|
||||
|
|
@ -172,12 +194,23 @@ class Trade:
|
|||
f"尝试不更新 exit_order_id"
|
||||
)
|
||||
# 只更新其他字段,不更新 exit_order_id
|
||||
update_fields = [
|
||||
"exit_price = %s", "exit_time = %s",
|
||||
"exit_reason = %s", "pnl = %s", "pnl_percent = %s", "status = 'closed'"
|
||||
]
|
||||
update_values = [exit_price, exit_time, exit_reason, pnl, pnl_percent]
|
||||
|
||||
if strategy_type is not None:
|
||||
update_fields.append("strategy_type = %s")
|
||||
update_values.append(strategy_type)
|
||||
if duration_minutes is not None:
|
||||
update_fields.append("duration_minutes = %s")
|
||||
update_values.append(duration_minutes)
|
||||
|
||||
update_values.append(trade_id)
|
||||
db.execute_update(
|
||||
"""UPDATE trades
|
||||
SET exit_price = %s, exit_time = %s,
|
||||
exit_reason = %s, pnl = %s, pnl_percent = %s, status = 'closed'
|
||||
WHERE id = %s""",
|
||||
(exit_price, exit_time, exit_reason, pnl, pnl_percent, trade_id)
|
||||
f"UPDATE trades SET {', '.join(update_fields)} WHERE id = %s",
|
||||
tuple(update_values)
|
||||
)
|
||||
else:
|
||||
# 其他错误,重新抛出
|
||||
|
|
|
|||
69
new_suggestion20260117.md
Normal file
69
new_suggestion20260117.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
一、当前系统可能存在的问题(结合你的反馈)
|
||||
1. 策略逻辑混乱,信号冲突频繁
|
||||
报告中提到信号强度计算未考虑指标冲突,比如RSI超买但MACD死叉,可能导致频繁开仓又平仓。
|
||||
|
||||
4H趋势判断过于简单(只用EMA20),在震荡行情中会频繁切换方向。
|
||||
|
||||
2. 止损止盈设置不合理
|
||||
移动止损激活太早(1%盈利就触发),容易在正常波动中被扫出局。
|
||||
|
||||
分步止盈后剩余仓位的盈亏计算有问题,可能导致统计失真。
|
||||
|
||||
3. 缺乏交易统计与回测
|
||||
无法知道是哪种策略(均值回归 vs 趋势跟踪)在亏钱。
|
||||
|
||||
无法优化参数,只能“盲调”。
|
||||
|
||||
4. WebSocket连接过多,性能可能不稳定
|
||||
币安可能限制连接数,连接断开后依赖定时检查,响应延迟。
|
||||
|
||||
二、优化建议(按优先级排序)
|
||||
✅ 第一步:立即调整止损止盈参数(最容易见效)
|
||||
python
|
||||
# 建议修改以下参数:
|
||||
STOP_LOSS_PERCENT = 0.10 # 止损从8%放宽到10%,避免被小幅波动触发
|
||||
TAKE_PROFIT_PERCENT = 0.20 # 止盈从15%提高到20%,提高盈亏比
|
||||
TRAILING_STOP_ACTIVATION = 0.05 # 移动止损激活从1%盈利改为5%
|
||||
TRAILING_STOP_PROTECT = 0.03 # 保护利润从1%提高到3%
|
||||
✅ 第二步:简化策略,避免信号冲突
|
||||
建议在初期只做趋势跟踪策略,放弃均值回归(震荡策略),因为两者逻辑相反,容易冲突。
|
||||
|
||||
python
|
||||
# 修改信号强度计算,只使用趋势指标:
|
||||
TREND_SIGNAL_WEIGHTS = {
|
||||
'macd_cross': 5, # MACD金叉/死叉
|
||||
'ema_cross': 4, # EMA20上穿/下穿EMA50
|
||||
'price_above_ema20': 3, # 价格在EMA20之上/下
|
||||
'4h_trend_confirmation': 2, # 4H趋势确认
|
||||
}
|
||||
# 移除RSI、布林带等震荡指标
|
||||
✅ 第三步:优化4H趋势判断
|
||||
使用多指标投票机制,避免单一指标误导:
|
||||
|
||||
python
|
||||
def judge_trend_4h(price, ema20, ema50, macd_hist):
|
||||
score = 0
|
||||
if price > ema20: score += 1
|
||||
if ema20 > ema50: score += 1
|
||||
if macd_hist > 0: score += 1
|
||||
return 'up' if score >= 2 else 'down' if score <= 1 else 'neutral'
|
||||
✅ 第四步:添加简易交易日志与统计
|
||||
在每次平仓后记录:
|
||||
|
||||
python
|
||||
{
|
||||
"symbol": "BTCUSDT",
|
||||
"side": "BUY",
|
||||
"entry": 50000,
|
||||
"exit": 51000,
|
||||
"pnl": 1000,
|
||||
"reason": "take_profit",
|
||||
"strategy_type": "trend_following",
|
||||
"duration_minutes": 120
|
||||
}
|
||||
每周或每天汇总胜率、盈亏比、每笔平均盈利/亏损。
|
||||
|
||||
|
||||
一句话总结
|
||||
“先做对,再做好”
|
||||
目前你的系统复杂度较高,建议先简化策略、优化止损止盈、做好统计,再逐步迭代。不要追求完美,先追求稳定。
|
||||
|
|
@ -168,8 +168,8 @@ def _get_trading_config():
|
|||
'MIN_CHANGE_PERCENT': 0.5, # 降低到0.5%以获取更多推荐(推荐系统可以更宽松)
|
||||
'TOP_N_SYMBOLS': 50, # 每次扫描后处理的交易对数量(增加到50以获取更多推荐)
|
||||
'MAX_SCAN_SYMBOLS': 500, # 扫描的最大交易对数量(0表示扫描所有)
|
||||
'STOP_LOSS_PERCENT': 0.08, # 止损百分比(相对于保证金),默认8%(更宽松,避免被正常波动触发)
|
||||
'TAKE_PROFIT_PERCENT': 0.15, # 止盈百分比(相对于保证金),默认15%(更宽松,给趋势更多空间)
|
||||
'STOP_LOSS_PERCENT': 0.10, # 止损百分比(相对于保证金),默认10%(更宽松,避免被小幅波动触发)
|
||||
'TAKE_PROFIT_PERCENT': 0.20, # 止盈百分比(相对于保证金),默认20%(提高盈亏比)
|
||||
'MIN_STOP_LOSS_PRICE_PCT': 0.02, # 最小止损价格变动百分比(如0.02表示2%),防止止损过紧,默认2%
|
||||
'MIN_TAKE_PROFIT_PRICE_PCT': 0.03, # 最小止盈价格变动百分比(如0.03表示3%),防止止盈过紧,默认3%
|
||||
'SCAN_INTERVAL': 3600,
|
||||
|
|
@ -179,13 +179,13 @@ def _get_trading_config():
|
|||
'ENTRY_INTERVAL': '15m',
|
||||
'MIN_VOLUME_24H': 5000000, # 降低到500万以获取更多推荐(推荐系统可以更宽松)
|
||||
'MIN_VOLATILITY': 0.02,
|
||||
'MIN_SIGNAL_STRENGTH': 3, # 降低至3以获取更多推荐(推荐系统使用,实际交易仍可设置更高阈值)
|
||||
'MIN_SIGNAL_STRENGTH': 7, # 提高至7,只交易高质量信号(简化策略后)
|
||||
'LEVERAGE': 10, # 基础杠杆倍数
|
||||
'USE_DYNAMIC_LEVERAGE': True, # 是否启用动态杠杆(根据信号强度调整)
|
||||
'MAX_LEVERAGE': 20, # 最大杠杆倍数(动态杠杆上限)
|
||||
'USE_TRAILING_STOP': True,
|
||||
'TRAILING_STOP_ACTIVATION': 0.01,
|
||||
'TRAILING_STOP_PROTECT': 0.01,
|
||||
'TRAILING_STOP_ACTIVATION': 0.05, # 移动止损激活从1%改为5%(避免过早触发)
|
||||
'TRAILING_STOP_PROTECT': 0.03, # 保护利润从1%提高到3%(更合理)
|
||||
'POSITION_SYNC_INTERVAL': 300, # 持仓状态同步间隔(秒),默认5分钟
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -315,6 +315,7 @@ class PositionManager:
|
|||
take_profit_2 = take_profit_price
|
||||
|
||||
# 记录持仓信息(包含动态止损止盈和分步止盈)
|
||||
from datetime import datetime
|
||||
position_info = {
|
||||
'symbol': symbol,
|
||||
'side': side,
|
||||
|
|
@ -332,6 +333,8 @@ class PositionManager:
|
|||
'initialStopLoss': stop_loss_price, # 初始止损(用于移动止损)
|
||||
'leverage': leverage,
|
||||
'entryReason': entry_reason,
|
||||
'entryTime': datetime.now(), # 记录入场时间(用于计算持仓持续时间)
|
||||
'strategyType': 'trend_following', # 策略类型(简化后只有趋势跟踪)
|
||||
'atr': atr,
|
||||
'maxProfit': 0.0, # 记录最大盈利(用于移动止损)
|
||||
'trailingStopActivated': False # 移动止损是否已激活
|
||||
|
|
@ -437,13 +440,33 @@ class PositionManager:
|
|||
pnl_percent = ((entry_price - exit_price) / entry_price) * 100
|
||||
|
||||
# 同步平仓时没有订单号,设为None
|
||||
# 计算持仓持续时间和策略类型
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
strategy_type = position_info.get('strategyType', 'trend_following')
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price,
|
||||
exit_reason=reason,
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=None # 同步平仓时没有订单号
|
||||
exit_order_id=None, # 同步平仓时没有订单号
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
logger.info(f"{symbol} [平仓] ✓ 数据库状态已更新")
|
||||
updated = True
|
||||
|
|
@ -561,13 +584,34 @@ class PositionManager:
|
|||
if exit_order_id:
|
||||
logger.info(f"{symbol} [平仓] 币安订单号: {exit_order_id}")
|
||||
|
||||
# 计算持仓持续时间
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
# 获取策略类型(从开仓原因或持仓信息中获取)
|
||||
strategy_type = position_info.get('strategyType', 'trend_following') # 默认趋势跟踪
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price_float,
|
||||
exit_reason=reason,
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=exit_order_id # 保存币安平仓订单号
|
||||
exit_order_id=exit_order_id, # 保存币安平仓订单号
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
logger.info(
|
||||
f"{symbol} [平仓] ✓ 数据库记录已更新 "
|
||||
|
|
@ -716,7 +760,39 @@ class PositionManager:
|
|||
)
|
||||
else:
|
||||
# 盈利超过阈值后,止损移至保护利润位(基于保证金)
|
||||
# 计算需要保护的利润金额
|
||||
# 如果已经部分止盈,使用剩余仓位计算
|
||||
if position_info.get('partialProfitTaken', False):
|
||||
remaining_quantity = position_info.get('remainingQuantity', quantity)
|
||||
remaining_margin = (entry_price * remaining_quantity) / leverage if leverage > 0 else (entry_price * remaining_quantity)
|
||||
protect_amount = remaining_margin * trailing_protect
|
||||
|
||||
# 计算剩余仓位的盈亏
|
||||
if position_info['side'] == 'BUY':
|
||||
remaining_pnl = (current_price - entry_price) * remaining_quantity
|
||||
else:
|
||||
remaining_pnl = (entry_price - current_price) * remaining_quantity
|
||||
|
||||
# 计算新的止损价(基于剩余仓位)
|
||||
if position_info['side'] == 'BUY':
|
||||
new_stop_loss = entry_price + (remaining_pnl - protect_amount) / remaining_quantity
|
||||
if new_stop_loss > position_info['stopLoss']:
|
||||
position_info['stopLoss'] = new_stop_loss
|
||||
logger.info(
|
||||
f"{symbol} 移动止损更新(剩余仓位): {new_stop_loss:.4f} "
|
||||
f"(保护{trailing_protect*100:.1f}% of remaining margin = {protect_amount:.4f} USDT, "
|
||||
f"剩余数量: {remaining_quantity:.4f})"
|
||||
)
|
||||
else:
|
||||
new_stop_loss = entry_price - (remaining_pnl - protect_amount) / remaining_quantity
|
||||
if new_stop_loss < position_info['stopLoss']:
|
||||
position_info['stopLoss'] = new_stop_loss
|
||||
logger.info(
|
||||
f"{symbol} 移动止损更新(剩余仓位): {new_stop_loss:.4f} "
|
||||
f"(保护{trailing_protect*100:.1f}% of remaining margin = {protect_amount:.4f} USDT, "
|
||||
f"剩余数量: {remaining_quantity:.4f})"
|
||||
)
|
||||
else:
|
||||
# 未部分止盈,使用原始仓位计算
|
||||
protect_amount = margin * trailing_protect
|
||||
# 计算对应的止损价
|
||||
if position_info['side'] == 'BUY':
|
||||
|
|
@ -739,13 +815,11 @@ class PositionManager:
|
|||
f"(保护{trailing_protect*100:.1f}% of margin = {protect_amount:.4f} USDT)"
|
||||
)
|
||||
|
||||
# 检查止损(使用更新后的止损价)
|
||||
# 检查止损(使用更新后的止损价,基于保证金收益比)
|
||||
stop_loss = position_info.get('stopLoss')
|
||||
if stop_loss is None:
|
||||
logger.warning(f"{symbol} 止损价未设置,跳过止损检查")
|
||||
# 检查止损(基于保证金收益比)
|
||||
stop_loss = position_info.get('stopLoss')
|
||||
if stop_loss is not None:
|
||||
elif stop_loss is not None:
|
||||
# 计算止损对应的保证金百分比目标
|
||||
if position_info['side'] == 'BUY':
|
||||
stop_loss_amount = (entry_price - stop_loss) * quantity
|
||||
|
|
@ -768,12 +842,33 @@ class PositionManager:
|
|||
trade_id = position_info.get('tradeId')
|
||||
if trade_id:
|
||||
try:
|
||||
# 计算持仓持续时间
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
# 获取策略类型
|
||||
strategy_type = position_info.get('strategyType', 'trend_following')
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=current_price,
|
||||
exit_reason=exit_reason,
|
||||
pnl=pnl_amount,
|
||||
pnl_percent=pnl_percent_margin
|
||||
pnl_percent=pnl_percent_margin,
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"更新止损记录失败: {e}")
|
||||
|
|
@ -826,29 +921,6 @@ class PositionManager:
|
|||
logger.info(f"{symbol} 剩余仓位止损移至成本价,配合移动止损博取更大利润")
|
||||
except Exception as e:
|
||||
logger.error(f"{symbol} 部分止盈失败: {e}")
|
||||
# 做空方向已经在上面统一处理了(基于保证金收益比)
|
||||
# 部分平仓50%
|
||||
partial_quantity = quantity * 0.5
|
||||
try:
|
||||
# 部分平仓
|
||||
partial_order = await self.client.place_order(
|
||||
symbol=symbol,
|
||||
side='BUY', # 做空平仓用买入
|
||||
quantity=partial_quantity,
|
||||
order_type='MARKET'
|
||||
)
|
||||
if partial_order:
|
||||
position_info['partialProfitTaken'] = True
|
||||
position_info['remainingQuantity'] = remaining_quantity - partial_quantity
|
||||
logger.info(
|
||||
f"{symbol} 部分止盈成功: 平仓{partial_quantity:.4f},剩余{position_info['remainingQuantity']:.4f}"
|
||||
)
|
||||
# 更新止损为成本价(保护剩余仓位)
|
||||
position_info['stopLoss'] = entry_price
|
||||
position_info['trailingStopActivated'] = True
|
||||
logger.info(f"{symbol} 剩余仓位止损移至成本价,配合移动止损博取更大利润")
|
||||
except Exception as e:
|
||||
logger.error(f"{symbol} 部分止盈失败: {e}")
|
||||
|
||||
# 第二目标:原始止盈价,平掉剩余仓位(基于保证金收益比)
|
||||
if partial_profit_taken and take_profit_2 is not None:
|
||||
|
|
@ -872,20 +944,47 @@ class PositionManager:
|
|||
logger.info(
|
||||
f"{symbol} 触发第二目标止盈(基于保证金): "
|
||||
f"剩余仓位盈亏={remaining_pnl_pct_margin:.2f}% of margin >= 目标={take_profit_2_pct_margin:.2f}% of margin | "
|
||||
f"当前价={current_price:.4f}, 目标价={take_profit_2:.4f}"
|
||||
f"当前价={current_price:.4f}, 目标价={take_profit_2:.4f}, "
|
||||
f"剩余数量={remaining_quantity:.4f}"
|
||||
)
|
||||
exit_reason = 'take_profit'
|
||||
# 计算总盈亏(原始仓位 + 部分止盈的盈亏)
|
||||
# 部分止盈时的价格需要从数据库或记录中获取,这里简化处理
|
||||
total_pnl_amount = remaining_pnl_amount # 简化:只计算剩余仓位盈亏
|
||||
total_pnl_percent = (total_pnl_amount / margin * 100) if margin > 0 else 0
|
||||
|
||||
# 更新数据库
|
||||
if DB_AVAILABLE:
|
||||
trade_id = position_info.get('tradeId')
|
||||
if trade_id:
|
||||
try:
|
||||
# 计算持仓持续时间
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
# 获取策略类型
|
||||
strategy_type = position_info.get('strategyType', 'trend_following')
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=current_price,
|
||||
exit_reason=exit_reason,
|
||||
pnl=pnl_amount,
|
||||
pnl_percent=pnl_percent_margin
|
||||
pnl=total_pnl_amount,
|
||||
pnl_percent=total_pnl_percent,
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"更新止盈记录失败: {e}")
|
||||
|
|
@ -916,12 +1015,32 @@ class PositionManager:
|
|||
trade_id = position_info.get('tradeId')
|
||||
if trade_id:
|
||||
try:
|
||||
# 计算持仓持续时间和策略类型
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
strategy_type = position_info.get('strategyType', 'trend_following')
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=current_price,
|
||||
exit_reason=exit_reason,
|
||||
pnl=pnl_amount,
|
||||
pnl_percent=pnl_percent_margin
|
||||
pnl_percent=pnl_percent_margin,
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"更新止盈记录失败: {e}")
|
||||
|
|
@ -1183,13 +1302,33 @@ class PositionManager:
|
|||
|
||||
# 使用 try-except 包裹,确保异常被正确处理
|
||||
try:
|
||||
# 计算持仓持续时间和策略类型
|
||||
entry_time = trade.get('entry_time')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
strategy_type = 'trend_following' # 默认策略类型
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=exit_price,
|
||||
exit_reason='sync', # 标记为同步更新
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent,
|
||||
exit_order_id=exit_order_id # 保存币安平仓订单号
|
||||
exit_order_id=exit_order_id, # 保存币安平仓订单号
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
except Exception as update_error:
|
||||
# update_exit 内部已经有异常处理,但如果仍然失败,记录错误但不中断同步流程
|
||||
|
|
@ -1759,12 +1898,32 @@ class PositionManager:
|
|||
pnl = (entry_price - current_price_float) * quantity
|
||||
|
||||
logger.info(f"{symbol} [自动平仓] 更新数据库记录 (ID: {trade_id})...")
|
||||
# 计算持仓持续时间和策略类型
|
||||
entry_time = position_info.get('entryTime')
|
||||
duration_minutes = None
|
||||
if entry_time:
|
||||
try:
|
||||
from datetime import datetime
|
||||
if isinstance(entry_time, str):
|
||||
entry_dt = datetime.strptime(entry_time, '%Y-%m-%d %H:%M:%S')
|
||||
else:
|
||||
entry_dt = entry_time
|
||||
exit_dt = datetime.now()
|
||||
duration = exit_dt - entry_dt
|
||||
duration_minutes = int(duration.total_seconds() / 60)
|
||||
except Exception as e:
|
||||
logger.debug(f"计算持仓持续时间失败: {e}")
|
||||
|
||||
strategy_type = position_info.get('strategyType', 'trend_following')
|
||||
|
||||
Trade.update_exit(
|
||||
trade_id=trade_id,
|
||||
exit_price=current_price_float,
|
||||
exit_reason=exit_reason,
|
||||
pnl=pnl,
|
||||
pnl_percent=pnl_percent_margin # 修复:使用 pnl_percent_margin 而不是 pnl_percent
|
||||
pnl_percent=pnl_percent_margin, # 修复:使用 pnl_percent_margin 而不是 pnl_percent
|
||||
strategy_type=strategy_type,
|
||||
duration_minutes=duration_minutes
|
||||
)
|
||||
logger.info(f"{symbol} [自动平仓] ✓ 数据库记录已更新 (盈亏: {pnl:.2f} USDT, {pnl_percent_margin:.2f}% of margin)")
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -301,19 +301,14 @@ class TradingStrategy:
|
|||
ema20 = symbol_info.get('ema20')
|
||||
ema50 = symbol_info.get('ema50')
|
||||
|
||||
# 多周期共振检查:4H定方向
|
||||
# 多周期共振检查:4H定方向(使用多指标投票机制)
|
||||
price_4h = symbol_info.get('price_4h', current_price)
|
||||
ema20_4h = symbol_info.get('ema20_4h')
|
||||
ema50_4h = symbol_info.get('ema50_4h')
|
||||
macd_4h = symbol_info.get('macd_4h')
|
||||
|
||||
# 判断4H周期趋势方向
|
||||
trend_4h = None # 'up', 'down', 'neutral'
|
||||
if ema20_4h is not None:
|
||||
if price_4h > ema20_4h:
|
||||
trend_4h = 'up'
|
||||
elif price_4h < ema20_4h:
|
||||
trend_4h = 'down'
|
||||
else:
|
||||
trend_4h = 'neutral'
|
||||
# 判断4H周期趋势方向(多指标投票)
|
||||
trend_4h = self._judge_trend_4h(price_4h, ema20_4h, ema50_4h, macd_4h)
|
||||
|
||||
signal_strength = 0
|
||||
reasons = []
|
||||
|
|
@ -326,136 +321,84 @@ class TradingStrategy:
|
|||
elif trend_4h == 'down':
|
||||
# 4H趋势向下,只允许做空信号
|
||||
logger.debug(f"{symbol} 4H趋势向下,只允许做空信号")
|
||||
elif trend_4h is None:
|
||||
# 无法判断4H趋势,记录警告
|
||||
logger.warning(f"{symbol} 无法判断4H趋势,可能影响信号质量")
|
||||
elif trend_4h == 'neutral':
|
||||
# 4H趋势中性,记录警告
|
||||
logger.warning(f"{symbol} 4H趋势中性,信号质量可能降低")
|
||||
|
||||
# 策略1:均值回归(震荡市场,高胜率)
|
||||
if market_regime == 'ranging':
|
||||
# RSI超卖,做多信号(需4H趋势向上或中性)
|
||||
if rsi and rsi < 30:
|
||||
if trend_4h in ('up', 'neutral', None):
|
||||
signal_strength += 4
|
||||
reasons.append(f"RSI超卖({rsi:.1f})")
|
||||
if direction is None:
|
||||
direction = 'BUY'
|
||||
else:
|
||||
reasons.append(f"RSI超卖但4H趋势向下,禁止逆势做多")
|
||||
# 简化策略:只做趋势跟踪,移除均值回归策略(避免信号冲突)
|
||||
# 策略权重配置(根据文档建议)
|
||||
TREND_SIGNAL_WEIGHTS = {
|
||||
'macd_cross': 5, # MACD金叉/死叉
|
||||
'ema_cross': 4, # EMA20上穿/下穿EMA50
|
||||
'price_above_ema20': 3, # 价格在EMA20之上/下
|
||||
'4h_trend_confirmation': 2, # 4H趋势确认
|
||||
}
|
||||
|
||||
# RSI超买,做空信号(需4H趋势向下或中性)
|
||||
elif rsi and rsi > 70:
|
||||
if trend_4h in ('down', 'neutral', None):
|
||||
signal_strength += 4
|
||||
reasons.append(f"RSI超买({rsi:.1f})")
|
||||
if direction is None:
|
||||
direction = 'SELL'
|
||||
else:
|
||||
reasons.append(f"RSI超买但4H趋势向上,禁止逆势做空")
|
||||
|
||||
# 布林带下轨,做多信号(需4H趋势向上或中性)
|
||||
if bollinger and current_price <= bollinger['lower']:
|
||||
if trend_4h in ('up', 'neutral', None):
|
||||
signal_strength += 3
|
||||
reasons.append("触及布林带下轨")
|
||||
if direction is None:
|
||||
direction = 'BUY'
|
||||
else:
|
||||
reasons.append("触及布林带下轨但4H趋势向下,禁止逆势做多")
|
||||
|
||||
# 布林带上轨,做空信号(需4H趋势向下或中性)
|
||||
elif bollinger and current_price >= bollinger['upper']:
|
||||
if trend_4h in ('down', 'neutral', None):
|
||||
signal_strength += 3
|
||||
reasons.append("触及布林带上轨")
|
||||
if direction is None:
|
||||
direction = 'SELL'
|
||||
else:
|
||||
reasons.append("触及布林带上轨但4H趋势向上,禁止逆势做空")
|
||||
|
||||
# 策略2:趋势跟踪(趋势市场)
|
||||
elif market_regime == 'trending':
|
||||
# MACD金叉,做多信号(需4H趋势向上或中性)
|
||||
# 趋势跟踪策略(不再区分市场状态,统一使用趋势指标)
|
||||
# MACD金叉/死叉(权重最高)
|
||||
if macd and macd['macd'] > macd['signal'] and macd['histogram'] > 0:
|
||||
# MACD金叉,做多信号(需4H趋势向上或中性)
|
||||
if trend_4h in ('up', 'neutral', None):
|
||||
signal_strength += 3
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['macd_cross']
|
||||
reasons.append("MACD金叉")
|
||||
if direction is None:
|
||||
direction = 'BUY'
|
||||
else:
|
||||
reasons.append("MACD金叉但4H趋势向下,禁止逆势做多")
|
||||
|
||||
# MACD死叉,做空信号(需4H趋势向下或中性)
|
||||
elif macd and macd['macd'] < macd['signal'] and macd['histogram'] < 0:
|
||||
# MACD死叉,做空信号(需4H趋势向下或中性)
|
||||
if trend_4h in ('down', 'neutral', None):
|
||||
signal_strength += 3
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['macd_cross']
|
||||
reasons.append("MACD死叉")
|
||||
if direction is None:
|
||||
direction = 'SELL'
|
||||
else:
|
||||
reasons.append("MACD死叉但4H趋势向上,禁止逆势做空")
|
||||
|
||||
# 均线系统
|
||||
# EMA均线系统
|
||||
if ema20 and ema50:
|
||||
if current_price > ema20 > ema50: # 上升趋势
|
||||
if trend_4h in ('up', 'neutral', None):
|
||||
signal_strength += 2
|
||||
reasons.append("价格在均线之上")
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['ema_cross']
|
||||
reasons.append("EMA20上穿EMA50,上升趋势")
|
||||
if direction is None:
|
||||
direction = 'BUY'
|
||||
else:
|
||||
reasons.append("1H均线向上但4H趋势向下,禁止逆势做多")
|
||||
elif current_price < ema20 < ema50: # 下降趋势
|
||||
if trend_4h in ('down', 'neutral', None):
|
||||
signal_strength += 2
|
||||
reasons.append("价格在均线之下")
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['ema_cross']
|
||||
reasons.append("EMA20下穿EMA50,下降趋势")
|
||||
if direction is None:
|
||||
direction = 'SELL'
|
||||
else:
|
||||
reasons.append("1H均线向下但4H趋势向上,禁止逆势做空")
|
||||
|
||||
# 策略3:综合信号(提高胜率)
|
||||
# 多个指标同时确认时,信号更强
|
||||
confirmations = 0
|
||||
# 价格与EMA20关系
|
||||
if ema20:
|
||||
if current_price > ema20:
|
||||
if trend_4h in ('up', 'neutral', None) and direction == 'BUY':
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['price_above_ema20']
|
||||
reasons.append("价格在EMA20之上")
|
||||
elif current_price < ema20:
|
||||
if trend_4h in ('down', 'neutral', None) and direction == 'SELL':
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['price_above_ema20']
|
||||
reasons.append("价格在EMA20之下")
|
||||
|
||||
# RSI确认
|
||||
if rsi:
|
||||
if direction == 'BUY' and rsi < 50:
|
||||
confirmations += 1
|
||||
elif direction == 'SELL' and rsi > 50:
|
||||
confirmations += 1
|
||||
|
||||
# MACD确认
|
||||
if macd:
|
||||
if direction == 'BUY' and macd['histogram'] > 0:
|
||||
confirmations += 1
|
||||
elif direction == 'SELL' and macd['histogram'] < 0:
|
||||
confirmations += 1
|
||||
|
||||
# 布林带确认
|
||||
if bollinger:
|
||||
if direction == 'BUY' and current_price < bollinger['middle']:
|
||||
confirmations += 1
|
||||
elif direction == 'SELL' and current_price > bollinger['middle']:
|
||||
confirmations += 1
|
||||
|
||||
# 多个指标确认时,增加信号强度
|
||||
if confirmations >= 2:
|
||||
signal_strength += 2
|
||||
reasons.append(f"{confirmations}个指标确认")
|
||||
|
||||
# 多周期共振加分:如果4H趋势与信号方向一致,增加信号强度
|
||||
# 4H趋势确认加分
|
||||
if direction and trend_4h:
|
||||
if (direction == 'BUY' and trend_4h == 'up') or (direction == 'SELL' and trend_4h == 'down'):
|
||||
signal_strength += 2
|
||||
signal_strength += TREND_SIGNAL_WEIGHTS['4h_trend_confirmation']
|
||||
reasons.append("4H周期共振确认")
|
||||
elif (direction == 'BUY' and trend_4h == 'down') or (direction == 'SELL' and trend_4h == 'up'):
|
||||
# 逆势信号,降低信号强度或直接拒绝
|
||||
signal_strength -= 3
|
||||
reasons.append("⚠️ 逆4H趋势,信号强度降低")
|
||||
# 逆势信号,直接拒绝
|
||||
signal_strength = 0
|
||||
reasons.append("❌ 逆4H趋势,拒绝交易")
|
||||
|
||||
# 判断是否应该交易(信号强度 >= 7 才交易,提高胜率)
|
||||
min_signal_strength = config.TRADING_CONFIG.get('MIN_SIGNAL_STRENGTH', 7)
|
||||
should_trade = signal_strength >= min_signal_strength
|
||||
should_trade = signal_strength >= min_signal_strength and direction is not None
|
||||
|
||||
# 如果信号方向与4H趋势相反,直接拒绝交易
|
||||
if direction and trend_4h:
|
||||
|
|
@ -464,16 +407,72 @@ class TradingStrategy:
|
|||
reasons.append("❌ 禁止逆4H趋势交易")
|
||||
|
||||
if not should_trade and direction:
|
||||
reasons.append(f"信号强度不足({signal_strength}/10)")
|
||||
reasons.append(f"信号强度不足({signal_strength}/10,需要≥{min_signal_strength})")
|
||||
|
||||
return {
|
||||
'should_trade': should_trade,
|
||||
'direction': direction,
|
||||
'reason': ', '.join(reasons) if reasons else '无明确信号',
|
||||
'strength': signal_strength,
|
||||
'trend_4h': trend_4h # 添加4H趋势信息,供推荐器使用
|
||||
'trend_4h': trend_4h, # 添加4H趋势信息,供推荐器使用
|
||||
'strategy_type': 'trend_following' # 标记策略类型
|
||||
}
|
||||
|
||||
def _judge_trend_4h(self, price_4h: float, ema20_4h: Optional[float], ema50_4h: Optional[float], macd_4h: Optional[Dict]) -> str:
|
||||
"""
|
||||
使用多指标投票机制判断4H趋势(避免单一指标误导)
|
||||
|
||||
Args:
|
||||
price_4h: 4H周期价格
|
||||
ema20_4h: 4H周期EMA20
|
||||
ema50_4h: 4H周期EMA50
|
||||
macd_4h: 4H周期MACD数据
|
||||
|
||||
Returns:
|
||||
'up', 'down', 'neutral'
|
||||
"""
|
||||
if ema20_4h is None:
|
||||
return 'neutral'
|
||||
|
||||
score = 0
|
||||
total_indicators = 0
|
||||
|
||||
# 指标1:价格 vs EMA20
|
||||
if price_4h > ema20_4h:
|
||||
score += 1
|
||||
elif price_4h < ema20_4h:
|
||||
score -= 1
|
||||
total_indicators += 1
|
||||
|
||||
# 指标2:EMA20 vs EMA50
|
||||
if ema50_4h is not None:
|
||||
if ema20_4h > ema50_4h:
|
||||
score += 1
|
||||
elif ema20_4h < ema50_4h:
|
||||
score -= 1
|
||||
total_indicators += 1
|
||||
|
||||
# 指标3:MACD histogram
|
||||
if macd_4h and isinstance(macd_4h, dict):
|
||||
macd_hist = macd_4h.get('histogram', 0)
|
||||
if macd_hist > 0:
|
||||
score += 1
|
||||
elif macd_hist < 0:
|
||||
score -= 1
|
||||
total_indicators += 1
|
||||
|
||||
# 多数投票:如果score >= 2,趋势向上;如果score <= -2,趋势向下;否则中性
|
||||
if total_indicators == 0:
|
||||
return 'neutral'
|
||||
|
||||
# 至少需要2个指标确认
|
||||
if score >= 2:
|
||||
return 'up'
|
||||
elif score <= -2:
|
||||
return 'down'
|
||||
else:
|
||||
return 'neutral'
|
||||
|
||||
async def _periodic_sync_positions(self):
|
||||
"""
|
||||
定期同步币安持仓状态(独立任务,不依赖市场扫描)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user