优化策略
This commit is contained in:
parent
5e738c4c2d
commit
cd001c0c00
@ -13,6 +13,7 @@
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from typing import Dict, Any, Optional, List
|
||||
from datetime import datetime
|
||||
@ -128,6 +129,100 @@ class MarketSignalAnalyzer:
|
||||
- **级别背离**:小级别反转 + 大级别强劲 → 谨慎,可能是假突破
|
||||
- **强反转信号**:多个小级别同时反转 + 大级别走弱 → **重点考虑反手**
|
||||
|
||||
## 🚨 趋势反转信号处理(新增 - 极其重要!)
|
||||
|
||||
### 系统会自动检测以下反转信号:
|
||||
1. **RSI 背离**:价格创新高/低但 RSI 不创新高/低(权重2)
|
||||
2. **MACD 柱状图缩短**:动能衰竭信号(权重1)
|
||||
3. **MACD 金叉/死叉**:趋势反转信号(权重1)
|
||||
4. **量价背离**:价格上涨但成交量下降(权重1)
|
||||
5. **关键K线形态**:吞没、锤子线、十字星(权重1-2)
|
||||
6. **多周期趋势不一致**:小周期反转但大周期未反应(权重1)
|
||||
|
||||
### 反转信号出现时的处理规则(必须遵守!)
|
||||
|
||||
**🔴 当检测到反转信号(置信度 ≥ 60%)时:**
|
||||
|
||||
1. **立即停止原方向新开仓**
|
||||
- 如果之前做多,现在检测到看跌反转 → **严禁开新多单**
|
||||
- 如果之前做空,现在检测到看涨反转 → **严禁开新空单**
|
||||
|
||||
2. **评估现有持仓**
|
||||
- 如果有同向持仓 → **建议立即平仓或收紧止损**
|
||||
- 不要等待反弹/回调,反转可能很快发生
|
||||
|
||||
3. **考虑反手操作(仅当反转置信度 ≥ 70%时)**
|
||||
- 可以考虑平掉旧仓位,同时开新方向仓位
|
||||
- 或者先平仓观望,等待反转确认后再入场
|
||||
|
||||
4. **观望等待确认**
|
||||
- 如果不确定,先平仓观望
|
||||
- 等待价格明确突破关键位再入场
|
||||
|
||||
**⚠️ 常见错误(必须避免):**
|
||||
- ❌ 检测到反转信号后继续原方向加仓("摊平成本")
|
||||
- ❌ 认为是"假突破"而忽略反转信号
|
||||
- ❌ 等待反弹/回调到更好价格(可能等不到)
|
||||
|
||||
## 🌅 趋势阶段判断(新增 - 避免趋势晚期被套)
|
||||
|
||||
### 系统会自动判断趋势处于哪个阶段:
|
||||
- **早期**:刚突破关键位,均线刚开始排列,动能开始释放
|
||||
- **中期**:均线排列稳定,价格沿趋势移动,量能健康
|
||||
- **晚期**:价格过度延伸,RSI极端区,量价背离,多次假突破
|
||||
|
||||
### 不同阶段的交易规则:
|
||||
|
||||
**✅ 早期阶段(可积极入场):**
|
||||
- 可以顺势轻仓入场
|
||||
- 设置止损后可持有更长时间
|
||||
- 目标可看更大空间(3-5%)
|
||||
|
||||
**✅ 中期阶段(稳健持仓):**
|
||||
- 等待回调/反弹入场
|
||||
- 顺势持仓,让利润奔跑
|
||||
- 不要被小波动洗出
|
||||
|
||||
**🔴 晚期阶段(强制谨慎):**
|
||||
- **严禁追涨/追空开新仓**
|
||||
- **现有盈利持仓建议逐步止盈**
|
||||
- **等待明确反转信号后再决策**
|
||||
- 宁可错过最后一段利润,也不要被套在高位/低位
|
||||
|
||||
### 晚期阶段的识别信号(系统自动检测):
|
||||
1. EMA 间距过大(> 3%)
|
||||
2. RSI 进入极端区(> 70 或 < 30)
|
||||
3. 价格偏离 EMA20 > 5%
|
||||
4. 量价背离(价格上涨但成交量下降)
|
||||
5. 连续 4 根以上同向K线
|
||||
6. ATR 收缩(动能衰竭)
|
||||
|
||||
## 📊 震荡区间交易规则(新增 - 基于明确区间交易)
|
||||
|
||||
### 系统会自动计算震荡区间:
|
||||
- **支撑位**:价格通道下沿 + 成交量密集区 + 布林带下轨
|
||||
- **压力位**:价格通道上沿 + 成交量密集区 + 布林带上轨
|
||||
- **区间宽度**:用于判断震荡有效性(< 5% 为有效震荡)
|
||||
|
||||
### 震荡市交易规则(当系统判断为震荡市时):
|
||||
|
||||
**✅ 推荐操作:**
|
||||
1. **下沿挂多单**:价格接近支撑位时,挂限价多单
|
||||
2. **上沿挂空单**:价格接近压力位时,挂限价空单
|
||||
3. **目标明确**:盈利目标就是对岸边界
|
||||
4. **快进快出**:持仓时间 30分钟-2小时
|
||||
|
||||
**🔴 严禁操作:**
|
||||
1. **追涨杀跌**:价格突破边界后不要追,通常会回落
|
||||
2. **期待大行情**:震荡市无大趋势,不要贪婪
|
||||
3. **持仓过久**:区间内快速进出,不要拿着不动
|
||||
4. **逆势加仓**:亏损后不要加仓"摊平成本"
|
||||
|
||||
### 震荡区间突破的处理:
|
||||
- 放量突破 + 多周期确认 → 可能转为趋势市,切换策略
|
||||
- 无量突破 + 快速回落 → 假突破,继续震荡策略
|
||||
- 区间收口 + 波动率下降 → 即将变盘,观望等待
|
||||
|
||||
## 🚨 铁律(违反即失败)
|
||||
1. **盈亏比第一**:所有交易必须满足盈亏比 ≥ 1:1.2
|
||||
- 盈亏比 = (目标盈利 - 入场价) / (入场价 - 止损价)
|
||||
@ -953,6 +1048,64 @@ class MarketSignalAnalyzer:
|
||||
context_parts.append(f"\n## 趋势位置分析")
|
||||
context_parts.append(trend_position_analysis)
|
||||
|
||||
# ========== 新增:震荡区间检测 ==========
|
||||
range_zone = self._detect_range_zone(data)
|
||||
if range_zone['is_ranging']:
|
||||
context_parts.append(f"\n## 🔔 震荡区间检测(重要!)")
|
||||
context_parts.append(f"**状态**: 震荡市(置信度: {range_zone['confidence']}%)")
|
||||
if range_zone['support_level'] and range_zone['resistance_level']:
|
||||
context_parts.append(f"**支撑位**: ${range_zone['support_level']:,.2f}")
|
||||
context_parts.append(f"**压力位**: ${range_zone['resistance_level']:,.2f}")
|
||||
context_parts.append(f"**区间宽度**: {range_zone['range_width_pct']:.2f}%")
|
||||
if range_zone['volume_profile_support']:
|
||||
context_parts.append(f"**成交量密集区支撑**: ${range_zone['volume_profile_support']:,.2f}")
|
||||
if range_zone['volume_profile_resistance']:
|
||||
context_parts.append(f"**成交量密集区压力**: ${range_zone['volume_profile_resistance']:,.2f}")
|
||||
context_parts.append(f"**分析**: {range_zone['analysis']}")
|
||||
context_parts.append(f"\n**震荡市交易策略**:")
|
||||
context_parts.append(f" → 下沿附近挂多单,上沿附近挂空单")
|
||||
context_parts.append(f" → 目标: 对岸边界,快进快出")
|
||||
context_parts.append(f" → 严禁追涨杀跌!")
|
||||
|
||||
# ========== 新增:趋势反转检测 ==========
|
||||
reversal_detection = self._detect_trend_reversal(data)
|
||||
if reversal_detection['is_reversing']:
|
||||
context_parts.append(f"\n## ⚠️ 趋势反转信号(非常重要!)")
|
||||
context_parts.append(f"**检测到反转信号**!置信度: {reversal_detection['confidence']}%")
|
||||
if reversal_detection['reversal_type'] == 'bullish_reversal':
|
||||
context_parts.append(f"**反转类型**: 看涨反转 📈")
|
||||
else:
|
||||
context_parts.append(f"**反转类型**: 看跌反转 📉")
|
||||
context_parts.append(f"\n**反转信号详情**:")
|
||||
for sig in reversal_detection['signals'][:5]: # 最多显示5个信号
|
||||
context_parts.append(f" - [{sig['type']}] {sig['desc']} (权重: {sig['weight']})")
|
||||
context_parts.append(f"\n**🚨 反转信号处理规则**:")
|
||||
context_parts.append(f" → 现有同向持仓建议平仓")
|
||||
context_parts.append(f" → 考虑反方向开仓(需等待确认)")
|
||||
context_parts.append(f" → 或者暂时观望,等待反转确认")
|
||||
context_parts.append(f" → 严禁继续原方向开新仓!")
|
||||
|
||||
# ========== 新增:趋势阶段检测 ==========
|
||||
trend_stage = self._detect_trend_stage(data)
|
||||
if trend_stage['stage'] != 'unknown':
|
||||
stage_emoji = {'early': '🌱', 'middle': '🔄', 'late': '🌅'}.get(trend_stage['stage'], '❓')
|
||||
stage_name = {'early': '早期', 'middle': '中期', 'late': '晚期'}.get(trend_stage['stage'], '未知')
|
||||
context_parts.append(f"\n## 趋势阶段分析")
|
||||
context_parts.append(f"**当前阶段**: {stage_emoji} {stage_name}(置信度: {trend_stage['confidence']}%)")
|
||||
context_parts.append(f"**分析**: {trend_stage['analysis']}")
|
||||
|
||||
if trend_stage['stage'] == 'late':
|
||||
context_parts.append(f"\n**⚠️ 晚期阶段警告**:")
|
||||
context_parts.append(f" → 趋势可能即将反转或进入震荡")
|
||||
context_parts.append(f" → 严禁追涨/追空开新仓")
|
||||
context_parts.append(f" → 现有盈利持仓建议逐步止盈")
|
||||
context_parts.append(f" → 等待明确反转信号后再决策")
|
||||
elif trend_stage['stage'] == 'early':
|
||||
context_parts.append(f"\n**✅ 早期阶段机会**:")
|
||||
context_parts.append(f" → 趋势刚启动,可顺势轻仓入场")
|
||||
context_parts.append(f" → 设置止损后可持有更长时间")
|
||||
context_parts.append(f" → 目标可看更大空间")
|
||||
|
||||
return "\n".join(context_parts)
|
||||
|
||||
async def _get_news_context(self, symbol: str) -> str:
|
||||
@ -1740,3 +1893,682 @@ class MarketSignalAnalyzer:
|
||||
lines.append(f"**布林带开口**: 宽度 {bb_width:.1f}%,趋势延续")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def _detect_range_zone(self, data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
||||
"""
|
||||
检测震荡区间 - 计算明确的支撑位和压力位
|
||||
|
||||
使用多种方法综合判断:
|
||||
1. 价格通道(最近N根K线的最高/最低价)
|
||||
2. 成交量密集区(Volume Profile)
|
||||
3. 布林带
|
||||
4. EMA支撑/压力
|
||||
"""
|
||||
result = {
|
||||
'is_ranging': False,
|
||||
'support_level': None,
|
||||
'resistance_level': None,
|
||||
'range_width_pct': None,
|
||||
'confidence': 0,
|
||||
'volume_profile_support': None,
|
||||
'volume_profile_resistance': None,
|
||||
'analysis': ''
|
||||
}
|
||||
|
||||
try:
|
||||
df_30m = data.get('30m')
|
||||
df_1h = data.get('1h')
|
||||
df_15m = data.get('15m')
|
||||
|
||||
if df_30m is None or len(df_30m) < 48: # 需要至少48根K线(24小时)
|
||||
return result
|
||||
|
||||
current_price = float(df_30m['close'].iloc[-1])
|
||||
|
||||
# ========== 1. 价格通道分析 ==========
|
||||
# 使用最近24-48根K线(12-24小时)计算价格通道
|
||||
lookback_periods = [24, 36, 48]
|
||||
price_channels = []
|
||||
|
||||
for period in lookback_periods:
|
||||
if len(df_30m) >= period:
|
||||
period_data = df_30m.iloc[-period:]
|
||||
high = period_data['high'].max()
|
||||
low = period_data['low'].min()
|
||||
price_channels.append({'high': high, 'low': low, 'width': high - low})
|
||||
|
||||
# 选择波动最稳定的通道(宽度变化最小的)
|
||||
if price_channels:
|
||||
avg_width = sum(pc['width'] for pc in price_channels) / len(price_channels)
|
||||
selected_channel = min(price_channels,
|
||||
key=lambda pc: abs(pc['width'] - avg_width))
|
||||
support = selected_channel['low']
|
||||
resistance = selected_channel['high']
|
||||
range_width = resistance - support
|
||||
range_width_pct = (range_width / current_price) * 100
|
||||
|
||||
# 震荡区间判断标准
|
||||
# 1. 区间宽度 < 5%(震荡市)
|
||||
# 2. 价格在区间中位数附近
|
||||
# 3. EMA 纠缠
|
||||
is_narrow_range = range_width_pct < 5.0
|
||||
price_in_middle = (current_price - support) / range_width > 0.3 and \
|
||||
(current_price - support) / range_width < 0.7
|
||||
|
||||
# EMA 纠缠检查
|
||||
ema5 = df_30m['ma5'].iloc[-1] if 'ma5' in df_30m.columns else None
|
||||
ema10 = df_30m['ma10'].iloc[-1] if 'ma10' in df_30m.columns else None
|
||||
ema20 = df_30m['ma20'].iloc[-1] if 'ma20' in df_30m.columns else None
|
||||
ema_entangled = False
|
||||
if all([ema5, ema10, ema20]):
|
||||
ema_spread = (max(ema5, ema10, ema20) - min(ema5, ema10, ema20)) / current_price * 100
|
||||
ema_entangled = ema_spread < 1.0 # EMA 排列差距 < 1%
|
||||
|
||||
# ========== 2. 成交量密集区分析 ==========
|
||||
volume_profile_support = None
|
||||
volume_profile_resistance = None
|
||||
|
||||
if len(df_30m) >= 48:
|
||||
# 找出成交量最大的价格区间
|
||||
df_30m_copy = df_30m.iloc[-48:].copy()
|
||||
df_30m_copy['avg_price'] = (df_30m_copy['high'] + df_30m_copy['low'] + df_30m_copy['close']) / 3
|
||||
df_30m_copy['volume_weight'] = df_30m_copy['volume'] * df_30m_copy['avg_price']
|
||||
|
||||
# 按价格分层,找出高成交量区域
|
||||
price_bins = pd.cut(df_30m_copy['avg_price'], bins=10)
|
||||
volume_by_price = df_30m_copy.groupby(price_bins, observed=True)['volume'].sum()
|
||||
|
||||
if len(volume_by_price) > 0:
|
||||
# 高成交量区作为支撑/压力
|
||||
max_vol_bin = volume_by_price.idxmax()
|
||||
if max_vol_bin is not None:
|
||||
vp_level = (max_vol_bin.left + max_vol_bin.right) / 2
|
||||
if vp_level < current_price * 0.98:
|
||||
volume_profile_support = float(vp_level)
|
||||
elif vp_level > current_price * 1.02:
|
||||
volume_profile_resistance = float(vp_level)
|
||||
|
||||
# ========== 3. 布林带支撑/压力 ==========
|
||||
bb_support = None
|
||||
bb_resistance = None
|
||||
if 'bb_lower' in df_30m.columns and 'bb_upper' in df_30m.columns:
|
||||
bb_support = float(df_30m['bb_lower'].iloc[-1])
|
||||
bb_resistance = float(df_30m['bb_upper'].iloc[-1])
|
||||
|
||||
# ========== 4. 关键价格点综合 ==========
|
||||
# 综合多个指标得出最可靠的支撑/压力位
|
||||
support_candidates = []
|
||||
resistance_candidates = []
|
||||
|
||||
if support:
|
||||
support_candidates.append(support)
|
||||
if volume_profile_support:
|
||||
support_candidates.append(volume_profile_support)
|
||||
if bb_support:
|
||||
support_candidates.append(bb_support)
|
||||
|
||||
if resistance:
|
||||
resistance_candidates.append(resistance)
|
||||
if volume_profile_resistance:
|
||||
resistance_candidates.append(volume_profile_resistance)
|
||||
if bb_resistance:
|
||||
resistance_candidates.append(bb_resistance)
|
||||
|
||||
# 取中位数作为最终的支撑/压力位
|
||||
final_support = np.median(support_candidates) if support_candidates else None
|
||||
final_resistance = np.median(resistance_candidates) if resistance_candidates else None
|
||||
|
||||
# ========== 5. 计算置信度 ==========
|
||||
confidence = 0
|
||||
reasons = []
|
||||
|
||||
if is_narrow_range:
|
||||
confidence += 30
|
||||
reasons.append(f"区间窄({range_width_pct:.1f}%)")
|
||||
|
||||
if price_in_middle:
|
||||
confidence += 20
|
||||
reasons.append("价格在中部")
|
||||
|
||||
if ema_entangled:
|
||||
confidence += 25
|
||||
reasons.append("EMA纠缠")
|
||||
|
||||
# 成交量分布检查 - 如果成交量在区间两端较小,说明是有效震荡
|
||||
if len(df_30m) >= 24:
|
||||
recent_vol = df_30m['volume'].iloc[-12:].mean()
|
||||
older_vol = df_30m['volume'].iloc[-24:-12].mean()
|
||||
if abs(recent_vol - older_vol) / older_vol < 0.3:
|
||||
confidence += 15
|
||||
reasons.append("成交量平稳")
|
||||
|
||||
# 价格反弹次数 - 检查在支撑/压力位附近是否有多次反弹
|
||||
if final_support and final_resistance:
|
||||
bounce_count = 0
|
||||
for i in range(-24, 0):
|
||||
if i >= -len(df_30m):
|
||||
row = df_30m.iloc[i]
|
||||
# 检查是否在支撑位附近反弹
|
||||
if abs(row['low'] - final_support) / final_support < 0.005 and row['close'] > row['open']:
|
||||
bounce_count += 1
|
||||
# 检查是否在压力位附近回落
|
||||
if abs(row['high'] - final_resistance) / final_resistance < 0.005 and row['close'] < row['open']:
|
||||
bounce_count += 1
|
||||
|
||||
if bounce_count >= 2:
|
||||
confidence += 10
|
||||
reasons.append(f"边界反弹{bounce_count}次")
|
||||
|
||||
result.update({
|
||||
'is_ranging': confidence >= 60,
|
||||
'support_level': float(final_support) if final_support else None,
|
||||
'resistance_level': float(final_resistance) if final_resistance else None,
|
||||
'range_width_pct': range_width_pct,
|
||||
'confidence': confidence,
|
||||
'volume_profile_support': volume_profile_support,
|
||||
'volume_profile_resistance': volume_profile_resistance,
|
||||
'analysis': f"震荡判断: {confidence}% ({', '.join(reasons) if reasons else '无'})"
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"震荡区间检测失败: {e}")
|
||||
import traceback
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
return result
|
||||
|
||||
def _detect_trend_reversal(self, data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
||||
"""
|
||||
检测趋势反转信号
|
||||
|
||||
综合多个指标判断趋势是否可能反转:
|
||||
1. RSI 背离(价格创新高但RSI不创新高 / 价格创新低但RSI不创新低)
|
||||
2. MACD 柱状图缩短/背离
|
||||
3. 量价背离(价格上涨但成交量下降)
|
||||
4. 关键K线形态(吞没、锤子线、十字星等)
|
||||
5. 多周期趋势不一致
|
||||
"""
|
||||
result = {
|
||||
'is_reversing': False,
|
||||
'reversal_type': None, # 'bullish_reversal' or 'bearish_reversal'
|
||||
'confidence': 0,
|
||||
'signals': [],
|
||||
'analysis': ''
|
||||
}
|
||||
|
||||
try:
|
||||
df_15m = data.get('15m')
|
||||
df_30m = data.get('30m')
|
||||
df_1h = data.get('1h')
|
||||
|
||||
if df_15m is None or len(df_15m) < 30:
|
||||
return result
|
||||
|
||||
reversal_signals = []
|
||||
bullish_signals = 0
|
||||
bearish_signals = 0
|
||||
|
||||
# ========== 1. RSI 背离检测 ==========
|
||||
if 'rsi' in df_15m.columns and len(df_15m) >= 20:
|
||||
recent_5 = df_15m.iloc[-5:]
|
||||
prev_5 = df_15m.iloc[-15:-10]
|
||||
|
||||
# 顶背离(看跌反转)
|
||||
recent_high = recent_5['high'].max()
|
||||
recent_rsi_at_high = recent_5.loc[recent_5['high'] == recent_high, 'rsi'].values[0]
|
||||
prev_high = prev_5['high'].max()
|
||||
prev_rsi_at_high = prev_5.loc[prev_5['high'] == prev_high, 'rsi'].values[0]
|
||||
|
||||
if recent_high > prev_high and recent_rsi_at_high < prev_rsi_at_high:
|
||||
bearish_signals += 2
|
||||
reversal_signals.append({
|
||||
'type': 'rsi_divergence',
|
||||
'direction': 'bearish',
|
||||
'weight': 2,
|
||||
'desc': 'RSI顶背离:价格创新高但RSI不创新高'
|
||||
})
|
||||
|
||||
# 底背离(看涨反转)
|
||||
recent_low = recent_5['low'].min()
|
||||
recent_rsi_at_low = recent_5.loc[recent_5['low'] == recent_low, 'rsi'].values[0]
|
||||
prev_low = prev_5['low'].min()
|
||||
prev_rsi_at_low = prev_5.loc[prev_5['low'] == prev_low, 'rsi'].values[0]
|
||||
|
||||
if recent_low < prev_low and recent_rsi_at_low > prev_rsi_at_low:
|
||||
bullish_signals += 2
|
||||
reversal_signals.append({
|
||||
'type': 'rsi_divergence',
|
||||
'direction': 'bullish',
|
||||
'weight': 2,
|
||||
'desc': 'RSI底背离:价格创新低但RSI不创新低'
|
||||
})
|
||||
|
||||
# ========== 2. MACD 柱状图分析 ==========
|
||||
if 'macd_hist' in df_15m.columns and len(df_15m) >= 10:
|
||||
hist_recent = df_15m['macd_hist'].iloc[-3:].values
|
||||
hist_prev = df_15m['macd_hist'].iloc[-6:-3].values
|
||||
|
||||
# MACD 柱状图缩短 = 动能衰竭
|
||||
if all(h > 0 for h in hist_prev): # 之前是正向
|
||||
if hist_recent[2] < hist_recent[1] < hist_recent[0]: # 持续缩短
|
||||
bearish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'macd_histogram',
|
||||
'direction': 'bearish',
|
||||
'weight': 1,
|
||||
'desc': 'MACD柱状图持续缩短:上涨动能衰竭'
|
||||
})
|
||||
|
||||
if all(h < 0 for h in hist_prev): # 之前是负向
|
||||
if hist_recent[2] > hist_recent[1] > hist_recent[0]: # 持续收窄
|
||||
bullish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'macd_histogram',
|
||||
'direction': 'bullish',
|
||||
'weight': 1,
|
||||
'desc': 'MACD柱状图持续收窄:下跌动能衰竭'
|
||||
})
|
||||
|
||||
# MACD 金叉/死叉
|
||||
if len(df_15m) >= 2:
|
||||
macd_current = df_15m['macd'].iloc[-1]
|
||||
signal_current = df_15m['macd_signal'].iloc[-1]
|
||||
macd_prev = df_15m['macd'].iloc[-2]
|
||||
signal_prev = df_15m['macd_signal'].iloc[-2]
|
||||
|
||||
# 金叉
|
||||
if macd_prev <= signal_prev and macd_current > signal_current:
|
||||
bullish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'macd_cross',
|
||||
'direction': 'bullish',
|
||||
'weight': 1,
|
||||
'desc': 'MACD金叉'
|
||||
})
|
||||
|
||||
# 死叉
|
||||
if macd_prev >= signal_prev and macd_current < signal_current:
|
||||
bearish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'macd_cross',
|
||||
'direction': 'bearish',
|
||||
'weight': 1,
|
||||
'desc': 'MACD死叉'
|
||||
})
|
||||
|
||||
# ========== 3. 量价背离检测 ==========
|
||||
if 'volume' in df_15m.columns and len(df_15m) >= 10:
|
||||
recent_price_change = (df_15m['close'].iloc[-1] - df_15m['close'].iloc[-5]) / df_15m['close'].iloc[-5]
|
||||
recent_volume = df_15m['volume'].iloc[-5:].mean()
|
||||
older_volume = df_15m['volume'].iloc[-10:-5].mean()
|
||||
|
||||
# 价格上涨但成交量下降(量价背离)
|
||||
if recent_price_change > 0.01 and recent_volume < older_volume * 0.8:
|
||||
bearish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'volume_divergence',
|
||||
'direction': 'bearish',
|
||||
'weight': 1,
|
||||
'desc': '量价背离:价格上涨但成交量萎缩'
|
||||
})
|
||||
|
||||
# 价格下跌但成交量下降(可能见底)
|
||||
if recent_price_change < -0.01 and recent_volume < older_volume * 0.7:
|
||||
bullish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'volume_divergence',
|
||||
'direction': 'bullish',
|
||||
'weight': 1,
|
||||
'desc': '下跌缩量:抛压枯竭,可能见底'
|
||||
})
|
||||
|
||||
# ========== 4. 关键K线形态检测 ==========
|
||||
if len(df_15m) >= 3:
|
||||
latest = df_15m.iloc[-1]
|
||||
prev = df_15m.iloc[-2]
|
||||
|
||||
# 吞没形态
|
||||
open_latest, close_latest = latest['open'], latest['close']
|
||||
open_prev, close_prev = prev['open'], prev['close']
|
||||
|
||||
# 阳包阴(看涨)
|
||||
if (close_latest > open_latest and # 当前是阳线
|
||||
close_prev < open_prev and # 前一个是阴线
|
||||
open_latest <= close_prev and # 开盘价低于前一个收盘价
|
||||
close_latest >= open_prev): # 收盘价高于前一个开盘价
|
||||
bullish_signals += 2
|
||||
reversal_signals.append({
|
||||
'type': 'candlestick',
|
||||
'direction': 'bullish',
|
||||
'weight': 2,
|
||||
'desc': '阳包阴吞没形态(强反转信号)'
|
||||
})
|
||||
|
||||
# 阴包阳(看跌)
|
||||
if (close_latest < open_latest and # 当前是阴线
|
||||
close_prev > open_prev and # 前一个是阳线
|
||||
open_latest >= close_prev and # 开盘价高于前一个收盘价
|
||||
close_latest <= open_prev): # 收盘价低于前一个开盘价
|
||||
bearish_signals += 2
|
||||
reversal_signals.append({
|
||||
'type': 'candlestick',
|
||||
'direction': 'bearish',
|
||||
'weight': 2,
|
||||
'desc': '阴包阳吞没形态(强反转信号)'
|
||||
})
|
||||
|
||||
# 锤子线/倒锤子
|
||||
body_size = abs(close_latest - open_latest)
|
||||
upper_shadow = df_15m['high'].iloc[-1] - max(open_latest, close_latest)
|
||||
lower_shadow = min(open_latest, close_latest) - df_15m['low'].iloc[-1]
|
||||
|
||||
# 锤子线(看涨)
|
||||
if lower_shadow >= body_size * 2 and upper_shadow < body_size * 0.5:
|
||||
bullish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'candlestick',
|
||||
'direction': 'bullish',
|
||||
'weight': 1,
|
||||
'desc': '锤子线(底部反转信号)'
|
||||
})
|
||||
|
||||
# 倒锤子(看跌)
|
||||
if upper_shadow >= body_size * 2 and lower_shadow < body_size * 0.5:
|
||||
bearish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'candlestick',
|
||||
'direction': 'bearish',
|
||||
'weight': 1,
|
||||
'desc': '倒锤子线(顶部反转信号)'
|
||||
})
|
||||
|
||||
# ========== 5. 多周期趋势不一致 ==========
|
||||
trend_15m = self._get_trend_direction(df_15m)
|
||||
trend_30m = self._get_trend_direction(df_30m)
|
||||
trend_1h = self._get_trend_direction(df_1h)
|
||||
|
||||
# 小周期反转但大周期未反应
|
||||
if trend_15m and trend_1h and trend_15m != trend_1h:
|
||||
if trend_15m == 'bull' and trend_1h == 'bear':
|
||||
bullish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'timeframe_divergence',
|
||||
'direction': 'bullish',
|
||||
'weight': 1,
|
||||
'desc': '15分钟转多但1小时仍看空(潜在反转)'
|
||||
})
|
||||
elif trend_15m == 'bear' and trend_1h == 'bull':
|
||||
bearish_signals += 1
|
||||
reversal_signals.append({
|
||||
'type': 'timeframe_divergence',
|
||||
'direction': 'bearish',
|
||||
'weight': 1,
|
||||
'desc': '15分钟转空但1小时仍看多(潜在反转)'
|
||||
})
|
||||
|
||||
# ========== 计算反转信号强度 ==========
|
||||
total_signals = len(reversal_signals)
|
||||
if total_signals >= 3:
|
||||
# 至少3个反转信号才认为可能反转
|
||||
if bullish_signals >= bearish_signals + 2:
|
||||
result['is_reversing'] = True
|
||||
result['reversal_type'] = 'bullish_reversal'
|
||||
result['confidence'] = min(90, bullish_signals * 15)
|
||||
result['signals'] = [s for s in reversal_signals if s['direction'] == 'bullish']
|
||||
elif bearish_signals >= bullish_signals + 2:
|
||||
result['is_reversing'] = True
|
||||
result['reversal_type'] = 'bearish_reversal'
|
||||
result['confidence'] = min(90, bearish_signals * 15)
|
||||
result['signals'] = [s for s in reversal_signals if s['direction'] == 'bearish']
|
||||
|
||||
if reversal_signals:
|
||||
result['analysis'] = f"检测到 {len(reversal_signals)} 个反转信号"
|
||||
else:
|
||||
result['analysis'] = "无反转信号"
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"趋势反转检测失败: {e}")
|
||||
import traceback
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
return result
|
||||
|
||||
def _get_trend_direction(self, df: pd.DataFrame) -> str:
|
||||
"""获取趋势方向:bull/bear/neutral"""
|
||||
if df is None or len(df) < 10:
|
||||
return 'neutral'
|
||||
|
||||
try:
|
||||
# 使用EMA判断
|
||||
ma5 = df['ma5'].iloc[-1] if 'ma5' in df.columns else None
|
||||
ma10 = df['ma10'].iloc[-1] if 'ma10' in df.columns else None
|
||||
ma20 = df['ma20'].iloc[-1] if 'ma20' in df.columns else None
|
||||
|
||||
if ma5 and ma10 and ma20:
|
||||
if ma5 > ma10 > ma20:
|
||||
return 'bull'
|
||||
elif ma5 < ma10 < ma20:
|
||||
return 'bear'
|
||||
|
||||
# 使用MACD判断
|
||||
if 'macd' in df.columns and 'macd_signal' in df.columns:
|
||||
macd = df['macd'].iloc[-1]
|
||||
signal = df['macd_signal'].iloc[-1]
|
||||
|
||||
if macd > signal and macd > 0:
|
||||
return 'bull'
|
||||
elif macd < signal and macd < 0:
|
||||
return 'bear'
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(f"趋势方向判断失败: {e}")
|
||||
|
||||
return 'neutral'
|
||||
|
||||
def _detect_trend_stage(self, data: Dict[str, pd.DataFrame]) -> Dict[str, Any]:
|
||||
"""
|
||||
检测趋势阶段:早期/中期/晚期
|
||||
|
||||
判断标准:
|
||||
1. 早期:刚突破关键位,均线刚开始排列,动能开始释放
|
||||
2. 中期:均线排列稳定,价格沿趋势移动,量能健康
|
||||
3. 晚期:价格过度延伸,RSI极端区,量价背离,多次假突破
|
||||
"""
|
||||
result = {
|
||||
'stage': 'unknown', # 'early', 'middle', 'late'
|
||||
'confidence': 0,
|
||||
'signals': [],
|
||||
'analysis': ''
|
||||
}
|
||||
|
||||
try:
|
||||
df_30m = data.get('30m')
|
||||
df_1h = data.get('1h')
|
||||
|
||||
if df_30m is None or len(df_30m) < 30:
|
||||
return result
|
||||
|
||||
current_price = float(df_30m['close'].iloc[-1])
|
||||
|
||||
stage_signals = []
|
||||
early_score = 0
|
||||
middle_score = 0
|
||||
late_score = 0
|
||||
|
||||
# ========== 1. EMA 排列状态 ==========
|
||||
ema5 = df_30m['ma5'].iloc[-1] if 'ma5' in df_30m.columns else None
|
||||
ema10 = df_30m['ma10'].iloc[-1] if 'ma10' in df_30m.columns else None
|
||||
ema20 = df_30m['ma20'].iloc[-1] if 'ma20' in df_30m.columns else None
|
||||
ema50 = df_30m['ma50'].iloc[-1] if 'ma50' in df_30m.columns else None
|
||||
|
||||
if all([ema5, ema10, ema20, ema50]):
|
||||
# 检查EMA排列是否形成
|
||||
if ema5 > ema10 > ema20 > ema50:
|
||||
# 多头排列
|
||||
# 检查排列刚刚形成(早期)还是已经稳定(中期/晚期)
|
||||
ema5_cross_ma20 = False
|
||||
if len(df_30m) >= 10:
|
||||
# 检查最近10根内是否发生过金叉
|
||||
for i in range(-10, 0):
|
||||
if df_30m['ma5'].iloc[i] > df_30m['ma20'].iloc[i]:
|
||||
if i > -10 and df_30m['ma5'].iloc[i-1] <= df_30m['ma20'].iloc[i-1]:
|
||||
ema5_cross_ma20 = True
|
||||
break
|
||||
|
||||
if ema5_cross_ma20:
|
||||
early_score += 30
|
||||
stage_signals.append("EMA排列刚形成(早期)")
|
||||
else:
|
||||
# 检查EMA间距
|
||||
ema_spread = (ema5 - ema20) / ema20 * 100
|
||||
if ema_spread > 3:
|
||||
late_score += 20
|
||||
stage_signals.append(f"EMA间距过大({ema_spread:.1f}%) - 可能过度延伸")
|
||||
else:
|
||||
middle_score += 20
|
||||
stage_signals.append("EMA排列稳定(中期)")
|
||||
|
||||
elif ema5 < ema10 < ema20 < ema50:
|
||||
# 空头排列
|
||||
ema5_cross_ma20 = False
|
||||
if len(df_30m) >= 10:
|
||||
for i in range(-10, 0):
|
||||
if df_30m['ma5'].iloc[i] < df_30m['ma20'].iloc[i]:
|
||||
if i > -10 and df_30m['ma5'].iloc[i-1] >= df_30m['ma20'].iloc[i-1]:
|
||||
ema5_cross_ma20 = True
|
||||
break
|
||||
|
||||
if ema5_cross_ma20:
|
||||
early_score += 30
|
||||
stage_signals.append("EMA排列刚形成(早期)")
|
||||
else:
|
||||
ema_spread = (ema20 - ema5) / ema20 * 100
|
||||
if ema_spread > 3:
|
||||
late_score += 20
|
||||
stage_signals.append(f"EMA间距过大({ema_spread:.1f}%) - 可能过度延伸")
|
||||
else:
|
||||
middle_score += 20
|
||||
stage_signals.append("EMA排列稳定(中期)")
|
||||
|
||||
# ========== 2. RSI 状态 ==========
|
||||
if 'rsi' in df_30m.columns:
|
||||
rsi_current = df_30m['rsi'].iloc[-1]
|
||||
rsi_prev = df_30m['rsi'].iloc[-5:-1].values
|
||||
|
||||
# RSI极端区 - 晚期信号
|
||||
if rsi_current > 70:
|
||||
late_score += 25
|
||||
stage_signals.append(f"RSI超买({rsi_current:.0f}) - 趋势晚期")
|
||||
elif rsi_current < 30:
|
||||
late_score += 25
|
||||
stage_signals.append(f"RSI超卖({rsi_current:.0f}) - 趋势晚期")
|
||||
elif 50 <= rsi_current <= 65:
|
||||
middle_score += 15
|
||||
stage_signals.append(f"RSI健康({rsi_current:.0f}) - 趋势中期")
|
||||
elif 40 <= rsi_current <= 60:
|
||||
early_score += 10
|
||||
stage_signals.append(f"RSI中性({rsi_current:.0f}) - 可能早期")
|
||||
|
||||
# RSI趋势检查
|
||||
if len(rsi_prev) >= 3:
|
||||
rsi_trend = "up" if rsi_current > rsi_prev[-1] else "down" if rsi_current < rsi_prev[-1] else "flat"
|
||||
if rsi_trend == "flat":
|
||||
late_score += 10
|
||||
stage_signals.append("RSI走平 - 动能衰竭")
|
||||
|
||||
# ========== 3. 价格偏离度 ==========
|
||||
if ema20:
|
||||
deviation = abs(current_price - ema20) / ema20 * 100
|
||||
|
||||
if deviation > 5:
|
||||
late_score += 30
|
||||
stage_signals.append(f"价格偏离EMA20 {deviation:.1f}% - 过度延伸")
|
||||
elif deviation > 3:
|
||||
late_score += 15
|
||||
stage_signals.append(f"价格偏离EMA20 {deviation:.1f}% - 警戒区域")
|
||||
elif deviation < 1:
|
||||
if early_score < middle_score: # 只在不是明显早期时加分
|
||||
middle_score += 10
|
||||
stage_signals.append("价格贴近EMA20 - 趋势稳固")
|
||||
|
||||
# ========== 4. 量价关系 ==========
|
||||
if 'volume' in df_30m.columns and len(df_30m) >= 10:
|
||||
recent_vol = df_30m['volume'].iloc[-5:].mean()
|
||||
older_vol = df_30m['volume'].iloc[-10:-5].mean()
|
||||
vol_change = (recent_vol - older_vol) / older_vol * 100
|
||||
|
||||
price_change_5 = (df_30m['close'].iloc[-1] - df_30m['close'].iloc[-5]) / df_30m['close'].iloc[-5] * 100
|
||||
|
||||
# 价格上涨但成交量下降(量价背离)- 晚期信号
|
||||
if price_change_5 > 1 and vol_change < -20:
|
||||
late_score += 20
|
||||
stage_signals.append(f"量价背离(涨{price_change_5:.1f}%量减{vol_change:.0f}%)- 可能见顶")
|
||||
elif price_change_5 < -1 and vol_change < -20:
|
||||
late_score += 20
|
||||
stage_signals.append(f"量价背离(跌{price_change_5:.1f}%量减{vol_change:.0f}%)- 可能见底")
|
||||
elif price_change_5 > 1 and vol_change > 30:
|
||||
early_score += 15
|
||||
stage_signals.append(f"放量上涨(涨{price_change_5:.1f}%量增{vol_change:.0f}%)- 可能早期")
|
||||
elif price_change_5 < -1 and vol_change > 30:
|
||||
early_score += 15
|
||||
stage_signals.append(f"放量下跌(跌{price_change_5:.1f}%量增{vol_change:.0f}%)- 可能早期")
|
||||
|
||||
# ========== 5. 波动率状态 ==========
|
||||
if 'atr' in df_30m.columns and len(df_30m) >= 20:
|
||||
recent_atr = df_30m['atr'].iloc[-5:].mean()
|
||||
older_atr = df_30m['atr'].iloc[-15:-5].mean()
|
||||
atr_change = (recent_atr - older_atr) / older_atr * 100 if older_atr > 0 else 0
|
||||
|
||||
if atr_change > 30:
|
||||
early_score += 10
|
||||
stage_signals.append(f"ATR扩张({atr_change:.0f}%) - 趋势启动")
|
||||
elif atr_change < -30:
|
||||
late_score += 10
|
||||
stage_signals.append(f"ATR收缩({atr_change:.0f}%) - 动能衰竭")
|
||||
|
||||
# ========== 6. 连续同向K线数量 ==========
|
||||
if len(df_30m) >= 5:
|
||||
recent_closes = df_30m['close'].iloc[-5:].values
|
||||
consecutive_up = sum(1 for i in range(1, len(recent_closes)) if recent_closes[i] > recent_closes[i-1])
|
||||
consecutive_down = sum(1 for i in range(1, len(recent_closes)) if recent_closes[i] < recent_closes[i-1])
|
||||
|
||||
if consecutive_up >= 4:
|
||||
late_score += 15
|
||||
stage_signals.append(f"连续{consecutive_up}根阳线 - 可能过度")
|
||||
elif consecutive_down >= 4:
|
||||
late_score += 15
|
||||
stage_signals.append(f"连续{consecutive_down}根阴线 - 可能过度")
|
||||
|
||||
# ========== 综合判断趋势阶段 ==========
|
||||
scores = {
|
||||
'early': early_score,
|
||||
'middle': middle_score,
|
||||
'late': late_score
|
||||
}
|
||||
max_score = max(scores.values())
|
||||
|
||||
if max_score < 20:
|
||||
result['stage'] = 'unknown'
|
||||
result['analysis'] = "趋势阶段不明确"
|
||||
elif max_score == late_score and late_score >= 40:
|
||||
result['stage'] = 'late'
|
||||
result['confidence'] = min(95, late_score)
|
||||
result['signals'] = stage_signals
|
||||
result['analysis'] = f"⚠️ 趋势晚期({late_score}分)- " + "; ".join(stage_signals[:3])
|
||||
elif max_score == early_score and early_score >= 30:
|
||||
result['stage'] = 'early'
|
||||
result['confidence'] = min(90, early_score)
|
||||
result['signals'] = stage_signals
|
||||
result['analysis'] = f"趋势早期({early_score}分)- " + "; ".join(stage_signals[:3])
|
||||
else:
|
||||
result['stage'] = 'middle'
|
||||
result['confidence'] = min(85, middle_score)
|
||||
result['signals'] = stage_signals
|
||||
result['analysis'] = f"趋势中期({middle_score}分)- " + "; ".join(stage_signals[:3])
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"趋势阶段检测失败: {e}")
|
||||
import traceback
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
return result
|
||||
|
||||
@ -23,6 +23,65 @@ class TradingDecisionMaker:
|
||||
## 🎯 核心理念
|
||||
**日内交易:快进快出 + 盈亏比第一 + 严控风险**
|
||||
|
||||
## 🚨 反转信号处理(最高优先级!)
|
||||
|
||||
### 系统会检测以下反转信号:
|
||||
1. **RSI 背离**:价格创新高/低但 RSI 不创新高/低(权重2)
|
||||
2. **MACD 柱状图缩短**:动能衰竭信号(权重1)
|
||||
3. **MACD 金叉/死叉**:趋势反转信号(权重1)
|
||||
4. **量价背离**:价格上涨但成交量下降(权重1)
|
||||
5. **关键K线形态**:吞没、锤子线、十字星(权重1-2)
|
||||
6. **多周期趋势不一致**:小周期反转但大周期未反应(权重1)
|
||||
|
||||
### 🔴 当检测到反转信号时(必须遵守!):
|
||||
|
||||
**1. 如果有同方向持仓 → 强制平仓**
|
||||
- 检测到看跌反转 + 有做多持仓 → **CLOSE(立即平仓)**
|
||||
- 检测到看涨反转 + 有做空持仓 → **CLOSE(立即平仓)**
|
||||
- 不要等待反弹/回调,反转可能很快发生
|
||||
|
||||
**2. 严禁继续原方向开新仓**
|
||||
- 检测到反转信号后 → **停止原方向任何新操作**
|
||||
- 之前做多,现在检测到看跌反转 → **严禁开新多单**
|
||||
- 之前做空,现在检测到看涨反转 → **严禁开新空单**
|
||||
|
||||
**3. 可以考虑反手操作(仅当反转置信度 ≥ 70%)**
|
||||
- FLIP_POSITION:平掉旧仓位 + 开立新方向仓位
|
||||
- 或者先平仓观望,等待反转确认后再入场
|
||||
|
||||
**4. 如果不确定 → 先平仓观望**
|
||||
- 宁可错过机会,也不要被套在反向位置
|
||||
|
||||
## 🌅 趋势阶段处理(避免晚期被套)
|
||||
|
||||
### 系统会判断趋势处于哪个阶段:
|
||||
- **早期**:刚突破关键位,均线刚开始排列,动能开始释放
|
||||
- **中期**:均线排列稳定,价格沿趋势移动,量能健康
|
||||
- **晚期**:价格过度延伸,RSI极端区,量价背离
|
||||
|
||||
### 不同阶段的处理规则:
|
||||
|
||||
**✅ 早期阶段(可积极入场):**
|
||||
- 可以顺势轻仓入场
|
||||
- 设置止损后可持有更长时间
|
||||
- 目标可看更大空间(3-5%)
|
||||
|
||||
**✅ 中期阶段(稳健持仓):**
|
||||
- 等待回调/反弹入场
|
||||
- 顺势持仓,让利润奔跑
|
||||
- 不要被小波动洗出
|
||||
|
||||
**🔴 晚期阶段(强制谨慎!):**
|
||||
- **严禁追涨/追空开新仓**
|
||||
- **现有盈利持仓建议逐步止盈**
|
||||
- **等待明确反转信号后再决策**
|
||||
- 宁可错过最后一段利润,也不要被套
|
||||
|
||||
### ⚠️ 铁律:趋势晚期 + 检测到反转信号 = 立即平仓
|
||||
- 晚期阶段本身风险就大
|
||||
- 如果再检测到反转信号 → 必须立即平仓
|
||||
- 不要幻想"最后一段利润"
|
||||
|
||||
### 🚨 盈亏比铁律(违反即拒绝)
|
||||
**所有交易必须满足盈亏比 ≥ 1:1.5,回调入场≥ 1:1.8**
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user