Files
chanlun_1/core/stroke.py
T
2025-05-23 19:09:55 +08:00

350 lines
11 KiB
Python

"""
笔模块:连接相邻的顶分型和底分型形成笔
笔的定义:由一个顶分型和一个底分型连接而成,且中间不能有其他分型
"""
import pandas as pd
import numpy as np
from typing import List, Tuple, Optional, Dict
from dataclasses import dataclass
import logging
from .fractal import FractalPoint
logger = logging.getLogger(__name__)
@dataclass
class StrokeElement:
"""笔元素数据类"""
start_fractal: FractalPoint # 起始分型
end_fractal: FractalPoint # 结束分型
direction: int # 方向:1为上升笔,-1为下降笔
length: float # 笔的长度(价格差)
duration: int # 持续时间(K线数量)
strength: float # 笔的强度
class Stroke:
"""笔识别和处理类"""
def __init__(self, fractals: List[FractalPoint], kline_data: pd.DataFrame):
"""
初始化笔处理器
Args:
fractals: 分型点列表
kline_data: K线数据
"""
self.fractals = fractals
self.kline_data = kline_data
self.strokes = []
self.up_strokes = [] # 上升笔
self.down_strokes = [] # 下降笔
def create_stroke(self, start_fractal: FractalPoint,
end_fractal: FractalPoint) -> StrokeElement:
"""
创建笔元素
Args:
start_fractal: 起始分型
end_fractal: 结束分型
Returns:
笔元素
"""
# 计算方向
if start_fractal.fractal_type == 'bottom' and end_fractal.fractal_type == 'top':
direction = 1 # 上升笔
elif start_fractal.fractal_type == 'top' and end_fractal.fractal_type == 'bottom':
direction = -1 # 下降笔
else:
raise ValueError("无效的分型组合")
# 计算长度
length = abs(end_fractal.price - start_fractal.price)
# 计算持续时间
duration = end_fractal.index - start_fractal.index
# 计算强度(可以基于多个因素)
strength = self._calculate_stroke_strength(start_fractal, end_fractal, length, duration)
return StrokeElement(
start_fractal=start_fractal,
end_fractal=end_fractal,
direction=direction,
length=length,
duration=duration,
strength=strength
)
def _calculate_stroke_strength(self, start_fractal: FractalPoint,
end_fractal: FractalPoint,
length: float, duration: int) -> float:
"""
计算笔的强度
Args:
start_fractal: 起始分型
end_fractal: 结束分型
length: 价格长度
duration: 时间长度
Returns:
笔的强度值
"""
# 基础强度:价格变化幅度
price_strength = length
# 分型强度加权
fractal_strength = (start_fractal.strength + end_fractal.strength) / 2
# 时间因子:适中的时间长度得分更高
time_factor = min(duration / 10, 1.0) if duration > 0 else 0
# 综合强度
strength = price_strength * (1 + fractal_strength * 0.1) * (1 + time_factor * 0.1)
return strength
def validate_stroke(self, start_fractal: FractalPoint,
end_fractal: FractalPoint) -> bool:
"""
验证笔的有效性
Args:
start_fractal: 起始分型
end_fractal: 结束分型
Returns:
是否为有效笔
"""
# 检查分型类型是否正确
valid_combinations = [
('bottom', 'top'), # 上升笔
('top', 'bottom') # 下降笔
]
combination = (start_fractal.fractal_type, end_fractal.fractal_type)
if combination not in valid_combinations:
return False
# 检查时间顺序
if start_fractal.index >= end_fractal.index:
return False
# 检查价格关系
if start_fractal.fractal_type == 'bottom':
# 上升笔:结束价格应该高于起始价格
if end_fractal.price <= start_fractal.price:
return False
else:
# 下降笔:结束价格应该低于起始价格
if end_fractal.price >= start_fractal.price:
return False
return True
def detect_strokes(self) -> List[StrokeElement]:
"""
检测所有笔
Returns:
笔列表
"""
if len(self.fractals) < 2:
logger.warning("分型数量不足,无法生成笔")
return []
strokes = []
# 按时间顺序排序分型
sorted_fractals = sorted(self.fractals, key=lambda x: x.index)
i = 0
while i < len(sorted_fractals) - 1:
start_fractal = sorted_fractals[i]
# 寻找下一个有效的分型来形成笔
j = i + 1
while j < len(sorted_fractals):
end_fractal = sorted_fractals[j]
# 检查是否可以形成有效笔
if self.validate_stroke(start_fractal, end_fractal):
stroke = self.create_stroke(start_fractal, end_fractal)
strokes.append(stroke)
# 分类存储
if stroke.direction == 1:
self.up_strokes.append(stroke)
else:
self.down_strokes.append(stroke)
# 从结束分型继续寻找下一笔
i = j
break
j += 1
else:
# 没有找到有效的结束分型,跳到下一个分型
i += 1
self.strokes = strokes
logger.info(f"检测到 {len(strokes)} 笔:上升笔 {len(self.up_strokes)} 个,下降笔 {len(self.down_strokes)} 个")
return strokes
def get_stroke_sequence(self) -> List[StrokeElement]:
"""
获取连续的笔序列
Returns:
连续笔序列
"""
return sorted(self.strokes, key=lambda x: x.start_fractal.index)
def find_stroke_extremes(self) -> Dict[str, List[StrokeElement]]:
"""
寻找笔的极值点
Returns:
包含最长、最短、最强笔的字典
"""
if not self.strokes:
return {}
# 按长度排序
by_length = sorted(self.strokes, key=lambda x: x.length, reverse=True)
# 按强度排序
by_strength = sorted(self.strokes, key=lambda x: x.strength, reverse=True)
# 按持续时间排序
by_duration = sorted(self.strokes, key=lambda x: x.duration, reverse=True)
return {
'longest': by_length[:5], # 最长的5笔
'strongest': by_strength[:5], # 最强的5笔
'longest_duration': by_duration[:5] # 持续时间最长的5笔
}
def analyze_stroke_patterns(self) -> Dict:
"""
分析笔的模式
Returns:
模式分析结果
"""
if len(self.strokes) < 3:
return {}
sequence = self.get_stroke_sequence()
# 分析连续同向笔(可能的延伸)
extensions = []
i = 0
while i < len(sequence) - 1:
current = sequence[i]
next_stroke = sequence[i + 1]
# 检查是否为同向延伸
if current.direction == next_stroke.direction:
extensions.append((current, next_stroke))
i += 1
# 分析笔的趋势强度
trend_strength = self._calculate_trend_strength(sequence)
# 统计平均笔长度
avg_length = np.mean([s.length for s in self.strokes]) if self.strokes else 0
avg_duration = np.mean([s.duration for s in self.strokes]) if self.strokes else 0
return {
'total_strokes': len(self.strokes),
'up_strokes': len(self.up_strokes),
'down_strokes': len(self.down_strokes),
'extensions': len(extensions),
'avg_length': avg_length,
'avg_duration': avg_duration,
'trend_strength': trend_strength
}
def _calculate_trend_strength(self, sequence: List[StrokeElement]) -> float:
"""
计算趋势强度
Args:
sequence: 笔序列
Returns:
趋势强度值
"""
if len(sequence) < 2:
return 0
# 计算方向变化的频率
direction_changes = 0
for i in range(1, len(sequence)):
if sequence[i].direction != sequence[i-1].direction:
direction_changes += 1
# 趋势强度与方向变化成反比
change_ratio = direction_changes / (len(sequence) - 1) if len(sequence) > 1 else 1
trend_strength = 1 - change_ratio
return trend_strength
def filter_strokes_by_strength(self, min_strength: float) -> List[StrokeElement]:
"""
按强度过滤笔
Args:
min_strength: 最小强度要求
Returns:
过滤后的笔列表
"""
return [s for s in self.strokes if s.strength >= min_strength]
def filter_strokes_by_length(self, min_length: float) -> List[StrokeElement]:
"""
按长度过滤笔
Args:
min_length: 最小长度要求
Returns:
过滤后的笔列表
"""
return [s for s in self.strokes if s.length >= min_length]
def to_dataframe(self) -> pd.DataFrame:
"""
将笔转换为DataFrame
Returns:
包含笔信息的DataFrame
"""
if not self.strokes:
return pd.DataFrame()
data = []
for i, stroke in enumerate(self.strokes):
data.append({
'stroke_id': i,
'start_time': stroke.start_fractal.timestamp,
'end_time': stroke.end_fractal.timestamp,
'start_price': stroke.start_fractal.price,
'end_price': stroke.end_fractal.price,
'direction': stroke.direction,
'length': stroke.length,
'duration': stroke.duration,
'strength': stroke.strength,
'start_fractal_type': stroke.start_fractal.fractal_type,
'end_fractal_type': stroke.end_fractal.fractal_type
})
return pd.DataFrame(data)