import abc from typing import Generic, List, TypeVar, Union, overload from Bi.Bi import CBi from Bi.BiList import CBiList from Common.CEnum import BI_DIR, LEFT_SEG_METHOD, SEG_TYPE from Common.ChanException import CChanException, ErrCode from .Seg import CSeg from .SegConfig import CSegConfig SUB_LINE_TYPE = TypeVar('SUB_LINE_TYPE', CBi, "CSeg") class CSegListComm(Generic[SUB_LINE_TYPE]): def __init__(self, seg_config=CSegConfig(), lv=SEG_TYPE.BI): self.lst: List[CSeg[SUB_LINE_TYPE]] = [] self.lv = lv self.do_init() self.config = seg_config def do_init(self): self.lst = [] def __iter__(self): yield from self.lst @overload def __getitem__(self, index: int) -> CSeg[SUB_LINE_TYPE]: ... @overload def __getitem__(self, index: slice) -> List[CSeg[SUB_LINE_TYPE]]: ... def __getitem__(self, index: Union[slice, int]) -> Union[List[CSeg[SUB_LINE_TYPE]], CSeg[SUB_LINE_TYPE]]: return self.lst[index] def __len__(self): return len(self.lst) def left_bi_break(self, bi_lst: CBiList): # 最后一个确定线段之后的笔有突破该线段最后一笔的 if len(self) == 0: return False last_seg_end_bi = self[-1].end_bi for bi in bi_lst[last_seg_end_bi.idx+1:]: if last_seg_end_bi.is_up() and bi._high() > last_seg_end_bi._high(): return True elif last_seg_end_bi.is_down() and bi._low() < last_seg_end_bi._low(): return True return False def collect_first_seg(self, bi_lst: CBiList): if len(bi_lst) < 3: return if self.config.left_method == LEFT_SEG_METHOD.PEAK: _high = max(bi._high() for bi in bi_lst) _low = min(bi._low() for bi in bi_lst) if abs(_high-bi_lst[0].get_begin_val()) >= abs(_low-bi_lst[0].get_begin_val()): peak_bi = FindPeakBi(bi_lst, is_high=True) assert peak_bi is not None self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.UP, split_first_seg=False, reason="0seg_find_high") else: peak_bi = FindPeakBi(bi_lst, is_high=False) assert peak_bi is not None self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.DOWN, split_first_seg=False, reason="0seg_find_low") self.collect_left_as_seg(bi_lst) elif self.config.left_method == LEFT_SEG_METHOD.ALL: _dir = BI_DIR.UP if bi_lst[-1].get_end_val() >= bi_lst[0].get_begin_val() else BI_DIR.DOWN self.add_new_seg(bi_lst, bi_lst[-1].idx, is_sure=False, seg_dir=_dir, split_first_seg=False, reason="0seg_collect_all") else: raise CChanException(f"unknown seg left_method = {self.config.left_method}", ErrCode.PARA_ERROR) def collect_left_seg_peak_method(self, last_seg_end_bi, bi_lst): if last_seg_end_bi.is_down(): peak_bi = FindPeakBi(bi_lst[last_seg_end_bi.idx+3:], is_high=True) if peak_bi and peak_bi.idx - last_seg_end_bi.idx >= 3: self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.UP, reason="collectleft_find_high") else: peak_bi = FindPeakBi(bi_lst[last_seg_end_bi.idx+3:], is_high=False) if peak_bi and peak_bi.idx - last_seg_end_bi.idx >= 3: self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.DOWN, reason="collectleft_find_low") last_seg_end_bi = self[-1].end_bi self.collect_left_as_seg(bi_lst) def collect_segs(self, bi_lst): last_bi = bi_lst[-1] last_seg_end_bi = self[-1].end_bi if last_bi.idx-last_seg_end_bi.idx < 3: return if last_seg_end_bi.is_down() and last_bi.get_end_val() <= last_seg_end_bi.get_end_val(): if peak_bi := FindPeakBi(bi_lst[last_seg_end_bi.idx+3:], is_high=True): self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.UP, reason="collectleft_find_high_force") self.collect_left_seg(bi_lst) elif last_seg_end_bi.is_up() and last_bi.get_end_val() >= last_seg_end_bi.get_end_val(): if peak_bi := FindPeakBi(bi_lst[last_seg_end_bi.idx+3:], is_high=False): self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=BI_DIR.DOWN, reason="collectleft_find_low_force") self.collect_left_seg(bi_lst) # 剩下线段的尾部相比于最后一个线段的尾部,高低关系和最后一个虚线段的方向一致 elif self.config.left_method == LEFT_SEG_METHOD.ALL: # 容易找不到二类买卖点!! self.collect_left_as_seg(bi_lst) elif self.config.left_method == LEFT_SEG_METHOD.PEAK: self.collect_left_seg_peak_method(last_seg_end_bi, bi_lst) else: raise CChanException(f"unknown seg left_method = {self.config.left_method}", ErrCode.PARA_ERROR) def collect_left_seg(self, bi_lst: CBiList): if len(self) == 0: self.collect_first_seg(bi_lst) else: self.collect_segs(bi_lst) def collect_left_as_seg(self, bi_lst: CBiList): last_bi = bi_lst[-1] last_seg_end_bi = self[-1].end_bi if last_seg_end_bi.idx+1 >= len(bi_lst): return if last_seg_end_bi.dir == last_bi.dir: self.add_new_seg(bi_lst, last_bi.idx-1, is_sure=False, reason="collect_left_1") else: self.add_new_seg(bi_lst, last_bi.idx, is_sure=False, reason="collect_left_0") def try_add_new_seg(self, bi_lst, end_bi_idx: int, is_sure=True, seg_dir=None, split_first_seg=True, reason="normal"): if len(self) == 0 and split_first_seg and end_bi_idx >= 3: if peak_bi := FindPeakBi(bi_lst[end_bi_idx-3::-1], bi_lst[end_bi_idx].is_down()): if (peak_bi.is_down() and (peak_bi._low() < bi_lst[0]._low() or peak_bi.idx == 0)) or \ (peak_bi.is_up() and (peak_bi._high() > bi_lst[0]._high() or peak_bi.idx == 0)): # 要比第一笔开头还高/低(因为没有比较到) self.add_new_seg(bi_lst, peak_bi.idx, is_sure=False, seg_dir=peak_bi.dir, reason="split_first_1st") self.add_new_seg(bi_lst, end_bi_idx, is_sure=False, reason="split_first_2nd") return bi1_idx = 0 if len(self) == 0 else self[-1].end_bi.idx+1 bi1 = bi_lst[bi1_idx] bi2 = bi_lst[end_bi_idx] self.lst.append(CSeg(len(self.lst), bi1, bi2, is_sure=is_sure, seg_dir=seg_dir, reason=reason)) if len(self.lst) >= 2: self.lst[-2].next = self.lst[-1] self.lst[-1].pre = self.lst[-2] self.lst[-1].update_bi_list(bi_lst, bi1_idx, end_bi_idx) def add_new_seg(self, bi_lst: CBiList, end_bi_idx: int, is_sure=True, seg_dir=None, split_first_seg=True, reason="normal"): try: self.try_add_new_seg(bi_lst, end_bi_idx, is_sure, seg_dir, split_first_seg, reason) except CChanException as e: if e.errcode == ErrCode.SEG_END_VALUE_ERR and len(self.lst) == 0: return False raise e except Exception as e: raise e return True @abc.abstractmethod def update(self, bi_lst: CBiList): ... def exist_sure_seg(self): return any(seg.is_sure for seg in self.lst) def FindPeakBi(bi_lst: Union[CBiList, List[CBi]], is_high): peak_val = float("-inf") if is_high else float("inf") peak_bi = None for bi in bi_lst: if (is_high and bi.get_end_val() >= peak_val and bi.is_up()) or (not is_high and bi.get_end_val() <= peak_val and bi.is_down()): if bi.pre and bi.pre.pre and ((is_high and bi.pre.pre.get_end_val() > bi.get_end_val()) or (not is_high and bi.pre.pre.get_end_val() < bi.get_end_val())): continue peak_val = bi.get_end_val() peak_bi = bi return peak_bi