diff --git a/analysis/llm_context.py b/analysis/llm_context.py index 56fe41e..288474a 100644 --- a/analysis/llm_context.py +++ b/analysis/llm_context.py @@ -2,7 +2,7 @@ LLM Context Builder - Generate structured market analysis for LLM decision making """ import logging -from typing import Dict, Any, Optional +from typing import Dict, Any, Optional, List from datetime import datetime import pandas as pd @@ -21,6 +21,17 @@ from signals.quantitative import QuantitativeSignalGenerator logger = logging.getLogger(__name__) +# K线数量配置 - 按时间周期不同 +KLINE_LIMITS = { + '5m': 288, # 1天 = 24*60/5 = 288 根 + '15m': 96, # 1天 = 24*60/15 = 96 根 + '1h': 72, # 3天 = 3*24 = 72 根 + '4h': 18, # 3天 = 3*24/4 = 18 根 + '1d': 30, # 30天 + '1w': 60, # 60周 +} + + class LLMContextBuilder: """Build structured context for LLM trading decisions""" @@ -38,8 +49,8 @@ class LLMContextBuilder: Dict with structured market analysis """ try: - # Fetch multi-timeframe data - mtf_data = self.data_reader.get_multi_timeframe_data() + # Fetch multi-timeframe data with custom limits + mtf_data = self._get_multi_timeframe_data_for_llm() if '5m' not in mtf_data or mtf_data['5m'].empty: logger.error("No 5m data available for analysis") @@ -57,17 +68,17 @@ class LLMContextBuilder: # Fetch order book data depth_data = self.data_reader.read_latest_depth() - # Build context sections + # Build context sections (移除支撑位压力位计算) context = { 'timestamp': datetime.now().isoformat(), 'symbol': symbol, 'current_price': round(current_price, 2), 'market_state': self._build_market_state(df_5m, mtf_data), - 'key_prices': self._build_key_prices(df_5m, current_price), 'momentum': self._build_momentum_analysis(df_5m, depth_data), 'volatility_analysis': self._build_volatility_analysis(df_5m), 'volume_analysis': self._build_volume_analysis(df_5m), 'multi_timeframe': self._build_mtf_summary(mtf_data), + 'kline_data': self._build_kline_data(mtf_data), # 新增 K 线数据 'signal_consensus': self._calculate_signal_consensus(df_5m, depth_data), 'risk_metrics': self._build_risk_metrics(df_5m, current_price), } @@ -79,6 +90,68 @@ class LLMContextBuilder: logger.error(f"Error building LLM context: {e}", exc_info=True) return self._empty_context() + def _get_multi_timeframe_data_for_llm(self) -> Dict[str, pd.DataFrame]: + """ + Fetch data from multiple timeframes with custom limits for LLM + + Returns: + Dict mapping timeframe to DataFrame + """ + from .config import config as analysis_config + + timeframes = { + '5m': (analysis_config.KLINE_5M_KEY, KLINE_LIMITS['5m']), + '15m': (analysis_config.KLINE_15M_KEY, KLINE_LIMITS['15m']), + '1h': (analysis_config.KLINE_1H_KEY, KLINE_LIMITS['1h']), + '4h': (analysis_config.KLINE_4H_KEY, KLINE_LIMITS['4h']), + '1d': (analysis_config.KLINE_1D_KEY, KLINE_LIMITS['1d']), + '1w': (analysis_config.KLINE_1W_KEY, KLINE_LIMITS['1w']), + } + + data = {} + for tf, (key, count) in timeframes.items(): + df = self.data_reader.read_kline_stream(key, count=count) + if not df.empty: + data[tf] = df + + return data + + def _build_kline_data(self, mtf_data: Dict[str, pd.DataFrame]) -> Dict[str, List[Dict]]: + """ + Build K-line data for LLM analysis + 不同时间周期提供不同数量的K线数据 + + Returns: + Dict mapping timeframe to list of kline dicts + """ + kline_data = {} + + for timeframe, df in mtf_data.items(): + if df.empty: + continue + + # 获取该周期需要的K线数量 + limit = KLINE_LIMITS.get(timeframe, 50) + + # 取最近的N根K线 + df_limited = df.tail(limit) + + # 转换为简洁格式 + klines = [] + for idx, row in df_limited.iterrows(): + klines.append({ + 't': idx.strftime('%Y-%m-%d %H:%M'), # 时间 + 'o': round(row['open'], 2), # 开盘价 + 'h': round(row['high'], 2), # 最高价 + 'l': round(row['low'], 2), # 最低价 + 'c': round(row['close'], 2), # 收盘价 + 'v': round(row['volume'], 2), # 成交量 + }) + + kline_data[timeframe] = klines + + return kline_data + def _build_market_state( self, df: pd.DataFrame, mtf_data: Dict[str, pd.DataFrame] ) -> Dict[str, Any]: @@ -113,32 +186,6 @@ class LLMContextBuilder: 'higher_timeframe_alignment': htf_alignment, } - def _build_key_prices(self, df: pd.DataFrame, current_price: float) -> Dict[str, Any]: - """Build key price levels section""" - sr_levels = MarketStructureAnalyzer.find_support_resistance(df, current_price) - breakout_info = MarketStructureAnalyzer.detect_breakout(df, sr_levels) - - # Format prices - support_str = f"${sr_levels['nearest_support']:,.0f}" if sr_levels.get('nearest_support') else "无明显支撑" - resistance_str = f"${sr_levels['nearest_resistance']:,.0f}" if sr_levels.get('nearest_resistance') else "无明显压力" - - # Get Bollinger Bands - latest = df.iloc[-1] - bb_upper = latest.get('bb_upper') - bb_lower = latest.get('bb_lower') - - return { - 'support': support_str, - 'support_level': sr_levels.get('nearest_support'), - 'resistance': resistance_str, - 'resistance_level': sr_levels.get('nearest_resistance'), - 'all_support_levels': sr_levels.get('support', []), - 'all_resistance_levels': sr_levels.get('resistance', []), - 'breakout_status': breakout_info, - 'bollinger_upper': round(bb_upper, 2) if bb_upper else None, - 'bollinger_lower': round(bb_lower, 2) if bb_lower else None, - } - def _build_momentum_analysis( self, df: pd.DataFrame, depth_data: Optional[Dict[str, Any]] ) -> Dict[str, Any]: @@ -243,25 +290,22 @@ class LLMContextBuilder: # Get momentum momentum = MarketStructureAnalyzer.calculate_momentum(df) - # Get support/resistance - sr_levels = MarketStructureAnalyzer.find_support_resistance(df, current_price) - - # Get ATR + # Get ATR (不再计算支撑位压力位) atr = latest.get('atr', 0) atr_pct = (atr / current_price * 100) if current_price > 0 else 0 # Get volume ratio volume_ratio = latest.get('volume_ratio', 1) - # ===== NEW: Calculate quantitative scores for this timeframe ===== - # Build mini analysis for this timeframe + # ===== Calculate quantitative scores for this timeframe ===== + # Build mini analysis for this timeframe (不包含支撑位压力位) mini_analysis = { 'current_price': current_price, 'trend_analysis': trend_info, 'momentum': momentum, - 'support_resistance': sr_levels, - 'breakout': {'has_breakout': False}, # Simplified for now - 'orderflow': None, # Orderflow only for 5m + 'support_resistance': {'support': [], 'resistance': [], 'nearest_support': None, 'nearest_resistance': None}, + 'breakout': {'has_breakout': False}, + 'orderflow': None, 'indicators': {'atr': atr} } @@ -303,10 +347,6 @@ class LLMContextBuilder: 'macd_signal': momentum.get('macd_signal', 'unknown'), 'macd_hist': round(momentum.get('macd_hist', 0), 2), - # Support/Resistance - 'support': sr_levels.get('nearest_support'), - 'resistance': sr_levels.get('nearest_resistance'), - # Volatility 'atr': round(atr, 2), 'atr_pct': round(atr_pct, 2), @@ -314,7 +354,7 @@ class LLMContextBuilder: # Volume 'volume_ratio': round(volume_ratio, 2), - # ===== NEW: Quantitative scores ===== + # Quantitative scores 'quantitative': quant_scores, } @@ -465,7 +505,6 @@ class LLMContextBuilder: 'timestamp': datetime.now().isoformat(), 'error': 'Insufficient data for analysis', 'market_state': {}, - 'key_prices': {}, 'momentum': {}, 'signal_consensus': 0.5, } @@ -482,17 +521,13 @@ class LLMContextBuilder: if 'error' in full_context: return full_context - # Extract and simplify to match user's example + # Extract and simplify (不再包含支撑位压力位) return { 'market_state': { 'trend_direction': full_context['market_state']['trend_direction'], 'market_phase': full_context['market_state']['market_phase'], 'volatility': full_context['market_state']['volatility'], }, - 'key_prices': { - 'support': full_context['key_prices']['support'], - 'resistance': full_context['key_prices']['resistance'], - }, 'momentum': { 'rsi_status': full_context['momentum']['rsi_status'], 'macd': full_context['momentum']['macd'], diff --git a/config/settings.py b/config/settings.py index e51594f..371b91c 100644 --- a/config/settings.py +++ b/config/settings.py @@ -71,5 +71,9 @@ class Settings(BaseSettings): LLM_MAX_CALLS_PER_DAY: int = 12 # 每天最多调用次数 LLM_MIN_INTERVAL_MINUTES: int = 15 # 最小调用间隔(分钟) + # 盈利空间过滤(过滤低利润机会) + MIN_PROFIT_PCT: float = 1.0 # 最小盈利空间百分比,低于此值的机会不给操作建议 + PREFER_INTRADAY: bool = True # 优先日内短线交易建议 + settings = Settings() diff --git a/output/latest_signal.json b/output/latest_signal.json index 9ec2fe6..e8180fd 100755 --- a/output/latest_signal.json +++ b/output/latest_signal.json @@ -1,43 +1,43 @@ { - "timestamp": "2025-12-02T14:50:51.220024", + "timestamp": "2025-12-03T16:25:01.783326", "aggregated_signal": { - "timestamp": "2025-12-02T14:50:51.218349", + "timestamp": "2025-12-03T16:25:01.781521", "final_signal": "HOLD", - "final_confidence": 0.3, + "final_confidence": 0.58, "consensus": "CONSENSUS_HOLD", - "agreement_score": 0.3, + "agreement_score": 0.58, "quantitative_signal": { "signal_type": "HOLD", "signal": "HOLD", - "confidence": 0.0, - "composite_score": 17.5, + "confidence": 0.5, + "composite_score": -12.8, "scores": { - "trend": 0.0, - "momentum": -30, - "orderflow": 100, + "trend": -23.1, + "momentum": -65, + "orderflow": 46.3, "breakout": 0 } }, "llm_signal": { "signal_type": "HOLD", "signal": "HOLD", - "confidence": 0.6, - "reasoning": "多周期分析显示市场处于严重分歧状态:短期(5m/15m)强劲看涨,中期(4h/1d)明确看跌,长期(1d/1w)方向矛盾。这种分歧导致整体信号为‘HOLD’。短期上涨面临中期趋势阻力和超买技术指标压制,上行空间可能受限。交易机会仅限于快进快出的日内多头,中长线需等待趋势共振。", + "confidence": 0.67, + "reasoning": "多周期综合分析显示市场处于关键分歧点。短期(1h)与超短期(5m/15m)趋势不一致,中期(4h与1d)趋势完全相反,长期(1d与1w)趋势也存在矛盾。各周期未能形成共振,市场缺乏统一方向,呈现震荡格局。当前价格位于多个周期的关键位之间,方向选择有待确认。", "key_factors": [ - "多周期趋势严重分歧", - "短期RSI超买与中期下跌趋势冲突", - "价格处于4小时关键压力位$89,177", - "成交量在短期放量但日线缩量", - "周线长期趋势方向待定" + "多周期趋势严重分歧,方向不明", + "成交量普遍萎缩,市场动能不足", + "价格位于关键支撑与压力区间内震荡", + "大周期(日线、周线)MACD信号矛盾", + "市场等待突破以选择后续方向" ], "opportunities": { "short_term_5m_15m_1h": { - "exists": true, - "direction": "LONG", - "entry_price": 89158.5, - "stop_loss": 88800.0, - "take_profit": 91592.0, - "reasoning": "5m和15m周期呈现强劲上涨趋势(量化评分28.4/40.9),MACD金叉,成交量放大,但RSI均超买(>80)。1h周期趋势下跌但MACD金叉扩大,价格接近1h压力位$91,592。短期存在基于小周期动量延续的做多机会,但需警惕超买回调风险。" + "exists": false, + "direction": null, + "entry_price": 0, + "stop_loss": 0, + "take_profit": 0, + "reasoning": "短期周期趋势分歧。5m和15m显示下跌趋势(量化评分-24.3),但1h显示上涨趋势(量化评分23.4)。动量指标(MACD死叉)与部分趋势信号矛盾,RSI处于中性区域,成交量萎缩,缺乏明确的共振入场信号。价格在1h支撑($91,637)和压力($92,273)之间震荡,方向不明。" }, "medium_term_4h_1d": { "exists": false, @@ -45,7 +45,7 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "4h和1d周期均显示强劲下跌趋势(量化评分-33.4/-23.4),与短期上涨趋势形成严重分歧。4h压力位$89,177与当前价格$89,158.5几乎重合,构成关键阻力。日线MACD虽金叉但趋势向下,RSI中性偏弱,缺乏明确的中期反转或延续信号,建议观望。" + "reasoning": "中期周期趋势严重分歧。4h周期显示上涨趋势(量化评分29.3,趋势强度moderate,RSI 60.2强势),但1d周期显示下跌趋势(量化评分-23.4,趋势强度strong)。MACD信号不一致(4h金叉收窄,1d金叉扩大),价格接近4h压力位($93,080)但未突破。成交量萎缩,市场缺乏明确的波段方向动能,建议观望等待趋势统一。" }, "long_term_1d_1w": { "exists": false, @@ -53,20 +53,20 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "日线(下跌趋势)与周线(上涨趋势)方向严重冲突。周线量化评分仅4.3,信号模糊,MACD死叉扩大,RSI弱势。日线趋势向下但MACD金叉,显示长期趋势不明朗,处于关键抉择期。无明确的长期趋势交易机会,需等待日线与周线趋势共振。" + "reasoning": "长期周期趋势存在分歧。1d周期显示下跌趋势(趋势强度strong),而1w周期显示上涨趋势(趋势强度moderate)。量化评分方向不一致(1d: -23.4, 1w: 11.8)。周线MACD仍为死叉(尽管收窄),日线MACD为金叉,信号矛盾。价格位于周线支撑($91,130)上方,但未形成明确的大周期共振趋势,缺乏长期布局的清晰入场点。" }, "ambush": { "exists": true, - "price_level": 86207.0, - "reasoning": "等待价格回调至1h关键支撑位$86,207附近。该位置接近4h支撑$86,261,若价格能在此企稳并出现1h或4h周期的反弹信号(如RSI从超卖区回升、MACD金叉),可考虑作为中期做多埋伏点,博弈日线下跌趋势中的反弹或反转。" + "price_level": 91130.0, + "reasoning": "基于周线关键支撑位$91,130设置埋伏。若价格回调至此位置,并伴随1h或4h周期出现明确的反弹反转信号(如RSI超卖反弹、MACD金叉、放量),可考虑分批布局多单,博弈周线级别上涨趋势的延续。" }, "intraday": { - "exists": true, - "direction": "LONG", - "entry_price": 89158.5, - "stop_loss": 88800.0, - "take_profit": 91592.0, - "reasoning": "5m和15m周期呈现强劲上涨趋势(量化评分28.4/40.9),MACD金叉,成交量放大,但RSI均超买(>80)。1h周期趋势下跌但MACD金叉扩大,价格接近1h压力位$91,592。短期存在基于小周期动量延续的做多机会,但需警惕超买回调风险。" + "exists": false, + "direction": null, + "entry_price": 0, + "stop_loss": 0, + "take_profit": 0, + "reasoning": "短期周期趋势分歧。5m和15m显示下跌趋势(量化评分-24.3),但1h显示上涨趋势(量化评分23.4)。动量指标(MACD死叉)与部分趋势信号矛盾,RSI处于中性区域,成交量萎缩,缺乏明确的共振入场信号。价格在1h支撑($91,637)和压力($92,273)之间震荡,方向不明。" }, "swing": { "exists": false, @@ -74,88 +74,84 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "4h和1d周期均显示强劲下跌趋势(量化评分-33.4/-23.4),与短期上涨趋势形成严重分歧。4h压力位$89,177与当前价格$89,158.5几乎重合,构成关键阻力。日线MACD虽金叉但趋势向下,RSI中性偏弱,缺乏明确的中期反转或延续信号,建议观望。" + "reasoning": "中期周期趋势严重分歧。4h周期显示上涨趋势(量化评分29.3,趋势强度moderate,RSI 60.2强势),但1d周期显示下跌趋势(量化评分-23.4,趋势强度strong)。MACD信号不一致(4h金叉收窄,1d金叉扩大),价格接近4h压力位($93,080)但未突破。成交量萎缩,市场缺乏明确的波段方向动能,建议观望等待趋势统一。" } }, "recommendations_by_timeframe": { - "short_term": "短期(5m/15m/1h)存在基于小周期动量的日内做多机会,但RSI已严重超买,风险较高。建议轻仓快进快出,严格设置止损于$88,800(基于5m ATR),目标看向1h压力位$91,592。若价格无法有效突破当前4h压力$89,177,应果断离场。", - "medium_term": "中期(4h/1d)趋势向下,但当前价格处于4h关键压力位,且与短期上涨动能背离。无明确的中期波段入场点,建议观望。可关注价格能否站稳$89,177上方以挑战日线压力$93,080,或回落至$86,200-$86,800支撑区域寻找企稳信号。", - "long_term": "长期(1d/1w)趋势矛盾,日线下跌与周线上涨形成拉锯。周线支撑$88,909已被短暂跌破,长期方向待定。建议长期投资者保持观望,等待日线趋势(当前下跌)与周线趋势(当前上涨)出现明确一致信号后再做布局,或利用‘ambush’点位分批建仓。" + "short_term": "短期(5m/15m/1h)操作建议:观望。当前5m/15m与1h趋势方向矛盾,市场处于震荡整理状态,缺乏清晰的日内交易机会。可关注价格对1h支撑$91,637和压力$92,273的突破情况,等待小周期形成共振后再考虑入场。", + "medium_term": "中期(4h/1d)操作建议:观望。4h看涨与1d看跌形成强烈分歧,市场方向不明。建议等待价格有效突破4h压力$93,080(确认中期转强)或跌破4h支撑$90,612(确认中期转弱),并结合成交量放大信号,再寻找波段交易机会。", + "long_term": "长期(1d/1w)操作建议:观望。日线与周线趋势不一致,长期趋势未明朗。可关注周线支撑$91,130的防守情况。若价格能站稳该支撑并推动日线趋势转涨,形成大周期共振,则可能开启长期上涨趋势;反之,若跌破,则长期趋势可能转弱。目前宜耐心等待更明确的趋势信号。" }, "trade_type": "MULTI_TIMEFRAME", - "risk_level": "HIGH" + "risk_level": "MEDIUM" }, "levels": { - "current_price": 89179.2, - "entry": 89179.2, - "stop_loss": 88999.95, - "take_profit_1": 90395.95, - "take_profit_2": 90395.95, - "take_profit_3": 90395.95, - "take_profit_1_range": { - "quant": 89199.9, - "llm": 91592.0, - "diff_pct": 2.65 - } + "current_price": 92028.4, + "entry": 92028.4, + "stop_loss": 92028.4, + "take_profit_1": 92028.4, + "take_profit_2": 92028.4, + "take_profit_3": 92028.4 }, - "risk_reward_ratio": 6.79, + "risk_reward_ratio": 0, "recommendation": "量化和AI分析均建议观望,等待更好的机会", - "warnings": [ - "⚠️ 量化信号置信度较低" - ] + "warnings": [] }, "market_analysis": { - "price": 89199.9, + "price": 92028.4, "trend": { - "direction": "unknown", - "strength": 0 + "direction": "下跌", + "strength": "weak", + "phase": "下跌中", + "adx": 12.9, + "ema_alignment": "bearish" }, "momentum": { - "rsi": 50, + "rsi": 44.0, "rsi_status": "中性偏弱", - "rsi_trend": "中性", - "macd_signal": "死叉收窄", - "macd_hist": 0 + "rsi_trend": "下降中", + "macd_signal": "死叉扩大", + "macd_hist": -23.7636 } }, "quantitative_signal": { - "timestamp": "2025-12-02T14:50:04.306308", + "timestamp": "2025-12-03T16:24:23.426153", "signal_type": "HOLD", - "signal_strength": 0.17, - "composite_score": 17.5, - "confidence": 0.0, - "consensus_score": 0.0, + "signal_strength": 0.13, + "composite_score": -12.8, + "confidence": 0.5, + "consensus_score": 0.7, "scores": { - "trend": 0.0, - "momentum": -30, - "orderflow": 100, + "trend": -23.1, + "momentum": -65, + "orderflow": 46.3, "breakout": 0 }, "levels": { - "current_price": 89199.9, - "entry": 89199.9, - "stop_loss": 89199.9, - "take_profit_1": 89199.9, - "take_profit_2": 89199.9, - "take_profit_3": 89199.9 + "current_price": 92028.4, + "entry": 92028.4, + "stop_loss": 92028.4, + "take_profit_1": 92028.4, + "take_profit_2": 92028.4, + "take_profit_3": 92028.4 }, "risk_reward_ratio": 0, - "reasoning": "趋势unknown (0); RSI=50; MACD 死叉收窄; 订单流: 强买方主导" + "reasoning": "趋势下跌 (weak); RSI=44; MACD 死叉扩大; 订单流: 强买方主导" }, "llm_signal": { - "timestamp": "2025-12-02T14:50:51.218012", + "timestamp": "2025-12-03T16:25:01.781138", "signal_type": "HOLD", - "confidence": 0.6, + "confidence": 0.67, "trade_type": "MULTI_TIMEFRAME", - "reasoning": "多周期分析显示市场处于严重分歧状态:短期(5m/15m)强劲看涨,中期(4h/1d)明确看跌,长期(1d/1w)方向矛盾。这种分歧导致整体信号为‘HOLD’。短期上涨面临中期趋势阻力和超买技术指标压制,上行空间可能受限。交易机会仅限于快进快出的日内多头,中长线需等待趋势共振。", + "reasoning": "多周期综合分析显示市场处于关键分歧点。短期(1h)与超短期(5m/15m)趋势不一致,中期(4h与1d)趋势完全相反,长期(1d与1w)趋势也存在矛盾。各周期未能形成共振,市场缺乏统一方向,呈现震荡格局。当前价格位于多个周期的关键位之间,方向选择有待确认。", "opportunities": { "short_term_5m_15m_1h": { - "exists": true, - "direction": "LONG", - "entry_price": 89158.5, - "stop_loss": 88800.0, - "take_profit": 91592.0, - "reasoning": "5m和15m周期呈现强劲上涨趋势(量化评分28.4/40.9),MACD金叉,成交量放大,但RSI均超买(>80)。1h周期趋势下跌但MACD金叉扩大,价格接近1h压力位$91,592。短期存在基于小周期动量延续的做多机会,但需警惕超买回调风险。" + "exists": false, + "direction": null, + "entry_price": 0, + "stop_loss": 0, + "take_profit": 0, + "reasoning": "短期周期趋势分歧。5m和15m显示下跌趋势(量化评分-24.3),但1h显示上涨趋势(量化评分23.4)。动量指标(MACD死叉)与部分趋势信号矛盾,RSI处于中性区域,成交量萎缩,缺乏明确的共振入场信号。价格在1h支撑($91,637)和压力($92,273)之间震荡,方向不明。" }, "medium_term_4h_1d": { "exists": false, @@ -163,7 +159,7 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "4h和1d周期均显示强劲下跌趋势(量化评分-33.4/-23.4),与短期上涨趋势形成严重分歧。4h压力位$89,177与当前价格$89,158.5几乎重合,构成关键阻力。日线MACD虽金叉但趋势向下,RSI中性偏弱,缺乏明确的中期反转或延续信号,建议观望。" + "reasoning": "中期周期趋势严重分歧。4h周期显示上涨趋势(量化评分29.3,趋势强度moderate,RSI 60.2强势),但1d周期显示下跌趋势(量化评分-23.4,趋势强度strong)。MACD信号不一致(4h金叉收窄,1d金叉扩大),价格接近4h压力位($93,080)但未突破。成交量萎缩,市场缺乏明确的波段方向动能,建议观望等待趋势统一。" }, "long_term_1d_1w": { "exists": false, @@ -171,20 +167,20 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "日线(下跌趋势)与周线(上涨趋势)方向严重冲突。周线量化评分仅4.3,信号模糊,MACD死叉扩大,RSI弱势。日线趋势向下但MACD金叉,显示长期趋势不明朗,处于关键抉择期。无明确的长期趋势交易机会,需等待日线与周线趋势共振。" + "reasoning": "长期周期趋势存在分歧。1d周期显示下跌趋势(趋势强度strong),而1w周期显示上涨趋势(趋势强度moderate)。量化评分方向不一致(1d: -23.4, 1w: 11.8)。周线MACD仍为死叉(尽管收窄),日线MACD为金叉,信号矛盾。价格位于周线支撑($91,130)上方,但未形成明确的大周期共振趋势,缺乏长期布局的清晰入场点。" }, "ambush": { "exists": true, - "price_level": 86207.0, - "reasoning": "等待价格回调至1h关键支撑位$86,207附近。该位置接近4h支撑$86,261,若价格能在此企稳并出现1h或4h周期的反弹信号(如RSI从超卖区回升、MACD金叉),可考虑作为中期做多埋伏点,博弈日线下跌趋势中的反弹或反转。" + "price_level": 91130.0, + "reasoning": "基于周线关键支撑位$91,130设置埋伏。若价格回调至此位置,并伴随1h或4h周期出现明确的反弹反转信号(如RSI超卖反弹、MACD金叉、放量),可考虑分批布局多单,博弈周线级别上涨趋势的延续。" }, "intraday": { - "exists": true, - "direction": "LONG", - "entry_price": 89158.5, - "stop_loss": 88800.0, - "take_profit": 91592.0, - "reasoning": "5m和15m周期呈现强劲上涨趋势(量化评分28.4/40.9),MACD金叉,成交量放大,但RSI均超买(>80)。1h周期趋势下跌但MACD金叉扩大,价格接近1h压力位$91,592。短期存在基于小周期动量延续的做多机会,但需警惕超买回调风险。" + "exists": false, + "direction": null, + "entry_price": 0, + "stop_loss": 0, + "take_profit": 0, + "reasoning": "短期周期趋势分歧。5m和15m显示下跌趋势(量化评分-24.3),但1h显示上涨趋势(量化评分23.4)。动量指标(MACD死叉)与部分趋势信号矛盾,RSI处于中性区域,成交量萎缩,缺乏明确的共振入场信号。价格在1h支撑($91,637)和压力($92,273)之间震荡,方向不明。" }, "swing": { "exists": false, @@ -192,31 +188,31 @@ "entry_price": 0, "stop_loss": 0, "take_profit": 0, - "reasoning": "4h和1d周期均显示强劲下跌趋势(量化评分-33.4/-23.4),与短期上涨趋势形成严重分歧。4h压力位$89,177与当前价格$89,158.5几乎重合,构成关键阻力。日线MACD虽金叉但趋势向下,RSI中性偏弱,缺乏明确的中期反转或延续信号,建议观望。" + "reasoning": "中期周期趋势严重分歧。4h周期显示上涨趋势(量化评分29.3,趋势强度moderate,RSI 60.2强势),但1d周期显示下跌趋势(量化评分-23.4,趋势强度strong)。MACD信号不一致(4h金叉收窄,1d金叉扩大),价格接近4h压力位($93,080)但未突破。成交量萎缩,市场缺乏明确的波段方向动能,建议观望等待趋势统一。" } }, "recommendations_by_timeframe": { - "short_term": "短期(5m/15m/1h)存在基于小周期动量的日内做多机会,但RSI已严重超买,风险较高。建议轻仓快进快出,严格设置止损于$88,800(基于5m ATR),目标看向1h压力位$91,592。若价格无法有效突破当前4h压力$89,177,应果断离场。", - "medium_term": "中期(4h/1d)趋势向下,但当前价格处于4h关键压力位,且与短期上涨动能背离。无明确的中期波段入场点,建议观望。可关注价格能否站稳$89,177上方以挑战日线压力$93,080,或回落至$86,200-$86,800支撑区域寻找企稳信号。", - "long_term": "长期(1d/1w)趋势矛盾,日线下跌与周线上涨形成拉锯。周线支撑$88,909已被短暂跌破,长期方向待定。建议长期投资者保持观望,等待日线趋势(当前下跌)与周线趋势(当前上涨)出现明确一致信号后再做布局,或利用‘ambush’点位分批建仓。" + "short_term": "短期(5m/15m/1h)操作建议:观望。当前5m/15m与1h趋势方向矛盾,市场处于震荡整理状态,缺乏清晰的日内交易机会。可关注价格对1h支撑$91,637和压力$92,273的突破情况,等待小周期形成共振后再考虑入场。", + "medium_term": "中期(4h/1d)操作建议:观望。4h看涨与1d看跌形成强烈分歧,市场方向不明。建议等待价格有效突破4h压力$93,080(确认中期转强)或跌破4h支撑$90,612(确认中期转弱),并结合成交量放大信号,再寻找波段交易机会。", + "long_term": "长期(1d/1w)操作建议:观望。日线与周线趋势不一致,长期趋势未明朗。可关注周线支撑$91,130的防守情况。若价格能站稳该支撑并推动日线趋势转涨,形成大周期共振,则可能开启长期上涨趋势;反之,若跌破,则长期趋势可能转弱。目前宜耐心等待更明确的趋势信号。" }, "levels": { - "current_price": 89158.5, - "entry": 89158.5, - "stop_loss": 88800.0, - "take_profit_1": 91592.0, - "take_profit_2": 91592.0, - "take_profit_3": 91592.0 + "current_price": 92028.4, + "entry": 92028.4, + "stop_loss": 0, + "take_profit_1": 0, + "take_profit_2": 0, + "take_profit_3": 0 }, - "risk_level": "HIGH", + "risk_level": "MEDIUM", "key_factors": [ - "多周期趋势严重分歧", - "短期RSI超买与中期下跌趋势冲突", - "价格处于4小时关键压力位$89,177", - "成交量在短期放量但日线缩量", - "周线长期趋势方向待定" + "多周期趋势严重分歧,方向不明", + "成交量普遍萎缩,市场动能不足", + "价格位于关键支撑与压力区间内震荡", + "大周期(日线、周线)MACD信号矛盾", + "市场等待突破以选择后续方向" ], - "raw_response": "```json\n{\n \"signal\": \"HOLD\",\n \"confidence\": 0.6,\n \"opportunities\": {\n \"short_term_5m_15m_1h\": {\n \"exists\": true,\n \"timeframe_label\": \"短期 (5m/15m/1h)\",\n \"direction\": \"LONG\",\n \"entry_price\": 89158.5,\n \"stop_loss\": 88800.0,\n \"take_profit\": 91592.0,\n \"reasoning\": \"5m和15m周期呈现强劲上涨趋势(量化评分28.4/40.9),MACD金叉,成交量放大,但RSI均超买(>80)。1h周期趋势下跌但MACD金叉扩大,价格接近1h压力位$91,592。短期存在基于小周期动量延续的做多机会,但需警惕超买回调风险。\",\n \"持仓时间\": \"几分钟到几小时\"\n },\n \"medium_term_4h_1d\": {\n \"exists\": false,\n \"timeframe_label\": \"中期 (4h/1d)\",\n \"direction\": null,\n \"entry_price\": null,\n \"stop_loss\": null,\n \"take_profit\": null,\n \"reasoning\": \"4h和1d周期均显示强劲下跌趋势(量化评分-33.4/-23.4),与短期上涨趋势形成严重分歧。4h压力位$89,177与当前价格$89,158.5几乎重合,构成关键阻力。日线MACD虽金叉但趋势向下,RSI中性偏弱,缺乏明确的中期反转或延续信号,建议观望。\",\n \"持仓时间\": \"数天到一周\"\n },\n \"long_term_1d_1w\": {\n \"exists\": false,\n \"timeframe_label\": \"长期 (1d/1w)\",\n \"direction\": null,\n \"entry_price\": null,\n \"stop_loss\": null,\n \"take_profit\": null,\n \"reasoning\": \"日线(下跌趋势)与周线(上涨趋势)方向严重冲突。周线量化评分仅4.3,信号模糊,MACD死叉扩大,RSI弱势。日线趋势向下但MACD金叉,显示长期趋势不明朗,处于关键抉择期。无明确的长期趋势交易机会,需等待日线与周线趋势共振。\",\n \"持仓时间\": \"数周到数月\"\n },\n \"ambush\": {\n \"exists\": true,\n \"price_level\": 86207.0,\n \"reasoning\": \"等待价格回调至1h关键支撑位$86,207附近。该位置接近4h支撑$86,261,若价格能在此企稳并出现1h或4h周期的反弹信号(如RSI从超卖区回升、MACD金叉),可考虑作为中期做多埋伏点,博弈日线下跌趋势中的反弹或反转。\",\n \"timeframe\": \"基于1小时和4小时级别的关键支撑位\"\n }\n },\n \"recommendations_by_timeframe\": {\n \"short_term\": \"短期(5m/15m/1h)存在基于小周期动量的日内做多机会,但RSI已严重超买,风险较高。建议轻仓快进快出,严格设置止损于$88,800(基于5m ATR),目标看向1h压力位$91,592。若价格无法有效突破当前4h压力$89,177,应果断离场。\",\n \"medium_term\": \"中期(4h/1d)趋势向下,但当前价格处于4h关键压力位,且与短期上涨动能背离。无明确的中期波段入场点,建议观望。可关注价格能否站稳$89,177上方以挑战日线压力$93,080,或回落至$86,200-$86,800支撑区域寻找企稳信号。\",\n \"long_term\": \"长期(1d/1w)趋势矛盾,日线下跌与周线上涨形成拉锯。周线支撑$88,909已被短暂跌破,长期方向待定。建议长期投资者保持观望,等待日线趋势(当前下跌)与周线趋势(当前上涨)出现明确一致信号后再做布局,或利用‘ambush’点位分批建仓。\"\n },\n \"reasoning\": \"多周期分析显示市场处于严重分歧状态:短期(5m/15m)强劲看涨,中期(4h/1d)明确看跌,长期(1d/1w)方向矛盾。这种分歧导致整体信号为‘HOLD’。短期上涨面临中期趋势阻力和超买技术指标压制,上行空间可能受限。交易机会仅限于快进快出的日内多头,中长线需等待趋势共振。\",\n \"risk_level\": \"HIGH\",\n \"key_factors\": [\"多周期趋势严重分歧\", \"短期RSI超买与中期下跌趋势冲突\", \"价格处于4小时关键压力位$89,177\", \"成交量在短期放量但日线缩量\", \"周线长期趋势方向待定\"]\n}\n```", - "risk_reward_ratio": 6.79 + "raw_response": "```json\n{\n \"signal\": \"HOLD\",\n \"confidence\": 0.67,\n \"opportunities\": {\n \"short_term_5m_15m_1h\": {\n \"exists\": false,\n \"timeframe_label\": \"短期 (5m/15m/1h)\",\n \"direction\": null,\n \"entry_price\": null,\n \"stop_loss\": null,\n \"take_profit\": null,\n \"reasoning\": \"短期周期趋势分歧。5m和15m显示下跌趋势(量化评分-24.3),但1h显示上涨趋势(量化评分23.4)。动量指标(MACD死叉)与部分趋势信号矛盾,RSI处于中性区域,成交量萎缩,缺乏明确的共振入场信号。价格在1h支撑($91,637)和压力($92,273)之间震荡,方向不明。\",\n \"持仓时间\": \"几分钟到几小时\"\n },\n \"medium_term_4h_1d\": {\n \"exists\": false,\n \"timeframe_label\": \"中期 (4h/1d)\",\n \"direction\": null,\n \"entry_price\": null,\n \"stop_loss\": null,\n \"take_profit\": null,\n \"reasoning\": \"中期周期趋势严重分歧。4h周期显示上涨趋势(量化评分29.3,趋势强度moderate,RSI 60.2强势),但1d周期显示下跌趋势(量化评分-23.4,趋势强度strong)。MACD信号不一致(4h金叉收窄,1d金叉扩大),价格接近4h压力位($93,080)但未突破。成交量萎缩,市场缺乏明确的波段方向动能,建议观望等待趋势统一。\",\n \"持仓时间\": \"数天到一周\"\n },\n \"long_term_1d_1w\": {\n \"exists\": false,\n \"timeframe_label\": \"长期 (1d/1w)\",\n \"direction\": null,\n \"entry_price\": null,\n \"stop_loss\": null,\n \"take_profit\": null,\n \"reasoning\": \"长期周期趋势存在分歧。1d周期显示下跌趋势(趋势强度strong),而1w周期显示上涨趋势(趋势强度moderate)。量化评分方向不一致(1d: -23.4, 1w: 11.8)。周线MACD仍为死叉(尽管收窄),日线MACD为金叉,信号矛盾。价格位于周线支撑($91,130)上方,但未形成明确的大周期共振趋势,缺乏长期布局的清晰入场点。\",\n \"持仓时间\": \"数周到数月\"\n },\n \"ambush\": {\n \"exists\": true,\n \"price_level\": 91130.0,\n \"reasoning\": \"基于周线关键支撑位$91,130设置埋伏。若价格回调至此位置,并伴随1h或4h周期出现明确的反弹反转信号(如RSI超卖反弹、MACD金叉、放量),可考虑分批布局多单,博弈周线级别上涨趋势的延续。\",\n \"timeframe\": \"基于周线(1w)的关键支撑位\"\n }\n },\n \"recommendations_by_timeframe\": {\n \"short_term\": \"短期(5m/15m/1h)操作建议:观望。当前5m/15m与1h趋势方向矛盾,市场处于震荡整理状态,缺乏清晰的日内交易机会。可关注价格对1h支撑$91,637和压力$92,273的突破情况,等待小周期形成共振后再考虑入场。\",\n \"medium_term\": \"中期(4h/1d)操作建议:观望。4h看涨与1d看跌形成强烈分歧,市场方向不明。建议等待价格有效突破4h压力$93,080(确认中期转强)或跌破4h支撑$90,612(确认中期转弱),并结合成交量放大信号,再寻找波段交易机会。\",\n \"long_term\": \"长期(1d/1w)操作建议:观望。日线与周线趋势不一致,长期趋势未明朗。可关注周线支撑$91,130的防守情况。若价格能站稳该支撑并推动日线趋势转涨,形成大周期共振,则可能开启长期上涨趋势;反之,若跌破,则长期趋势可能转弱。目前宜耐心等待更明确的趋势信号。\"\n },\n \"reasoning\": \"多周期综合分析显示市场处于关键分歧点。短期(1h)与超短期(5m/15m)趋势不一致,中期(4h与1d)趋势完全相反,长期(1d与1w)趋势也存在矛盾。各周期未能形成共振,市场缺乏统一方向,呈现震荡格局。当前价格位于多个周期的关键位之间,方向选择有待确认。\",\n \"risk_level\": \"MEDIUM\",\n \"key_factors\": [\"多周期趋势严重分歧,方向不明\", \"成交量普遍萎缩,市场动能不足\", \"价格位于关键支撑与压力区间内震荡\", \"大周期(日线、周线)MACD信号矛盾\", \"市场等待突破以选择后续方向\"]\n}\n```", + "risk_reward_ratio": 0 } } \ No newline at end of file diff --git a/signals/llm_decision.py b/signals/llm_decision.py index 33dae5b..a76e8a5 100644 --- a/signals/llm_decision.py +++ b/signals/llm_decision.py @@ -8,6 +8,8 @@ from typing import Dict, Any, Optional from datetime import datetime import os +from config.settings import settings + logger = logging.getLogger(__name__) @@ -139,241 +141,189 @@ class LLMDecisionMaker: # Extract context elements market_state = market_context.get('market_state', {}) - key_prices = market_context.get('key_prices', {}) momentum = market_context.get('momentum', {}) signal_consensus = market_context.get('signal_consensus', 0.5) current_price = market_context.get('current_price', 0) + kline_data = market_context.get('kline_data', {}) # Build structured prompt - prompt = f"""你是一个专业的加密货币交易分析师。基于以下多时间周期市场分析数据,提供分层次的交易建议。 + prompt = f"""你是一个专业的加密货币交易分析师。基于以下多时间周期的K线数据和技术指标,提供分层次的交易建议。 + +**重要**: 你需要自己从K线数据中识别支撑位和压力位,不要依赖预先计算的值。 ## 当前价格 ${current_price:,.2f} +## 你的分析任务 + +1. **分析K线数据** - 识别各周期的支撑位、压力位、趋势结构 +2. **结合技术指标** - RSI、MACD、成交量等确认信号 +3. **给出交易建议** - 分短期/中期/长期三个级别 + ## 请提供以下内容 (使用JSON格式): {{ "signal": "BUY" | "SELL" | "HOLD", "confidence": 0.0-1.0, + // 你识别的关键价位 + "key_levels": {{ + "short_term": {{ + "support": [支撑位数组], + "resistance": [压力位数组] + }}, + "medium_term": {{ + "support": [支撑位数组], + "resistance": [压力位数组] + }}, + "long_term": {{ + "support": [支撑位数组], + "resistance": [压力位数组] + }} + }}, + // 分时间级别的交易机会分析 "opportunities": {{ "short_term_5m_15m_1h": {{ "exists": true/false, - "timeframe_label": "短期 (5m/15m/1h)", "direction": "LONG" | "SHORT" | null, "entry_price": 进场价格数值或null, "stop_loss": 止损价格数值或null, "take_profit": 止盈价格数值或null, - "reasoning": "短期日内机会说明", - "持仓时间": "几分钟到几小时" + "reasoning": "短期日内机会说明" }}, "medium_term_4h_1d": {{ "exists": true/false, - "timeframe_label": "中期 (4h/1d)", "direction": "LONG" | "SHORT" | null, "entry_price": 进场价格数值或null, "stop_loss": 止损价格数值或null, "take_profit": 止盈价格数值或null, - "reasoning": "中期波段机会说明", - "持仓时间": "数天到一周" + "reasoning": "中期波段机会说明" }}, "long_term_1d_1w": {{ "exists": true/false, - "timeframe_label": "长期 (1d/1w)", "direction": "LONG" | "SHORT" | null, "entry_price": 进场价格数值或null, "stop_loss": 止损价格数值或null, "take_profit": 止盈价格数值或null, - "reasoning": "长期趋势机会说明", - "持仓时间": "数周到数月" + "reasoning": "长期趋势机会说明" }}, "ambush": {{ "exists": true/false, "price_level": 埋伏价格数值或null, - "reasoning": "埋伏点位说明 (等待回调/反弹到关键位)", - "timeframe": "基于哪个时间级别的关键位" + "reasoning": "埋伏点位说明" }} }}, - // 分级别操作建议(必填,即使某级别无机会也要说明原因) + // 分级别操作建议(必填) "recommendations_by_timeframe": {{ "short_term": "短期(5m/15m/1h)操作建议", "medium_term": "中期(4h/1d)操作建议", "long_term": "长期(1d/1w)操作建议" }}, - // 综合分析 - "reasoning": "多周期综合分析 (3-5句话,说明各周期是否一致)", + "reasoning": "多周期综合分析", "risk_level": "LOW" | "MEDIUM" | "HIGH", "key_factors": ["影响因素1", "影响因素2", ...] }} -**输出说明**: -1. **signal**: 主要交易信号 (BUY/SELL/HOLD) -2. **confidence**: 对主要信号的信心度 (0-1) -3. **opportunities**: 分时间级别详细分析 - - **short_term_5m_15m_1h**: 短期日内交易机会 (持仓几分钟到几小时) - - 基于5m/15m/1h周期共振 - - 止损: 5m/15m ATR × 1.5, 通常0.3%-0.5% - - 止盈: 1h压力/支撑位, 风险回报比≥1:2 - - **medium_term_4h_1d**: 中期波段交易机会 (持仓数天到一周) - - 基于4h/1d周期趋势 - - 止损: 4h ATR × 1.5, 通常1%-2% - - 止盈: 日线关键位, 风险回报比≥1:2.5 - - **long_term_1d_1w**: 长期趋势交易机会 (持仓数周到数月) - - 基于1d/1w周期趋势 - - 止损: 日线ATR × 1.5, 通常2%-4% - - 止盈: 周线关键位, 风险回报比≥1:3 - - **ambush**: 埋伏点位机会 - - 基于日线/周线关键支撑压力位 - - 等待价格到达后再决定入场 -4. **recommendations_by_timeframe**: 各级别操作建议(必填) - - 即使某级别无明确机会,也要说明原因和观望理由 -5. **reasoning**: 多周期综合分析,说明各周期是否一致,存在哪些分歧 - **重要原则**: -1. **平等对待所有时间级别** - 不要偏向任何周期,根据量化评分客观分析 -2. **可以同时存在多级别机会** - 例如: 短期做多(日内) + 中期观望 + 长期做空(趋势) -3. **各级别独立分析** - 短期、中期、长期分别给出建议,不要混淆 -4. **必须填写recommendations_by_timeframe** - 即使是HOLD也要说明理由 -5. **止损止盈必须匹配时间级别** - 短期用小止损,长期用大止损 -6. **响应必须是有效的JSON格式** - 不要包含注释 +1. **优先日内短线** - 重点关注 short_term_5m_15m_1h 的日内交易机会 +2. **盈利空间≥1%** - 只有预期盈利 ≥1% 时才给出操作建议,否则 exists=false +3. **自行识别支撑压力位** - 从K线数据中找出重要的高低点作为支撑压力位 +4. **响应必须是有效的JSON格式** - 不要包含注释 + """ - # Add comprehensive multi-timeframe analysis if available + # Add multi-timeframe technical indicators if 'multi_timeframe' in market_context: mtf = market_context['multi_timeframe'] - prompt += f"\n## 多时间框架技术分析 (完整指标)\n\n" + prompt += "\n## 各周期技术指标\n\n" - # Define timeframe order and display names tf_order = [ - ('5m', '5分钟'), - ('15m', '15分钟'), - ('1h', '1小时'), - ('4h', '4小时'), - ('1d', '日线'), - ('1w', '周线') + ('5m', '5分钟', '日内短线参考'), + ('15m', '15分钟', '日内短线参考'), + ('1h', '1小时', '短期趋势'), + ('4h', '4小时', '中期趋势'), + ('1d', '日线', '大趋势'), + ('1w', '周线', '长期趋势') ] - for tf_key, tf_name in tf_order: + for tf_key, tf_name, tf_desc in tf_order: if tf_key not in mtf: continue data = mtf[tf_key] quant = data.get('quantitative', {}) - prompt += f"### {tf_name}周期 ({tf_key})\n" - - # ===== NEW: 量化评分优先展示 ===== - prompt += f"**量化评分**: {quant.get('composite_score', 0):.1f} (信号: {quant.get('signal_type', 'HOLD')}, 置信度: {quant.get('confidence', 0):.0%})\n" - prompt += f"- 趋势得分: {quant.get('trend_score', 0):.1f} | 动量得分: {quant.get('momentum_score', 0):.1f} | 订单流: {quant.get('orderflow_score', 0):.1f}\n" - - # 原有技术指标 - prompt += f"- 趋势: {data.get('trend_direction', '未知')} (强度: {data.get('trend_strength', 'weak')})\n" + prompt += f"### {tf_name} ({tf_desc})\n" + prompt += f"- 量化评分: {quant.get('composite_score', 0):.1f} | 信号: {quant.get('signal_type', 'HOLD')}\n" + prompt += f"- 趋势: {data.get('trend_direction', '未知')} ({data.get('trend_strength', 'weak')})\n" prompt += f"- RSI: {data.get('rsi', 50):.1f} ({data.get('rsi_status', '中性')})\n" - prompt += f"- MACD: {data.get('macd_signal', '未知')} (柱状图: {data.get('macd_hist', 0):.2f})\n" - - # Support/Resistance - support = data.get('support') - resistance = data.get('resistance') - support_str = f"${support:,.0f}" if support else "无" - resistance_str = f"${resistance:,.0f}" if resistance else "无" - prompt += f"- 支撑位: {support_str} | 压力位: {resistance_str}\n" - - # Volatility - atr = data.get('atr', 0) - atr_pct = data.get('atr_pct', 0) - prompt += f"- 波动率: ATR ${atr:.2f} ({atr_pct:.2f}%)\n" - - # Volume + prompt += f"- MACD: {data.get('macd_signal', '未知')}\n" + prompt += f"- ATR: ${data.get('atr', 0):.2f} ({data.get('atr_pct', 0):.2f}%)\n" vol_ratio = data.get('volume_ratio', 1) vol_status = "放量" if vol_ratio > 1.2 else "缩量" if vol_ratio < 0.8 else "正常" - prompt += f"- 成交量: {vol_status} (比率: {vol_ratio:.2f}x)\n" - prompt += "\n" + prompt += f"- 成交量: {vol_status} ({vol_ratio:.2f}x)\n\n" - # Add cross-timeframe analysis insights - prompt += "### 多周期分析方法\n\n" + # Add K-line data + if kline_data: + prompt += "\n## K线数据 (请从中识别支撑位和压力位)\n\n" + prompt += "格式: t=时间, o=开盘, h=最高, l=最低, c=收盘, v=成交量\n\n" - prompt += "#### 📊 分时间级别交易框架\n\n" + tf_kline_order = [ + ('5m', '5分钟K线 (最近1天)'), + ('15m', '15分钟K线 (最近1天)'), + ('1h', '1小时K线 (最近3天)'), + ('4h', '4小时K线 (最近3天)'), + ('1d', '日线K线 (最近30天)'), + ('1w', '周线K线 (最近60周)') + ] - prompt += "**1️⃣ 短期交易 (short_term_5m_15m_1h)** - 持仓: 几分钟到几小时\n\n" - prompt += "判断标准:\n" - prompt += "- ✅ **短周期共振**: 5m/15m/1h趋势方向一致\n" - prompt += "- ✅ **动量确认**: 5m MACD金叉/死叉 + 15m MACD同向\n" - prompt += "- ✅ **RSI信号**: 5m/15m RSI从超卖(<30)反弹或超买(>70)回落\n" - prompt += "- ✅ **价格位置**: 触及1h或4h支撑/压力位后反弹\n" - prompt += "- ⚠️ **大趋势**: 日线/周线至少不强烈相反\n" - prompt += "- ✅ **成交量**: 5m/15m放量确认突破/反转\n\n" - prompt += "入场条件:\n" - prompt += "- 做多: 5m/15m/1h上涨 + 5m金叉 + 价格>1h支撑 + 放量\n" - prompt += "- 做空: 5m/15m/1h下跌 + 5m死叉 + 价格<1h压力 + 放量\n\n" - prompt += "止盈止损:\n" - prompt += "- 止损: 5m ATR × 1.5 或15m最近低/高点, 约0.3%-0.5%\n" - prompt += "- 止盈: 1h压力/支撑位, 风险回报比≥1:2\n" - prompt += "- 策略: 快进快出, 达成50%目标后移动止损到成本\n\n" + for tf_key, tf_desc in tf_kline_order: + if tf_key not in kline_data: + continue - prompt += "**2️⃣ 中期交易 (medium_term_4h_1d)** - 持仓: 数天到一周\n\n" - prompt += "判断标准:\n" - prompt += "- ✅ **中周期趋势**: 4h/1d方向一致且趋势明显\n" - prompt += "- ✅ **量化评分**: 4h和1d的量化综合得分方向一致\n" - prompt += "- ✅ **MACD共振**: 日线金叉/死叉 + 周线趋势确认\n" - prompt += "- ✅ **关键位突破**: 突破或回踩日线/周线支撑压力位\n" - prompt += "- ✅ **RSI位置**: 日线RSI从超卖(<30)反转或超买(>70)回落\n" - prompt += "- ✅ **入场时机**: 4h/1h回调到位,提供更好入场点\n" - prompt += "- ✅ **成交量**: 日线放量突破确认趋势\n\n" - prompt += "入场条件:\n" - prompt += "- 做多: 日线+周线上涨 + 日线金叉 + 4h回调到日线支撑 + 1h反弹\n" - prompt += "- 做空: 日线+周线下跌 + 日线死叉 + 4h反弹到日线压力 + 1h回落\n\n" - prompt += "止盈止损:\n" - prompt += "- 止损: 4h ATR × 1.5, 约1%-2%\n" - prompt += "- 止盈: 日线关键位, 风险回报比≥1:2.5\n" - prompt += "- 策略: 波段持仓,关注日线趋势变化\n\n" + klines = kline_data[tf_key] + if not klines: + continue - prompt += "**3️⃣ 长期交易 (long_term_1d_1w)** - 持仓: 数周到数月\n\n" - prompt += "判断标准:\n" - prompt += "- ✅ **大周期趋势**: 1d/1w方向一致且强劲(strong/moderate)\n" - prompt += "- ✅ **量化评分**: 日线和周线的量化综合得分方向一致且分值高\n" - prompt += "- ✅ **周线MACD**: 周线金叉/死叉确认趋势\n" - prompt += "- ✅ **关键位突破**: 突破周线/月线级别支撑压力位\n" - prompt += "- ✅ **趋势确认**: 多个大周期指标共振,形成明确趋势\n\n" - prompt += "入场条件:\n" - prompt += "- 做多: 日线+周线上涨 + 周线金叉 + 日线回调到周线支撑 + 4h反弹\n" - prompt += "- 做空: 日线+周线下跌 + 周线死叉 + 日线反弹到周线压力 + 4h回落\n\n" - prompt += "止盈止损:\n" - prompt += "- 止损: 日线ATR × 1.5, 约2%-4%\n" - prompt += "- 止盈: 周线压力/支撑位, 风险回报比≥1:3\n" - prompt += "- 策略: 长期持仓,趋势不破不出,移动止损锁定利润\n\n" + prompt += f"### {tf_desc}\n" + prompt += "```\n" - prompt += "**4️⃣ 埋伏点位 (ambush)** - 提前布局等待机会\n\n" - prompt += "适用场景:\n" - prompt += "- 📌 **当前位置不佳**: 价格处于中间位置,没有好的入场点\n" - prompt += "- 📌 **关键位等待**: 有明确的日线/周线支撑压力位可等待\n" - prompt += "- 📌 **趋势延续**: 大周期趋势明确,等待回调/反弹入场\n" - prompt += "- 📌 **反转布局**: 价格接近关键转折点,等待突破确认\n\n" - prompt += "埋伏位置示例:\n" - prompt += "- 做多埋伏: 等待回调到周线/日线支撑位 (例: 价格90500,埋伏88900)\n" - prompt += "- 做空埋伏: 等待反弹到周线/日线压力位 (例: 价格90500,埋伏93000)\n" - prompt += "- 突破埋伏: 等待突破关键位后回踩 (例: 突破91000后回踩90800)\n\n" - prompt += "埋伏策略:\n" - prompt += "- 基于: 日线/周线的关键支撑压力位\n" - prompt += "- 触发: 价格到达埋伏位 + 短周期(1h/4h)出现反转信号\n" - prompt += "- 止损: 埋伏位下方/上方1-2个ATR\n" - prompt += "- 止盈: 下一个日线/周线关键位\n\n" + # 对于短周期,显示所有K线;对于长周期,可能只显示最近的一部分 + display_klines = klines + for k in display_klines: + prompt += f"{k['t']} | o:{k['o']} h:{k['h']} l:{k['l']} c:{k['c']} v:{k['v']:.0f}\n" - prompt += "**5️⃣ 观望情况** - recommendations_by_timeframe中标注\n" - prompt += "- ❌ 某周期趋势不明确或震荡\n" - prompt += "- ❌ 量化评分接近0 (无明确方向)\n" - prompt += "- ❌ 多个周期趋势严重分歧\n" - prompt += "- ❌ 成交量萎缩,市场缺乏动能\n" - prompt += "- ❌ 价格在关键位之间震荡\n\n" + prompt += "```\n\n" - prompt += "#### 🎯 关键分析要点\n" - prompt += "1. **平等对待各周期** - 周线、日线、小时级别都重要,根据持仓时间选择\n" - prompt += "2. **利用量化评分** - 每个周期都有量化综合得分,优先参考这个数值\n" - prompt += "3. **分级别独立分析** - 短期、中期、长期可以有不同甚至相反的建议\n" - prompt += "4. **趋势共振**: 同级别内多周期一致时,信号最强\n" - prompt += "5. **分歧利用**: 短期看多+长期看空 = 日内做多但不持仓过夜\n" - prompt += "6. **必须填写所有级别建议** - recommendations_by_timeframe三个字段都要填\n\n" + # Add analysis guidelines + prompt += """ +## 支撑压力位识别方法 + +1. **短期支撑压力 (5m/15m/1h)** + - 近1天内的明显高低点 + - 多次触及但未突破的价格 + - 整数关口 (如 91000, 92000) + +2. **中期支撑压力 (4h/1d)** + - 近几天的重要高低点 + - 趋势线位置 + - 前期成交密集区 + +3. **长期支撑压力 (1d/1w)** + - 周线/月线级别的高低点 + - 历史重要价格区间 + - 大周期趋势线 + +## 止盈止损设置 + +- 短期: 止损 0.3%-0.5%, 止盈 ≥1% +- 中期: 止损 1%-2%, 止盈 ≥2% +- 长期: 止损 2%-4%, 止盈 ≥4% + +只有当 (take_profit - entry) / entry ≥ 1% 时才给出具体建议! +""" return prompt @@ -454,6 +404,28 @@ ${current_price:,.2f} except (ValueError, TypeError): return default + # Helper function to calculate profit percentage + def calc_profit_pct(entry, take_profit, direction): + """Calculate profit percentage for a trade""" + if not entry or not take_profit or entry <= 0: + return 0 + if direction == 'LONG': + return (take_profit - entry) / entry * 100 + elif direction == 'SHORT': + return (entry - take_profit) / entry * 100 + return 0 + + # Helper function to check if opportunity meets minimum profit threshold + def meets_profit_threshold(opp, min_profit_pct=1.0): + """Check if opportunity has at least min_profit_pct profit potential""" + if not opp.get('exists'): + return False + entry = safe_float(opp.get('entry_price'), 0) + tp = safe_float(opp.get('take_profit'), 0) + direction = opp.get('direction') + profit_pct = calc_profit_pct(entry, tp, direction) + return profit_pct >= min_profit_pct + # Parse opportunities structure (support both old and new format) opportunities = llm_decision.get('opportunities', {}) @@ -472,7 +444,43 @@ ${current_price:,.2f} medium_term = swing long_term = {} - # Determine primary levels (priority: short > medium > long) + # Apply minimum profit filter to all opportunities + MIN_PROFIT_PCT = settings.MIN_PROFIT_PCT + + # Filter short_term + short_term_valid = meets_profit_threshold(short_term, MIN_PROFIT_PCT) + if short_term.get('exists') and not short_term_valid: + profit_pct = calc_profit_pct( + safe_float(short_term.get('entry_price'), 0), + safe_float(short_term.get('take_profit'), 0), + short_term.get('direction') + ) + logger.info(f"短期机会被过滤: 盈利空间 {profit_pct:.2f}% < {MIN_PROFIT_PCT}%") + short_term = {'exists': False, 'reasoning': f'盈利空间不足1% (仅{profit_pct:.2f}%),建议观望'} + + # Filter medium_term + medium_term_valid = meets_profit_threshold(medium_term, MIN_PROFIT_PCT) + if medium_term.get('exists') and not medium_term_valid: + profit_pct = calc_profit_pct( + safe_float(medium_term.get('entry_price'), 0), + safe_float(medium_term.get('take_profit'), 0), + medium_term.get('direction') + ) + logger.info(f"中期机会被过滤: 盈利空间 {profit_pct:.2f}% < {MIN_PROFIT_PCT}%") + medium_term = {'exists': False, 'reasoning': f'盈利空间不足1% (仅{profit_pct:.2f}%),建议观望'} + + # Filter long_term + long_term_valid = meets_profit_threshold(long_term, MIN_PROFIT_PCT) + if long_term.get('exists') and not long_term_valid: + profit_pct = calc_profit_pct( + safe_float(long_term.get('entry_price'), 0), + safe_float(long_term.get('take_profit'), 0), + long_term.get('direction') + ) + logger.info(f"长期机会被过滤: 盈利空间 {profit_pct:.2f}% < {MIN_PROFIT_PCT}%") + long_term = {'exists': False, 'reasoning': f'盈利空间不足1% (仅{profit_pct:.2f}%),建议观望'} + + # Determine primary levels (priority: short > medium > long) - 优先日内短线 entry = market_context.get('current_price', 0) stop_loss = 0 take_profit = 0 diff --git a/signals/quantitative.py b/signals/quantitative.py index 08e34ca..cfb28fd 100644 --- a/signals/quantitative.py +++ b/signals/quantitative.py @@ -5,6 +5,8 @@ import logging from typing import Dict, Any, List, Optional from datetime import datetime +from config.settings import settings + logger = logging.getLogger(__name__) @@ -71,6 +73,24 @@ class QuantitativeSignalGenerator: ) # Build signal + # Calculate profit percentage + if signal_type == 'BUY' and entry_level > 0: + profit_pct = (take_profit_levels[0] - entry_level) / entry_level * 100 + elif signal_type == 'SELL' and entry_level > 0: + profit_pct = (entry_level - take_profit_levels[0]) / entry_level * 100 + else: + profit_pct = 0 + + # Apply minimum profit filter - 盈利空间不足时建议观望 + MIN_PROFIT_PCT = settings.MIN_PROFIT_PCT + original_signal_type = signal_type + filtered_reason = "" + if signal_type != 'HOLD' and profit_pct < MIN_PROFIT_PCT: + logger.info(f"量化信号被过滤: {signal_type} 盈利空间 {profit_pct:.2f}% < {MIN_PROFIT_PCT}%,改为 HOLD") + filtered_reason = f" (原{original_signal_type}信号盈利空间不足{MIN_PROFIT_PCT}%,仅{profit_pct:.2f}%)" + signal_type = 'HOLD' + signal_strength = 0 + signal = { 'timestamp': datetime.now().isoformat(), 'signal_type': signal_type, # 'BUY', 'SELL', 'HOLD' @@ -80,6 +100,7 @@ class QuantitativeSignalGenerator: trend, momentum, orderflow ), 'consensus_score': round(consensus_score, 2), # 共识得分 (关键指标) + 'profit_pct': round(profit_pct, 2), # 预期盈利空间百分比 'scores': { 'trend': round(trend_score, 1), 'momentum': round(momentum_score, 1), @@ -98,8 +119,8 @@ class QuantitativeSignalGenerator: entry_level, stop_loss, take_profit_levels[0] ), 'reasoning': QuantitativeSignalGenerator._generate_reasoning( - signal_type, trend, momentum, orderflow, breakout - ), + original_signal_type, trend, momentum, orderflow, breakout + ) + filtered_reason, } logger.info(