Files
2025-05-23 19:09:55 +08:00

238 lines
7.6 KiB
Python

"""
K线包含关系处理模块
"""
import pandas as pd
import numpy as np
from typing import List, Tuple, Optional
import logging
logger = logging.getLogger(__name__)
class KLineElement:
"""单个K线元素类"""
def __init__(self, high: float, low: float, open_price: float,
close: float, volume: float, timestamp: pd.Timestamp):
"""
初始化K线元素
Args:
high: 最高价
low: 最低价
open_price: 开盘价
close: 收盘价
volume: 成交量
timestamp: 时间戳
"""
self.high = high
self.low = low
self.open = open_price
self.close = close
self.volume = volume
self.timestamp = timestamp
self.direction = 1 if close >= open_price else -1
def __repr__(self):
return f"KLineElement(H:{self.high}, L:{self.low}, O:{self.open}, C:{self.close})"
class KLine:
"""K线包含关系处理类"""
def __init__(self, data: pd.DataFrame):
"""
初始化K线处理器
Args:
data: 包含OHLCV数据的DataFrame
"""
self.original_data = data.copy()
self.processed_data = None
self.kline_elements = []
self._process_data()
def _process_data(self):
"""处理原始数据为K线元素"""
self.kline_elements = []
for idx, row in self.original_data.iterrows():
element = KLineElement(
high=row['high'],
low=row['low'],
open_price=row['open'],
close=row['close'],
volume=row['volume'],
timestamp=idx
)
self.kline_elements.append(element)
def has_containment(self, k1: KLineElement, k2: KLineElement) -> bool:
"""
判断两根K线是否存在包含关系
Args:
k1: 第一根K线
k2: 第二根K线
Returns:
是否存在包含关系
"""
# K1包含K2:K1的高低点完全包含K2
k1_contains_k2 = (k1.high >= k2.high and k1.low <= k2.low)
# K2包含K1:K2的高低点完全包含K1
k2_contains_k1 = (k2.high >= k1.high and k2.low <= k1.low)
return k1_contains_k2 or k2_contains_k1
def merge_contained_klines(self, k1: KLineElement, k2: KLineElement,
direction: int) -> KLineElement:
"""
合并包含关系的K线
Args:
k1: 第一根K线
k2: 第二根K线
direction: 当前趋势方向(1为上升,-1为下降)
Returns:
合并后的K线
"""
if direction > 0: # 上升趋势中
# 取两根K线的最高点的最大值,最低点的最大值
merged_high = max(k1.high, k2.high)
merged_low = max(k1.low, k2.low)
else: # 下降趋势中
# 取两根K线的最高点的最小值,最低点的最小值
merged_high = min(k1.high, k2.high)
merged_low = min(k1.low, k2.low)
# 开盘价和收盘价使用第一根K线的值
merged_open = k1.open
merged_close = k2.close
merged_volume = k1.volume + k2.volume
merged_timestamp = k2.timestamp # 使用最后一根K线的时间
return KLineElement(
high=merged_high,
low=merged_low,
open_price=merged_open,
close=merged_close,
volume=merged_volume,
timestamp=merged_timestamp
)
def handle_containment(self) -> List[KLineElement]:
"""
处理所有K线的包含关系
Returns:
处理包含关系后的K线列表
"""
if len(self.kline_elements) < 2:
return self.kline_elements.copy()
processed_klines = [self.kline_elements[0]] # 第一根K线
# 初始方向:根据前两根K线确定
if len(self.kline_elements) >= 2:
k1, k2 = self.kline_elements[0], self.kline_elements[1]
if k2.high > k1.high:
current_direction = 1 # 上升
elif k2.high < k1.high:
current_direction = -1 # 下降
else:
current_direction = 1 if k2.low >= k1.low else -1
else:
current_direction = 1
i = 1
while i < len(self.kline_elements):
current_k = self.kline_elements[i]
last_processed = processed_klines[-1]
if self.has_containment(last_processed, current_k):
# 存在包含关系,进行合并
merged = self.merge_contained_klines(
last_processed, current_k, current_direction
)
processed_klines[-1] = merged # 替换最后一个
else:
# 不存在包含关系,直接添加
processed_klines.append(current_k)
# 更新方向
if current_k.high > last_processed.high:
current_direction = 1
elif current_k.high < last_processed.high:
current_direction = -1
# 如果high相等,保持原方向
i += 1
logger.info(f"包含关系处理完成:{len(self.kline_elements)} -> {len(processed_klines)}")
return processed_klines
def to_dataframe(self, processed_klines: Optional[List[KLineElement]] = None) -> pd.DataFrame:
"""
将处理后的K线转换为DataFrame
Args:
processed_klines: 处理后的K线列表,如果为None则使用默认处理结果
Returns:
包含处理后K线的DataFrame
"""
if processed_klines is None:
processed_klines = self.handle_containment()
data = []
for kline in processed_klines:
data.append({
'timestamp': kline.timestamp,
'open': kline.open,
'high': kline.high,
'low': kline.low,
'close': kline.close,
'volume': kline.volume,
'direction': kline.direction
})
df = pd.DataFrame(data)
if not df.empty:
df.set_index('timestamp', inplace=True)
return df
def get_processed_data(self) -> pd.DataFrame:
"""
获取处理包含关系后的数据
Returns:
处理后的DataFrame
"""
if self.processed_data is None:
processed_klines = self.handle_containment()
self.processed_data = self.to_dataframe(processed_klines)
return self.processed_data
def visualize_containment(self) -> dict:
"""
生成包含关系可视化信息
Returns:
包含可视化信息的字典
"""
original_count = len(self.kline_elements)
processed_klines = self.handle_containment()
processed_count = len(processed_klines)
return {
'original_count': original_count,
'processed_count': processed_count,
'merged_count': original_count - processed_count,
'merge_ratio': (original_count - processed_count) / original_count if original_count > 0 else 0
}