Initial commit
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from Common.cache import make_cache
|
||||
from Common.CEnum import BI_DIR, BI_TYPE, DATA_FIELD, FX_TYPE, MACD_ALGO
|
||||
from Common.ChanException import CChanException, ErrCode
|
||||
from KLine.KLine import CKLine
|
||||
from KLine.KLine_Unit import CKLine_Unit
|
||||
|
||||
|
||||
class CBi:
|
||||
def __init__(self, begin_klc: CKLine, end_klc: CKLine, idx: int, is_sure: bool):
|
||||
# self.__begin_klc = begin_klc
|
||||
# self.__end_klc = end_klc
|
||||
self.__dir = None
|
||||
self.__idx = idx
|
||||
self.__type = BI_TYPE.STRICT
|
||||
|
||||
self.set(begin_klc, end_klc)
|
||||
|
||||
self.__is_sure = is_sure
|
||||
self.__sure_end: List[CKLine] = []
|
||||
|
||||
self.__seg_idx: Optional[int] = None
|
||||
|
||||
from Seg.Seg import CSeg
|
||||
self.parent_seg: Optional[CSeg[CBi]] = None # 在哪个线段里面
|
||||
|
||||
from BuySellPoint.BS_Point import CBS_Point
|
||||
self.bsp: Optional[CBS_Point] = None # 尾部是不是买卖点
|
||||
|
||||
self.next: Optional[CBi] = None
|
||||
self.pre: Optional[CBi] = None
|
||||
|
||||
def clean_cache(self):
|
||||
self._memoize_cache = {}
|
||||
|
||||
@property
|
||||
def begin_klc(self): return self.__begin_klc
|
||||
|
||||
@property
|
||||
def end_klc(self): return self.__end_klc
|
||||
|
||||
@property
|
||||
def dir(self): return self.__dir
|
||||
|
||||
@property
|
||||
def idx(self): return self.__idx
|
||||
|
||||
@property
|
||||
def type(self): return self.__type
|
||||
|
||||
@property
|
||||
def is_sure(self): return self.__is_sure
|
||||
|
||||
@property
|
||||
def sure_end(self): return self.__sure_end
|
||||
|
||||
@property
|
||||
def klc_lst(self):
|
||||
klc = self.begin_klc
|
||||
while True:
|
||||
yield klc
|
||||
klc = klc.next
|
||||
if not klc or klc.idx > self.end_klc.idx:
|
||||
break
|
||||
|
||||
@property
|
||||
def klc_lst_re(self):
|
||||
klc = self.end_klc
|
||||
while True:
|
||||
yield klc
|
||||
klc = klc.pre
|
||||
if not klc or klc.idx < self.begin_klc.idx:
|
||||
break
|
||||
|
||||
@property
|
||||
def seg_idx(self): return self.__seg_idx
|
||||
|
||||
def set_seg_idx(self, idx):
|
||||
self.__seg_idx = idx
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dir}|{self.begin_klc} ~ {self.end_klc}"
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
if self.is_down():
|
||||
assert self.begin_klc.high > self.end_klc.low
|
||||
else:
|
||||
assert self.begin_klc.low < self.end_klc.high
|
||||
except Exception as e:
|
||||
raise CChanException(f"{self.idx}:{self.begin_klc[0].time}~{self.end_klc[-1].time}笔的方向和收尾位置不一致!", ErrCode.BI_ERR) from e
|
||||
|
||||
def set(self, begin_klc: CKLine, end_klc: CKLine):
|
||||
self.__begin_klc: CKLine = begin_klc
|
||||
self.__end_klc: CKLine = end_klc
|
||||
if begin_klc.fx == FX_TYPE.BOTTOM:
|
||||
self.__dir = BI_DIR.UP
|
||||
elif begin_klc.fx == FX_TYPE.TOP:
|
||||
self.__dir = BI_DIR.DOWN
|
||||
else:
|
||||
raise CChanException("ERROR DIRECTION when creating bi", ErrCode.BI_ERR)
|
||||
self.check()
|
||||
self.clean_cache()
|
||||
|
||||
@make_cache
|
||||
def get_begin_val(self):
|
||||
return self.begin_klc.low if self.is_up() else self.begin_klc.high
|
||||
|
||||
@make_cache
|
||||
def get_end_val(self):
|
||||
return self.end_klc.high if self.is_up() else self.end_klc.low
|
||||
|
||||
@make_cache
|
||||
def get_begin_klu(self) -> CKLine_Unit:
|
||||
if self.is_up():
|
||||
return self.begin_klc.get_peak_klu(is_high=False)
|
||||
else:
|
||||
return self.begin_klc.get_peak_klu(is_high=True)
|
||||
|
||||
@make_cache
|
||||
def get_end_klu(self) -> CKLine_Unit:
|
||||
if self.is_up():
|
||||
return self.end_klc.get_peak_klu(is_high=True)
|
||||
else:
|
||||
return self.end_klc.get_peak_klu(is_high=False)
|
||||
|
||||
@make_cache
|
||||
def amp(self):
|
||||
return abs(self.get_end_val() - self.get_begin_val())
|
||||
|
||||
@make_cache
|
||||
def get_klu_cnt(self):
|
||||
return self.get_end_klu().idx - self.get_begin_klu().idx + 1
|
||||
|
||||
@make_cache
|
||||
def get_klc_cnt(self):
|
||||
assert self.end_klc.idx == self.get_end_klu().klc.idx
|
||||
assert self.begin_klc.idx == self.get_begin_klu().klc.idx
|
||||
return self.end_klc.idx - self.begin_klc.idx + 1
|
||||
|
||||
@make_cache
|
||||
def _high(self):
|
||||
return self.end_klc.high if self.is_up() else self.begin_klc.high
|
||||
|
||||
@make_cache
|
||||
def _low(self):
|
||||
return self.begin_klc.low if self.is_up() else self.end_klc.low
|
||||
|
||||
@make_cache
|
||||
def _mid(self):
|
||||
return (self._high() + self._low()) / 2 # 笔的中位价
|
||||
|
||||
@make_cache
|
||||
def is_down(self):
|
||||
return self.dir == BI_DIR.DOWN
|
||||
|
||||
@make_cache
|
||||
def is_up(self):
|
||||
return self.dir == BI_DIR.UP
|
||||
|
||||
def update_virtual_end(self, new_klc: CKLine):
|
||||
self.append_sure_end(self.end_klc)
|
||||
self.update_new_end(new_klc)
|
||||
self.__is_sure = False
|
||||
|
||||
def restore_from_virtual_end(self, sure_end: CKLine):
|
||||
self.__is_sure = True
|
||||
self.update_new_end(new_klc=sure_end)
|
||||
self.__sure_end = []
|
||||
|
||||
def append_sure_end(self, klc: CKLine):
|
||||
self.__sure_end.append(klc)
|
||||
|
||||
def update_new_end(self, new_klc: CKLine):
|
||||
self.__end_klc = new_klc
|
||||
self.check()
|
||||
self.clean_cache()
|
||||
|
||||
def cal_macd_metric(self, macd_algo, is_reverse):
|
||||
if macd_algo == MACD_ALGO.AREA:
|
||||
return self.Cal_MACD_half(is_reverse)
|
||||
elif macd_algo == MACD_ALGO.PEAK:
|
||||
return self.Cal_MACD_peak()
|
||||
elif macd_algo == MACD_ALGO.FULL_AREA:
|
||||
return self.Cal_MACD_area()
|
||||
elif macd_algo == MACD_ALGO.DIFF:
|
||||
return self.Cal_MACD_diff()
|
||||
elif macd_algo == MACD_ALGO.SLOPE:
|
||||
return self.Cal_MACD_slope()
|
||||
elif macd_algo == MACD_ALGO.AMP:
|
||||
return self.Cal_MACD_amp()
|
||||
elif macd_algo == MACD_ALGO.AMOUNT:
|
||||
return self.Cal_MACD_trade_metric(DATA_FIELD.FIELD_TURNOVER, cal_avg=False)
|
||||
elif macd_algo == MACD_ALGO.VOLUMN:
|
||||
return self.Cal_MACD_trade_metric(DATA_FIELD.FIELD_VOLUME, cal_avg=False)
|
||||
elif macd_algo == MACD_ALGO.VOLUMN_AVG:
|
||||
return self.Cal_MACD_trade_metric(DATA_FIELD.FIELD_VOLUME, cal_avg=True)
|
||||
elif macd_algo == MACD_ALGO.AMOUNT_AVG:
|
||||
return self.Cal_MACD_trade_metric(DATA_FIELD.FIELD_TURNOVER, cal_avg=True)
|
||||
elif macd_algo == MACD_ALGO.TURNRATE_AVG:
|
||||
return self.Cal_MACD_trade_metric(DATA_FIELD.FIELD_TURNRATE, cal_avg=True)
|
||||
elif macd_algo == MACD_ALGO.RSI:
|
||||
return self.Cal_Rsi()
|
||||
else:
|
||||
raise CChanException(f"unsupport macd_algo={macd_algo}, should be one of area/full_area/peak/diff/slope/amp", ErrCode.PARA_ERROR)
|
||||
|
||||
@make_cache
|
||||
def Cal_Rsi(self):
|
||||
rsi_lst: List[float] = []
|
||||
for klc in self.klc_lst:
|
||||
rsi_lst.extend(klu.rsi for klu in klc.lst)
|
||||
return 10000.0/(min(rsi_lst)+1e-7) if self.is_down() else max(rsi_lst)
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_area(self):
|
||||
_s = 1e-7
|
||||
begin_klu = self.get_begin_klu()
|
||||
end_klu = self.get_end_klu()
|
||||
for klc in self.klc_lst:
|
||||
for klu in klc.lst:
|
||||
if klu.idx < begin_klu.idx or klu.idx > end_klu.idx:
|
||||
continue
|
||||
if (self.is_down() and klu.macd.macd < 0) or (self.is_up() and klu.macd.macd > 0):
|
||||
_s += abs(klu.macd.macd)
|
||||
return _s
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_peak(self):
|
||||
peak = 1e-7
|
||||
for klc in self.klc_lst:
|
||||
for klu in klc.lst:
|
||||
if abs(klu.macd.macd) > peak:
|
||||
if self.is_down() and klu.macd.macd < 0:
|
||||
peak = abs(klu.macd.macd)
|
||||
elif self.is_up() and klu.macd.macd > 0:
|
||||
peak = abs(klu.macd.macd)
|
||||
return peak
|
||||
|
||||
def Cal_MACD_half(self, is_reverse):
|
||||
if is_reverse:
|
||||
return self.Cal_MACD_half_reverse()
|
||||
else:
|
||||
return self.Cal_MACD_half_obverse()
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_half_obverse(self):
|
||||
_s = 1e-7
|
||||
begin_klu = self.get_begin_klu()
|
||||
peak_macd = begin_klu.macd.macd
|
||||
for klc in self.klc_lst:
|
||||
for klu in klc.lst:
|
||||
if klu.idx < begin_klu.idx:
|
||||
continue
|
||||
if klu.macd.macd*peak_macd > 0:
|
||||
_s += abs(klu.macd.macd)
|
||||
else:
|
||||
break
|
||||
else: # 没有被break,继续找写一个KLC
|
||||
continue
|
||||
break
|
||||
return _s
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_half_reverse(self):
|
||||
_s = 1e-7
|
||||
begin_klu = self.get_end_klu()
|
||||
peak_macd = begin_klu.macd.macd
|
||||
for klc in self.klc_lst_re:
|
||||
for klu in klc[::-1]:
|
||||
if klu.idx > begin_klu.idx:
|
||||
continue
|
||||
if klu.macd.macd*peak_macd > 0:
|
||||
_s += abs(klu.macd.macd)
|
||||
else:
|
||||
break
|
||||
else: # 没有被break,继续找写一个KLC
|
||||
continue
|
||||
break
|
||||
return _s
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_diff(self):
|
||||
"""
|
||||
macd红绿柱最大值最小值之差
|
||||
"""
|
||||
_max, _min = float("-inf"), float("inf")
|
||||
for klc in self.klc_lst:
|
||||
for klu in klc.lst:
|
||||
macd = klu.macd.macd
|
||||
if macd > _max:
|
||||
_max = macd
|
||||
if macd < _min:
|
||||
_min = macd
|
||||
return _max-_min
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_slope(self):
|
||||
begin_klu = self.get_begin_klu()
|
||||
end_klu = self.get_end_klu()
|
||||
if self.is_up():
|
||||
return (end_klu.high - begin_klu.low)/end_klu.high/(end_klu.idx - begin_klu.idx + 1)
|
||||
else:
|
||||
return (begin_klu.high - end_klu.low)/begin_klu.high/(end_klu.idx - begin_klu.idx + 1)
|
||||
|
||||
@make_cache
|
||||
def Cal_MACD_amp(self):
|
||||
begin_klu = self.get_begin_klu()
|
||||
end_klu = self.get_end_klu()
|
||||
if self.is_down():
|
||||
return (begin_klu.high-end_klu.low)/begin_klu.high
|
||||
else:
|
||||
return (end_klu.high-begin_klu.low)/begin_klu.low
|
||||
|
||||
def Cal_MACD_trade_metric(self, metric: str, cal_avg=False) -> float:
|
||||
_s = 0
|
||||
for klc in self.klc_lst:
|
||||
for klu in klc.lst:
|
||||
metric_res = klu.trade_info.metric[metric]
|
||||
if metric_res is None:
|
||||
return 0.0
|
||||
_s += metric_res
|
||||
return _s / self.get_klu_cnt() if cal_avg else _s
|
||||
|
||||
# def set_klc_lst(self, lst):
|
||||
# self.__klc_lst = lst
|
||||
@@ -0,0 +1,30 @@
|
||||
from Common.CEnum import FX_CHECK_METHOD
|
||||
from Common.ChanException import CChanException, ErrCode
|
||||
|
||||
|
||||
class CBiConfig:
|
||||
def __init__(
|
||||
self,
|
||||
bi_algo="normal",
|
||||
is_strict=True,
|
||||
bi_fx_check="half",
|
||||
gap_as_kl=True,
|
||||
bi_end_is_peak=True,
|
||||
bi_allow_sub_peak=True,
|
||||
):
|
||||
self.bi_algo = bi_algo
|
||||
self.is_strict = is_strict
|
||||
if bi_fx_check == "strict":
|
||||
self.bi_fx_check = FX_CHECK_METHOD.STRICT
|
||||
elif bi_fx_check == "loss":
|
||||
self.bi_fx_check = FX_CHECK_METHOD.LOSS
|
||||
elif bi_fx_check == "half":
|
||||
self.bi_fx_check = FX_CHECK_METHOD.HALF
|
||||
elif bi_fx_check == 'totally':
|
||||
self.bi_fx_check = FX_CHECK_METHOD.TOTALLY
|
||||
else:
|
||||
raise CChanException(f"unknown bi_fx_check={bi_fx_check}", ErrCode.PARA_ERROR)
|
||||
|
||||
self.gap_as_kl = gap_as_kl
|
||||
self.bi_end_is_peak = bi_end_is_peak
|
||||
self.bi_allow_sub_peak = bi_allow_sub_peak
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
from typing import List, Optional, Union, overload
|
||||
|
||||
from Common.CEnum import FX_TYPE, KLINE_DIR
|
||||
from KLine.KLine import CKLine
|
||||
|
||||
from .Bi import CBi
|
||||
from .BiConfig import CBiConfig
|
||||
|
||||
|
||||
class CBiList:
|
||||
def __init__(self, bi_conf=CBiConfig()):
|
||||
self.bi_list: List[CBi] = []
|
||||
self.last_end = None # 最后一笔的尾部
|
||||
self.config = bi_conf
|
||||
|
||||
self.free_klc_lst = [] # 仅仅用作第一笔未画出来之前的缓存,为了获得更精准的结果而已,不加这块逻辑其实对后续计算没太大影响
|
||||
|
||||
def __str__(self):
|
||||
return "\n".join([str(bi) for bi in self.bi_list])
|
||||
|
||||
def __iter__(self):
|
||||
yield from self.bi_list
|
||||
|
||||
@overload
|
||||
def __getitem__(self, index: int) -> CBi: ...
|
||||
|
||||
@overload
|
||||
def __getitem__(self, index: slice) -> List[CBi]: ...
|
||||
|
||||
def __getitem__(self, index: Union[slice, int]) -> Union[List[CBi], CBi]:
|
||||
return self.bi_list[index]
|
||||
|
||||
def __len__(self):
|
||||
return len(self.bi_list)
|
||||
|
||||
def try_create_first_bi(self, klc: CKLine) -> bool:
|
||||
for exist_free_klc in self.free_klc_lst:
|
||||
if exist_free_klc.fx == klc.fx:
|
||||
continue
|
||||
if self.can_make_bi(klc, exist_free_klc):
|
||||
self.add_new_bi(exist_free_klc, klc)
|
||||
self.last_end = klc
|
||||
return True
|
||||
self.free_klc_lst.append(klc)
|
||||
self.last_end = klc
|
||||
return False
|
||||
|
||||
def update_bi(self, klc: CKLine, last_klc: CKLine, cal_virtual: bool) -> bool:
|
||||
# klc: 倒数第二根klc
|
||||
# last_klc: 倒数第1根klc
|
||||
flag1 = self.update_bi_sure(klc)
|
||||
if cal_virtual:
|
||||
flag2 = self.try_add_virtual_bi(last_klc)
|
||||
return flag1 or flag2
|
||||
else:
|
||||
return flag1
|
||||
|
||||
def can_update_peak(self, klc: CKLine):
|
||||
if self.config.bi_allow_sub_peak or len(self.bi_list) < 2:
|
||||
return False
|
||||
if self.bi_list[-1].is_down() and klc.high < self.bi_list[-1].get_begin_val():
|
||||
return False
|
||||
if self.bi_list[-1].is_up() and klc.low > self.bi_list[-1].get_begin_val():
|
||||
return False
|
||||
if not end_is_peak(self.bi_list[-2].begin_klc, klc):
|
||||
return False
|
||||
if self[-1].is_down() and self[-1].get_end_val() < self[-2].get_begin_val():
|
||||
return False
|
||||
if self[-1].is_up() and self[-1].get_end_val() > self[-2].get_begin_val():
|
||||
return False
|
||||
return True
|
||||
|
||||
def update_peak(self, klc: CKLine, for_virtual=False):
|
||||
if not self.can_update_peak(klc):
|
||||
return False
|
||||
_tmp_last_bi = self.bi_list[-1]
|
||||
self.bi_list.pop()
|
||||
if not self.try_update_end(klc, for_virtual=for_virtual):
|
||||
self.bi_list.append(_tmp_last_bi)
|
||||
return False
|
||||
else:
|
||||
if for_virtual:
|
||||
self.bi_list[-1].append_sure_end(_tmp_last_bi.end_klc)
|
||||
return True
|
||||
|
||||
def update_bi_sure(self, klc: CKLine) -> bool:
|
||||
# klc: 倒数第二根klc
|
||||
_tmp_end = self.get_last_klu_of_last_bi()
|
||||
self.delete_virtual_bi()
|
||||
# 返回值:是否出现新笔
|
||||
if klc.fx == FX_TYPE.UNKNOWN:
|
||||
return _tmp_end != self.get_last_klu_of_last_bi() # 虚笔是否有变
|
||||
if self.last_end is None or len(self.bi_list) == 0:
|
||||
return self.try_create_first_bi(klc)
|
||||
if klc.fx == self.last_end.fx:
|
||||
return self.try_update_end(klc)
|
||||
elif self.can_make_bi(klc, self.last_end):
|
||||
self.add_new_bi(self.last_end, klc)
|
||||
self.last_end = klc
|
||||
return True
|
||||
elif self.update_peak(klc):
|
||||
return True
|
||||
return _tmp_end != self.get_last_klu_of_last_bi()
|
||||
|
||||
def delete_virtual_bi(self):
|
||||
if len(self) > 0 and not self.bi_list[-1].is_sure:
|
||||
sure_end_list = [klc for klc in self.bi_list[-1].sure_end]
|
||||
if len(sure_end_list):
|
||||
self.bi_list[-1].restore_from_virtual_end(sure_end_list[0])
|
||||
self.last_end = self[-1].end_klc
|
||||
for sure_end in sure_end_list[1:]:
|
||||
self.add_new_bi(self.last_end, sure_end, is_sure=True)
|
||||
self.last_end = self[-1].end_klc
|
||||
else:
|
||||
del self.bi_list[-1]
|
||||
self.last_end = self[-1].end_klc if len(self) > 0 else None
|
||||
if len(self) > 0:
|
||||
self[-1].next = None
|
||||
|
||||
def try_add_virtual_bi(self, klc: CKLine, need_del_end=False):
|
||||
if need_del_end:
|
||||
self.delete_virtual_bi()
|
||||
if len(self) == 0:
|
||||
return False
|
||||
if klc.idx == self[-1].end_klc.idx:
|
||||
return False
|
||||
if (self[-1].is_up() and klc.high >= self[-1].end_klc.high) or (self[-1].is_down() and klc.low <= self[-1].end_klc.low):
|
||||
# 更新最后一笔
|
||||
self.bi_list[-1].update_virtual_end(klc)
|
||||
return True
|
||||
_tmp_klc = klc
|
||||
while _tmp_klc and _tmp_klc.idx > self[-1].end_klc.idx:
|
||||
assert _tmp_klc is not None
|
||||
if self.can_make_bi(_tmp_klc, self[-1].end_klc, for_virtual=True):
|
||||
# 新增一笔
|
||||
self.add_new_bi(self.last_end, _tmp_klc, is_sure=False)
|
||||
return True
|
||||
elif self.update_peak(_tmp_klc, for_virtual=True):
|
||||
return True
|
||||
_tmp_klc = _tmp_klc.pre
|
||||
return False
|
||||
|
||||
def add_new_bi(self, pre_klc, cur_klc, is_sure=True):
|
||||
self.bi_list.append(CBi(pre_klc, cur_klc, idx=len(self.bi_list), is_sure=is_sure))
|
||||
if len(self.bi_list) >= 2:
|
||||
self.bi_list[-2].next = self.bi_list[-1]
|
||||
self.bi_list[-1].pre = self.bi_list[-2]
|
||||
|
||||
def satisfy_bi_span(self, klc: CKLine, last_end: CKLine):
|
||||
bi_span = self.get_klc_span(klc, last_end)
|
||||
if self.config.is_strict:
|
||||
return bi_span >= 4
|
||||
uint_kl_cnt = 0
|
||||
tmp_klc = last_end.next
|
||||
while tmp_klc:
|
||||
uint_kl_cnt += len(tmp_klc.lst)
|
||||
if not tmp_klc.next: # 最后尾部虚笔的时候,可能klc.idx == last_end.idx+1
|
||||
return False
|
||||
if tmp_klc.next.idx < klc.idx:
|
||||
tmp_klc = tmp_klc.next
|
||||
else:
|
||||
break
|
||||
return bi_span >= 3 and uint_kl_cnt >= 3
|
||||
|
||||
def get_klc_span(self, klc: CKLine, last_end: CKLine) -> int:
|
||||
span = klc.idx - last_end.idx
|
||||
if not self.config.gap_as_kl:
|
||||
return span
|
||||
if span >= 4: # 加速运算,如果span需要真正精确的值,需要去掉这一行
|
||||
return span
|
||||
tmp_klc = last_end
|
||||
while tmp_klc and tmp_klc.idx < klc.idx:
|
||||
if tmp_klc.has_gap_with_next():
|
||||
span += 1
|
||||
tmp_klc = tmp_klc.next
|
||||
return span
|
||||
|
||||
def can_make_bi(self, klc: CKLine, last_end: CKLine, for_virtual: bool = False):
|
||||
satisify_span = True if self.config.bi_algo == 'fx' else self.satisfy_bi_span(klc, last_end)
|
||||
if not satisify_span:
|
||||
return False
|
||||
if not last_end.check_fx_valid(klc, self.config.bi_fx_check, for_virtual):
|
||||
return False
|
||||
if self.config.bi_end_is_peak and not end_is_peak(last_end, klc):
|
||||
return False
|
||||
return True
|
||||
|
||||
def try_update_end(self, klc: CKLine, for_virtual=False) -> bool:
|
||||
def check_top(klc: CKLine, for_virtual):
|
||||
if for_virtual:
|
||||
return klc.dir == KLINE_DIR.UP
|
||||
else:
|
||||
return klc.fx == FX_TYPE.TOP
|
||||
|
||||
def check_bottom(klc: CKLine, for_virtual):
|
||||
if for_virtual:
|
||||
return klc.dir == KLINE_DIR.DOWN
|
||||
else:
|
||||
return klc.fx == FX_TYPE.BOTTOM
|
||||
|
||||
if len(self.bi_list) == 0:
|
||||
return False
|
||||
last_bi = self.bi_list[-1]
|
||||
if (last_bi.is_up() and check_top(klc, for_virtual) and klc.high >= last_bi.get_end_val()) or \
|
||||
(last_bi.is_down() and check_bottom(klc, for_virtual) and klc.low <= last_bi.get_end_val()):
|
||||
last_bi.update_virtual_end(klc) if for_virtual else last_bi.update_new_end(klc)
|
||||
self.last_end = klc
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_last_klu_of_last_bi(self) -> Optional[int]:
|
||||
return self.bi_list[-1].get_end_klu().idx if len(self) > 0 else None
|
||||
|
||||
|
||||
def end_is_peak(last_end: CKLine, cur_end: CKLine) -> bool:
|
||||
if last_end.fx == FX_TYPE.BOTTOM:
|
||||
cmp_thred = cur_end.high # 或者严格点选择get_klu_max_high()
|
||||
klc = last_end.get_next()
|
||||
while True:
|
||||
if klc.idx >= cur_end.idx:
|
||||
return True
|
||||
if klc.high > cmp_thred:
|
||||
return False
|
||||
klc = klc.get_next()
|
||||
elif last_end.fx == FX_TYPE.TOP:
|
||||
cmp_thred = cur_end.low # 或者严格点选择get_klu_min_low()
|
||||
klc = last_end.get_next()
|
||||
while True:
|
||||
if klc.idx >= cur_end.idx:
|
||||
return True
|
||||
if klc.low < cmp_thred:
|
||||
return False
|
||||
klc = klc.get_next()
|
||||
return True
|
||||
Reference in New Issue
Block a user