683 lines
26 KiB
Python
683 lines
26 KiB
Python
"""
|
||
分型识别模块:识别顶分型和底分型
|
||
分型定义:至少需要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 |