trading.ai/technical_analyzer.py
2025-08-14 10:06:19 +08:00

2350 lines
97 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pandas as pd
import talib
from typing import Dict, List, Tuple, Optional
import logging
from dataclasses import dataclass
# 配置日志输出到控制台
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(), # 输出到控制台
logging.FileHandler('technical_analysis.log') # 输出到文件
],
force=True # 强制重新配置日志
)
@dataclass
class CoinSignal:
symbol: str
score: float
reason: str
entry_price: float
stop_loss: float
take_profit: float
timeframe: str
confidence: str
strategy_type: str # 短线/中线/长线
holding_period: int # 预期持仓周期(天)
risk_reward_ratio: float # 风险回报比
expiry_hours: int # 选币信号有效期(小时)
action_suggestion: str # 操作建议
signal_type: str = "LONG" # 新增:信号类型 LONG/SHORT
direction: str = "BUY" # 新增:操作方向 BUY/SELL
@dataclass
class StrategyConfig:
name: str
primary_timeframe: str
confirm_timeframe: str
holding_period_days: int
expiry_hours: int
risk_reward_min: float
risk_reward_max: float
rsi_period: int
macd_fast: int
macd_slow: int
macd_signal: int
class TechnicalAnalyzer:
def __init__(self):
self.min_score = 60 # 提高最低选币分数,注重质量
self.max_selections = 15 # 适中的选币数量,注重质量而非数量
# 策略配置 - 重新设计时间周期
self.strategies = {
'超短线': StrategyConfig(
name='超短线',
primary_timeframe='5m',
confirm_timeframe='1m',
holding_period_days=1,
expiry_hours=24,
risk_reward_min=1.2,
risk_reward_max=1.8,
rsi_period=5,
macd_fast=3,
macd_slow=8,
macd_signal=2
),
'短线': StrategyConfig(
name='短线',
primary_timeframe='15m',
confirm_timeframe='5m',
holding_period_days=3,
expiry_hours=72,
risk_reward_min=1.5,
risk_reward_max=2.5,
rsi_period=7,
macd_fast=5,
macd_slow=13,
macd_signal=3
),
'中线': StrategyConfig(
name='中线',
primary_timeframe='1h',
confirm_timeframe='15m',
holding_period_days=7,
expiry_hours=168, # 7天
risk_reward_min=2.0,
risk_reward_max=3.5,
rsi_period=14,
macd_fast=12,
macd_slow=26,
macd_signal=9
),
'波段': StrategyConfig(
name='波段',
primary_timeframe='4h',
confirm_timeframe='1h',
holding_period_days=15,
expiry_hours=360, # 15天
risk_reward_min=2.5,
risk_reward_max=4.0,
rsi_period=14,
macd_fast=12,
macd_slow=26,
macd_signal=9
),
'长线': StrategyConfig(
name='长线',
primary_timeframe='1d',
confirm_timeframe='4h',
holding_period_days=30,
expiry_hours=720, # 30天
risk_reward_min=3.0,
risk_reward_max=6.0,
rsi_period=21,
macd_fast=26,
macd_slow=50,
macd_signal=12
),
'趋势': StrategyConfig(
name='趋势',
primary_timeframe='1w',
confirm_timeframe='1d',
holding_period_days=60,
expiry_hours=1440, # 60天
risk_reward_min=4.0,
risk_reward_max=8.0,
rsi_period=21,
macd_fast=26,
macd_slow=50,
macd_signal=12
)
}
def get_required_timeframes(self) -> Dict[str, List[str]]:
"""获取每个策略需要的时间周期"""
return {
'超短线': ['1m', '5m', '15m'], # 需要更短周期确认
'短线': ['5m', '15m', '1h'], # 短线需要分钟级别
'中线': ['15m', '1h', '4h'], # 中线需要小时级别
'波段': ['1h', '4h', '1d'], # 波段需要日内到日线
'长线': ['4h', '1d', '3d'], # 长线需要日线级别
'趋势': ['1d', '3d', '1w'] # 趋势需要周线级别
}
def get_optimal_timeframes_for_analysis(self, market_conditions: Dict = None) -> List[str]:
"""根据市场条件动态选择最优时间周期组合"""
# 基础时间周期组合 - 包含所有策略需要的时间周期
base_timeframes = ['1m', '5m', '15m', '1h', '4h', '1d', '3d', '1w']
# 如果有市场条件信息,可以进一步优化
if market_conditions:
volatility = market_conditions.get('volatility', 'medium')
trend_strength = market_conditions.get('trend_strength', 'medium')
if volatility == 'high':
# 高波动市场,增加短周期权重
return ['1m', '5m', '15m', '1h', '4h', '1d']
elif volatility == 'low':
# 低波动市场,增加长周期权重
return ['1h', '4h', '1d', '3d', '1w']
return base_timeframes
def determine_best_strategy(self, df: pd.DataFrame) -> str:
"""根据币种特性确定最佳策略 - 增强版"""
try:
if len(df) < 50:
logging.info("数据不足50条使用默认中线策略")
return '中线' # 默认策略
recent_data = df.tail(20)
# 1. 计算波动率
volatility = recent_data['close'].std() / recent_data['close'].mean()
# 2. 计算价格变化速度
price_change_5d = (df.iloc[-1]['close'] - df.iloc[-5]['close']) / df.iloc[-5]['close']
# 3. 成交量特性
volume_recent = recent_data['volume'].mean()
volume_avg = df['volume'].mean()
volume_ratio = volume_recent / volume_avg if volume_avg > 0 else 1
logging.info(f"策略选择指标: 波动率={volatility:.3f}, 5日价格变化={price_change_5d:.3f}, 成交量比率={volume_ratio:.2f}")
# 添加详细的策略选择调试信息
logging.info(f"策略选择条件判断:")
logging.info(f" 超短线: volatility({volatility:.3f}) > 0.20? {volatility > 0.20}, abs_change({abs(price_change_5d):.3f}) > 0.10? {abs(price_change_5d) > 0.10}")
logging.info(f" 短线: volatility({volatility:.3f}) > 0.10? {volatility > 0.10}, abs_change({abs(price_change_5d):.3f}) > 0.05? {abs(price_change_5d) > 0.05}")
logging.info(f" 中线: volatility({volatility:.3f}) > 0.06? {volatility > 0.06}, volume_ratio({volume_ratio:.2f}) > 1.1? {volume_ratio > 1.1}")
logging.info(f" 波段: 0.03 < volatility({volatility:.3f}) <= 0.08? {0.03 < volatility <= 0.08}")
# 策略选择逻辑 - 修复条件,确保所有策略都能被选中
if volatility > 0.20 and abs(price_change_5d) > 0.10:
# 极高波动,快速变化 → 超短线策略
logging.info("选择超短线策略: 极高波动且快速变化")
return '超短线'
elif volatility > 0.08 and abs(price_change_5d) > 0.03:
# 高波动,较快变化 → 短线策略(进一步降低门槛)
logging.info("选择短线策略: 高波动且较快变化")
return '短线'
elif volatility > 0.06 and volume_ratio > 1.1:
# 中等波动,有资金关注 → 中线策略
logging.info("选择中线策略: 中等波动且有资金关注")
return '中线'
elif volatility > 0.03 and volatility <= 0.08:
# 中低波动,适合波段操作 → 波段策略
logging.info("选择波段策略: 中低波动适合波段操作")
return '波段'
elif volatility <= 0.04 and volume_ratio < 1.0:
# 低波动,适合长期持有 → 长线策略
logging.info("选择长线策略: 低波动适合长期持有")
return '长线'
elif volatility <= 0.02:
# 极低波动,趋势跟踪 → 趋势策略
logging.info("选择趋势策略: 极低波动适合趋势跟踪")
return '趋势'
else:
# 根据波动率范围选择策略
if volatility <= 0.03:
logging.info("选择趋势策略: 基于波动率范围")
return '趋势'
elif volatility <= 0.06:
logging.info("选择长线策略: 基于波动率范围")
return '长线'
elif volatility <= 0.10:
logging.info("选择波段策略: 基于波动率范围")
return '波段'
else:
logging.info("选择中线策略: 默认选择")
return '中线'
except Exception as e:
logging.warning(f"策略选择失败,使用默认中线策略: {e}")
return '中线'
def calculate_technical_indicators(self, df: pd.DataFrame, strategy: str = '中线') -> pd.DataFrame:
"""计算技术指标"""
if df.empty or len(df) < 50:
return df
config = self.strategies[strategy]
# 移动平均线
df['ma20'] = talib.SMA(df['close'].values, timeperiod=20)
df['ma50'] = talib.SMA(df['close'].values, timeperiod=50)
df['ma200'] = talib.SMA(df['close'].values, timeperiod=200)
# 根据策略使用不同参数的RSI
df['rsi'] = talib.RSI(df['close'].values, timeperiod=config.rsi_period)
# 根据策略使用不同参数的MACD
df['macd'], df['macd_signal'], df['macd_hist'] = talib.MACD(
df['close'].values,
fastperiod=config.macd_fast,
slowperiod=config.macd_slow,
signalperiod=config.macd_signal
)
# 布林带
df['bb_upper'], df['bb_middle'], df['bb_lower'] = talib.BBANDS(
df['close'].values, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
)
# 成交量移动平均
df['volume_ma'] = talib.SMA(df['volume'].values, timeperiod=20)
# ATR (平均真实波动范围)
df['atr'] = talib.ATR(df['high'].values, df['low'].values, df['close'].values, timeperiod=14)
return df
def calculate_fibonacci_levels(self, df: pd.DataFrame, lookback=50) -> Dict[str, float]:
"""计算斐波那契回调位"""
if len(df) < lookback:
return {}
recent_data = df.tail(lookback)
high = recent_data['high'].max()
low = recent_data['low'].min()
diff = high - low
return {
'fib_0': high,
'fib_236': high - 0.236 * diff,
'fib_382': high - 0.382 * diff,
'fib_500': high - 0.500 * diff,
'fib_618': high - 0.618 * diff,
'fib_786': high - 0.786 * diff,
'fib_100': low
}
def find_support_resistance(self, df: pd.DataFrame, window=20) -> Tuple[float, float]:
"""寻找支撑阻力位"""
if len(df) < window * 2:
return 0, 0
# 寻找局部高点和低点
recent_data = df.tail(window)
# 获取最近的支撑和阻力
resistance = recent_data['high'].max()
support = recent_data['low'].min()
return support, resistance
def simple_long_analysis(self, df: pd.DataFrame) -> float:
"""简化的多头分析 - 只看核心指标"""
if len(df) < 10:
return 0
score = 0
current = df.iloc[-1]
recent_data = df.tail(5)
# 1. 价格趋势 (30分) - 最简单直接
price_changes = recent_data['close'].pct_change().dropna()
positive_days = (price_changes > 0).sum()
recent_return = (current['close'] - df.iloc[-5]['close']) / df.iloc[-5]['close']
if positive_days >= 3: # 5天中3天上涨
score += 15
if 0.02 <= recent_return <= 0.15: # 5日涨幅2%-15%
score += 15
# 2. 量能确认 (20分) - 简化量价分析
if 'volume' in df.columns:
recent_volume = recent_data['volume'].mean()
prev_volume = df.iloc[-10:-5]['volume'].mean() if len(df) >= 10 else recent_volume
if recent_volume > prev_volume * 1.2: # 量能放大
score += 20
# 3. 相对位置 (20分) - 避免高位追高
if len(df) >= 20:
high_20 = df.tail(20)['high'].max()
low_20 = df.tail(20)['low'].min()
position = (current['close'] - low_20) / (high_20 - low_20) if high_20 != low_20 else 0.5
if 0.2 <= position <= 0.7: # 中低位置
score += 20
elif position > 0.85: # 过高位置
score -= 10
# 4. 动量指标 (10分) - 只看简单RSI
if 'rsi' in df.columns and not df['rsi'].isna().all():
rsi = current.get('rsi', 50)
if 40 <= rsi <= 65: # RSI健康区间
score += 10
elif rsi > 75: # 过热
score -= 5
return min(max(score, 0), 70) # 限制区间
def simple_short_analysis(self, df: pd.DataFrame) -> float:
"""简化的空头分析 - 只看核心指标"""
if len(df) < 10:
return 0
score = 0
current = df.iloc[-1]
recent_data = df.tail(5)
# 1. 价格趋势 (30分) - 直接看下跌
price_changes = recent_data['close'].pct_change().dropna()
negative_days = (price_changes < 0).sum()
recent_return = (current['close'] - df.iloc[-5]['close']) / df.iloc[-5]['close']
if negative_days >= 3: # 5天中3天下跌
score += 15
if -0.15 <= recent_return <= -0.02: # 5日跌幅2%-15%
score += 15
# 2. 量能确认 (20分) - 价跌量增
if 'volume' in df.columns:
recent_volume = recent_data['volume'].mean()
prev_volume = df.iloc[-10:-5]['volume'].mean() if len(df) >= 10 else recent_volume
if recent_volume > prev_volume * 1.2: # 量能放大
score += 20
# 3. 相对位置 (20分) - 高位做空
if len(df) >= 20:
high_20 = df.tail(20)['high'].max()
low_20 = df.tail(20)['low'].min()
position = (current['close'] - low_20) / (high_20 - low_20) if high_20 != low_20 else 0.5
if 0.6 <= position <= 0.95: # 高位区间
score += 20
elif position < 0.3: # 过低位置
score -= 10
# 4. 动量指标 (10分) - 只看RSI
if 'rsi' in df.columns and not df['rsi'].isna().all():
rsi = current.get('rsi', 50)
if 35 <= rsi <= 60: # RSI适中区间
score += 10
elif rsi < 25: # 过度超卖
score -= 5
return min(max(score, 0), 70) # 限制区间
def _analyze_consecutive_moves(self, df: pd.DataFrame) -> float:
"""分析连续上涨动量"""
if len(df) < 10:
return 0
score = 0
recent_closes = df['close'].tail(10).values
# 计算连续上涨天数
consecutive_up = 0
for i in range(1, len(recent_closes)):
if recent_closes[i] > recent_closes[i-1]:
consecutive_up += 1
else:
break
# 计算最近3日涨幅
recent_gain = (recent_closes[-1] - recent_closes[-4]) / recent_closes[-4] if len(recent_closes) >= 4 else 0
# 连续上涨但涨幅适中得高分
if consecutive_up >= 3 and 0.03 <= recent_gain <= 0.12: # 3%-12%涨幅
score += 8
elif consecutive_up >= 2 and 0.02 <= recent_gain <= 0.08:
score += 5
elif consecutive_up >= 4: # 连续上涨但可能过热
score += 3
# 价格加速上涨警告
if recent_gain > 0.20: # 超过20%涨幅减分
score -= 5
return max(score, 0)
def _analyze_price_breakthrough(self, df: pd.DataFrame) -> float:
"""分析价格突破情况"""
if len(df) < 50:
return 0
score = 0
current_price = df.iloc[-1]['close']
# 1. 突破早期高点 (10分)
# 计算过去20-50日的高点
historical_data = df.iloc[-50:-10] # 排除最近10天
if len(historical_data) > 0:
resistance_levels = []
# 寻找局部高点
for i in range(5, len(historical_data)-5):
local_high = historical_data.iloc[i]['high']
is_peak = True
for j in range(max(0, i-5), min(len(historical_data), i+6)):
if j != i and historical_data.iloc[j]['high'] > local_high:
is_peak = False
break
if is_peak:
resistance_levels.append(local_high)
# 检查是否突破这些阻力位
for resistance in resistance_levels:
if current_price > resistance * 1.01: # 突破且有一定幅度
# 检查突破的时间和幅度
breakthrough_ratio = (current_price - resistance) / resistance
if breakthrough_ratio <= 0.05: # 刚刚突破,没有过度追高
score += 8
elif breakthrough_ratio <= 0.10:
score += 5
break
# 2. 突破近期高点 (5分)
recent_high = df.tail(10)['high'].max()
if current_price > recent_high * 1.005: # 突破近期高点
score += 5
return min(score, 15)
def _analyze_wave_structure(self, df: pd.DataFrame) -> float:
"""分析波动结构 - 寻找低点抬高模式"""
if len(df) < 30:
return 0
score = 0
recent_data = df.tail(30)
# 寻找近30天的低点
lows = []
highs = []
for i in range(3, len(recent_data)-3):
current_low = recent_data.iloc[i]['low']
current_high = recent_data.iloc[i]['high']
# 判断是否为局部低点
is_local_low = True
is_local_high = True
for j in range(i-3, i+4):
if j != i:
if recent_data.iloc[j]['low'] < current_low:
is_local_low = False
if recent_data.iloc[j]['high'] > current_high:
is_local_high = False
if is_local_low:
lows.append((i, current_low))
if is_local_high:
highs.append((i, current_high))
# 分析低点抬高趋势
if len(lows) >= 2:
# 检查最近两个低点是否抬高
latest_lows = sorted(lows, key=lambda x: x[0])[-2:]
if latest_lows[1][1] > latest_lows[0][1]: # 低点抬高
score += 5
# 分析高点突破趋势
if len(highs) >= 2:
latest_highs = sorted(highs, key=lambda x: x[0])[-2:]
if latest_highs[1][1] > latest_highs[0][1]: # 高点突破
score += 5
return min(score, 10)
def analyze_volume_price_relationship(self, df: pd.DataFrame) -> float:
"""分析量价关系 - 识别资金流入模式 (最高30分)"""
if len(df) < 20:
return 0
score = 0
# 1. 价涨量增模式分析 (15分)
price_volume_score = self._analyze_price_volume_trend(df)
score += price_volume_score
# 2. 量能突破分析 (10分)
volume_breakthrough_score = self._analyze_volume_breakthrough(df)
score += volume_breakthrough_score
# 3. 资金流入模式 (5分)
accumulation_score = self._analyze_accumulation_pattern(df)
score += accumulation_score
return min(score, 30)
def _analyze_price_volume_trend(self, df: pd.DataFrame) -> float:
"""分析价涨量增模式"""
if len(df) < 15:
return 0
score = 0
recent_data = df.tail(15)
# 计算价格和成交量的相关性
price_changes = recent_data['close'].pct_change().dropna()
volume_changes = recent_data['volume'].pct_change().dropna()
if len(price_changes) >= 10 and len(volume_changes) >= 10:
# 只分析上涨日的量价关系
up_days = price_changes > 0
up_price_changes = price_changes[up_days]
up_volume_changes = volume_changes[up_days]
if len(up_price_changes) >= 5:
# 计算上涨日的量价相关性
correlation = up_price_changes.corr(up_volume_changes)
if correlation > 0.5: # 强正相关
score += 10
elif correlation > 0.3:
score += 6
elif correlation > 0.1:
score += 3
# 分析最近5天的量价配合
recent_5 = df.tail(5)
up_days_recent = (recent_5['close'].diff() > 0).sum()
avg_volume_recent = recent_5['volume'].mean()
avg_volume_before = df.iloc[-20:-5]['volume'].mean() if len(df) >= 20 else avg_volume_recent
volume_ratio = avg_volume_recent / avg_volume_before if avg_volume_before > 0 else 1
# 上涨日多且成交量放大
if up_days_recent >= 3 and volume_ratio > 1.2:
score += 5
return min(score, 15)
def _analyze_volume_breakthrough(self, df: pd.DataFrame) -> float:
"""分析量能突破"""
if len(df) < 30:
return 0
score = 0
current_volume = df.iloc[-1]['volume']
avg_volume_30 = df.tail(30)['volume'].mean()
avg_volume_10 = df.tail(10)['volume'].mean()
# 1. 近期量能突破
volume_ratio_current = current_volume / avg_volume_30 if avg_volume_30 > 0 else 1
volume_ratio_recent = avg_volume_10 / avg_volume_30 if avg_volume_30 > 0 else 1
if volume_ratio_current > 2.0: # 当日爆量
# 检查是否伴随价格上涨
price_change = (df.iloc[-1]['close'] - df.iloc[-2]['close']) / df.iloc[-2]['close']
if price_change > 0.02: # 价涨量增
score += 6
elif price_change > 0: # 价涨但幅度不大
score += 3
if 1.3 <= volume_ratio_recent <= 2.5: # 温和持续放量
score += 4
return min(score, 10)
def _analyze_accumulation_pattern(self, df: pd.DataFrame) -> float:
"""分析资金积累模式"""
if len(df) < 20:
return 0
score = 0
recent_data = df.tail(20)
# 分析OBV(能量潮)趋势
# 简化版OBV计算
obv = []
obv_value = 0
for i in range(len(recent_data)):
if i == 0:
obv.append(obv_value)
continue
price_change = recent_data.iloc[i]['close'] - recent_data.iloc[i-1]['close']
volume = recent_data.iloc[i]['volume']
if price_change > 0:
obv_value += volume
elif price_change < 0:
obv_value -= volume
obv.append(obv_value)
# 分析OBV趋势
if len(obv) >= 10:
obv_recent = obv[-5:] # 最近5天
obv_before = obv[-10:-5] # 之前5天
if sum(obv_recent) > sum(obv_before): # OBV上升
score += 3
# 检查价格横盘但量能放大的情况(积累)
price_volatility = recent_data['close'].std() / recent_data['close'].mean()
volume_avg_recent = recent_data.tail(10)['volume'].mean()
volume_avg_before = recent_data.head(10)['volume'].mean()
if (price_volatility < 0.08 and # 价格相对稳定
volume_avg_recent > volume_avg_before * 1.2): # 但量能放大
score += 2
return min(score, 5)
def analyze_technical_patterns(self, df: pd.DataFrame) -> float:
"""分析技术形态 - 综合K线形态和价格结构 (最高25分)"""
if len(df) < 20:
return 0
score = 0
# 1. K线形态识别 (15分)
candlestick_score = self._analyze_enhanced_candlestick_patterns(df)
score += candlestick_score
# 2. 价格结构分析 (10分)
structure_score = self._analyze_price_structure(df)
score += structure_score
return min(score, 25)
def _analyze_enhanced_candlestick_patterns(self, df: pd.DataFrame) -> float:
"""增强型K线形态分析"""
if len(df) < 10:
return 0
score = 0
# 使用talib的K线形态识别
patterns = {
'hammer': talib.CDLHAMMER(df['open'], df['high'], df['low'], df['close']),
'doji': talib.CDLDOJI(df['open'], df['high'], df['low'], df['close']),
'engulfing': talib.CDLENGULFING(df['open'], df['high'], df['low'], df['close']),
'morning_star': talib.CDLMORNINGSTAR(df['open'], df['high'], df['low'], df['close']),
'three_white_soldiers': talib.CDL3WHITESOLDIERS(df['open'], df['high'], df['low'], df['close']),
'piercing': talib.CDLPIERCING(df['open'], df['high'], df['low'], df['close']),
'harami_bullish': talib.CDLHARAMI(df['open'], df['high'], df['low'], df['close'])
}
# 检查最近3天的K线形态
recent_signals = 0
for pattern_name, pattern_data in patterns.items():
if len(pattern_data) > 3:
# 检查最近3天是否有信号
for i in range(-3, 0):
if pattern_data.iloc[i] > 0: # 看涨信号
recent_signals += 1
if pattern_name in ['morning_star', 'three_white_soldiers', 'piercing']:
score += 4 # 强看涨形态
elif pattern_name in ['hammer', 'engulfing']:
score += 3 # 中度看涨形态
else:
score += 2 # 弱看涨形态
break
# 手动分析一些简单形态
manual_score = self._analyze_manual_patterns(df.tail(10))
score += manual_score
return min(score, 15)
def _analyze_manual_patterns(self, df: pd.DataFrame) -> float:
"""手动分析一些简单形态"""
if len(df) < 5:
return 0
score = 0
# 分析最近5天的走势
last_5 = df.tail(5)
# 1. 阶段性低点上移模式
lows = last_5['low'].tolist()
if len(lows) >= 3:
# 检查是否有低点逐步抬高的趋势
ascending_lows = True
for i in range(1, len(lows)):
if lows[i] < lows[i-1] * 0.98: # 允许小幅波动
ascending_lows = False
break
if ascending_lows:
score += 3
# 2. 突破前高点模式
current_high = df.iloc[-1]['high']
prev_highs = df.iloc[-5:-1]['high'].tolist()
if current_high > max(prev_highs) * 1.01: # 突破前高
score += 2
return min(score, 5)
def _analyze_price_structure(self, df: pd.DataFrame) -> float:
"""分析价格结构"""
if len(df) < 30:
return 0
score = 0
# 1. 分析关键支撑位突破 (6分)
support_score = self._analyze_support_breakthrough(df)
score += support_score
# 2. 分析价格区间突破 (4分)
range_score = self._analyze_range_breakthrough(df)
score += range_score
return min(score, 10)
def _analyze_support_breakthrough(self, df: pd.DataFrame) -> float:
"""分析支撑位突破"""
if len(df) < 30:
return 0
score = 0
current_price = df.iloc[-1]['close']
# 寻找近30天内的重要支撑位
recent_30 = df.tail(30)
# 计算支撑位(多次触及的低点)
support_levels = []
for i in range(5, len(recent_30)-5):
low_price = recent_30.iloc[i]['low']
# 检查这个低点是否被多次测试
test_count = 0
for j in range(len(recent_30)):
if abs(recent_30.iloc[j]['low'] - low_price) / low_price < 0.02: # 2%范围内
test_count += 1
if test_count >= 2: # 至少被测试2次
support_levels.append(low_price)
# 检查是否突破了这些支撑位
for support in support_levels:
if current_price > support * 1.03: # 突破支撑位3%以上
score += 3
break
return min(score, 6)
def _analyze_range_breakthrough(self, df: pd.DataFrame) -> float:
"""分析区间突破"""
if len(df) < 20:
return 0
score = 0
recent_20 = df.tail(20)
current_price = df.iloc[-1]['close']
# 计算近20天的价格区间
price_high = recent_20['high'].max()
price_low = recent_20['low'].min()
price_range = price_high - price_low
# 检查是否在盘整后突破
if price_range > 0:
# 检查是否经历了盘整阶段
middle_period = recent_20.iloc[5:15] # 中间一段
consolidation_range = middle_period['high'].max() - middle_period['low'].min()
# 如果中间阶段波动较小,说明有盘整
if consolidation_range < price_range * 0.6:
# 检查是否突破了区间高点
if current_price > price_high * 0.995: # 接近或突破高点
score += 4
return min(score, 4)
def analyze_price_behavior(self, df: pd.DataFrame) -> float:
"""分析价格行为 - 核心指标 (最高35分)"""
if len(df) < 30:
return 0
score = 0
recent_data = df.tail(20)
# 1. 连续上涨动量分析 (12分)
consecutive_score = self._analyze_consecutive_moves(df)
score += consecutive_score
# 2. 价格突破分析 (15分)
breakthrough_score = self._analyze_price_breakthrough(df)
score += breakthrough_score
# 3. 波动结构分析 (8分)
wave_score = self._analyze_wave_structure(df)
score += wave_score
return min(score, 35)
def analyze_breakout_potential(self, df: pd.DataFrame) -> float:
"""分析突破潜力 - 辅助指标 (最高20分)"""
if len(df) < 30:
return 0
score = 0
current = df.iloc[-1]
recent_data = df.tail(20)
# 1. 价格位置分析 - 寻找埋伏位置 (8分)
recent_high = recent_data['high'].max()
recent_low = recent_data['low'].min()
price_range = recent_high - recent_low
current_position = (current['close'] - recent_low) / price_range if price_range > 0 else 0
# 偏好在区间中下部的币种
if 0.2 <= current_position <= 0.5: # 理想埋伏位置
score += 6
elif 0.5 < current_position <= 0.7: # 次优位置
score += 3
elif current_position > 0.8: # 已经在高位,减分
score -= 5
# 2. 整理形态分析 (6分)
volatility = recent_data['close'].std() / recent_data['close'].mean()
if 0.05 <= volatility <= 0.12: # 适度整理,蓄势待发
score += 5
elif volatility < 0.03: # 过度整理,缺乏动力
score += 1
elif volatility > 0.20: # 波动过大,风险较高
score -= 3
# 3. 量能配合 (6分)
volume_ratio = recent_data['volume'] / recent_data['volume'].rolling(10).mean()
moderate_volume_days = ((volume_ratio >= 1.1) & (volume_ratio <= 1.8)).sum()
explosive_volume_days = (volume_ratio > 2.5).sum()
if moderate_volume_days >= 3: # 持续温和放量
score += 4
if explosive_volume_days <= 1: # 没有过度爆量
score += 2
return max(min(score, 20), 0)
def analyze_short_signals(self, df: pd.DataFrame, strategy: str = '中线') -> Optional[CoinSignal]:
"""分析做空信号 - 识别下跌趋势币种"""
if df.empty or len(df) < 50:
return None
try:
df = self.calculate_technical_indicators(df, strategy)
config = self.strategies[strategy]
total_score = 0
reasons = []
# === 做空信号评分体系 ===
# 1. 空头价格行为分析 (35分)
bear_price_score = self.analyze_bearish_price_behavior(df)
if bear_price_score > 0:
total_score += bear_price_score
if bear_price_score >= 25:
reasons.append(f"强势下跌信号({bear_price_score}分)")
elif bear_price_score >= 15:
reasons.append(f"下跌信号({bear_price_score}分)")
# 2. 空头量价关系 (30分)
bear_volume_score = self.analyze_bearish_volume_price(df)
if bear_volume_score > 0:
total_score += bear_volume_score
if bear_volume_score >= 20:
reasons.append(f"理想空头量价({bear_volume_score}分)")
elif bear_volume_score >= 10:
reasons.append(f"空头量价({bear_volume_score}分)")
# 3. 空头技术形态 (25分)
bear_pattern_score = self.analyze_bearish_patterns(df)
if bear_pattern_score > 0:
total_score += bear_pattern_score
if bear_pattern_score >= 15:
reasons.append(f"强空头形态({bear_pattern_score}分)")
elif bear_pattern_score >= 8:
reasons.append(f"空头形态({bear_pattern_score}分)")
# 4. 下跌突破潜力 (20分)
breakdown_score = self.analyze_breakdown_potential(df)
if breakdown_score > 0:
total_score += breakdown_score
if breakdown_score >= 15:
reasons.append(f"高下跌潜力({breakdown_score}分)")
elif breakdown_score >= 8:
reasons.append(f"下跌潜力({breakdown_score}分)")
# 5. 均线空头系统 (10分)
bear_ma_score = self.analyze_bearish_moving_average(df)
if bear_ma_score > 0:
total_score += bear_ma_score
if bear_ma_score >= 6:
reasons.append(f"均线空头({int(bear_ma_score)}分)")
# 过滤低分币种
if total_score < 65: # 做空也需要足够的信号强度
return None
# 确定信心等级
if total_score >= 80:
confidence = ""
elif total_score >= 65:
confidence = ""
else:
confidence = ""
# 计算空头入场和出场位
entry_price, stop_loss, take_profit, risk_reward = self.calculate_short_entry_exit_levels(df, strategy)
# 生成空头操作建议
current_price = df.iloc[-1]['close']
action_suggestion = self.generate_short_action_suggestion(df, entry_price, current_price, total_score)
return CoinSignal(
symbol=None, # 将在调用时设置
score=total_score,
reason=" | ".join(reasons),
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=strategy,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="SHORT",
direction="SELL"
)
except Exception as e:
logging.error(f"分析空头信号失败: {e}")
return None
def analyze_bearish_price_behavior(self, df: pd.DataFrame) -> float:
"""分析空头价格行为 - 核心指标 (35分)"""
if len(df) < 30:
return 0
score = 0
recent_data = df.tail(20)
# 1. 连续下跌动量 (10分)
consecutive_down = 0
recent_closes = recent_data['close'].tail(10).values
for i in range(1, len(recent_closes)):
if recent_closes[i] < recent_closes[i-1]:
consecutive_down += 1
else:
break
recent_decline = (recent_closes[-1] - recent_closes[-4]) / recent_closes[-4] if len(recent_closes) >= 4 else 0
if consecutive_down >= 3 and -0.12 <= recent_decline <= -0.03: # 3%-12%下跌
score += 8
elif consecutive_down >= 2 and -0.08 <= recent_decline <= -0.02:
score += 5
# 2. 价格跌破分析 (15分)
current_price = df.iloc[-1]['close']
# 跌破支撑位
historical_data = df.iloc[-50:-10]
if len(historical_data) > 0:
support_levels = []
for i in range(5, len(historical_data)-5):
local_low = historical_data.iloc[i]['low']
is_valley = True
for j in range(max(0, i-5), min(len(historical_data), i+6)):
if j != i and historical_data.iloc[j]['low'] < local_low:
is_valley = False
break
if is_valley:
support_levels.append(local_low)
for support in support_levels:
if current_price < support * 0.97: # 跌破支撑位3%以上
breakdown_ratio = (support - current_price) / support
if breakdown_ratio <= 0.05: # 刚刚跌破
score += 10
elif breakdown_ratio <= 0.10:
score += 6
break
# 3. 高点逐步下移模式 (10分)
recent_highs = []
for i in range(3, len(recent_data)-3):
current_high = recent_data.iloc[i]['high']
is_local_high = True
for j in range(i-3, i+4):
if j != i and recent_data.iloc[j]['high'] > current_high:
is_local_high = False
break
if is_local_high:
recent_highs.append((i, current_high))
if len(recent_highs) >= 2:
latest_highs = sorted(recent_highs, key=lambda x: x[0])[-2:]
if latest_highs[1][1] < latest_highs[0][1]: # 高点下移
score += 8
return min(score, 35)
def _comprehensive_long_analysis(self, main_df: pd.DataFrame, timeframe_data: Dict, strategy: str) -> float:
"""综合多头分析 - 优化策略本身而非降低分数"""
if len(main_df) < 30:
return 0
total_score = 0
score_details = {} # 用于记录各项得分
# 1. 价格行为分析 (35分)
price_score = self.analyze_price_behavior(main_df)
total_score += price_score
score_details['价格行为'] = price_score
# 2. 量价关系 (30分)
volume_score = self.analyze_volume_price_relationship(main_df)
total_score += volume_score
score_details['量价关系'] = volume_score
# 3. 技术形态 (25分)
pattern_score = self.analyze_technical_patterns(main_df)
total_score += pattern_score
score_details['技术形态'] = pattern_score
# 4. 突破潜力 (20分)
breakout_score = self.analyze_breakout_potential(main_df)
total_score += breakout_score
score_details['突破潜力'] = breakout_score
# 5. 多时间框架确认 (15分) - 新增
multi_tf_score = self._analyze_multi_timeframe_confirmation(timeframe_data, strategy, 'LONG')
total_score += multi_tf_score
score_details['多时间框架确认'] = multi_tf_score
# 6. 风险评估调整 (-30到+10分) - 新增
risk_adjustment = self._analyze_risk_factors(main_df, 'LONG')
total_score += risk_adjustment
score_details['风险评估调整'] = risk_adjustment
# 记录详细评分 - 改为100分制
final_score = min(total_score, 100)
logging.info(f"多头分析详细评分: 价格行为({price_score:.1f}) + 量价关系({volume_score:.1f}) + "
f"技术形态({pattern_score:.1f}) + 突破潜力({breakout_score:.1f}) + "
f"多时间框架({multi_tf_score:.1f}) + 风险调整({risk_adjustment:.1f}) = {final_score:.1f}")
return final_score
def _comprehensive_short_analysis(self, main_df: pd.DataFrame, timeframe_data: Dict, strategy: str) -> float:
"""综合空头分析 - 优化策略本身而非降低分数"""
if len(main_df) < 30:
return 0
total_score = 0
score_details = {} # 用于记录各项得分
# 1. 空头价格行为 (35分)
bear_price_score = self.analyze_bearish_price_behavior(main_df)
total_score += bear_price_score
score_details['空头价格行为'] = bear_price_score
# 2. 空头量价关系 (30分)
bear_volume_score = self.analyze_bearish_volume_price(main_df)
total_score += bear_volume_score
score_details['空头量价关系'] = bear_volume_score
# 3. 空头技术形态 (25分)
bear_pattern_score = self.analyze_bearish_patterns(main_df)
total_score += bear_pattern_score
score_details['空头技术形态'] = bear_pattern_score
# 4. 下跌突破潜力 (20分)
breakdown_score = self.analyze_breakdown_potential(main_df)
total_score += breakdown_score
score_details['下跌突破潜力'] = breakdown_score
# 5. 多时间框架确认 (15分) - 新增
multi_tf_score = self._analyze_multi_timeframe_confirmation(timeframe_data, strategy, 'SHORT')
total_score += multi_tf_score
score_details['多时间框架确认'] = multi_tf_score
# 6. 风险评估调整 (-30到+10分) - 新增
risk_adjustment = self._analyze_risk_factors(main_df, 'SHORT')
total_score += risk_adjustment
score_details['风险评估调整'] = risk_adjustment
# 记录详细评分 - 改为100分制
final_score = min(total_score, 100)
logging.info(f"空头分析详细评分: 空头价格行为({bear_price_score:.1f}) + 空头量价关系({bear_volume_score:.1f}) + "
f"空头技术形态({bear_pattern_score:.1f}) + 下跌突破潜力({breakdown_score:.1f}) + "
f"多时间框架({multi_tf_score:.1f}) + 风险调整({risk_adjustment:.1f}) = {final_score:.1f}")
return final_score
def _analyze_multi_timeframe_confirmation(self, timeframe_data: Dict, strategy: str, signal_type: str) -> float:
"""多时间框架确认分析"""
score = 0
config = self.strategies[strategy]
# 获取确认时间框架数据
confirm_df = timeframe_data.get(config.confirm_timeframe)
if confirm_df is None or len(confirm_df) < 20:
return 0
confirm_df = self.calculate_technical_indicators(confirm_df, strategy)
if signal_type == 'LONG':
# 确认时间框架的多头信号
confirm_price_score = self.simple_long_analysis(confirm_df)
if confirm_price_score >= 30:
score += 15
elif confirm_price_score >= 20:
score += 10
elif confirm_price_score >= 15:
score += 5
else: # SHORT
# 确认时间框架的空头信号
confirm_price_score = self.simple_short_analysis(confirm_df)
if confirm_price_score >= 40:
score += 15
elif confirm_price_score >= 30:
score += 10
elif confirm_price_score >= 20:
score += 5
return score
def _analyze_risk_factors(self, df: pd.DataFrame, signal_type: str) -> float:
"""风险因素分析"""
if len(df) < 20:
return 0
score = 0
current = df.iloc[-1]
recent_data = df.tail(20)
# 1. RSI过热/过冷检查
if hasattr(current, 'rsi'):
rsi = current['rsi']
if signal_type == 'LONG':
if rsi > 80: # 严重超买
score -= 15
elif rsi > 70: # 超买
score -= 8
elif 40 <= rsi <= 65: # 健康区间
score += 5
else: # SHORT
if rsi < 20: # 严重超卖
score -= 15
elif rsi < 30: # 超卖
score -= 8
elif 35 <= rsi <= 60: # 健康区间
score += 5
# 2. 价格位置风险
recent_high = recent_data['high'].max()
recent_low = recent_data['low'].min()
price_range = recent_high - recent_low
current_position = (current['close'] - recent_low) / price_range if price_range > 0 else 0.5
if signal_type == 'LONG':
if current_position > 0.9: # 极高位置
score -= 20
elif current_position > 0.8: # 高位置
score -= 10
elif 0.2 <= current_position <= 0.6: # 理想位置
score += 5
else: # SHORT
if current_position < 0.1: # 极低位置
score -= 20
elif current_position < 0.2: # 低位置
score -= 10
elif 0.4 <= current_position <= 0.8: # 理想位置
score += 5
# 3. 成交量异常检查
if hasattr(current, 'volume') and hasattr(current, 'volume_ma'):
volume_ratio = current['volume'] / current['volume_ma'] if current['volume_ma'] > 0 else 1
if volume_ratio > 5: # 异常爆量
score -= 10
elif volume_ratio < 0.3: # 成交量萎缩
score -= 5
return max(min(score, 10), -30)
def analyze_volume_accumulation(self, df: pd.DataFrame) -> float:
"""分析成交量积累模式"""
if len(df) < 20:
return 0
score = 0
recent_data = df.tail(10)
# 寻找温和放量而非暴涨放量
volume_ratio = recent_data['volume'] / recent_data['volume_ma']
# 温和持续放量(1.2-2倍)比突然暴涨放量更好
moderate_volume_days = ((volume_ratio >= 1.2) & (volume_ratio <= 2.0)).sum()
explosive_volume_days = (volume_ratio > 3.0).sum()
if moderate_volume_days >= 3: # 持续温和放量
score += 15
if explosive_volume_days >= 2: # 已经暴涨放量,减分
score -= 10
return min(score, 20)
def check_early_trend_signals(self, df: pd.DataFrame) -> float:
"""检查早期趋势信号"""
if len(df) < 50:
return 0
score = 0
current = df.iloc[-1]
prev = df.iloc[-2]
# 1. 均线金叉但涨幅不大
if (prev['ma20'] <= prev['ma50'] and current['ma20'] > current['ma50']):
# 刚刚金叉,还未大涨
recent_gain = (current['close'] - df.iloc[-5]['close']) / df.iloc[-5]['close']
if recent_gain < 0.10: # 5日涨幅小于10%
score += 20
elif recent_gain > 0.25: # 已经涨幅过大
score -= 15
# 2. RSI从超卖区域回升但未超买
if 35 <= current['rsi'] <= 55: # 从底部回升但未过热
score += 10
elif current['rsi'] > 70: # 已经超买
score -= 20
# 3. MACD即将金叉或刚刚金叉
if current['macd'] > current['macd_signal']:
# 检查金叉时间
macd_cross_days = 0
for i in range(1, min(6, len(df))):
if df.iloc[-i]['macd'] <= df.iloc[-i]['macd_signal']:
macd_cross_days = i - 1
break
if macd_cross_days <= 2: # 刚刚金叉2天内
score += 15
elif macd_cross_days >= 10: # 金叉很久了
score -= 10
return min(score, 25)
def analyze_candlestick_patterns(self, df: pd.DataFrame) -> float:
"""分析K线形态"""
if len(df) < 20:
return 0
score = 0
# 使用talib的K线形态识别
patterns = {
'hammer': talib.CDLHAMMER(df['open'], df['high'], df['low'], df['close']),
'doji': talib.CDLDOJI(df['open'], df['high'], df['low'], df['close']),
'engulfing': talib.CDLENGULFING(df['open'], df['high'], df['low'], df['close']),
'morning_star': talib.CDLMORNINGSTAR(df['open'], df['high'], df['low'], df['close']),
'three_white_soldiers': talib.CDL3WHITESOLDIERS(df['open'], df['high'], df['low'], df['close'])
}
# 检查最近的K线形态
recent_signals = 0
for pattern_name, pattern_data in patterns.items():
if len(pattern_data) > 0 and pattern_data.iloc[-1] > 0:
recent_signals += 1
if pattern_name in ['morning_star', 'three_white_soldiers']:
score += 15
elif pattern_name in ['hammer', 'engulfing']:
score += 10
else:
score += 5
return min(score, 20)
def analyze_moving_average_trend(self, df: pd.DataFrame) -> float:
"""分析均线系统"""
if len(df) < 50:
return 0
score = 0
current = df.iloc[-1]
# 均线多头排列
if current['close'] > current['ma20'] > current['ma50']:
score += 15
# 价格突破关键均线
prev = df.iloc[-2]
if prev['close'] <= prev['ma20'] and current['close'] > current['ma20']:
score += 10
# 均线斜率向上
ma20_slope = (current['ma20'] - df.iloc[-5]['ma20']) / 5
if ma20_slope > 0:
score += 5
return min(score, 25)
def analyze_momentum_indicators(self, df: pd.DataFrame) -> float:
"""分析动量指标"""
if len(df) < 20:
return 0
score = 0
current = df.iloc[-1]
# RSI分析
if 30 <= current['rsi'] <= 50: # RSI从超卖区域回升
score += 10
elif 50 <= current['rsi'] <= 70: # RSI强势但未超买
score += 5
# MACD分析
if current['macd'] > current['macd_signal'] and current['macd_hist'] > 0:
score += 10
# MACD金叉
prev = df.iloc[-2]
if prev['macd'] <= prev['macd_signal'] and current['macd'] > current['macd_signal']:
score += 15
return min(score, 25)
def calculate_entry_exit_levels(self, df: pd.DataFrame, fib_levels: Dict, strategy: str) -> Tuple[float, float, float]:
"""计算入场、止损、止盈位"""
current_price = df.iloc[-1]['close']
atr = df.iloc[-1]['atr']
support, resistance = self.find_support_resistance(df)
config = self.strategies[strategy]
# 入场价策略 - 不使用当前市价,而是基于技术分析
ma20 = df.iloc[-1]['ma20']
ma50 = df.iloc[-1]['ma50']
# 策略1: 均线回踩入场
if current_price > ma20 > ma50: # 多头趋势
# 等待回踩MA20附近入场给出5%的缓冲
entry_price = ma20 * 1.02 # MA20上方2%
else:
# 策略2: 突破确认入场
# 等待突破近期阻力位
recent_high = df.tail(10)['high'].max()
if current_price >= recent_high * 0.98: # 接近突破
entry_price = recent_high * 1.005 # 突破确认价位
else:
# 策略3: 斐波那契回调入场
if fib_levels and 'fib_618' in fib_levels:
entry_price = max(fib_levels['fib_618'], current_price * 0.98)
else:
# 策略4: 当前价格适当下方等待
entry_price = current_price * 0.995 # 当前价下方0.5%
# 确保入场价不会过低(距离当前价不超过5%)
if entry_price < current_price * 0.95:
entry_price = current_price * 0.98
# 止损位: 支撑位下方或ATR的1.5倍
stop_loss_atr = entry_price - (atr * 1.5)
stop_loss_support = support * 0.98 if support > 0 else entry_price * 0.92
stop_loss = max(stop_loss_atr, stop_loss_support)
# 根据策略动态设置风险回报比
risk = entry_price - stop_loss
target_ratio = (config.risk_reward_min + config.risk_reward_max) / 2
take_profit_ratio = entry_price + (risk * target_ratio)
# 如果有阻力位,取较小值
if resistance > entry_price:
take_profit_resistance = resistance * 0.98
take_profit = min(take_profit_ratio, take_profit_resistance)
else:
take_profit = take_profit_ratio
# 计算实际风险回报比
actual_risk_reward = (take_profit - entry_price) / risk if risk > 0 else target_ratio
return entry_price, stop_loss, take_profit, actual_risk_reward
def generate_action_suggestion(self, df: pd.DataFrame, signal: CoinSignal, current_price: float) -> str:
"""根据评分和价格位置生成操作建议"""
try:
# 计算价格差异百分比
entry_diff = (current_price - signal.entry_price) / signal.entry_price * 100
# 获取技术指标状态
current = df.iloc[-1]
recent_data = df.tail(10)
# 计算价格在最近区间的位置
recent_high = recent_data['high'].max()
recent_low = recent_data['low'].min()
price_range = recent_high - recent_low
current_position = (current_price - recent_low) / price_range if price_range > 0 else 0.5
# 基于评分等级的基础建议
if signal.score >= 85: # 高分
if entry_diff <= 1: # 接近建议价位
if current_position <= 0.4: # 在低位
return "立即买入"
elif current_position <= 0.6:
return "现价买入"
else:
return "等待回调买入"
elif entry_diff <= 3: # 稍高于建议价位
if signal.strategy_type == "短线":
return "现价买入"
else:
return "等待回调买入"
else: # 明显高于建议价位
return "等待大幅回调"
elif signal.score >= 70: # 中等分数
if entry_diff <= -2: # 低于建议价位
return "现价买入"
elif entry_diff <= 1: # 接近建议价位
if current_position <= 0.5:
return "现价买入"
else:
return "等待回调买入"
elif entry_diff <= 5: # 高于建议价位
return "等待回调买入"
else:
return "暂时观望"
else: # 较低分数 (60-70)
if entry_diff <= -3: # 明显低于建议价位
return "分批买入"
elif entry_diff <= 0: # 低于或等于建议价位
if current_position <= 0.3:
return "小仓位试探"
else:
return "等待回调买入"
elif entry_diff <= 3:
return "等待回调买入"
else:
return "暂时观望"
return "等待回调买入" # 默认建议
except Exception as e:
logging.error(f"生成操作建议时出错: {e}")
return "谨慎观望"
def analyze_single_coin(self, symbol: str, timeframe_data: Dict[str, pd.DataFrame], volume_24h_usd: float = 0) -> List[CoinSignal]:
"""分析单个币种 - 优化策略"""
try:
logging.info(f"\n=== 开始分析 {symbol} ===")
logging.info(f"24小时交易量: ${volume_24h_usd:,.0f}")
# 先执行基础分析,即使交易量不够也要显示评分
basic_analysis_executed = False
# 24小时交易量过滤 - 但仍然显示评分
if volume_24h_usd < 10_000_000: # 降低到1000万美金进行调试
logging.info(f"{symbol} 交易量不足1000万美金但仍显示评分")
# 继续执行分析以显示评分,但最后返回空列表
basic_analysis_executed = True
signals = []
# 首先使用4h数据确定最适合的策略
initial_df = timeframe_data.get('4h')
if initial_df is None or len(initial_df) < 20:
logging.info(f"{symbol} 4h数据不足跳过分析")
return []
# 确定最佳策略
best_strategy = self.determine_best_strategy(initial_df)
config = self.strategies[best_strategy]
logging.info(f"{symbol} 选择策略: {best_strategy} (主时间周期: {config.primary_timeframe}, 确认时间周期: {config.confirm_timeframe})")
# 检查可用的时间周期数据
available_timeframes = list(timeframe_data.keys())
logging.info(f"{symbol} 可用时间周期: {available_timeframes}")
# 获取策略对应的主要时间周期数据
main_df = timeframe_data.get(config.primary_timeframe)
if main_df is None or len(main_df) < 20:
# 尝试使用该策略的确认时间周期
confirm_df = timeframe_data.get(config.confirm_timeframe)
if confirm_df is not None and len(confirm_df) >= 20:
main_df = confirm_df
logging.info(f"{symbol} 使用确认时间周期{config.confirm_timeframe}替代主时间周期{config.primary_timeframe}")
else:
# 最后才回退到初始数据和中线策略
main_df = initial_df
config = self.strategies['中线']
best_strategy = '中线'
logging.info(f"{symbol} 数据不足,回退到中线策略")
# 使用策略相应的参数计算技术指标
main_df = self.calculate_technical_indicators(main_df, best_strategy)
# === 综合多头信号分析 ===
logging.info(f"\n--- {symbol} 多头分析 ---")
long_score = self._comprehensive_long_analysis(main_df, timeframe_data, best_strategy)
# 记录多头分析结果(不管是否通过筛选)
logging.info(f"{symbol} 多头分析结果: {long_score:.1f}/100分 (门槛: 70分)")
if long_score >= 70: # 70分入选门槛
logging.info(f"{symbol} 多头信号通过筛选")
# 计算入场出场位
entry_price, stop_loss, take_profit, risk_reward = self.calculate_entry_exit_levels(
main_df, {}, best_strategy
)
# 确定信心等级 - 基于100分制
if long_score >= 85:
confidence = ""
elif long_score >= 75:
confidence = ""
else:
confidence = ""
# 生成操作建议 - 基于综合分析
current_price = main_df.iloc[-1]['close']
action_suggestion = "现价买入" if long_score >= 85 else "等待回调买入"
logging.info(f"{symbol} 多头信号详情: 入场${entry_price:.4f}, 止损${stop_loss:.4f}, 止盈${take_profit:.4f}, 风险回报比1:{risk_reward:.2f}")
long_signal = CoinSignal(
symbol=symbol,
score=long_score,
reason=f"综合多头分析: 高质量信号({long_score}分)",
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=best_strategy,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="LONG",
direction="BUY"
)
signals.append(long_signal)
else:
logging.info(f"{symbol} 多头信号未通过筛选 (差距: {70 - long_score:.1f}分)")
# === 综合空头信号分析 ===
logging.info(f"\n--- {symbol} 空头分析 ---")
short_score = self._comprehensive_short_analysis(main_df, timeframe_data, best_strategy)
# 记录空头分析结果(不管是否通过筛选)
logging.info(f"{symbol} 空头分析结果: {short_score:.1f}/100分 (门槛: 70分)")
if short_score >= 70: # 与多头门槛保持一致
logging.info(f"{symbol} 空头信号通过筛选")
# 计算入场出场位
entry_price, stop_loss, take_profit, risk_reward = self.calculate_short_entry_exit_levels(
main_df, best_strategy
)
# 确定信心等级 - 基于100分制
if short_score >= 85:
confidence = ""
elif short_score >= 75:
confidence = ""
else:
confidence = ""
# 生成操作建议 - 基于综合分析
current_price = main_df.iloc[-1]['close']
action_suggestion = "现价做空" if short_score >= 85 else "等待反弹做空"
logging.info(f"{symbol} 空头信号详情: 入场${entry_price:.4f}, 止损${stop_loss:.4f}, 止盈${take_profit:.4f}, 风险回报比1:{risk_reward:.2f}")
short_signal = CoinSignal(
symbol=symbol,
score=short_score,
reason=f"综合空头分析: 高质量信号({short_score}分)",
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=best_strategy,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="SHORT",
direction="SELL"
)
signals.append(short_signal)
else:
logging.info(f"{symbol} 空头信号未通过筛选 (差距: {70 - short_score:.1f}分)")
# 总结该币种分析结果
if signals:
signal_types = [s.signal_type for s in signals]
logging.info(f"{symbol} 分析完成 - 生成{len(signals)}个信号: {', '.join(signal_types)}")
else:
logging.info(f"{symbol} 分析完成 - 无符合条件的信号 (多头:{long_score:.1f}, 空头:{short_score:.1f})")
# 如果是因为交易量不足而跳过,返回空列表
if basic_analysis_executed:
logging.info(f"{symbol} 因交易量不足,不生成实际交易信号")
return []
return signals
except Exception as e:
logging.error(f"分析{symbol}时出错: {e}")
return []
def analyze_single_coin_with_strategy(self, symbol: str, timeframe_data: Dict[str, pd.DataFrame],
volume_24h_usd: float, strategy_name: str) -> List[CoinSignal]:
"""使用指定策略分析单个币种"""
try:
# 24小时交易量过滤
if volume_24h_usd < 50_000_000:
return []
# 验证策略是否存在
if strategy_name not in self.strategies:
logging.error(f"未知策略: {strategy_name}")
return []
config = self.strategies[strategy_name]
# 获取策略对应的主要时间周期数据
main_df = timeframe_data.get(config.primary_timeframe)
if main_df is None or len(main_df) < 20:
return []
# 使用策略相应的参数计算技术指标
main_df = self.calculate_technical_indicators(main_df, strategy_name)
signals = []
# 综合多头信号分析
long_score = self._comprehensive_long_analysis(main_df, timeframe_data, strategy_name)
if long_score >= 80:
# 生成多头信号
entry_price, stop_loss, take_profit, risk_reward = self.calculate_entry_exit_levels(
main_df, {}, strategy_name
)
confidence = "" if long_score >= 85 else "" if long_score >= 75 else ""
action_suggestion = "现价买入" if long_score >= 85 else "等待回调买入"
long_signal = CoinSignal(
symbol=symbol,
score=long_score,
reason=f"{strategy_name}策略多头信号({long_score}分)",
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=strategy_name,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="LONG",
direction="BUY"
)
signals.append(long_signal)
# 综合空头信号分析
short_score = self._comprehensive_short_analysis(main_df, timeframe_data, strategy_name)
if short_score >= 90:
# 生成空头信号
entry_price, stop_loss, take_profit, risk_reward = self.calculate_short_entry_exit_levels(
main_df, strategy_name
)
confidence = "" if short_score >= 85 else "" if short_score >= 75 else ""
action_suggestion = "现价做空" if short_score >= 85 else "等待反弹做空"
short_signal = CoinSignal(
symbol=symbol,
score=short_score,
reason=f"{strategy_name}策略空头信号({short_score}分)",
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=strategy_name,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="SHORT",
direction="SELL"
)
signals.append(short_signal)
return signals
except Exception as e:
logging.error(f"使用{strategy_name}策略分析{symbol}时出错: {e}")
return []
def _analyze_long_signal(self, symbol: str, main_df: pd.DataFrame, timeframe_data: Dict, config: StrategyConfig, best_strategy: str) -> Optional[CoinSignal]:
"""分析多头信号 - 原有逻辑"""
total_score = 0
reasons = []
# === 核心评分体系:以价格行为和技术形态为主导 ===
# 1. 价格行为分析 - 最重要 (最高35分)
price_behavior_score = self.analyze_price_behavior(main_df)
if price_behavior_score > 0:
total_score += price_behavior_score
if price_behavior_score >= 25:
reasons.append(f"强势价格行为({price_behavior_score}分)")
elif price_behavior_score >= 15:
reasons.append(f"积极价格行为({price_behavior_score}分)")
# 2. 量价关系分析 - 核心指标 (最高30分)
volume_price_score = self.analyze_volume_price_relationship(main_df)
if volume_price_score > 0:
total_score += volume_price_score
if volume_price_score >= 20:
reasons.append(f"理想量价配合({volume_price_score}分)")
elif volume_price_score >= 10:
reasons.append(f"量价配合({volume_price_score}分)")
# 3. 技术形态确认 - 重要指标 (最高25分)
pattern_score = self.analyze_technical_patterns(main_df)
if pattern_score > 0:
total_score += pattern_score
if pattern_score >= 15:
reasons.append(f"强势技术形态({pattern_score}分)")
elif pattern_score >= 8:
reasons.append(f"技术形态({pattern_score}分)")
# 4. 突破潜力分析 - 寻找爆发机会 (最高20分)
breakout_score = self.analyze_breakout_potential(main_df)
if breakout_score > 0:
total_score += breakout_score
if breakout_score >= 15:
reasons.append(f"高突破潜力({breakout_score}分)")
elif breakout_score >= 8:
reasons.append(f"突破潜力({breakout_score}分)")
# 5. 均线系统 - 辅助确认 (最高10分权重降低)
ma_score = self.analyze_moving_average_trend(main_df)
if ma_score > 0:
ma_score = min(ma_score * 0.4, 10) # 大幅降低权重
total_score += ma_score
if ma_score >= 6:
reasons.append(f"均线支撑({int(ma_score)}分)")
# 6. 确认时间周期验证 - 多周期确认 (最高5分)
confirm_df = timeframe_data.get(config.confirm_timeframe)
if confirm_df is not None and len(confirm_df) > 20:
confirm_df = self.calculate_technical_indicators(confirm_df, best_strategy)
confirm_score = self.analyze_price_behavior(confirm_df) * 0.2 # 降低权重
if confirm_score > 3:
total_score += min(confirm_score, 5)
reasons.append(f"{config.confirm_timeframe}确认")
# 过滤低分币种 - 适度放宽条件
if price_behavior_score < 5 and volume_price_score < 5: # 降低要求
return None
# 适度放宽的分数过滤
min_total_score = 50 # 降低最低门槛
if total_score < min_total_score:
return None
# 计算新波那契回调位
fib_levels = self.calculate_fibonacci_levels(main_df)
# 计算入场和出场位 - 使用对应策略
entry_price, stop_loss, take_profit, risk_reward = self.calculate_entry_exit_levels(
main_df, fib_levels, best_strategy
)
# 确定信心等级 - 适度放宽标准
if total_score >= 75: # 降低高信心阈值
confidence = ""
elif total_score >= 55: # 降低中等信心阈值
confidence = ""
else:
confidence = ""
# 生成操作建议
current_price = main_df.iloc[-1]['close']
temp_signal = CoinSignal(
symbol=symbol,
score=total_score,
reason=" | ".join(reasons),
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=best_strategy,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion="", # 临时占位
signal_type="LONG",
direction="BUY"
)
# 生成操作建议
action_suggestion = self.generate_action_suggestion(main_df, temp_signal, current_price)
return CoinSignal(
symbol=symbol,
score=total_score,
reason=" | ".join(reasons),
entry_price=entry_price,
stop_loss=stop_loss,
take_profit=take_profit,
timeframe=config.primary_timeframe,
confidence=confidence,
strategy_type=best_strategy,
holding_period=config.holding_period_days,
risk_reward_ratio=risk_reward,
expiry_hours=config.expiry_hours,
action_suggestion=action_suggestion,
signal_type="LONG",
direction="BUY"
)
def select_coins(self, market_data: Dict[str, Dict]) -> Dict[str, List[CoinSignal]]:
"""批量选币 - 使用优化策略"""
logging.info(f"\n{'='*60}")
logging.info(f"开始批量选币分析,共{len(market_data)}个币种")
logging.info(f"{'='*60}")
long_signals = []
short_signals = []
filtered_count = 0
low_volume_count = 0
strategy_stats = {} # 统计各策略使用次数
all_scores = [] # 记录所有币种的评分
for i, (symbol, data) in enumerate(market_data.items(), 1):
logging.info(f"\n进度: {i}/{len(market_data)} - 分析{symbol}")
timeframe_data = data.get('timeframes', {})
volume_24h_usd = data.get('volume_24h_usd', 0)
# 24小时交易量过滤统计
if volume_24h_usd < 10_000_000: # 降低到1000万进行调试
low_volume_count += 1
logging.info(f"{symbol} 交易量过滤: ${volume_24h_usd:,.0f} < $10,000,000")
continue
signals = self.analyze_single_coin(symbol, timeframe_data, volume_24h_usd)
# 获取该币种的评分信息(用于统计)
try:
# 重新计算评分用于统计(避免重复计算,这里简化处理)
initial_df = timeframe_data.get('4h')
if initial_df is not None and len(initial_df) >= 20:
best_strategy = self.determine_best_strategy(initial_df)
config = self.strategies[best_strategy]
main_df = timeframe_data.get(config.primary_timeframe, initial_df)
if len(main_df) >= 20:
main_df = self.calculate_technical_indicators(main_df, best_strategy)
temp_long_score = self._comprehensive_long_analysis(main_df, timeframe_data, best_strategy)
temp_short_score = self._comprehensive_short_analysis(main_df, timeframe_data, best_strategy)
all_scores.append({
'symbol': symbol,
'strategy': best_strategy,
'long_score': temp_long_score,
'short_score': temp_short_score,
'volume_24h': volume_24h_usd
})
except Exception as e:
logging.debug(f"获取{symbol}评分统计信息失败: {e}")
for signal in signals:
if signal:
# 统计策略使用情况
strategy_key = signal.strategy_type
if strategy_key not in strategy_stats:
strategy_stats[strategy_key] = {'long': 0, 'short': 0, 'total': 0}
# 使用统一的70分门槛
if signal.score >= 70: # 统一70分入选门槛
if signal.signal_type == "LONG":
long_signals.append(signal)
strategy_stats[strategy_key]['long'] += 1
strategy_stats[strategy_key]['total'] += 1
logging.info(f"{symbol} 多头信号入选: {signal.score:.1f}分 ({signal.strategy_type})")
elif signal.signal_type == "SHORT":
short_signals.append(signal)
strategy_stats[strategy_key]['short'] += 1
strategy_stats[strategy_key]['total'] += 1
logging.info(f"{symbol} 空头信号入选: {signal.score:.1f}分 ({signal.strategy_type})")
else:
filtered_count += 1
logging.info(f"{symbol} {signal.signal_type}信号过滤: {signal.score:.1f}分 < 80分")
# 按分数排序
long_signals.sort(key=lambda x: x.score, reverse=True)
short_signals.sort(key=lambda x: x.score, reverse=True)
# 选择最优信号 - 统一70分门槛
def filter_quality_signals(signals):
quality_signals = []
for signal in signals:
# 只选择符合70分门槛的信号
if signal.score >= 80: # 优质信号优先
quality_signals.append(signal)
elif len(quality_signals) < self.max_selections and signal.score >= 70: # 补充达标信号
quality_signals.append(signal)
return quality_signals[:self.max_selections]
selected_long_signals = filter_quality_signals(long_signals)
selected_short_signals = filter_quality_signals(short_signals)
# 详细统计信息
logging.info(f"\n{'='*60}")
logging.info("批量选币分析完成 - 详细统计")
logging.info(f"{'='*60}")
logging.info(f"总币种数量: {len(market_data)}")
logging.info(f"交易量过滤: {low_volume_count}")
logging.info(f"分数过滤: {filtered_count}")
logging.info(f"原始信号: 多头{len(long_signals)}个, 空头{len(short_signals)}")
logging.info(f"最终选择: 多头{len(selected_long_signals)}个, 空头{len(selected_short_signals)}")
# 策略分布统计
if strategy_stats:
logging.info(f"\n策略使用分布:")
for strategy, stats in sorted(strategy_stats.items(), key=lambda x: x[1]['total'], reverse=True):
logging.info(f" {strategy}: {stats['total']}个信号 (多头{stats['long']}, 空头{stats['short']})")
# 评分分布统计
if all_scores:
logging.info(f"\n全部币种评分分布:")
long_scores = [item['long_score'] for item in all_scores]
short_scores = [item['short_score'] for item in all_scores]
# 多头评分统计
long_high = len([s for s in long_scores if s >= 80])
long_medium = len([s for s in long_scores if 60 <= s < 80])
long_low = len([s for s in long_scores if 40 <= s < 60])
long_very_low = len([s for s in long_scores if s < 40])
logging.info(f" 多头评分分布: ≥80分({long_high}个), 60-79分({long_medium}个), 40-59分({long_low}个), <40分({long_very_low}个)")
# 空头评分统计
short_high = len([s for s in short_scores if s >= 90])
short_medium = len([s for s in short_scores if 70 <= s < 90])
short_low = len([s for s in short_scores if 50 <= s < 70])
short_very_low = len([s for s in short_scores if s < 50])
logging.info(f" 空头评分分布: ≥90分({short_high}个), 70-89分({short_medium}个), 50-69分({short_low}个), <50分({short_very_low}个)")
# 显示前10名多头和空头候选
top_long_candidates = sorted(all_scores, key=lambda x: x['long_score'], reverse=True)[:10]
top_short_candidates = sorted(all_scores, key=lambda x: x['short_score'], reverse=True)[:10]
logging.info(f"\n前10名多头候选:")
for i, item in enumerate(top_long_candidates, 1):
status = "✓入选" if any(s.symbol == item['symbol'] and s.signal_type == "LONG" for s in selected_long_signals) else "未选中"
logging.info(f" {i:2d}. {item['symbol']:12s}: {item['long_score']:5.1f}分 ({item['strategy']}) - {status}")
logging.info(f"\n前10名空头候选:")
for i, item in enumerate(top_short_candidates, 1):
status = "✓入选" if any(s.symbol == item['symbol'] and s.signal_type == "SHORT" for s in selected_short_signals) else "未选中"
logging.info(f" {i:2d}. {item['symbol']:12s}: {item['short_score']:5.1f}分 ({item['strategy']}) - {status}")
# 最终信号详情
if selected_long_signals:
logging.info(f"\n最终多头信号详情:")
for i, signal in enumerate(selected_long_signals, 1):
logging.info(f" {i}. {signal.symbol}: {signal.score:.1f}分 ({signal.strategy_type}-{signal.confidence})")
if selected_short_signals:
logging.info(f"\n最终空头信号详情:")
for i, signal in enumerate(selected_short_signals, 1):
logging.info(f" {i}. {signal.symbol}: {signal.score:.1f}分 ({signal.strategy_type}-{signal.confidence})")
return {
'long': selected_long_signals,
'short': selected_short_signals,
'all': selected_long_signals + selected_short_signals
}
# === 空头分析方法 ===
def analyze_bearish_volume_price(self, df: pd.DataFrame) -> float:
"""分析空头量价关系 (30分)"""
if len(df) < 20:
return 0
score = 0
recent_data = df.tail(15)
# 计算价格和成交量的相关性
price_changes = recent_data['close'].pct_change().dropna()
volume_changes = recent_data['volume'].pct_change().dropna()
if len(price_changes) >= 10 and len(volume_changes) >= 10:
# 分析下跌日的量价关系
down_days = price_changes < 0
down_price_changes = price_changes[down_days]
down_volume_changes = volume_changes[down_days]
if len(down_price_changes) >= 5:
# 计算下跌日的量价相关性
correlation = down_price_changes.corr(down_volume_changes)
if correlation < -0.5: # 强负相关(价跌量增)
score += 15
elif correlation < -0.3:
score += 10
elif correlation < -0.1:
score += 5
# 分析最近5天的量价配合
recent_5 = df.tail(5)
down_days_recent = (recent_5['close'].diff() < 0).sum()
avg_volume_recent = recent_5['volume'].mean()
avg_volume_before = df.iloc[-20:-5]['volume'].mean() if len(df) >= 20 else avg_volume_recent
volume_ratio = avg_volume_recent / avg_volume_before if avg_volume_before > 0 else 1
# 下跌日多且成交量放大
if down_days_recent >= 3 and volume_ratio > 1.2:
score += 15
return min(score, 30)
def analyze_bearish_patterns(self, df: pd.DataFrame) -> float:
"""分析空头技术形态 (25分)"""
if len(df) < 20:
return 0
score = 0
# 使用talib的K线形态识别空头形态
patterns = {
'hanging_man': talib.CDLHANGINGMAN(df['open'], df['high'], df['low'], df['close']),
'shooting_star': talib.CDLSHOOTINGSTAR(df['open'], df['high'], df['low'], df['close']),
'dark_cloud': talib.CDLDARKCLOUDCOVER(df['open'], df['high'], df['low'], df['close']),
'evening_star': talib.CDLEVENINGSTAR(df['open'], df['high'], df['low'], df['close']),
'three_black_crows': talib.CDL3BLACKCROWS(df['open'], df['high'], df['low'], df['close']),
'bearish_engulfing': talib.CDLENGULFING(df['open'], df['high'], df['low'], df['close'])
}
# 检查最近3天的K线形态
for pattern_name, pattern_data in patterns.items():
if len(pattern_data) > 3:
# 检查最近3天是否有信号
for i in range(-3, 0):
if pattern_data.iloc[i] < 0: # 看跌信号
if pattern_name in ['evening_star', 'three_black_crows', 'dark_cloud']:
score += 6 # 强看跌形态
elif pattern_name in ['hanging_man', 'shooting_star']:
score += 4 # 中度看跌形态
else:
score += 3 # 弱看跌形态
break
# 手动分析一些简单空头形态
manual_score = self._analyze_manual_bearish_patterns(df.tail(10))
score += manual_score
return min(score, 25)
def _analyze_manual_bearish_patterns(self, df: pd.DataFrame) -> float:
"""手动分析空头形态"""
if len(df) < 5:
return 0
score = 0
# 分析最近5天的走势
last_5 = df.tail(5)
# 1. 阶段性高点下移模式
highs = last_5['high'].tolist()
if len(highs) >= 3:
# 检查是否有高点逐步下移的趋势
descending_highs = True
for i in range(1, len(highs)):
if highs[i] > highs[i-1] * 1.02: # 允许小幅波动
descending_highs = False
break
if descending_highs:
score += 4
# 2. 跌破前低点模式
current_low = df.iloc[-1]['low']
prev_lows = df.iloc[-5:-1]['low'].tolist()
if current_low < min(prev_lows) * 0.99: # 跌破前低
score += 3
return min(score, 7)
def analyze_breakdown_potential(self, df: pd.DataFrame) -> float:
"""分析下跌突破潜力 (20分)"""
if len(df) < 30:
return 0
score = 0
current = df.iloc[-1]
recent_data = df.tail(20)
# 1. 价格位置分析 - 寻找做空机会 (8分)
recent_high = recent_data['high'].max()
recent_low = recent_data['low'].min()
price_range = recent_high - recent_low
current_position = (current['close'] - recent_low) / price_range if price_range > 0 else 0
# 偏好在高位的币种
if 0.7 <= current_position <= 0.9: # 高位做空机会
score += 6
elif 0.5 <= current_position < 0.7:
score += 3
elif current_position < 0.3: # 已经低位,做空机会不大
score -= 5
# 2. 顶部形态分析 (6分)
volatility = recent_data['close'].std() / recent_data['close'].mean()
if 0.08 <= volatility <= 0.15: # 高位整理,可能形成顶部
score += 5
elif volatility > 0.20: # 高波动,可能已经开始下跌
score += 3
# 3. 量能衰竭 (6分)
volume_ratio = recent_data['volume'] / recent_data['volume'].rolling(10).mean()
weak_volume_days = ((volume_ratio < 0.8)).sum() # 量能萱缩
if weak_volume_days >= 3: # 量能持续萱缩
score += 4
return max(min(score, 20), 0)
def analyze_bearish_moving_average(self, df: pd.DataFrame) -> float:
"""分析空头均线系统 (10分)"""
if len(df) < 50:
return 0
score = 0
current = df.iloc[-1]
# 1. 均线空头排列
if current['close'] < current['ma20'] < current['ma50']:
score += 6
# 2. 价格跌破关键均线
prev = df.iloc[-2]
if prev['close'] >= prev['ma20'] and current['close'] < current['ma20']:
score += 3
# 3. 均线斜率向下
ma20_slope = (current['ma20'] - df.iloc[-5]['ma20']) / 5
if ma20_slope < 0:
score += 1
return min(score, 10)
def calculate_short_entry_exit_levels(self, df: pd.DataFrame, strategy: str) -> Tuple[float, float, float, float]:
"""计算空头入场、止损、止盈位"""
current_price = df.iloc[-1]['close']
atr = df.iloc[-1]['atr']
support, resistance = self.find_support_resistance(df)
config = self.strategies[strategy]
# 空头入场价策略
ma20 = df.iloc[-1]['ma20']
ma50 = df.iloc[-1]['ma50']
# 策略1: 均线反弹入场
if current_price < ma20 < ma50: # 空头趋势
# 等待反弹至MA20附近做空
entry_price = ma20 * 0.98 # MA20下方2%
else:
# 策略2: 跌破确认入场
recent_low = df.tail(10)['low'].min()
if current_price <= recent_low * 1.02: # 接近跌破
entry_price = recent_low * 0.995 # 跌破确认价位
else:
# 策略3: 当前价格适当上方等待
entry_price = current_price * 1.005 # 当前价上方0.5%
# 确保入场价不会过高(距离当前价不超过5%)
if entry_price > current_price * 1.05:
entry_price = current_price * 1.02
# 止损位: 阻力位上方或ATR的1.5倍
stop_loss_atr = entry_price + (atr * 1.5)
stop_loss_resistance = resistance * 1.02 if resistance > 0 else entry_price * 1.08
stop_loss = min(stop_loss_atr, stop_loss_resistance)
# 根据策略动态设置风险回报比
risk = stop_loss - entry_price
target_ratio = (config.risk_reward_min + config.risk_reward_max) / 2
take_profit_ratio = entry_price - (risk * target_ratio)
# 如果有支撑位,取较大值
if support > 0 and support < entry_price:
take_profit_support = support * 1.02
take_profit = max(take_profit_ratio, take_profit_support)
else:
take_profit = take_profit_ratio
# 计算实际风险回报比
actual_risk_reward = (entry_price - take_profit) / risk if risk > 0 else target_ratio
return entry_price, stop_loss, take_profit, actual_risk_reward
def generate_short_action_suggestion(self, df: pd.DataFrame, entry_price: float, current_price: float, score: float) -> str:
"""生成空头操作建议"""
try:
# 计算价格差异百分比
entry_diff = (current_price - entry_price) / entry_price * 100
# 获取技术指标状态
current = df.iloc[-1]
recent_data = df.tail(10)
# 计算价格在最近区间的位置
recent_high = recent_data['high'].max()
recent_low = recent_data['low'].min()
price_range = recent_high - recent_low
current_position = (current_price - recent_low) / price_range if price_range > 0 else 0.5
# 基于评分等级的基础建议
if score >= 80: # 高分
if entry_diff >= -1: # 接近或高于建议价位
if current_position >= 0.7: # 在高位
return "立即做空"
elif current_position >= 0.5:
return "现价做空"
else:
return "等待反弹做空"
elif entry_diff >= -3: # 稍低于建议价位
return "等待反弹做空"
else: # 明显低于建议价位
return "等待大幅反弹"
elif score >= 65: # 中等分数
if entry_diff <= 2: # 高于建议价位
return "现价做空"
elif entry_diff <= -1: # 接近建议价位
if current_position >= 0.6:
return "现价做空"
else:
return "等待反弹做空"
elif entry_diff <= -5: # 低于建议价位
return "等待反弹做空"
else:
return "暂时观望"
else: # 较低分数
if entry_diff <= 3: # 明显高于建议价位
return "小仓位做空"
elif entry_diff <= 0:
if current_position >= 0.8:
return "小仓位试探"
else:
return "等待反弹做空"
else:
return "暂时观望"
return "等待反弹做空" # 默认建议
except Exception as e:
logging.error(f"生成空头操作建议时出错: {e}")
return "谨慎观望"
def test_strategy_distribution(self, market_data: Dict[str, Dict]) -> Dict[str, int]:
"""测试策略分布,确保所有策略都能被选中"""
strategy_counts = {name: 0 for name in self.strategies.keys()}
for symbol, data in list(market_data.items())[:20]: # 只测试前20个
timeframe_data = data.get('timeframes', {})
initial_df = timeframe_data.get('4h')
if initial_df is not None and len(initial_df) >= 20:
strategy = self.determine_best_strategy(initial_df)
strategy_counts[strategy] += 1
logging.info(f"测试策略分布: {symbol} -> {strategy}")
logging.info(f"策略分布测试结果: {strategy_counts}")
return strategy_counts