优化策略

This commit is contained in:
aaron 2026-03-22 12:34:31 +08:00
parent 5e738c4c2d
commit cd001c0c00
2 changed files with 891 additions and 0 deletions

View File

@ -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

View File

@ -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**