Files
2025-06-10 01:16:09 +08:00

150 lines
6.1 KiB
Python

from typing import List, Optional
from Bi.Bi import CBi
from Bi.BiList import CBiList
from Common.CEnum import BI_DIR, FX_TYPE, KLINE_DIR, SEG_TYPE
from Common.ChanException import CChanException, ErrCode
from Common.func_util import revert_bi_dir
from .Eigen import CEigen
class CEigenFX:
def __init__(self, _dir: BI_DIR, exclude_included=True, lv=SEG_TYPE.BI):
self.lv = lv
self.dir = _dir # 线段方向
self.ele: List[Optional[CEigen]] = [None, None, None]
self.lst: List[CBi] = []
self.exclude_included = exclude_included
self.kl_dir = KLINE_DIR.UP if _dir == BI_DIR.UP else KLINE_DIR.DOWN
self.last_evidence_bi: Optional[CBi] = None
def treat_first_ele(self, bi: CBi) -> bool:
self.ele[0] = CEigen(bi, self.kl_dir)
return False
def treat_second_ele(self, bi: CBi) -> bool:
assert self.ele[0] is not None
combine_dir = self.ele[0].try_add(bi, exclude_included=self.exclude_included)
if combine_dir != KLINE_DIR.COMBINE: # 不能合并
self.ele[1] = CEigen(bi, self.kl_dir)
if (self.is_up() and self.ele[1].high < self.ele[0].high) or \
(self.is_down() and self.ele[1].low > self.ele[0].low): # 前两元素不可能成为分形
return self.reset()
return False
def treat_third_ele(self, bi: CBi) -> bool:
assert self.ele[0] is not None
assert self.ele[1] is not None
self.last_evidence_bi = bi
allow_top_equal = (1 if bi.is_down() else -1) if self.exclude_included else None
combine_dir = self.ele[1].try_add(bi, allow_top_equal=allow_top_equal)
if combine_dir == KLINE_DIR.COMBINE:
return False
self.ele[2] = CEigen(bi, combine_dir)
if not self.actual_break():
return self.reset()
self.ele[1].update_fx(self.ele[0], self.ele[2], exclude_included=self.exclude_included, allow_top_equal=allow_top_equal) # type: ignore
fx = self.ele[1].fx
is_fx = (self.is_up() and fx == FX_TYPE.TOP) or (self.is_down() and fx == FX_TYPE.BOTTOM)
return True if is_fx else self.reset()
def add(self, bi: CBi) -> bool: # 返回是否出现分形
assert bi.dir != self.dir
self.lst.append(bi)
if self.ele[0] is None: # 第一元素
return self.treat_first_ele(bi)
elif self.ele[1] is None: # 第二元素
return self.treat_second_ele(bi)
elif self.ele[2] is None: # 第三元素
return self.treat_third_ele(bi)
else:
raise CChanException(f"特征序列3个都找齐了还没处理!! 当前笔:{bi.idx},当前:{str(self)}", ErrCode.SEG_EIGEN_ERR)
def reset(self):
bi_tmp_list = list(self.lst[1:])
if self.exclude_included:
self.clear()
for bi in bi_tmp_list:
if self.add(bi):
return True
else:
assert self.ele[1] is not None
ele2_begin_idx = self.ele[1].lst[0].idx
self.ele[0], self.ele[1], self.ele[2] = self.ele[1], self.ele[2], None
self.lst = [bi for bi in bi_tmp_list if bi.idx >= ele2_begin_idx] # 从第二元素开始
return False
def can_be_end(self, bi_lst: CBiList):
assert self.ele[1] is not None
if self.ele[1].gap:
assert self.ele[0] is not None
end_bi_idx = self.GetPeakBiIdx()
thred_value = bi_lst[end_bi_idx].get_end_val()
break_thred = self.ele[0].low if self.is_up() else self.ele[0].high
return self.find_revert_fx(bi_lst, end_bi_idx+2, thred_value, break_thred)
else:
return True
def is_down(self):
return self.dir == BI_DIR.DOWN
def is_up(self):
return self.dir == BI_DIR.UP
def GetPeakBiIdx(self):
assert self.ele[1] is not None
return self.ele[1].GetPeakBiIdx()
def all_bi_is_sure(self):
assert self.last_evidence_bi is not None
return next((False for bi in self.lst if not bi.is_sure), self.last_evidence_bi.is_sure)
def clear(self):
self.ele = [None, None, None]
self.lst = []
def __str__(self):
_t = [f"{[] if ele is None else ','.join([str(b.idx) for b in ele.lst])}" for ele in self.ele]
return " | ".join(_t)
def actual_break(self):
if not self.exclude_included:
return True
assert self.ele[2] and self.ele[1]
if (self.is_up() and self.ele[2].low < self.ele[1][-1]._low()) or \
(self.is_down() and self.ele[2].high > self.ele[1][-1]._high()): # 防止第二元素因为合并导致后面没有实际突破
return True
assert len(self.ele[2]) == 1
ele2_bi = self.ele[2][0]
if ele2_bi.next and ele2_bi.next.next:
if ele2_bi.is_down() and ele2_bi.next.next._low() < ele2_bi._low():
self.last_evidence_bi = ele2_bi.next.next
return True
elif ele2_bi.is_up() and ele2_bi.next.next._high() > ele2_bi._high():
self.last_evidence_bi = ele2_bi.next.next
return True
return False
def find_revert_fx(self, bi_list: CBiList, begin_idx: int, thred_value: float, break_thred: float):
COMMON_COMBINE = True # 是否用普通分形合并规则处理
# 如果返回None,表示找到最后了
first_bi_dir = bi_list[begin_idx].dir # down则是要找顶分型
egien_fx = CEigenFX(revert_bi_dir(first_bi_dir), exclude_included=not COMMON_COMBINE, lv=self.lv) # 顶分型的话要找上升线段
for bi in bi_list[begin_idx::2]:
if egien_fx.add(bi):
if COMMON_COMBINE:
return True
while True:
_test = egien_fx.can_be_end(bi_list)
if _test in [True, None]:
self.last_evidence_bi = bi
return _test
elif not egien_fx.reset():
break
if (bi.is_down() and bi._low() < thred_value) or (bi.is_up() and bi._high() > thred_value):
return False
return None