import sys import os from typing import Dict, List from functools import reduce from pandas import DataFrame from datetime import datetime, timedelta, timezone # 外部 chan.py 与本仓库包名 `chan` 在 macOS 大小写不敏感磁盘上冲突。 # 临时卸下本包后再导入上游,再恢复本包。 _EXT_ROOT = os.path.abspath("/Users/jack/Project/chan.py") def _load_external_chan(): saved = {} for key in list(sys.modules): low = key.lower() if low == "chan" or low.startswith("chan."): saved[key] = sys.modules.pop(key) inserted = False if _EXT_ROOT not in sys.path: sys.path.insert(0, _EXT_ROOT) inserted = True try: from Chan import CChan as _CChan from BuySellPoint.BS_Point import CBS_Point as _CBS_Point from ChanConfig import CChanConfig as _CChanConfig from Common.CEnum import ( AUTYPE as _AUTYPE, DATA_SRC as _DATA_SRC, KL_TYPE as _KL_TYPE, DATA_FIELD as _DATA_FIELD, BSP_TYPE as _BSP_TYPE, FX_TYPE as _FX_TYPE, BI_DIR as _BI_DIR, KLINE_DIR as _KLINE_DIR, SEG_DIR as _SEG_DIR, ) from KLine.KLine_Unit import CKLine_Unit as _CKLine_Unit from Common.CTime import CTime as _CTime from Common.func_util import kltype_lt_day as _kltype_lt_day, str2float as _str2float from Bi.Bi import CBi as _CBi return { "CChan": _CChan, "CBS_Point": _CBS_Point, "CChanConfig": _CChanConfig, "AUTYPE": _AUTYPE, "DATA_SRC": _DATA_SRC, "KL_TYPE": _KL_TYPE, "DATA_FIELD": _DATA_FIELD, "BSP_TYPE": _BSP_TYPE, "FX_TYPE": _FX_TYPE, "BI_DIR": _BI_DIR, "KLINE_DIR": _KLINE_DIR, "SEG_DIR": _SEG_DIR, "CKLine_Unit": _CKLine_Unit, "CTime": _CTime, "kltype_lt_day": _kltype_lt_day, "str2float": _str2float, "CBi": _CBi, } finally: # 清除上游以 Chan/chan 注册的模块,避免污染本包 for key in list(sys.modules): low = key.lower() if low == "chan" or low.startswith("chan."): sys.modules.pop(key, None) sys.modules.update(saved) if inserted and _EXT_ROOT in sys.path: try: sys.path.remove(_EXT_ROOT) except ValueError: pass _ext = _load_external_chan() CChan = _ext["CChan"] CBS_Point = _ext["CBS_Point"] CChanConfig = _ext["CChanConfig"] AUTYPE = _ext["AUTYPE"] DATA_SRC = _ext["DATA_SRC"] KL_TYPE = _ext["KL_TYPE"] DATA_FIELD = _ext["DATA_FIELD"] BSP_TYPE = _ext["BSP_TYPE"] FX_TYPE = _ext["FX_TYPE"] BI_DIR = _ext["BI_DIR"] KLINE_DIR = _ext["KLINE_DIR"] SEG_DIR = _ext["SEG_DIR"] CKLine_Unit = _ext["CKLine_Unit"] CTime = _ext["CTime"] kltype_lt_day = _ext["kltype_lt_day"] str2float = _ext["str2float"] CBi = _ext["CBi"] def GetColumnNameFromFieldList(fileds: str): _dict = { "time": DATA_FIELD.FIELD_TIME, "open": DATA_FIELD.FIELD_OPEN, "high": DATA_FIELD.FIELD_HIGH, "low": DATA_FIELD.FIELD_LOW, "close": DATA_FIELD.FIELD_CLOSE, "volume": DATA_FIELD.FIELD_VOLUME } return [_dict[x] for x in fileds.split(",")] class ChanPY(): k_type = KL_TYPE.K_5M config = CChanConfig({ "bi_strict": True, "bi_algo": "normal", "trigger_step": True, "skip_step": 0, "divergence_rate": float("inf"), "bsp2_follow_1": False, "bsp3_follow_1": False, "min_zs_cnt": 1, "bs1_peak": False, "macd_algo": "peak", "bs_type": '1,2,3a,1p,2s,3b', "print_warning": True, "zs_algo": "normal", }) chan = CChan( code="BTC/USDT:USDT", data_src=DATA_SRC.CCXT, lv_list=[k_type], config=config, autype=AUTYPE.QFQ, ) klu_list = [] bsps = [] chanIn = True #def __init__(self, dataframe): #self.klu_list = self.get_kl_data(dataframe) #for klu in self.klu_list: #self.chan.trigger_load({self.k_type: [klu]}) def add_klu(self, klu): if klu: self.chan.trigger_load({self.k_type: [klu]}) self.klu_list.append(klu) def add_klu_from_dataframe(self, dataframe): if len(dataframe) > len(self.klu_list) and len(dataframe) - len(self.klu_list) == 1: klu = self.get_last_klu(dataframe) self.chan.trigger_load({self.k_type: [klu]}) self.klu_list.append(klu) def parse_time_column(self, inp): if len(inp) == 10: year = int(inp[:4]) month = int(inp[5:7]) day = int(inp[8:10]) hour = minute = 0 elif len(inp) == 17: year = int(inp[:4]) month = int(inp[4:6]) day = int(inp[6:8]) hour = int(inp[8:10]) minute = int(inp[10:12]) elif len(inp) == 19: year = int(inp[:4]) month = int(inp[5:7]) day = int(inp[8:10]) hour = int(inp[11:13]) minute = int(inp[14:16]) else: raise Exception(f"unknown time column from TradingView:{inp}") return CTime(year, month, day, hour, minute, auto=not kltype_lt_day(self.k_type)) def create_item_dict(self, data, column_name): for i in range(len(data)): data[i] = self.parse_time_column(data[i]) if i == 0 else str2float(data[i]) return dict(zip(column_name, data)) def get_last_klu(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" item = dataframe.iloc[-1] date = item['date'] o = item['open'] h = item['high'] l = item['low'] c = item['close'] v = item['volume'] #time_obj = date.fromtimestamp(date) time_str = date.strftime('%Y-%m-%d %H:%M:%S') item_data = [ time_str, o, h, l, c, v ] klu = CKLine_Unit(self.create_item_dict(item_data, GetColumnNameFromFieldList(fields)), autofix=True) klu.set_idx(len(dataframe)-1) return klu def get_kl_data(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" klu_list = [] for i in range(0, len(dataframe)): item = dataframe.iloc[i] date = item['date'] o = item['open'] h = item['high'] l = item['low'] c = item['close'] v = item['volume'] #time_obj = date.fromtimestamp(date) time_str = date.strftime('%Y-%m-%d %H:%M:%S') item_data = [ time_str, o, h, l, c, v ] klu = CKLine_Unit(self.create_item_dict(item_data, GetColumnNameFromFieldList(fields)), autofix=True) klu.set_idx(i) klu_list.append(klu) return klu_list def get_bsp_type(self, bsp_type, is_buy): if is_buy: if bsp_type == BSP_TYPE.T1: return 1 if bsp_type == BSP_TYPE.T1P: return 2 if bsp_type == BSP_TYPE.T2: return 3 if bsp_type == BSP_TYPE.T2S: return 4 if bsp_type == BSP_TYPE.T3A: return 5 if bsp_type == BSP_TYPE.T3B: return 6 else: if bsp_type == BSP_TYPE.T1: return -1 if bsp_type == BSP_TYPE.T1P: return -2 if bsp_type == BSP_TYPE.T2: return -3 if bsp_type == BSP_TYPE.T2S: return -4 if bsp_type == BSP_TYPE.T3A: return -5 if bsp_type == BSP_TYPE.T3B: return -6 def get_bsps(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" bsps = [] updown = [] bi_sure = [] if self.chanIn: kl_data = self.get_kl_data(dataframe) bsp_list = [] bsp_list_pre_len = 0 last_bsp_value = 0 last_updown = -1 bi_list_pre_len = 0 pre_bi = None zs_list_pre_len = 0 pre_zs = None for klu in kl_data: # 获取单根K线 self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 self.last_kline = klu bsp_list = self.chan.get_bsp() kl_datas = self.chan.kl_datas[self.k_type] bi_list = kl_datas.bi_list lst = kl_datas.lst if len(bsp_list) > 0: last_bsp = bsp_list[-1] #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, lst[-2].fx, bi_list[-1].dir, bi_list[-1].is_sure,klu.close) if bsp_list_pre_len > len(bsp_list): if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: bsps.append(1) #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, 98) else: bsps.append(99) else: if bsp_list_pre_len == len(bsp_list): if klu.idx == last_bsp.klu.idx: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) bsps.append(last_bsp_value) #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) else: bsps.append(0) else: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) bsps.append(last_bsp_value) #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) else: bsps.append(0) bsp_list_pre_len = len(bsp_list) #Check zs ----------------------------------- zs_list = kl_datas.zs_list if len(zs_list) > 0: zs = zs_list[-1] #if zs_list_pre_len > len(zs_list): #print("No zs", zs.begin.time) #if len(zs_list) > zs_list_pre_len: #print(zs.begin.time, zs.end.time, zs.end.idx, zs.high, zs.low, zs.peak_high, zs.peak_low) zs_list_pre_len = len(zs_list) pre_zs = zs #Check Bi ----------------------------------- if len(bi_list) > 0: last_bi = bi_list[-1] if len(bi_list) == 1: if last_bi.dir == BI_DIR.UP: updown.append(1) last_updown = 1 else: updown.append(-1) last_updown = -1 else: if last_updown == 1: if last_bi.dir == BI_DIR.UP: updown.append(0) else: updown.append(-1) last_updown = -1 else: if last_bi.dir == BI_DIR.DOWN: updown.append(0) else: updown.append(1) last_updown = 1 else: updown.append(0) bi_list = kl_datas.bi_list if len(bi_list) > 0: last_bi = bi_list[-1] #if bi_list_pre_len > len(bi_list): #print("Bi ", klu.time, pre_bi.idx, pre_bi.is_sure, bi_list[-1].idx, bi_list[-1].is_sure) if last_bi.is_sure: bi_sure.append(1) #print(klu.time, last_bi.is_sure) else: bi_sure.append(0) pre_bi = bi_list[-1] bi_list_pre_len = len(bi_list) else: bi_sure.append(0) #if bsps[-1] != 0 or updown[-1] != 0: #print(klu.time, bsps[-1], updown[-1], bi_list[-1].is_sure) self.chanIn = False else: klu = self.get_last_klu(dataframe) if self.last_kline.time < klu.time: self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 self.last_kline = klu for index in range(0, len(bsps)): if not (abs(bsps[index]) == 1 or abs(bsps[index]) == 2): bsps[index] = 0 else: if bsps[index] == 2: bsps[index] = 1 else: if bsps[index] == -2: bsps[index] = -1 else: bsps[index] = 0 #print(bsps) #print(updown) kl_datas = self.chan.kl_datas[self.k_type] #for zs in kl_datas.zs_list: #print(zs.begin.time, zs.end.time) return bsps, updown, bi_sure def get_bsp_state1(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" bsps = [] if self.chanIn: kl_data = self.get_kl_data(dataframe) self.chan.trigger_load({self.k_type: kl_data}) bsp_list = self.chan.get_bsp() bsp_index = 0 for klu in kl_data: if bsp_index >= len(bsp_list): bsp_index = len(bsp_list) - 1 bsp = bsp_list[bsp_index] if klu.idx == bsp.klu.idx: bsp_type = self.get_bsp_type(bsp.type[0], bsp.is_buy) if abs(bsp_type) == 1 or abs(bsp_type) == 10: bsps.append(1) else: bsps.append(0) bsp_index = bsp_index + 1 else: bsps.append(0) self.chanIn = False else: klu = CKLine_Unit(self.create_item_dict(self.get_last_item_data(dataframe), GetColumnNameFromFieldList(fields)), autofix=True) if self.last_kline.time < klu.time: self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 self.last_kline = klu return bsps def get_bsp_state(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" if self.chanIn: kl_data = self.get_kl_data(dataframe) bsp_list = [] bsp_list_pre_len = 0 last_bsp_value = 0 last_bsp_index = 0 for klu in kl_data: # 获取单根K线 self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 self.last_kline = klu bsp_list = self.chan.get_bsp() kl_datas = self.chan.kl_datas[self.k_type] bi_list = kl_datas.bi_list lst = kl_datas.lst if len(bsp_list) > 0: last_bsp = bsp_list[-1] #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, lst[-2].fx, bi_list[-1].dir, bi_list[-1].is_sure,klu.close) if bsp_list_pre_len > len(bsp_list): if abs(last_bsp_value) == 1: self.bsps.append(1) #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, 98) else: self.bsps.append(99) else: if bsp_list_pre_len == len(bsp_list): if klu.idx == last_bsp.klu.idx: if last_bsp.klu.idx - last_bsp_index > 3: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) self.bsps.append(last_bsp_value) else: self.bsps.append(0) last_bsp_index = last_bsp.klu.idx #if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, "Knonw") else: self.bsps.append(0) else: if klu.idx == last_bsp.klu.idx: if last_bsp.klu.idx - last_bsp_index > 3: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) self.bsps.append(last_bsp_value) else: self.bsps.append(0) last_bsp_index = last_bsp.klu.idx #if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, "Knonw") else: self.bsps.append(0) else: self.bsps.append(0) bsp_list_pre_len = len(bsp_list) self.chanIn = False else: klu = self.get_last_klu(dataframe) if self.last_kline.time < klu.time: self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 self.last_kline = klu bsp_list = self.chan.get_bsp() last_bsp = bsp_list[-1] if last_bsp.klu.idx == klu.idx: self.bsps.append(self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy)) else: self.bsps.append(0) for index in range(0, len(self.bsps)): if not (abs(self.bsps[index]) == 1 or abs(self.bsps[index]) == 2): self.bsps[index] = 0 else: if self.bsps[index] == 2: self.bsps[index] = 10 else: if self.bsps[index] == -2: self.bsps[index] = -10 else: if self.bsps[index] == 1: self.bsps[index] = 1 else: if self.bsps[index] == -1: self.bsps[index] = -1 else: self.bsps[index] = 0 return self.bsps