import copy from typing import Dict, Optional from Common.CEnum import DATA_FIELD, TRADE_INFO_LST, TREND_TYPE from Common.ChanException import CChanException, ErrCode from Common.CTime import CTime from Math.BOLL import BOLL_Metric, BollModel from Math.Demark import CDemarkEngine, CDemarkIndex from Math.KDJ import KDJ from Math.MACD import CMACD, CMACD_item from Math.RSI import RSI from Math.TrendModel import CTrendModel from .TradeInfo import CTradeInfo class CKLine_Unit: def __init__(self, kl_dict, autofix=False): # _time, _close, _open, _high, _low, _extra_info={} self.kl_type = None self.time: CTime = kl_dict[DATA_FIELD.FIELD_TIME] self.close = kl_dict[DATA_FIELD.FIELD_CLOSE] self.open = kl_dict[DATA_FIELD.FIELD_OPEN] self.high = kl_dict[DATA_FIELD.FIELD_HIGH] self.low = kl_dict[DATA_FIELD.FIELD_LOW] self.check(autofix) self.trade_info = CTradeInfo(kl_dict) self.demark: CDemarkIndex = CDemarkIndex() self.sub_kl_list = [] # 次级别KLU列表 self.sup_kl: Optional[CKLine_Unit] = None # 指向更高级别KLU from KLine.KLine import CKLine self.__klc: Optional[CKLine] = None # 指向KLine # self.macd: Optional[CMACD_item] = None # self.boll: Optional[BOLL_Metric] = None self.trend: Dict[TREND_TYPE, Dict[int, float]] = {} # int -> float self.limit_flag = 0 # 0:普通 -1:跌停,1:涨停 self.pre: Optional[CKLine_Unit] = None self.next: Optional[CKLine_Unit] = None self.set_idx(-1) def __deepcopy__(self, memo): _dict = { DATA_FIELD.FIELD_TIME: self.time, DATA_FIELD.FIELD_CLOSE: self.close, DATA_FIELD.FIELD_OPEN: self.open, DATA_FIELD.FIELD_HIGH: self.high, DATA_FIELD.FIELD_LOW: self.low, } for metric in TRADE_INFO_LST: if metric in self.trade_info.metric: _dict[metric] = self.trade_info.metric[metric] obj = CKLine_Unit(_dict) obj.demark = copy.deepcopy(self.demark, memo) obj.trend = copy.deepcopy(self.trend, memo) obj.limit_flag = self.limit_flag obj.macd = copy.deepcopy(self.macd, memo) obj.boll = copy.deepcopy(self.boll, memo) if hasattr(self, "rsi"): obj.rsi = copy.deepcopy(self.rsi, memo) if hasattr(self, "kdj"): obj.kdj = copy.deepcopy(self.kdj, memo) obj.set_idx(self.idx) memo[id(self)] = obj return obj @property def klc(self): assert self.__klc is not None return self.__klc def set_klc(self, klc): self.__klc = klc @property def idx(self): return self.__idx def set_idx(self, idx): self.__idx: int = idx def __str__(self): return f"{self.idx}:{self.time}/{self.kl_type} open={self.open} close={self.close} high={self.high} low={self.low} {self.trade_info}" def check(self, autofix=False): if self.low > min([self.low, self.open, self.high, self.close]): if autofix: self.low = min([self.low, self.open, self.high, self.close]) else: raise CChanException(f"{self.time} low price={self.low} is not min of [low={self.low}, open={self.open}, high={self.high}, close={self.close}]", ErrCode.KL_DATA_INVALID) if self.high < max([self.low, self.open, self.high, self.close]): if autofix: self.high = max([self.low, self.open, self.high, self.close]) else: raise CChanException(f"{self.time} high price={self.high} is not max of [low={self.low}, open={self.open}, high={self.high}, close={self.close}]", ErrCode.KL_DATA_INVALID) def add_children(self, child): self.sub_kl_list.append(child) def set_parent(self, parent: 'CKLine_Unit'): self.sup_kl = parent def get_children(self): yield from self.sub_kl_list def _low(self): return self.low def _high(self): return self.high def set_metric(self, metric_model_lst: list) -> None: for metric_model in metric_model_lst: if isinstance(metric_model, CMACD): self.macd: CMACD_item = metric_model.add(self.close) elif isinstance(metric_model, CTrendModel): if metric_model.type not in self.trend: self.trend[metric_model.type] = {} self.trend[metric_model.type][metric_model.T] = metric_model.add(self.close) elif isinstance(metric_model, BollModel): self.boll: BOLL_Metric = metric_model.add(self.close) elif isinstance(metric_model, CDemarkEngine): self.demark = metric_model.update(idx=self.idx, close=self.close, high=self.high, low=self.low) elif isinstance(metric_model, RSI): self.rsi = metric_model.add(self.close) elif isinstance(metric_model, KDJ): self.kdj = metric_model.add(self.high, self.low, self.close) def get_parent_klc(self): assert self.sup_kl is not None return self.sup_kl.klc def include_sub_lv_time(self, sub_lv_t: str) -> bool: if self.time.to_str() == sub_lv_t: return True for sub_klu in self.sub_kl_list: if sub_klu.time.to_str() == sub_lv_t: return True if sub_klu.include_sub_lv_time(sub_lv_t): return True return False def set_pre_klu(self, pre_klu: Optional['CKLine_Unit']): if pre_klu is None: return pre_klu.next = self self.pre = pre_klu