优化策略

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 json
import re import re
import numpy as np
import pandas as pd import pandas as pd
from typing import Dict, Any, Optional, List from typing import Dict, Any, Optional, List
from datetime import datetime 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 1. **盈亏比第一**所有交易必须满足盈亏比 1:1.2
- 盈亏比 = (目标盈利 - 入场价) / (入场价 - 止损价) - 盈亏比 = (目标盈利 - 入场价) / (入场价 - 止损价)
@ -953,6 +1048,64 @@ class MarketSignalAnalyzer:
context_parts.append(f"\n## 趋势位置分析") context_parts.append(f"\n## 趋势位置分析")
context_parts.append(trend_position_analysis) 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) return "\n".join(context_parts)
async def _get_news_context(self, symbol: str) -> str: async def _get_news_context(self, symbol: str) -> str:
@ -1740,3 +1893,682 @@ class MarketSignalAnalyzer:
lines.append(f"**布林带开口**: 宽度 {bb_width:.1f}%,趋势延续") lines.append(f"**布林带开口**: 宽度 {bb_width:.1f}%,趋势延续")
return "\n".join(lines) 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** **所有交易必须满足盈亏比 1:1.5回调入场 1:1.8**