from datetime import timedelta from pandas import DataFrame from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_SEG_DIR, Chan_ZS_DIR, Chan_BSP_DIR, Chan_BSP_TYPE, Chan_KLC_FX, Chan_PRICE_TREND from ChanKLU import ChanKLU from ChanKLC import ChanKLC from ChanBI import ChanBI from ChanSBI import ChanSBI from ChanSEG import ChanSEG from ChanZS import ChanZS from ChanBSP import ChanBSP import talib.abstract as ta import pandas as pd import matplotlib.pyplot as plt from matplotlib.dates import DateFormatter, date2num import matplotlib.patches as patches from technical.util import resample_to_interval from decimal import Decimal import xgboost as xgb import numpy as np from ChanMACD import ChanMACD class TF_DF(): def __init__(self, df, interval, timeframe): self.timeframe = timeframe self.interval = interval self.dataframe = resample_to_interval(df, interval) self.dataframe = self.add_indicators(self.dataframe) self.klu_list = [] self.klc_list = [] self.bi_list = [] self.zs_list = [] self.bsp_list = [] self.seg_list = [] self.init_TF_DF() def init_TF_DF(self): self.klu_list = self.cal_kl_data(self.dataframe) self.klc_list = self.cal_klc_list(self.klu_list) self.bi_list = self.cal_bi_list(self.klc_list) self.seg_list = self.cal_seg_list(self.bi_list) self.zs_list = self.cal_zs_list(self.bi_list, self.seg_list) self.chanmacd = ChanMACD(self.klu_list) self.klu_list = self.chanmacd.cal_macd_state() def get_ema52(self): if self.klu_list: ema52_value = self.klu_list[-1].ema52 # 处理NaN值 if pd.isna(ema52_value) or ema52_value is None: return None return float(ema52_value) return None def get_ema24(self): if self.klu_list: ema24_value = self.klu_list[-1].ema24 # 处理NaN值 if pd.isna(ema24_value) or ema24_value is None: return None return float(ema24_value) return None def get_current_klc(self): if len(self.klc_list) > 0: return self.klc_list[-2] return None def add_indicators(self, df): fast = 12 slow = 26 period = 9 macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) bb365 = ta.BBANDS(df, timeperiod=365, nbdevup=3.0, nbdevdn=3.0, matype=0) bb120 = ta.BBANDS(df, timeperiod=120, nbdevup=3.0, nbdevdn=3.0, matype=0) bb30 = ta.BBANDS(df, timeperiod=41, nbdevup=2.3, nbdevdn=2.3, matype=0) bb302 = ta.BBANDS(df, timeperiod=41, nbdevup=2.0, nbdevdn=2.0, matype=0) bb30 = ta.BBANDS(df, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0) bb302 = ta.BBANDS(df, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, matype=0) # 计算布林带中轨(移动平均线) bb30_middle = ta.SMA(df, timeperiod=90) # 手动计算布林带 %B 指标 (BBP) # %B = (Price - Lower Band) / (Upper Band - Lower Band) bbp365 = (df['close'] - bb365['lowerband']) / (bb365['upperband'] - bb365['lowerband']) bbp120 = (df['close'] - bb120['lowerband']) / (bb120['upperband'] - bb120['lowerband']) bbp30 = (df['close'] - bb30['lowerband']) / (bb30['upperband'] - bb30['lowerband']) bbp302 = (df['close'] - bb302['lowerband']) / (bb302['upperband'] - bb302['lowerband']) df['atr'] = ta.ATR(df, timeperiod=14) df['bbup365'] = bb365['upperband'] df['bblow365'] = bb365['lowerband'] df['bbp365'] = bbp365 df['bbup120'] = bb120['upperband'] df['bblow120'] = bb120['lowerband'] df['bbp120'] = bbp120 df['bbup30'] = bb30['upperband'] df['bblow30'] = bb30['lowerband'] df['bbmiddle30'] = bb30_middle # 添加bb30中轨 df['bbp30'] = bbp30 df['bbup302'] = bb302['upperband'] df['bblow302'] = bb302['lowerband'] df['bbp302'] = bbp302 df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] df['macdhist'] = macd['macdhist'] df['ema5'] = self.cal_ema(df, 5) df['ema10'] = self.cal_ema(df, 10) df['ema24'] = self.cal_ema(df, 24) df['ema26'] = self.cal_ema(df, 26) df['ema52'] = self.cal_ema(df, 52) df['rsi'] = ta.RSI(df, timeperiod=14) df['volume_ratio'] = self.cal_volume_ratio(df) return df @staticmethod def cal_ema(df, timeperiod): """ 计算 EMA,优先使用 pandas ewm(adjust=False) 以贴近前端/TradingView 显示; 必要时回退到 TA-Lib(abstract)。 """ try: series = df['close'].astype(float) if isinstance(df, pd.DataFrame) else pd.Series(df).astype(float) return series.ewm(span=int(timeperiod), adjust=False).mean() except Exception: try: if isinstance(df, pd.DataFrame): return ta.EMA(df, timeperiod=int(timeperiod)) except Exception: pass # 最后回退:返回同索引的 NaN 序列 if isinstance(df, pd.DataFrame) and 'close' in df: return pd.Series(np.nan, index=df.index) return pd.Series(dtype=float) def check_fx(self, klc): if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high: if klc.macd > 0 and klc.signal > klc.macdhist: klc.set_fx(Chan_FX_TYPE.TOP) # print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP") return Chan_FX_TYPE.TOP elif klc.low < klc.pre.low and klc.low < klc.next.low: if klc.macd < 0 and klc.signal < klc.macdhist: klc.set_fx(Chan_FX_TYPE.BOTTOM) # print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM") return Chan_FX_TYPE.BOTTOM return Chan_FX_TYPE.UNKNOWN def cal_volume_ratio(self, dataframe, window=10): df = dataframe.copy() # 计算过去N根K线的平均成交量 df['avg_volume'] = df['volume'].rolling(window=window).mean() # 计算量比 df['volume_ratio'] = df['volume'] / df['avg_volume'] # 填充缺失值(前N根K线) df['volume_ratio'] = df['volume_ratio'].fillna(1.0) return df['volume_ratio'] def cal_trend(self, klc_list): """ 基于价格与EMA24/EMA52的位置关系、以及MACD/Signal/Hist的方向, 为每个KLC打上趋势标签:'UP' / 'DOWN' / 'FLAT'。 仅设置 klc.trend,不影响其它字段。 """ if not klc_list: return klc_list last_trend = Chan_PRICE_TREND.UNKNOWN for klc in klc_list: price = getattr(klc, 'close', None) ema24 = getattr(klc, 'ema24', None) ema52 = getattr(klc, 'ema52', None) macd = getattr(klc, 'macd', 0) if getattr(klc, 'macd', None) is not None else 0 signal = getattr(klc, 'signal', 0) if getattr(klc, 'signal', None) is not None else 0 hist = getattr(klc, 'macdhist', 0) if getattr(klc, 'macdhist', None) is not None else 0 rsi = getattr(klc, 'rsi', None) trend = Chan_PRICE_TREND.UNKNOWN try: # 有效性 price_valid = price is not None and price != 0 ema24_valid = ema24 is not None and ema24 != 0 ema52_valid = ema52 is not None and ema52 != 0 # 多因子投票 score = 0 # 1) 均线结构 + 价位 if ema24_valid and ema52_valid: score += 1 if ema24 > ema52 else -1 if price_valid and ema24_valid: score += 1 if price > ema24 else -1 if price_valid and ema52_valid: score += 1 if price > ema52 else -1 # 2) MACD结构 score += 1 if macd >= signal else -1 if hist != 0: score += 1 if hist > 0 else -1 # 3) 动量与均线差分斜率 pre = getattr(klc, 'pre', None) if pre: pre_close = getattr(pre, 'close', None) if price_valid and pre_close is not None: score += 1 if price >= pre_close else -1 pre_ema24 = getattr(pre, 'ema24', None) pre_ema52 = getattr(pre, 'ema52', None) if ema24_valid and ema52_valid and pre_ema24 not in (None, 0) and pre_ema52 not in (None, 0): spread_now = ema24 - ema52 spread_pre = pre_ema24 - pre_ema52 score += 1 if spread_now >= spread_pre else -1 # 4) RSI 辅助 if rsi is not None: if rsi >= 55: score += 1 elif rsi <= 45: score -= 1 # 5) 指标未就绪回退(EMA/MACD缺失时,用动量与RSI辅助,延续趋势) has_full_ind = ema24_valid and ema52_valid and not (macd == 0 and signal == 0 and hist == 0) if not has_full_ind: # 仅根据价动量/RSI做轻量判断,默认延续 last_trend,除非出现强反向 strong_up = False strong_down = False pre = getattr(klc, 'pre', None) if pre: pre_close = getattr(pre, 'close', None) if price_valid and pre_close is not None: strong_up = (price >= pre_close) strong_down = (price < pre_close) if rsi is not None: if rsi >= 60: strong_up = True elif rsi <= 40: strong_down = True if last_trend == Chan_PRICE_TREND.UP and not strong_down: trend = Chan_PRICE_TREND.UP elif last_trend == Chan_PRICE_TREND.DOWN and not strong_up: trend = Chan_PRICE_TREND.DOWN else: trend = Chan_PRICE_TREND.UP if strong_up and not strong_down else (Chan_PRICE_TREND.DOWN if strong_down and not strong_up else Chan_PRICE_TREND.FLAT) else: # 6) 震荡过滤(仅当极近EMA52且MACD贴合时判作震荡) near_flat = False if price_valid and ema52_valid: near_ema52 = abs(price - ema52) / abs(ema52) <= 0.0005 # 0.05% near_macd = abs(macd - signal) <= (abs(price) * 0.00005 if price_valid else 0) near_flat = near_ema52 and near_macd # 7) 动态阈值 + 趋势记忆(更强粘滞:趋势中容忍小幅反分) if near_flat: trend = Chan_PRICE_TREND.FLAT else: if last_trend == Chan_PRICE_TREND.UP: # 仅当出现明显反向才翻转,否则维持UP if score <= -2: trend = Chan_PRICE_TREND.DOWN else: trend = Chan_PRICE_TREND.UP elif last_trend == Chan_PRICE_TREND.DOWN: if score >= 2: trend = Chan_PRICE_TREND.UP else: trend = Chan_PRICE_TREND.DOWN else: # 初始无记忆时,降低进入门槛 if score >= 1: trend = Chan_PRICE_TREND.UP elif score <= -1: trend = Chan_PRICE_TREND.DOWN else: trend = Chan_PRICE_TREND.FLAT except Exception: trend = Chan_PRICE_TREND.UNKNOWN # 写回趋势 if hasattr(klc, 'set_trend'): klc.set_trend(trend) else: setattr(klc, 'trend', trend) last_trend = trend price_diff = klc.close - klc.pre.close if klc.pre else 0 #print(klc.start_time, klc.end_time, klc.close, klc.ema24, klc.ema52, klc.macd, klc.signal, klc.macdhist, klc.trend, price_diff) #print(klc.start_time, klc.end_time, klc.trend, price_diff) return klc_list def cal_kl_data(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" klu_list = [] last_klu = None 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) # date = date + timedelta(hours=8) time_str = date.strftime('%Y-%m-%d %H:%M:%S') item_data = [ time_str, o, h, l, c, v ] # klu = KLU(self.create_item_dict(item_data, GetColumnNameFromFieldList(fields))) klu = ChanKLU(time_str, o, h, l, c, v) # print(klu.time, klu.open, klu.high, klu.low, klu.close, klu.volume) klu.set_idx(i) klu_list.append(klu) if last_klu: last_klu.set_next(klu) klu.set_pre(last_klu) last_klu = klu if 'macd' in item: klu.set_indicators(item) return klu_list def cal_klc_list(self, klu_list): klc_list = [] last_klu = None macd = ChanMACD(klu_list) klu_list = macd.cal_macd_state() for klu in klu_list: if len(klc_list) > 0: last_klc = klc_list[-1] included = last_klc.check_klu_included(klu) if not included: ddir = Chan_KLINE_DIR.DOWN if last_klc.high < klu.high: ddir = Chan_KLINE_DIR.UP klc = ChanKLC(klu, index=len(klc_list), ddir=ddir) klc_list.append(klc) last_klc.set_next(klc) klc.set_pre(last_klc) last_klc.set_end_klu(last_klu) klc.set_pre_fx() else: last_klc.add_klu(klu) else: ddir = Chan_KLINE_DIR.UP if klu.open > klu.close: ddir = Chan_KLINE_DIR.DOWN klc = ChanKLC(klu, 0, ddir) klc_list.append(klc) last_klu = klu klc_list = self.cal_trend(klc_list) return klc_list def cal_seg_list(self, bi_list): seg_list = [] up_bi_list = [] down_bi_list = [] last_up_bi = None last_down_bi = None last_up_sbi = None last_down_sbi = None last_seg = None up_sbi_list = [] down_sbi_list = [] look_for_bottom = False look_for_top = False for bi in bi_list: #print(len(up_sbi_list), len(down_sbi_list)) if len(seg_list) > 0: # Last seg is up if last_seg.dir == Chan_SEG_DIR.UP: if bi.dir == Chan_BI_DIR.DOWN: if len(down_sbi_list) > 1: # Check down sbi inclusion included = last_down_sbi.check_bi_included(bi) if not included: down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) last_down_sbi.set_next(down_sbi) last_down_sbi.set_end_bi(last_down_bi) down_sbi.set_pre(last_down_sbi) down_sbi_list.append(down_sbi) fx = last_down_sbi.check_fx() # Found top if fx == Chan_FX_TYPE.TOP: if look_for_top: seg_list[-2].set_sure(bi) look_for_top = False #print(bi.start_time, look_for_top, "UP 1") # Has gap and search for bottom fx if last_down_sbi.has_fx_gap: look_for_bottom = True last_seg.pre_set_end_bi(bi_list[last_down_sbi.start_bi.index - 1]) seg = ChanSEG(last_down_sbi.start_bi, len(seg_list), Chan_SEG_DIR.DOWN) seg_list.append(seg) last_seg.set_next(seg) seg.set_pre(last_seg) last_seg = seg up_sbi_list = [] last_up_sbi = ChanSBI(last_up_bi, len(up_sbi_list), last_up_bi.dir) up_sbi_list.append(last_up_sbi) #up_sbi_list.append(last_up_sbi) #print(last_up_bi.start_time, last_up_sbi.start_bi.start_time, "Reset up sbi list 1") #print(bi.start_time, look_for_top, "UP 2") # No gap end SEG else: if look_for_bottom: look_for_bottom = False last_seg.set_start_bi(last_down_sbi.start_bi) seg_list[-2].set_end_bi(bi_list[last_down_sbi.start_bi.index - 1], bi) up_sbi_list = [] last_up_sbi = ChanSBI(last_up_bi, len(up_sbi_list), last_up_bi.dir) up_sbi_list.append(last_up_sbi) last_seg.add_bi(bi) #up_sbi_list.append(last_up_sbi) #print(last_up_bi.start_time, last_up_sbi.start_bi.start_time, "Reset up sbi list 2") #print(bi.start_time, look_for_top, "UP 3") else: last_seg.set_end_bi(bi_list[last_down_sbi.start_bi.index - 1], bi) seg = ChanSEG(last_down_sbi.start_bi, len(seg_list), Chan_SEG_DIR.DOWN) seg_list.append(seg) last_seg.set_next(seg) seg.set_pre(last_seg) last_seg = seg #print(last_down_sbi.end_bi.start_time, "Normal UP SEG", last_up_sbi.start_bi.start_time, bi.start_time) #l_up_sbi = up_sbi_list[-1] up_sbi_list = [] last_up_sbi = ChanSBI(last_up_bi, len(up_sbi_list), last_up_bi.dir) up_sbi_list.append(last_up_sbi) #up_sbi_list.append(last_up_sbi) #print(last_up_bi.start_time, last_up_sbi.start_bi.start_time, "Reset up sbi list 3") last_down_sbi = down_sbi last_seg.add_bi(bi) else: if len(down_sbi_list) == 1: included = last_down_sbi.check_bi_included(bi) if not included: down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) last_down_sbi.set_next(down_sbi) last_down_sbi.set_end_bi(last_down_bi) down_sbi.set_pre(last_down_sbi) down_sbi_list.append(down_sbi) last_down_sbi = down_sbi #print(bi.start_time, look_for_top, "UP 4") last_seg.add_bi(bi) else: last_down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) down_sbi_list.append(last_down_sbi) last_seg.add_bi(bi) #print(bi.start_time, look_for_top, "UP 5") else: if last_up_sbi: included = last_up_sbi.check_bi_included(bi) if not included: up_sbi = ChanSBI(bi, len(up_sbi_list), bi.dir) last_up_sbi.set_next(up_sbi) last_up_sbi.set_end_bi(last_up_bi) up_sbi.set_pre(last_up_sbi) up_sbi_list.append(up_sbi) last_up_sbi = up_sbi #print(bi.start_time, look_for_top, "UP 6") last_seg.add_bi(bi) # Last seg is down else: if bi.dir == Chan_BI_DIR.UP: if len(up_sbi_list) > 1: # Check down sbi inclusion included = last_up_sbi.check_bi_included(bi) if not included: up_sbi = ChanSBI(bi, len(up_sbi_list), bi.dir) last_up_sbi.set_next(up_sbi) last_up_sbi.set_end_bi(last_up_bi) up_sbi.set_pre(last_up_sbi) up_sbi_list.append(up_sbi) fx = last_up_sbi.check_fx() # Found bottom if fx == Chan_FX_TYPE.BOTTOM: if look_for_bottom: seg_list[-2].set_sure(bi) look_for_bottom = False #print(bi.start_time, look_for_top, "DOWN 1") # Has gap and search for bottom fx if last_up_sbi.has_fx_gap: look_for_top = True last_seg.pre_set_end_bi(bi_list[last_up_sbi.start_bi.index - 1]) seg = ChanSEG(last_up_sbi.start_bi, len(seg_list), Chan_SEG_DIR.UP) seg_list.append(seg) last_seg.set_next(seg) seg.set_pre(last_seg) last_seg = seg down_sbi_list = [] last_down_sbi = ChanSBI(last_down_bi, len(down_sbi_list), last_down_bi.dir) down_sbi_list.append(last_down_sbi) #down_sbi_list.append(last_down_sbi) #print(last_down_bi.start_time, last_down_sbi.start_bi.start_time, "Reset down sbi list 1") #print(bi.start_time, look_for_top, "DOWN 2") # No gap end SEG else: if look_for_top: look_for_top = False last_seg.set_start_bi(last_up_sbi.start_bi) seg_list[-2].set_end_bi(bi_list[last_up_sbi.start_bi.index - 1], bi) down_sbi_list = [] last_down_sbi = ChanSBI(last_down_bi, len(down_sbi_list), last_down_bi.dir) down_sbi_list.append(last_down_sbi) last_seg.add_bi(bi) #down_sbi_list.append(last_down_sbi) #print(last_down_bi.start_time, last_down_sbi.start_bi.start_time, "Reset down sbi list 2") #print(bi.start_time, look_for_top, "DOWN 3") else: last_seg.set_end_bi(bi_list[last_up_sbi.start_bi.index - 1], bi) seg = ChanSEG(last_up_sbi.start_bi, len(seg_list), Chan_SEG_DIR.UP) #print(last_up_sbi.start_bi.start_time) last_seg.set_next(seg) seg.set_pre(last_seg) seg_list.append(seg) last_seg = seg #print(last_up_sbi.end_bi.start_time, "Normal DOWN SEG", last_down_sbi.start_bi.start_time, bi.start_time) down_sbi_list = [] last_down_sbi = ChanSBI(last_down_bi, len(down_sbi_list), last_down_bi.dir) down_sbi_list.append(last_down_sbi) #down_sbi_list.append(last_down_sbi) #print(last_down_bi.start_time, last_down_sbi.start_bi.start_time, "Reset down sbi list 3") last_up_sbi = up_sbi last_seg.add_bi(bi) else: if len(up_sbi_list) == 1: #last_up_sbi = up_sbi_list[-1] included = last_up_sbi.check_bi_included(bi) if not included: up_sbi = ChanSBI(bi, len(up_sbi_list), bi.dir) last_up_sbi.set_next(up_sbi) last_up_sbi.set_end_bi(last_up_bi) up_sbi.set_pre(last_up_sbi) up_sbi_list.append(up_sbi) last_up_sbi = up_sbi last_seg.add_bi(bi) #print(bi.start_time, look_for_top, "DOWN 4") else: last_up_sbi = ChanSBI(bi, len(up_sbi_list), bi.dir) up_sbi_list.append(last_up_sbi) last_seg.add_bi(bi) #print(bi.start_time, look_for_top, "DOWN 5") else: if last_down_sbi: included = last_down_sbi.check_bi_included(bi) if not included: down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) last_down_sbi.set_next(down_sbi) last_down_sbi.set_end_bi(last_down_bi) down_sbi.set_pre(last_down_sbi) down_sbi_list.append(down_sbi) last_down_sbi = down_sbi last_seg.add_bi(bi) #print(bi.start_time, look_for_top, look_for_bottom, "DOWN 6") # len(seg_list) = 0 else: if bi.check_overlap(): if bi.dir == Chan_BI_DIR.UP: seg = ChanSEG(bi, len(seg_list), Chan_SEG_DIR.UP) last_up_bi = bi last_up_sbi = ChanSBI(bi, len(up_sbi_list), bi.dir) seg_list.append(seg) last_seg = seg #print(bi.start_time, 'Create first UP SEG') else: seg = ChanSEG(bi, len(seg_list), Chan_SEG_DIR.DOWN) last_down_bi = bi last_down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) seg_list.append(seg) last_seg = seg #print(bi.start_time, 'Create first DOWN SEG') if bi.dir == Chan_BI_DIR.UP: last_up_bi = bi up_bi_list.append(bi) else: last_down_bi = bi down_bi_list.append(bi) """ if len(seg_list) > 1: seg = seg_list[-1] last_seg = seg_list[-2] last_seg_bi = last_seg.bi_list[-3] bi_index = seg.start_bi.index for i in range(bi_index, len(bi_list) - 1): # last seg is down if seg.dir == Chan_SEG_DIR.UP: if bi_list[i].dir == Chan_BI_DIR.UP: last_seg_peak = last_seg_bi.high if bi_list[i].high > last_seg_peak: # The confirmed print("Last UP seg is broken, create a new seg. 1") seg.pre_set_end_bi(bi_list[i]) seg = ChanSEG(bi_list[i+1], len(seg_list), Chan_SEG_DIR.DOWN) seg_list.append(seg) last_seg = seg_list[-2] if len(last_seg.bi_list) > 3: last_seg_bi = last_seg.bi_list[-3] else: if bi_list[i].dir == Chan_BI_DIR.DOWN: last_seg_peak = last_seg_bi.low if bi_list[i].low < last_seg_peak: print("Last DOWN seg is broken, create a new seg. 1") seg.pre_set_end_bi(bi_list[i]) seg = ChanSEG(bi_list[i+1], len(seg_list), Chan_SEG_DIR.UP) seg_list.append(seg) last_seg = seg_list[-2] if len(last_seg.bi_list) > 3: last_seg_bi = last_seg.bi_list[-3] else: if len(seg_list) == 1: last_seg = seg_list[-1] bi_index = last_seg.bi_list[0].index for i in range(bi_index, len(bi_list) - 1): if i > bi_index + 2: last_seg_peak = bi_list[i-2].high # last seg is down if last_seg.dir == Chan_SEG_DIR.DOWN: if bi_list[i].dir == Chan_BI_DIR.UP: if bi_list[i].high > last_seg_peak: print("Last seg is broken, create a new seg. 2") last_seg.pre_set_end_bi(bi_list[i-1]) seg = ChanSEG(bi_list[i], len(seg_list), Chan_SEG_DIR.UP) seg_list.append(seg) last_seg = seg last_seg_bi = bi_list[i] break """ return seg_list def cal_bi_list(self, klc_list): bi_list = [] last_top = None last_bottom = None for klc in klc_list: fx = self.check_fx(klc) # Do nothing if fx == Chan_FX_TYPE.UNKNOWN: continue if len(bi_list) > 0 and klc.end_klu: last_bi = bi_list[-1] #print(klc.start_time, last_bi.start_time, last_bi.end_time, last_bi.dir, last_bi.high, last_bi.low, last_bottom.end_time, "last bi") if last_top and last_bi.dir == Chan_BI_DIR.DOWN: if last_bottom and klc.high > last_bi.high: last_bi.set_end_klc(last_bottom, klc) bi = ChanBI(last_bottom, len(bi_list), Chan_BI_DIR.UP) #klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM7) #klc.bb_out = True last_bi.set_next(bi) bi.set_pre(last_bi) for klc_index in range(last_bi.end_klc.index, len(klc_list)): bi.add_klc(klc_list[klc_index]) bi_list.append(bi) last_top = klc klc.set_bi(bi) #print(klc.start_time, bi.start_time, bi.end_time, bi.dir, bi.high, bi.low, bi.is_sure) else: if last_bottom and last_bi.dir == Chan_BI_DIR.UP: if last_top and klc.low < last_bi.low: last_bi.set_end_klc(last_top, klc) bi = ChanBI(last_top, len(bi_list), Chan_BI_DIR.DOWN) #klc.set_klc_fx_type(Chan_KLC_FX.TOP6) #klc.bb_out = True last_bi.set_next(bi) bi.set_pre(last_bi) for klc_index in range(last_bi.end_klc.index, len(klc_list)): bi.add_klc(klc_list[klc_index]) bi_list.append(bi) last_bottom = klc klc.set_bi(bi) #print(klc.start_time, bi.start_time, bi.end_time, bi.dir, bi.high, bi.low, bi.is_sure) else: if fx == Chan_FX_TYPE.TOP: if last_top: if last_bottom: #print(klc.start_time, last_bottom.start_time, last_top.start_time) if last_bottom.index < last_top.index: # Second top lower to be second sell point if last_top.high > klc.high: #klc.set_fx(Chan_FX_TYPE.TT) #klc.set_state("20") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #klc.set_klc_fx_type(Chan_KLC_FX.TOP3) #print(klc.start_time, klc.fx, "二类卖点Sell 1") else: # A new top found #last_top.set_fx(Chan_FX_TYPE.UNKNOWN) last_top = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1") klc.set_klc_fx_type(Chan_KLC_FX.TOP1) #print(klc.end_time, klc.fx, "一类卖点Sell 1") #klc.set_fx(fx) #klc.set_state("10") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: # 不满足结合律的分型 if last_bottom.index + 4 > klc.index: if last_top.high > klc.high: #print(klc.start_time, klc.fx, "二类卖点Sell 1") #klc.set_fx(Chan_FX_TYPE.PTOP) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) # New TOP Found replace last top else: if last_top.index + 4 < klc.index and len(bi_list) > 1: pre_last_bi = bi_list[-2] last_bi = bi_list[-1] if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.UP and False: pre_last_bi.update_bi(klc) bi_list.remove(last_bi) pre_last_bi.set_next(None) #last_top.set_fx(Chan_FX_TYPE.PTOP) last_top = klc last_bottom = pre_last_bi.start_klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Bottom Change 1") klc.set_klc_fx_type(Chan_KLC_FX.TOP2) #print(klc.start_time, last_bi.start_klc.start_time, "New TOP Found reset last bi") #klc.set_state("10") #print(klc.start_time, klc.fx, "笔卖点Sell 1") ###klc.set_klc_fx_type(Chan_KLC_FX.TOP2) # when bi is down but the fx is top bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: klc.set_fx(Chan_FX_TYPE.PTOP) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "无效分型") # 满足结合律 else: # New Temp TOP and last bottom confirmed ***** confirm last down bi(last bottom and last top) last_bi = bi_list[-1] if not last_bi.is_sure: last_bi.set_end_klc(last_bottom, klc) bi = ChanBI(last_bottom, len(bi_list), Chan_BI_DIR.UP) #klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM7) #klc.bb_out = True last_bi.set_next(bi) bi.set_pre(last_bi) bi.add_klc(klc) bi_list.append(bi) last_top = klc #print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Top Change 2") klc.set_klc_fx_type(Chan_KLC_FX.TOP2) #klc.set_state('30') bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, last_bottom.start_time, "Normal TOP Found, Confirm down bi 4") #print(klc.start_time, klc.fx, "笔卖点Sell 2") # last bottom = None else: if last_top.high < klc.high: last_bi = bi_list[-1] last_bi.set_start_klc(klc, Chan_BI_DIR.DOWN) #last_top.set_fx(Chan_FX_TYPE.UNKNOWN) last_top = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 3") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "笔卖点Sell 3") else: klc.set_fx(Chan_FX_TYPE.TT) #klc.set_state('20') #print(klc.start_time, klc.fx, "二类卖点Sell 2") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: if last_bottom: # 不满足结合律的分型 if last_bottom.index + 4 > klc.index: #klc.set_fx(Chan_FX_TYPE.PTOP) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "中枢卖点Sell 1") else: # First temp top and last bottom confirmed last_top = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 4") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "一类卖点Sell 1") # Last top = None, last bottom = None, create first down bi else: # First temp top last_top = klc bi = ChanBI(klc, len(bi_list), Chan_BI_DIR.DOWN) #klc.set_klc_fx_type(Chan_KLC_FX.TOP6) #klc.bb_out = True bi_list.append(bi) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 5") #print(klc.start_time, 'Create first top') #print(klc.start_time, klc.fx, "笔卖点Sell 1") #klc.fx = Bottom ======================== else: if last_bottom: if last_top: # Bottom after top and find a new bottom if last_top.index < last_bottom.index: # Second bottom uppper to be second buy point and confirm last bi if last_bottom.low < klc.low: #klc.set_fx(Chan_FX_TYPE.BB) #klc.set_state("-20") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM3) #print(last_bottom.start_time, last_bottom.end_time, "--------------------------------1") #print(klc.start_time, klc.fx, "二类买点Buy 1") else: # A new bottom found #last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN) last_bottom = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1") klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1) #print(klc.start_time, klc.fx, "一类买点Buy 1") #klc.set_state("-10") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: # 不满足结合律的分型 if last_top.index + 4 > klc.index: if last_bottom.low < klc.low: #klc.set_fx(Chan_FX_TYPE.PBOTTOM) #klc.set_fx(Chan_FX_TYPE.BB) #klc.set_state("-100") #print(klc.start_time, klc.fx, "中枢买点Buy 1") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) # Found new bottom else: if last_bottom.index + 4 < klc.index and len(bi_list) > 1: pre_last_bi = bi_list[-2] last_bi = bi_list[-1] if pre_last_bi.is_sure and not last_bi.is_sure and pre_last_bi.dir == Chan_BI_DIR.DOWN and False: pre_last_bi.update_bi(klc) bi_list.remove(last_bi) pre_last_bi.set_next(None) #last_bottom.set_fx(Chan_FX_TYPE.PBOTTOM) last_bottom = klc last_top = pre_last_bi.start_klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Bottom Change 2") klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2) #print(klc.start_time, last_bi.start_klc.start_time, "New BOTTOM Found reset last bi") #klc.set_state("-10") #print(klc.start_time, klc.fx, "笔买点Buy 1") ###klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2) # when bi is up but the fx is bottom bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: #klc.set_fx(Chan_FX_TYPE.UNKNOWN) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "无效分型") # 满足结合律的分型 else: # New Temp Bottom and last top confirmed ***** confirm last up bi(last bottom and last top) last_bi = bi_list[-1] if not last_bi.is_sure: last_bi.set_end_klc(last_top, klc) bi = ChanBI(last_top, len(bi_list), Chan_BI_DIR.DOWN) #klc.set_klc_fx_type(Chan_KLC_FX.TOP6) #klc.bb_out = True last_bi.set_next(bi) bi.set_pre(last_bi) bi.add_klc(klc) bi_list.append(bi) last_bottom = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 2") klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM2) #klc.set_state('-30') bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "笔买点Buy 2") #print(klc.start_time, last_top.start_time, "Normal Bottom Found, Confirm up bi 6") # last_top = None else: if last_bottom.low > klc.low: last_bi = bi_list[-1] last_bi.set_start_klc(klc, Chan_BI_DIR.UP) #last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN) last_bottom = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 3") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "笔买点Buy 3") else: klc.set_fx(Chan_FX_TYPE.BB) #klc.set_state('-20') #print(klc.start_time, klc.fx, "二类买点Buy 2") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) # last_bottom = None else: if last_top: # 不满足结合律的分型 if last_top.index + 4 > klc.index: #klc.set_fx(Chan_FX_TYPE.PBOTTOM) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "中枢买点Buy 1") else: # First temp bottom and last top confirmed last_bottom = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 4") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, "一类买点Buy 1") # Last top = None, last bottom = None, create first up bi else: # First temp bottom and no top yet last_bottom = klc bi = ChanBI(klc, len(bi_list), Chan_BI_DIR.UP) #klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM7) #klc.bb_out = True bi_list.append(bi) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 5") #print(klc.start_time, klc.fx, "笔买点Buy 4") #if klc.fx != Chan_FX_TYPE.UNKNOWN: #print(klc.start_time, klc.fx, klc.index) """ for klc in klc_list: if klc.fx == Chan_FX_TYPE.TOP: klc.state = "10" #print(klc.time, klc.state) if klc.fx == Chan_FX_TYPE.BOTTOM: klc.state = "-10" #print(klc.time, klc.state) """ #for index in range(0, 10): #print(bi_list[index].start_time, bi_list[index].start_klc.start_time, bi_list[index].dir) return bi_list def get_decimal(self, value): return Decimal("{:.2f}".format(value)) def cal_zs_list(self, bi_list, seg_list): zs_list = [] bsp_list = [] if len(seg_list) > 3: last_zs = None first_bi_out = None in_again = False bi_out_count = 0 zs_count = 0 for seg in seg_list: # No zs or Last ZS is completed if len(zs_list) == 0 or (last_zs and last_zs.is_sure): # Has three completed segments if seg.next and seg.next.next: if seg.next.next.is_sure: zg = min(seg.high, seg.next.high, seg.next.next.high) zd = max(seg.low, seg.next.low, seg.next.next.low) gg = max(seg.high, seg.next.high, seg.next.next.high) dd = min(seg.low, seg.next.low, seg.next.next.low) ddir = Chan_ZS_DIR.UP ddir = None if last_zs: if zg < last_zs.zd: ddir = Chan_ZS_DIR.DOWN else: if zd > last_zs.zg: ddir = Chan_ZS_DIR.UP else: ddir = None else: if seg.dir == Chan_SEG_DIR.UP: ddir = Chan_ZS_DIR.DOWN else: ddir = Chan_ZS_DIR.UP if (seg.dir == Chan_SEG_DIR.DOWN and ddir == Chan_ZS_DIR.DOWN) or (seg.dir == Chan_SEG_DIR.UP and ddir == Chan_ZS_DIR.UP): ddir = None if ddir and zg > zd: # New ZS zs = ChanZS(seg, len(zs_list), ddir) zs.set_zg(zg) zs.set_zd(zd) zs.set_gg(gg) zs.set_dd(dd) if last_zs: last_zs.set_next(zs) zs.set_pre(last_zs) zs_list.append(zs) if last_zs and last_zs.dir == zs.dir: zs_count += 1 else: zs_count = 1 last_zs = zs # Last ZS is not completed else: # Last ZS is not completed if last_zs and not last_zs.is_sure: if first_bi_out: # SEG is not in ZS if seg.is_sure: if ((seg.low > last_zs.zg and seg.high > last_zs.zg) or (seg.high < last_zs.zd and seg.low < last_zs.zd)): last_zs.set_end_klc(last_zs.last_bi_in.end_klc, seg.sure_time, bi_out_count, seg) bi_out_count = 0 #print(seg.start_bi.start_klc.start_time) first_bi_out = None # Last ZS is completed and look for new ZS if seg.next and seg.next.next: if seg.next.next.is_sure: zg = min(seg.high, seg.next.high, seg.next.next.high) zd = max(seg.low, seg.next.low, seg.next.next.low) gg = max(seg.high, seg.next.high, seg.next.next.high) dd = min(seg.low, seg.next.low, seg.next.next.low) ddir = None if last_zs: if zg < last_zs.zd: ddir = Chan_ZS_DIR.DOWN else: if zd > last_zs.zg: ddir = Chan_ZS_DIR.UP else: ddir = None else: if seg.dir == Chan_SEG_DIR.UP: ddir = Chan_ZS_DIR.DOWN else: ddir = Chan_ZS_DIR.UP if (seg.dir == Chan_SEG_DIR.DOWN and ddir == Chan_ZS_DIR.DOWN) or (seg.dir == Chan_SEG_DIR.UP and ddir == Chan_ZS_DIR.UP): ddir = None if ddir and zg > zd: # New ZS zs = ChanZS(seg, len(zs_list), ddir) zs.set_zg(zg) zs.set_zd(zd) zs.set_gg(gg) zs.set_dd(dd) last_zs.set_next(zs) zs.set_pre(last_zs) zs_list.append(zs) if last_zs and last_zs.dir == zs.dir: zs_count += 1 else: zs_count = 1 last_zs = zs # Last SEG is in ZS else: # SEG is inside ZS if seg.end_bi: for index in range(seg.start_bi.index, seg.end_bi.index+1): bi = bi_list[index] if (bi.high >= last_zs.zd and bi.high <= last_zs.zg) or (bi.low >= last_zs.zd and bi.low <= last_zs.zg) or (bi.high >= last_zs.zg and bi.low <= last_zs.zd): in_again = True last_zs.set_bi_out(None, None) last_zs.set_last_bi_in(None) last_zs.set_end_seg(None) first_bi_out = None #print("Bi in again 3", bi.start_klc.start_time) if in_again and (bi.low > last_zs.zg or bi.high < last_zs.zd): last_zs.set_bi_out(bi, seg) last_zs.set_last_bi_in(bi_list[index - 1]) #last_zs.set_end_seg(seg.next.next) bi_out_count += 1 first_bi_out = bi if (bi.dir == Chan_BI_DIR.UP and seg.dir == Chan_SEG_DIR.DOWN) or (bi.dir == Chan_BI_DIR.DOWN and seg.dir == Chan_SEG_DIR.UP): bsp = ChanBSP(first_bi_out, len(bsp_list), Chan_BSP_TYPE.T3, Chan_BSP_DIR.BUY if first_bi_out.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.SELL, first_bi_out.sure_time, zs_count, zs, seg) bsp_list.append(bsp) #print("First bi out 3", first_bi_out.start_klc.start_time) in_again = False """" if first_bi_out: if seg.dir == Chan_SEG_DIR.UP and bi.dir == Chan_BI_DIR.UP: #print(bi.start_klc.start_time, bi.high, seg.high) if bi.high == seg.high: bsp = ChanBSP(bi, len(bsp_list), Chan_BSP_TYPE.T3E, Chan_BSP_DIR.SELL if bi.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.BUY, bi.sure_time, zs_count, zs, seg) bsp_list.append(bsp) else: if seg.dir == Chan_SEG_DIR.DOWN and bi.dir == Chan_BI_DIR.DOWN: if bi.low == seg.low: bsp = ChanBSP(bi, len(bsp_list), Chan_BSP_TYPE.T3E, Chan_BSP_DIR.BUY if bi.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.SELL, bi.sure_time, zs_count, zs, seg) bsp_list.append(bsp) """ else: # SEG in ZS and not out and find first bi out if seg.end_bi: for index in range(seg.start_bi.index, seg.end_bi.index+1): bi = bi_list[index] if (bi.high >= last_zs.zd and bi.high <= last_zs.zg) or (bi.low >= last_zs.zd and bi.low <= last_zs.zg) or (bi.high >= last_zs.zg and bi.low <= last_zs.zd): in_again = True last_zs.set_bi_out(None, None) last_zs.set_last_bi_in(None) last_zs.set_end_seg(None) first_bi_out = None #print("Bi in again 4", bi.start_klc.start_time) if in_again and (bi.low > last_zs.zg or bi.high < last_zs.zd): last_zs.set_bi_out(bi, seg) last_zs.set_last_bi_in(bi_list[index - 1]) #last_zs.set_end_seg(seg.next.next) bi_out_count += 1 first_bi_out = bi if (bi.dir == Chan_BI_DIR.UP and seg.dir == Chan_SEG_DIR.DOWN) or (bi.dir == Chan_BI_DIR.DOWN and seg.dir == Chan_SEG_DIR.UP): bsp = ChanBSP(first_bi_out, len(bsp_list), Chan_BSP_TYPE.T3, Chan_BSP_DIR.BUY if first_bi_out.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.SELL, first_bi_out.sure_time, zs_count, zs, seg) bsp_list.append(bsp) #print("First bi out 4", first_bi_out.start_klc.start_time) in_again = False if first_bi_out: if seg.dir == Chan_SEG_DIR.UP and bi.dir == Chan_BI_DIR.UP: #print(bi.start_klc.start_time, bi.high, seg.high) if bi.high == seg.high: bsp = ChanBSP(bi, len(bsp_list), Chan_BSP_TYPE.T3E, Chan_BSP_DIR.SELL if bi.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.BUY, bi.sure_time, zs_count, zs, seg) bsp_list.append(bsp) else: if seg.dir == Chan_SEG_DIR.DOWN and bi.dir == Chan_BI_DIR.DOWN: if bi.low == seg.low: bsp = ChanBSP(bi, len(bsp_list), Chan_BSP_TYPE.T3E, Chan_BSP_DIR.BUY if bi.dir == Chan_BI_DIR.DOWN else Chan_BSP_DIR.SELL, bi.sure_time, zs_count, zs, seg) bsp_list.append(bsp) #self.print_zs(zs_list) return zs_list