diff --git a/backend/app/crypto_agent/llm_signal_analyzer.py b/backend/app/crypto_agent/llm_signal_analyzer.py index c63d719..cc77fc8 100644 --- a/backend/app/crypto_agent/llm_signal_analyzer.py +++ b/backend/app/crypto_agent/llm_signal_analyzer.py @@ -15,7 +15,7 @@ class LLMSignalAnalyzer: """LLM 驱动的交易信号分析器""" # 系统提示词 - 让 LLM 自主分析 - SYSTEM_PROMPT = """你是一位专业的加密货币交易员和技术分析师。你的任务是综合分析市场数据和新闻舆情,**积极寻找交易机会**。 + SYSTEM_PROMPT = """你是一位专业的加密货币交易员和技术分析师。你的任务是综合分析**K线数据、量价关系、技术指标和新闻舆情**,给出交易信号。 ## 核心理念 加密货币市场波动大,每天都有交易机会。你的目标是: @@ -23,37 +23,76 @@ class LLMSignalAnalyzer: - 短线交易重点关注:超跌反弹、超涨回落、关键位突破 - 中线交易重点关注:趋势回调、形态突破、多周期共振 -## 你的分析方法 -运用以下技术分析方法寻找入场点: -- **趋势判断**:均线排列、高低点结构 -- **动量指标**:RSI 超买超卖(<30 超卖机会,>70 超买机会)、MACD 金叉死叉 -- **支撑阻力**:关键价位的突破或反弹 -- **K线形态**:锤子线、吞没形态、十字星等反转信号 -- **布林带**:触及下轨反弹、触及上轨回落、收口后突破 +## 一、量价分析(最重要) +量价关系是判断趋势真假的核心: -## 日内交易机会识别 -以下情况应该给出**短线信号**(置信度 60-75): -1. RSI < 30 且出现止跌迹象(超跌反弹做多) -2. RSI > 70 且出现滞涨迹象(超涨回落做空) -3. 价格触及布林带下轨并企稳(反弹做多) -4. 价格触及布林带上轨并受阻(回落做空) -5. 5分钟/15分钟级别 MACD 金叉/死叉 + 量能配合 -6. 关键支撑位/阻力位的突破或反弹 +### 1. 健康上涨信号 +- **放量上涨**:价格上涨 + 成交量放大(量比>1.5)= 上涨有效,可追多 +- **缩量回调**:上涨后回调 + 成交量萎缩(量比<0.7)= 回调健康,可低吸 -## 波段交易机会识别 -以下情况应该给出**中线信号**(置信度 70-85): -1. 1小时级别趋势明确 + 回调到均线支撑 -2. 4小时级别形态突破(三角形、旗形等) -3. 多周期 RSI 共振(如 1H 和 4H 同时超卖) -4. 重大利好/利空消息 + 技术面配合 +### 2. 健康下跌信号 +- **放量下跌**:价格下跌 + 成交量放大 = 下跌有效,可追空 +- **缩量反弹**:下跌后反弹 + 成交量萎缩 = 反弹无力,可做空 -## 新闻舆情分析 -结合最新市场新闻: -- 重大利好/利空消息 -- 市场情绪(恐慌/贪婪) -- 大户/机构动向 +### 3. 量价背离(重要反转信号) +- **顶背离**:价格创新高,但成交量未创新高 → 上涨动能衰竭,警惕回落 +- **底背离**:价格创新低,但成交量未创新低 → 下跌动能衰竭,关注反弹 +- **天量见顶**:极端放量(量比>3)后价格滞涨 → 主力出货信号 +- **地量见底**:极端缩量(量比<0.3)后价格企稳 → 抛压枯竭信号 -## 入场方式 +### 4. 突破确认 +- **有效突破**:突破关键位 + 放量确认(量比>1.5)= 真突破 +- **假突破**:突破关键位 + 缩量 = 假突破,可能回落 + +## 二、K线形态分析 +### 反转形态 +- **锤子线/倒锤子**:下跌趋势中出现,下影线长 = 底部信号 +- **吞没形态**:大阳吞没前一根阴线 = 看涨;大阴吞没前一根阳线 = 看跌 +- **十字星**:在高位/低位出现 = 变盘信号 +- **早晨之星/黄昏之星**:三根K线组合的反转信号 + +### 持续形态 +- **三连阳/三连阴**:趋势延续信号 +- **旗形整理**:趋势中的健康回调 + +## 三、技术指标分析 +### RSI(相对强弱指标) +- RSI < 30:超卖区,关注反弹机会 +- RSI > 70:超买区,关注回落风险 +- RSI 背离:价格与 RSI 走势相反 = 重要反转信号 + +### MACD +- 金叉(DIF 上穿 DEA):做多信号 +- 死叉(DIF 下穿 DEA):做空信号 +- 零轴上方金叉:强势做多 +- 零轴下方死叉:强势做空 +- MACD 柱状图背离:重要反转信号 + +### 布林带 +- 触及下轨 + 企稳:反弹做多 +- 触及上轨 + 受阻:回落做空 +- 布林带收口:即将变盘 +- 布林带开口:趋势启动 + +### 均线系统 +- 多头排列(MA5>MA10>MA20):上涨趋势 +- 空头排列(MA5 str: - """格式化最近 K 线""" + """格式化最近 K 线(含量价分析)""" # 根据周期决定显示数量 count = {'4h': 6, '1h': 12, '15m': 8, '5m': 6}.get(interval, 6) if len(df) < count: count = len(df) - lines = [f"\n最近 {count} 根 K 线:"] - lines.append("| 时间 | 开盘 | 最高 | 最低 | 收盘 | 涨跌 | RSI |") - lines.append("|------|------|------|------|------|------|-----|") + lines = [f"\n最近 {count} 根 K 线(含量价数据):"] + lines.append("| 时间 | 开盘 | 最高 | 最低 | 收盘 | 涨跌 | 成交量 | 量比 | RSI |") + lines.append("|------|------|------|------|------|------|--------|------|-----|") for i in range(-count, 0): row = df.iloc[i] @@ -299,11 +340,92 @@ class LLMSignalAnalyzer: time_str = row['open_time'].strftime('%m-%d %H:%M') if pd.notna(row.get('open_time')) else 'N/A' rsi = row.get('rsi', 0) rsi_str = f"{rsi:.0f}" if pd.notna(rsi) else "-" + + # 成交量和量比 + volume = row.get('volume', 0) + volume_ratio = row.get('volume_ratio', 1.0) + if pd.notna(volume) and volume > 0: + # 格式化成交量(大数字用K/M表示) + if volume >= 1000000: + vol_str = f"{volume/1000000:.1f}M" + elif volume >= 1000: + vol_str = f"{volume/1000:.1f}K" + else: + vol_str = f"{volume:.0f}" + else: + vol_str = "-" + + vol_ratio_str = f"{volume_ratio:.2f}" if pd.notna(volume_ratio) else "-" + lines.append(f"| {time_str} | {row['open']:.2f} | {row['high']:.2f} | " - f"{row['low']:.2f} | {row['close']:.2f} | {change_str} | {rsi_str} |") + f"{row['low']:.2f} | {row['close']:.2f} | {change_str} | {vol_str} | {vol_ratio_str} | {rsi_str} |") + + # 添加量价分析提示 + lines.append(self._analyze_volume_price(df, count)) return "\n".join(lines) + def _analyze_volume_price(self, df: pd.DataFrame, count: int) -> str: + """分析量价关系""" + if len(df) < count: + return "" + + recent = df.iloc[-count:] + analysis = [] + + # 计算价格趋势 + price_change = (recent.iloc[-1]['close'] - recent.iloc[0]['close']) / recent.iloc[0]['close'] * 100 + + # 计算成交量趋势 + vol_first_half = recent.iloc[:count//2]['volume'].mean() if 'volume' in recent.columns else 0 + vol_second_half = recent.iloc[count//2:]['volume'].mean() if 'volume' in recent.columns else 0 + + if vol_first_half > 0 and vol_second_half > 0: + vol_change = (vol_second_half - vol_first_half) / vol_first_half * 100 + + # 量价分析 + if price_change > 1: # 上涨 + if vol_change > 20: + analysis.append("📈 **量价分析**: 放量上涨,上涨有效") + elif vol_change < -20: + analysis.append("⚠️ **量价分析**: 缩量上涨,警惕回调") + else: + analysis.append("➡️ **量价分析**: 量能平稳上涨") + elif price_change < -1: # 下跌 + if vol_change > 20: + analysis.append("📉 **量价分析**: 放量下跌,下跌有效") + elif vol_change < -20: + analysis.append("💡 **量价分析**: 缩量下跌,关注企稳") + else: + analysis.append("➡️ **量价分析**: 量能平稳下跌") + else: # 横盘 + if vol_change < -30: + analysis.append("🔄 **量价分析**: 缩量整理,等待方向") + else: + analysis.append("🔄 **量价分析**: 横盘震荡") + + # 检测量价背离 + if len(df) >= 10: + recent_10 = df.iloc[-10:] + # 检查是否有新高/新低 + price_high_idx = recent_10['high'].idxmax() + price_low_idx = recent_10['low'].idxmin() + + if 'volume' in recent_10.columns: + # 顶背离检测 + if price_high_idx == recent_10.index[-1]: # 最新K线创新高 + prev_high_idx = recent_10['high'].iloc[:-1].idxmax() + if recent_10.loc[price_high_idx, 'volume'] < recent_10.loc[prev_high_idx, 'volume'] * 0.8: + analysis.append("🔴 **顶背离**: 价格新高但量能不足,警惕回落") + + # 底背离检测 + if price_low_idx == recent_10.index[-1]: # 最新K线创新低 + prev_low_idx = recent_10['low'].iloc[:-1].idxmin() + if recent_10.loc[price_low_idx, 'volume'] < recent_10.loc[prev_low_idx, 'volume'] * 0.8: + analysis.append("🟢 **底背离**: 价格新低但量能萎缩,关注反弹") + + return "\n" + "\n".join(analysis) if analysis else "" + def _parse_response(self, response: str) -> Dict[str, Any]: """解析 LLM 响应""" result = {