155 lines
5.6 KiB
Python
155 lines
5.6 KiB
Python
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
|