150 lines
6.1 KiB
Python
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
|