Add files to chanlun_1

This commit is contained in:
jackyu66git
2025-05-23 19:09:55 +08:00
parent c0e218d23d
commit 4481c47c34
20 changed files with 5574 additions and 0 deletions
+683
View File
@@ -0,0 +1,683 @@
"""
分型识别模块:识别顶分型和底分型
分型定义:至少需要3根K线,中间K线的高点(或低点)比两侧都高(或低)
增强版:包含多维度强弱程度评估
"""
import pandas as pd
import numpy as np
from typing import List, Tuple, Optional, Dict
from dataclasses import dataclass
import logging
logger = logging.getLogger(__name__)
@dataclass
class FractalPoint:
"""分型点数据类"""
index: int # 在原始数据中的位置
timestamp: pd.Timestamp # 时间戳
price: float # 分型价格(高点或低点)
fractal_type: str # 'top' 或 'bottom'
strength: int # 基础分型强度(左右确认的K线数量)
enhanced_strength: float # 增强强度评分(0-100
price_dominance: float # 价格优势度(0-100
volume_strength: float # 成交量强度(0-100
trend_position: float # 趋势位置强度(0-100
confirmed: bool = False # 是否已确认
class Fractal:
"""分型识别类(增强版)"""
def __init__(self, kline_data: pd.DataFrame, min_strength: int = 1):
"""
初始化分型识别器
Args:
kline_data: 处理包含关系后的K线数据
min_strength: 最小分型强度(左右各需多少根K线确认)
"""
self.data = kline_data.copy()
self.min_strength = max(1, min_strength) # 至少为1
self.top_fractals = []
self.bottom_fractals = []
self.all_fractals = []
# 计算一些辅助指标
self._calculate_auxiliary_indicators()
def _calculate_auxiliary_indicators(self):
"""计算辅助技术指标"""
# 成交量移动平均
self.data['volume_ma'] = self.data['volume'].rolling(window=20, min_periods=1).mean()
# 价格振幅
self.data['range'] = self.data['high'] - self.data['low']
self.data['range_ma'] = self.data['range'].rolling(window=20, min_periods=1).mean()
# 相对位置(高点在整个区间的位置)
window = 20
self.data['highest'] = self.data['high'].rolling(window=window, min_periods=1).max()
self.data['lowest'] = self.data['low'].rolling(window=window, min_periods=1).min()
# 趋势强度(简单移动平均斜率)
self.data['close_ma'] = self.data['close'].rolling(window=10, min_periods=1).mean()
self.data['trend_slope'] = self.data['close_ma'].diff(5) # 5期斜率
def _calculate_price_dominance(self, idx: int, fractal_type: str, strength: int) -> float:
"""
计算价格优势度:分型价格相对于周围价格的优势程度
Args:
idx: 分型位置
fractal_type: 分型类型
strength: 基础强度
Returns:
价格优势度(0-100)
"""
try:
# 扩展检查范围
check_range = max(strength * 2, 10)
start_idx = max(0, idx - check_range)
end_idx = min(len(self.data), idx + check_range + 1)
if fractal_type == 'top':
center_price = self.data.iloc[idx]['high']
range_data = self.data.iloc[start_idx:end_idx]
max_around = range_data['high'].max()
second_max = range_data['high'].nlargest(2).iloc[1] if len(range_data) > 1 else center_price
# 计算相对优势
if max_around == center_price:
price_gap = center_price - second_max
avg_range = self.data.iloc[idx]['range_ma']
dominance = min((price_gap / avg_range) * 50, 100) if avg_range > 0 else 50
else:
dominance = 0
else: # bottom
center_price = self.data.iloc[idx]['low']
range_data = self.data.iloc[start_idx:end_idx]
min_around = range_data['low'].min()
second_min = range_data['low'].nsmallest(2).iloc[1] if len(range_data) > 1 else center_price
# 计算相对优势
if min_around == center_price:
price_gap = second_min - center_price
avg_range = self.data.iloc[idx]['range_ma']
dominance = min((price_gap / avg_range) * 50, 100) if avg_range > 0 else 50
else:
dominance = 0
return max(0, dominance)
except Exception as e:
logger.warning(f"计算价格优势度失败: {e}")
return 30 # 默认值
def _calculate_volume_strength(self, idx: int, fractal_type: str, strength: int) -> float:
"""
计算成交量强度:分型形成时的成交量特征
Args:
idx: 分型位置
fractal_type: 分型类型
strength: 基础强度
Returns:
成交量强度(0-100)
"""
try:
center_volume = self.data.iloc[idx]['volume']
volume_ma = self.data.iloc[idx]['volume_ma']
# 基础成交量比率
volume_ratio = center_volume / volume_ma if volume_ma > 0 else 1
base_score = min(volume_ratio * 30, 60) # 最高60分
# 检查分型形成过程中的成交量模式
pattern_score = 0
if strength >= 2:
# 检查左右成交量是否递减(表示力度衰竭)
left_volumes = [self.data.iloc[idx - i]['volume'] for i in range(1, strength + 1)]
right_volumes = [self.data.iloc[idx + i]['volume'] for i in range(1, strength + 1)]
# 中心成交量应该相对突出
center_prominence = sum([1 for v in left_volumes + right_volumes if center_volume > v])
total_compared = len(left_volumes + right_volumes)
pattern_score = (center_prominence / total_compared) * 40 if total_compared > 0 else 20
total_score = base_score + pattern_score
return max(0, min(total_score, 100))
except Exception as e:
logger.warning(f"计算成交量强度失败: {e}")
return 40 # 默认值
def _calculate_trend_position_strength(self, idx: int, fractal_type: str) -> float:
"""
计算趋势位置强度:分型在整体趋势中的位置优势
Args:
idx: 分型位置
fractal_type: 分型类型
Returns:
趋势位置强度(0-100)
"""
try:
current_price = self.data.iloc[idx]['high' if fractal_type == 'top' else 'low']
highest = self.data.iloc[idx]['highest']
lowest = self.data.iloc[idx]['lowest']
trend_slope = self.data.iloc[idx]['trend_slope']
# 计算在价格区间中的相对位置
price_range = highest - lowest
if price_range > 0:
if fractal_type == 'top':
# 顶分型:越接近高点越强
position_ratio = (current_price - lowest) / price_range
else:
# 底分型:越接近低点越强
position_ratio = (highest - current_price) / price_range
else:
position_ratio = 0.5
position_score = position_ratio * 60 # 位置得分最高60分
# 趋势方向得分
trend_score = 0
if trend_slope is not None and not np.isnan(trend_slope):
if fractal_type == 'top' and trend_slope < 0:
# 顶分型形成在下降趋势中更有效
trend_score = min(abs(trend_slope) * 1000, 40)
elif fractal_type == 'bottom' and trend_slope > 0:
# 底分型形成在上升趋势中更有效
trend_score = min(abs(trend_slope) * 1000, 40)
else:
trend_score = 20 # 趋势方向不匹配给予中等分数
total_score = position_score + trend_score
return max(0, min(total_score, 100))
except Exception as e:
logger.warning(f"计算趋势位置强度失败: {e}")
return 50 # 默认值
def _calculate_enhanced_strength(self, idx: int, fractal_type: str, basic_strength: int) -> Dict[str, float]:
"""
计算增强强度评分
Args:
idx: 分型位置
fractal_type: 分型类型
basic_strength: 基础强度
Returns:
包含各维度强度的字典
"""
# 计算各维度强度
price_dominance = self._calculate_price_dominance(idx, fractal_type, basic_strength)
volume_strength = self._calculate_volume_strength(idx, fractal_type, basic_strength)
trend_position = self._calculate_trend_position_strength(idx, fractal_type)
# 基础强度转换为评分(强度越高,分数越高)
basic_score = min(basic_strength * 20, 60) # 基础强度最高60分
# 计算综合强度(加权平均)
enhanced_strength = (
basic_score * 0.3 + # 基础强度 30%
price_dominance * 0.4 + # 价格优势 40%
volume_strength * 0.2 + # 成交量强度 20%
trend_position * 0.1 # 趋势位置 10%
)
return {
'enhanced_strength': enhanced_strength,
'price_dominance': price_dominance,
'volume_strength': volume_strength,
'trend_position': trend_position
}
def is_top_fractal(self, idx: int, strength: int = None) -> Tuple[bool, int]:
"""
判断指定位置是否为顶分型
Args:
idx: 检查的位置索引
strength: 检查强度,如果为None则使用类默认值
Returns:
(是否为顶分型, 实际强度)
"""
if strength is None:
strength = self.min_strength
data_len = len(self.data)
# 检查边界
if idx < strength or idx >= data_len - strength:
return False, 0
center_high = self.data.iloc[idx]['high']
# 检查左侧K线
left_valid = True
for i in range(1, strength + 1):
if self.data.iloc[idx - i]['high'] >= center_high:
left_valid = False
break
# 检查右侧K线
right_valid = True
for i in range(1, strength + 1):
if self.data.iloc[idx + i]['high'] >= center_high:
right_valid = False
break
is_fractal = left_valid and right_valid
actual_strength = strength if is_fractal else 0
return is_fractal, actual_strength
def is_bottom_fractal(self, idx: int, strength: int = None) -> Tuple[bool, int]:
"""
判断指定位置是否为底分型
Args:
idx: 检查的位置索引
strength: 检查强度,如果为None则使用类默认值
Returns:
(是否为底分型, 实际强度)
"""
if strength is None:
strength = self.min_strength
data_len = len(self.data)
# 检查边界
if idx < strength or idx >= data_len - strength:
return False, 0
center_low = self.data.iloc[idx]['low']
# 检查左侧K线
left_valid = True
for i in range(1, strength + 1):
if self.data.iloc[idx - i]['low'] <= center_low:
left_valid = False
break
# 检查右侧K线
right_valid = True
for i in range(1, strength + 1):
if self.data.iloc[idx + i]['low'] <= center_low:
right_valid = False
break
is_fractal = left_valid and right_valid
actual_strength = strength if is_fractal else 0
return is_fractal, actual_strength
def find_max_strength_fractal(self, idx: int, fractal_type: str, max_strength: int = 5) -> Tuple[bool, int]:
"""
寻找指定位置的最大强度分型
Args:
idx: 检查位置
fractal_type: 'top''bottom'
max_strength: 最大检查强度
Returns:
(是否为分型, 最大强度)
"""
max_valid_strength = 0
for strength in range(self.min_strength, max_strength + 1):
if fractal_type == 'top':
is_valid, _ = self.is_top_fractal(idx, strength)
else:
is_valid, _ = self.is_bottom_fractal(idx, strength)
if is_valid:
max_valid_strength = strength
else:
break # 一旦失败就停止,因为更高强度也不会成功
return max_valid_strength > 0, max_valid_strength
def detect_fractals(self, use_max_strength: bool = True) -> List[FractalPoint]:
"""
检测所有分型
Args:
use_max_strength: 是否使用最大强度检测
Returns:
所有分型点列表
"""
fractals = []
data_len = len(self.data)
logger.info(f"开始检测分型,数据长度: {data_len}")
# 遍历所有可能的分型位置
for idx in range(self.min_strength, data_len - self.min_strength):
timestamp = self.data.index[idx]
# 检测顶分型
if use_max_strength:
is_top, top_strength = self.find_max_strength_fractal(idx, 'top')
else:
is_top, top_strength = self.is_top_fractal(idx)
if is_top:
# 计算增强强度
strength_metrics = self._calculate_enhanced_strength(idx, 'top', top_strength)
fractal = FractalPoint(
index=idx,
timestamp=timestamp,
price=self.data.iloc[idx]['high'],
fractal_type='top',
strength=top_strength,
enhanced_strength=strength_metrics['enhanced_strength'],
price_dominance=strength_metrics['price_dominance'],
volume_strength=strength_metrics['volume_strength'],
trend_position=strength_metrics['trend_position'],
confirmed=True # 简化处理,认为都已确认
)
fractals.append(fractal)
self.top_fractals.append(fractal)
# 检测底分型
if use_max_strength:
is_bottom, bottom_strength = self.find_max_strength_fractal(idx, 'bottom')
else:
is_bottom, bottom_strength = self.is_bottom_fractal(idx)
if is_bottom:
# 计算增强强度
strength_metrics = self._calculate_enhanced_strength(idx, 'bottom', bottom_strength)
fractal = FractalPoint(
index=idx,
timestamp=timestamp,
price=self.data.iloc[idx]['low'],
fractal_type='bottom',
strength=bottom_strength,
enhanced_strength=strength_metrics['enhanced_strength'],
price_dominance=strength_metrics['price_dominance'],
volume_strength=strength_metrics['volume_strength'],
trend_position=strength_metrics['trend_position'],
confirmed=True
)
fractals.append(fractal)
self.bottom_fractals.append(fractal)
# 按时间排序
fractals.sort(key=lambda x: x.index)
self.all_fractals = fractals
logger.info(f"检测完成:顶分型 {len(self.top_fractals)} 个,底分型 {len(self.bottom_fractals)}")
return fractals
def filter_fractals_by_strength(self, min_strength: int) -> List[FractalPoint]:
"""
按强度过滤分型
Args:
min_strength: 最小强度要求
Returns:
过滤后的分型列表
"""
return [f for f in self.all_fractals if f.strength >= min_strength]
def get_fractal_sequence(self) -> List[FractalPoint]:
"""
获取交替的分型序列(顶-底-顶-底...)
Returns:
交替分型序列
"""
if not self.all_fractals:
return []
sequence = []
last_type = None
for fractal in self.all_fractals:
if fractal.fractal_type != last_type:
sequence.append(fractal)
last_type = fractal.fractal_type
return sequence
def validate_fractal_sequence(self, sequence: List[FractalPoint]) -> bool:
"""
验证分型序列的有效性
Args:
sequence: 分型序列
Returns:
是否有效
"""
if len(sequence) < 2:
return True
for i in range(1, len(sequence)):
prev_fractal = sequence[i-1]
curr_fractal = sequence[i]
# 检查类型是否交替
if prev_fractal.fractal_type == curr_fractal.fractal_type:
return False
# 检查价格关系是否合理
if prev_fractal.fractal_type == 'top':
# 顶分型后应该是底分型,且价格应该更低
if curr_fractal.price >= prev_fractal.price:
return False
else:
# 底分型后应该是顶分型,且价格应该更高
if curr_fractal.price <= prev_fractal.price:
return False
return True
def get_fractal_statistics(self) -> Dict:
"""
获取分型统计信息(增强版)
Returns:
统计信息字典
"""
if not self.all_fractals:
return {}
top_count = len(self.top_fractals)
bottom_count = len(self.bottom_fractals)
# 基础统计
top_strengths = [f.strength for f in self.top_fractals]
bottom_strengths = [f.strength for f in self.bottom_fractals]
# 增强强度统计
enhanced_strengths = [f.enhanced_strength for f in self.all_fractals]
price_dominances = [f.price_dominance for f in self.all_fractals]
volume_strengths = [f.volume_strength for f in self.all_fractals]
trend_positions = [f.trend_position for f in self.all_fractals]
# 分级统计(按增强强度)
strong_fractals = [f for f in self.all_fractals if f.enhanced_strength >= 70]
medium_fractals = [f for f in self.all_fractals if 40 <= f.enhanced_strength < 70]
weak_fractals = [f for f in self.all_fractals if f.enhanced_strength < 40]
stats = {
'basic_info': {
'total_fractals': len(self.all_fractals),
'top_fractals': top_count,
'bottom_fractals': bottom_count,
'avg_basic_strength': np.mean([f.strength for f in self.all_fractals]),
'max_basic_strength': max([f.strength for f in self.all_fractals]),
},
'enhanced_strength': {
'avg_enhanced_strength': np.mean(enhanced_strengths),
'max_enhanced_strength': max(enhanced_strengths),
'min_enhanced_strength': min(enhanced_strengths),
'strong_count': len(strong_fractals), # 强势分型数量
'medium_count': len(medium_fractals), # 中等分型数量
'weak_count': len(weak_fractals), # 弱势分型数量
},
'dimension_analysis': {
'avg_price_dominance': np.mean(price_dominances),
'avg_volume_strength': np.mean(volume_strengths),
'avg_trend_position': np.mean(trend_positions),
},
'top_fractals_detail': {
'count': top_count,
'avg_basic_strength': np.mean(top_strengths) if top_strengths else 0,
'avg_enhanced_strength': np.mean([f.enhanced_strength for f in self.top_fractals]) if self.top_fractals else 0,
'strong_tops': len([f for f in self.top_fractals if f.enhanced_strength >= 70]),
},
'bottom_fractals_detail': {
'count': bottom_count,
'avg_basic_strength': np.mean(bottom_strengths) if bottom_strengths else 0,
'avg_enhanced_strength': np.mean([f.enhanced_strength for f in self.bottom_fractals]) if self.bottom_fractals else 0,
'strong_bottoms': len([f for f in self.bottom_fractals if f.enhanced_strength >= 70]),
}
}
return stats
def to_dataframe(self) -> pd.DataFrame:
"""
将分型转换为DataFrame
Returns:
包含分型信息的DataFrame
"""
if not self.all_fractals:
return pd.DataFrame()
data = []
for fractal in self.all_fractals:
data.append({
'timestamp': fractal.timestamp,
'index': fractal.index,
'price': fractal.price,
'type': fractal.fractal_type,
'strength': fractal.strength,
'enhanced_strength': fractal.enhanced_strength,
'price_dominance': fractal.price_dominance,
'volume_strength': fractal.volume_strength,
'trend_position': fractal.trend_position,
'confirmed': fractal.confirmed
})
df = pd.DataFrame(data)
df.set_index('timestamp', inplace=True)
return df
def update_fractal_confirmation(self, current_idx: int):
"""
更新分型确认状态
Args:
current_idx: 当前处理到的K线位置
"""
for fractal in self.all_fractals:
if not fractal.confirmed:
# 检查是否已经过了足够的确认期
required_confirmation = fractal.strength
time_passed = current_idx - fractal.index
if time_passed >= required_confirmation:
fractal.confirmed = True
def get_confirmed_fractals(self, current_idx: int) -> List[FractalPoint]:
"""
获取已确认的分型列表
Args:
current_idx: 当前处理到的K线位置
Returns:
已确认的分型列表
"""
confirmed_fractals = []
for fractal in self.all_fractals:
required_confirmation = fractal.strength
time_passed = current_idx - fractal.index
if time_passed >= required_confirmation:
confirmed_fractals.append(fractal)
return confirmed_fractals
def detect_real_time_fractals(self, current_idx: int, lookback_periods: int = 50) -> List[FractalPoint]:
"""
实时分型检测(避免使用未来数据)
Args:
current_idx: 当前K线位置
lookback_periods: 回看周期数
Returns:
实时可用的分型列表
"""
real_time_fractals = []
data_len = len(self.data)
# 只检测到当前位置之前的分型
end_idx = min(current_idx, data_len - self.min_strength)
start_idx = max(self.min_strength, end_idx - lookback_periods)
for idx in range(start_idx, end_idx):
timestamp = self.data.index[idx]
# 检测顶分型(但只能检测已确认的)
is_top, top_strength = self.find_max_strength_fractal(idx, 'top')
if is_top and current_idx - idx >= top_strength: # 已确认
strength_metrics = self._calculate_enhanced_strength(idx, 'top', top_strength)
fractal = FractalPoint(
index=idx,
timestamp=timestamp,
price=self.data.iloc[idx]['high'],
fractal_type='top',
strength=top_strength,
enhanced_strength=strength_metrics['enhanced_strength'],
price_dominance=strength_metrics['price_dominance'],
volume_strength=strength_metrics['volume_strength'],
trend_position=strength_metrics['trend_position'],
confirmed=True
)
real_time_fractals.append(fractal)
# 检测底分型(但只能检测已确认的)
is_bottom, bottom_strength = self.find_max_strength_fractal(idx, 'bottom')
if is_bottom and current_idx - idx >= bottom_strength: # 已确认
strength_metrics = self._calculate_enhanced_strength(idx, 'bottom', bottom_strength)
fractal = FractalPoint(
index=idx,
timestamp=timestamp,
price=self.data.iloc[idx]['low'],
fractal_type='bottom',
strength=bottom_strength,
enhanced_strength=strength_metrics['enhanced_strength'],
price_dominance=strength_metrics['price_dominance'],
volume_strength=strength_metrics['volume_strength'],
trend_position=strength_metrics['trend_position'],
confirmed=True
)
real_time_fractals.append(fractal)
return real_time_fractals