From fceb57d2b852b6d8852f47050498d2b914aa91e0 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Sun, 27 Apr 2025 18:37:50 +0800 Subject: [PATCH] change something --- .gitignore | 4 +- ChanBI.py | 181 ++++++++++++++++++----------------- ChanKLC.py | 53 ++-------- ChanKLU.py | 3 + ChanLun.py | 33 ++++++- ChanLun_Classifier.py | 5 +- strategies/ChanLun_SOL_15.py | 19 +++- strategies/ChanLun_SOL_5.py | 32 ++++--- web/app.py | 43 ++++++++- web/templates/index.html | 74 +++++++++++++- 10 files changed, 285 insertions(+), 162 deletions(-) diff --git a/.gitignore b/.gitignore index 286c6e2..6e955b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .DS_Store __pycache__/ -strategies/__pycache__/ \ No newline at end of file +strategies/__pycache__/ +/__pycache__ +*.pyc diff --git a/ChanBI.py b/ChanBI.py index 34a70b8..f9a0f83 100644 --- a/ChanBI.py +++ b/ChanBI.py @@ -1,92 +1,95 @@ +from decimal import Decimal import ChanKLC from ChanEnum import Chan_BI_DIR class ChanBI(): - def __init__(self, klc: ChanKLC, index, ddir=Chan_BI_DIR.UP): - self.start_klc = klc - self.end_klc = None - self.next = None - self.pre = None - self.dir = ddir - self.index = index - self.is_sure = False - self.high = klc.high - self.low = klc.low - self.sure_time = None - self.klc_list = [] - self.klc_list.append(klc) - self.end_time = None - self.start_time = klc.start_time - self.macd_hist = 0 - self.macd_div = 0 - def set_macdhist(self, macd_hist): - self.macd_hist = macd_hist - def set_macd_div(self, macd_div): - self.macd_div = macd_div - def cal_macd_div(self): - self.macd_div = 0.0 - if self.pre and self.pre.pre: - if self.pre.pre.macd_hist == 0: - self.macd_div = 0.0 - else: - self.macd_div = self.macd_hist / self.pre.pre.macd_hist - def cal_macdhist(self): - self.macd_hist = 0 - for klc in self.klc_list: - for klu in klc.klus: - if self.dir == Chan_BI_DIR.UP and klu.macdhist > 0: - self.macd_hist += klu.macdhist - if self.dir == Chan_BI_DIR.DOWN and klu.macdhist < 0: - self.macd_hist -= klu.macdhist - def check_overlap(self): - if self.next and self.next.next: - if self.dir == Chan_BI_DIR.UP: - return self.high > self.next.low and self.high < self.next.next.high - else: - return self.high > self.next.high and self.low > self.next.next.low - else: - return False - def set_end_klc(self, klc, sure_klc): - if self.dir == Chan_BI_DIR.UP and klc.high > self.high: - self.high = klc.high - if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low: - self.low = klc.low - self.end_klc = klc - self.set_is_sure(True, sure_klc.end_time) - self.end_time = klc.end_time - #print(self.start_time, klc.start_time, klc.fx, "This bi is ended") - def set_is_sure(self, is_sure, time): - self.is_sure = is_sure - self.sure_time = time - def set_start_klc(self, klc, ddir): - self.start_klc = klc - self.klc_list = [] - self.klc_list.append(klc) - self.high = klc.high - self.low = klc.low - self.dir = ddir - def set_pre(self, bi): - self.pre = bi - def set_next(self, bi): - self.next = bi - def add_klc(self, klc): - added = False - if len(self.klc_list) > 0: - for index in range(0, len(self.klc_list)): - if self.klc_list[index].index == klc.index: - added = True - break - if not added: - self.klc_list.append(klc) - self.cal_macdhist() - self.cal_macd_div() - def append_klc_list(self, klc_list): - self.klc_list.append(klc_list) - def update_bi(self, klc): - self.end_klc = None - if self.dir == Chan_BI_DIR.UP and klc.high > self.high: - self.high = klc.high - if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low: - self.low = klc.low - self.is_sure = False - self.sure_time = None - #print(self.start_time, klc.start_time, klc.fx, "This bi is extended") \ No newline at end of file + def __init__(self, klc: ChanKLC, index, ddir=Chan_BI_DIR.UP): + self.start_klc = klc + self.end_klc = None + self.next = None + self.pre = None + self.dir = ddir + self.index = index + self.is_sure = False + self.high = klc.high + self.low = klc.low + self.sure_time = None + self.klc_list = [] + self.klc_list.append(klc) + self.end_time = None + self.start_time = klc.start_time + self.macd_hist = 0 + self.macd_div = 0 + def set_macdhist(self, macd_hist): + self.macd_hist = macd_hist + def set_macd_div(self, macd_div): + self.macd_div = macd_div + def cal_macd_div(self): + self.macd_div = 0.0 + if self.pre and self.pre.pre: + if self.pre.pre.macd_hist == 0: + self.macd_div = 0.0 + else: + self.macd_div = self.macd_hist / self.pre.pre.macd_hist + def cal_macdhist(self): + self.macd_hist = 0 + for klc in self.klc_list: + for klu in klc.klus: + if self.dir == Chan_BI_DIR.UP and klu.macdhist > 0: + self.macd_hist += klu.macdhist + if self.dir == Chan_BI_DIR.DOWN and klu.macdhist < 0: + self.macd_hist -= klu.macdhist + def check_overlap(self): + if self.next and self.next.next: + if self.dir == Chan_BI_DIR.UP: + return self.high > self.next.low and self.high < self.next.next.high + else: + return self.high > self.next.high and self.low > self.next.next.low + else: + return False + def set_end_klc(self, klc, sure_klc): + if self.dir == Chan_BI_DIR.UP and klc.high > self.high: + self.high = klc.high + if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low: + self.low = klc.low + self.end_klc = klc + self.set_is_sure(True, sure_klc.end_time) + self.end_time = klc.end_time + #print(self.start_time, klc.start_time, klc.fx, "This bi is ended") + def set_is_sure(self, is_sure, time): + self.is_sure = is_sure + self.sure_time = time + def set_start_klc(self, klc, ddir): + self.start_klc = klc + self.klc_list = [] + self.klc_list.append(klc) + self.high = klc.high + self.low = klc.low + self.dir = ddir + def set_pre(self, bi): + self.pre = bi + def set_next(self, bi): + self.next = bi + def add_klc(self, klc): + added = False + if len(self.klc_list) > 0: + for index in range(0, len(self.klc_list)): + if self.klc_list[index].index == klc.index: + added = True + break + if not added: + self.klc_list.append(klc) + self.cal_macdhist() + self.cal_macd_div() + def append_klc_list(self, klc_list): + self.klc_list.append(klc_list) + def get_decimal(self, value): + return Decimal("{:.2f}".format(value)) + def update_bi(self, klc): + self.end_klc = None + if self.dir == Chan_BI_DIR.UP and klc.high > self.high: + self.high = klc.high + if self.dir == Chan_BI_DIR.DOWN and klc.low < self.low: + self.low = klc.low + self.is_sure = False + self.sure_time = None + #print(self.start_time, klc.start_time, klc.fx, "This bi is extended") \ No newline at end of file diff --git a/ChanKLC.py b/ChanKLC.py index f9339e8..a5f625e 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -25,35 +25,14 @@ class ChanKLC(): self.open = klu.open self.close = klu.close self.volume = klu.volume - self.macdhist = 0 - self.bi_macdhist = 0 - self.bi_macd_div = 0.0 self.bi = None self.distance = 0 self.klc_fx_type = Chan_KLC_FX.UNKNOWN + self.rsi = klu.rsi + self.volume_ratio = klu.volume_ratio def set_klc_fx_type(self, klc_fx_type): #print(self.start_time, klc_fx_type, self.get_feature_data()['klu_macd'], self.get_feature_data()['klu_macdhist'], self.get_feature_data()['klu_rsi']) - if self.check_klc_fx_type(klc_fx_type): - self.klc_fx_type = klc_fx_type self.klc_fx_type = klc_fx_type - def check_klc_fx_type(self, klc_fx_type): - if klc_fx_type == Chan_KLC_FX.BOTTOM1: - features = self.cal_klu_features() - if features['klu_macd'] > 0: - #print(self.start_time, klc_fx_type, features['klu_macd']) - return False - else: - return True - elif klc_fx_type == Chan_KLC_FX.TOP1: - features = self.get_feature_data() - if features['klu_macd'] < 0: - #print(self.start_time, klc_fx_type, features['klu_macd']) - return False - else: - return True - else: - return True - def add_klu(self, klu): self.klus.append(klu) def set_end_klu(self, klu): @@ -62,6 +41,10 @@ class ChanKLC(): self.close = klu.close for index in range(1, len(self.klus)): self.volume += self.klus[index].volume + self.rsi += self.klus[index].rsi + self.volume_ratio += self.klus[index].volume_ratio + self.rsi = self.rsi / len(self.klus) + self.volume_ratio = self.volume_ratio / len(self.klus) def set_next(self, klc): self.next = klc def set_pre(self, klc): @@ -138,27 +121,7 @@ class ChanKLC(): return Chan_FX_TYPE.UNKNOWN def set_bi(self, bi): self.bi = bi - self.get_macdhist() - for index in range(0, len(self.bi.klc_list)): - klc = self.bi.klc_list[index] - if klc.index == self.index: - break - else: - if bi.dir == Chan_BI_DIR.UP and klc.get_macdhist() > 0: - self.bi_macdhist += klc.get_macdhist() - elif bi.dir == Chan_BI_DIR.DOWN and klc.get_macdhist() < 0: - self.bi_macdhist -= klc.get_macdhist() self.distance = self.index - bi.start_klc.index - if False: - if bi.is_sure: - print(self.start_time, self.distance, bi.index, bi.start_time, bi.dir, bi.end_time) - else: - print(self.start_time, self.distance, bi.index, bi.start_time, bi.dir) - def get_macdhist(self): - self.macdhist = 0 - for klu in self.klus: - self.macdhist += klu.macdhist - return self.macdhist def cal_klu_features(self): features = dict() feature_sums = dict() @@ -196,6 +159,8 @@ class ChanKLC(): features['klc_pre_pre_fx'] = (0 if self.pre.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre and self.pre.pre else 0 #11 features['klc_pre_pre_pre_fx'] = (0 if self.pre.pre.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.pre.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre and self.pre.pre and self.pre.pre.pre else 0 #12 features['klc_distance'] = self.distance #13 + features['klc_volume_ratio'] = self.volume_ratio #14 + features['klc_rsi'] = self.rsi #15 # New Add 20250422 #features['klc_macdhist'] = self.get_macdhist() #11 #features['klc_bi_macdhist'] = self.bi_macdhist #12 @@ -492,6 +457,6 @@ class ChanKLC(): features['klc_is_inner_inclusive'] = 0 # 从KLU获取其他特征 - features.update(self.cal_klu_features()) + #features.update(self.cal_klu_features()) return features \ No newline at end of file diff --git a/ChanKLU.py b/ChanKLU.py index be68190..9485333 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -18,6 +18,7 @@ class ChanKLU: self.ma30 = 0 self.ma250 = 0 self.rsi = 0 + self.volume_ratio = 0 def set_idx(self, idx): self.idx = idx self.index = idx @@ -30,6 +31,7 @@ class ChanKLU: self.ma30 = float(item['ma30']) if item['ma30'] else 0 self.ma250 = float(item['ma250']) if item['ma250'] else 0 self.rsi = float(item['rsi']) if item['rsi'] else 0 + self.volume_ratio = float(item['volume_ratio']) if item['volume_ratio'] else 0 def get_feature_data(self): features = dict() features['klu_close'] = self.close @@ -46,4 +48,5 @@ class ChanKLU: features['klu_ma30'] = self.ma30 features['klu_ma250'] = self.ma250 features['klu_rsi'] = self.rsi + features['klu_volume_ratio'] = self.volume_ratio return features \ No newline at end of file diff --git a/ChanLun.py b/ChanLun.py index f9e9c39..925448e 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -80,8 +80,8 @@ class ChanLun(): return Chan_FX_TYPE.UNKNOWN def get_macd(self, df): fast = 8 - slow = 16 - period = 6 + slow = 15 + period = 2 macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] @@ -212,6 +212,27 @@ class ChanLun(): klc_list = self.get_klc_list(dataframe) bi_list = self.cal_bi_list(klc_list) return klc_list + def print_bi_klc(self, dataframe): + klc_list = self.get_klc_list(dataframe) + bi_list = self.cal_bi_list(klc_list) + rsi_list = dataframe['rsi'] + fx_list = [] + for klc in klc_list: + if klc.klc_fx_type != Chan_KLC_FX.UNKNOWN: + if klc.bi.dir == Chan_BI_DIR.UP and klc.volume_ratio > 2: + print(klc.start_time, klc.end_time, klc.klc_fx_type, klc.rsi, klc.volume_ratio) + fx_list.append(klc) + elif klc.bi.dir == Chan_BI_DIR.DOWN and klc.volume_ratio > 2: + print(klc.start_time, klc.end_time, klc.klc_fx_type, klc.rsi, klc.volume_ratio) + fx_list.append(klc) + bi_start_index_list = [] + for bi in bi_list: + bi_start_index_list.append(bi.start_klc.index) + if bi.end_klc: + bi.cal_macdhist() + bi.cal_macd_div() + print("Bi:", bi.start_time, bi.end_time, bi.dir, bi.macd_hist, bi.macd_div) + klc_index_count = 0 def get_seg_list(self, bi_list): seg_list = [] up_bi_list = [] @@ -639,6 +660,7 @@ class ChanLun(): #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) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: @@ -758,6 +780,7 @@ class ChanLun(): #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) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: @@ -1903,10 +1926,10 @@ class ChanLun(): bbox=dict(boxstyle="round,pad=0.2", fc=style['color'], alpha=0.5)) """ # 绘制MACD(使用小周期数据) - exp1 = small_df['close'].ewm(span=8, adjust=False).mean() - exp2 = small_df['close'].ewm(span=16, adjust=False).mean() + exp1 = small_df['close'].ewm(span=12, adjust=False).mean() + exp2 = small_df['close'].ewm(span=26, adjust=False).mean() macd = exp1 - exp2 - signal = macd.ewm(span=6, adjust=False).mean() + signal = macd.ewm(span=9, adjust=False).mean() histogram = macd - signal ax3.bar(small_dates_num, histogram, width=0.0002, color=['red' if h > 0 else 'green' for h in histogram]) diff --git a/ChanLun_Classifier.py b/ChanLun_Classifier.py index ab7fd23..c72c794 100644 --- a/ChanLun_Classifier.py +++ b/ChanLun_Classifier.py @@ -288,7 +288,6 @@ class ChanLunClassifier: # 使用ChanLun获取bi_list klc_list = self.chan.get_klc_list(dataframe) bi_list = self.chan.cal_bi_list(klc_list) - seg_list = self.chan.get_seg_list(bi_list) # 筛选方向为UP的bi的起始klc feature_data = [] labels = [] @@ -312,7 +311,6 @@ class ChanLunClassifier: # 保存第一个样本的特征名称,用于CSV列名 if len(feature_keys) == 0: feature_keys = list(features.keys()) - # 将特征转换为模型可用的格式 feature_vec = [] for key, value in features.items(): @@ -335,7 +333,8 @@ class ChanLunClassifier: feature_data.append(feature_vec) labels.append(label) - + for index, key in enumerate(feature_keys): + print(index, key, feature_data[0][index]) # 如果需要保存到CSV if save_csv: # 创建DataFrame保存特征数据 diff --git a/strategies/ChanLun_SOL_15.py b/strategies/ChanLun_SOL_15.py index 6aa6936..40a77f6 100644 --- a/strategies/ChanLun_SOL_15.py +++ b/strategies/ChanLun_SOL_15.py @@ -117,6 +117,8 @@ class ChanLun_SOL_15(IStrategy): #self.print_macd_div_list(dataframe) #self.print_resample_df(dataframe, 1, 50) #self.chan.get_bi_list(dataframe_30) + #self.chan.plot_dual(dataframe_5, dataframe_30) + self.chan.print_bi_klc(dataframe_5) #if self.last_time + timedelta(minutes=1) < datetime.now(): #print(informative.iloc[-1]) #self.print_klc(dataframe, "1m: ") @@ -279,10 +281,11 @@ class ChanLun_SOL_15(IStrategy): cn3 = 'resample_{}_state'.format(self.get_ticker_indicator()*time) logger.info(f'{df[cn1][index]}, {df[cn2][index]}, {df[cn3][index]}') def add_indicators(self, df): - fast = 9 - slow = 24 - period = 14 + fast = 8 + slow = 16 + period = 6 macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) + df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] df['macdhist'] = macd['macdhist'] @@ -299,7 +302,17 @@ class ChanLun_SOL_15(IStrategy): df['ma30'] = df['ma30'].fillna(0) df['ma250'] = df['ma250'].fillna(0) df['rsi'] = df['rsi'].fillna(0) + df['volume_ratio'] = self.cal_volume_ratio(df) return df + 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 populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ diff --git a/strategies/ChanLun_SOL_5.py b/strategies/ChanLun_SOL_5.py index eb569b7..00796ef 100644 --- a/strategies/ChanLun_SOL_5.py +++ b/strategies/ChanLun_SOL_5.py @@ -115,9 +115,9 @@ class ChanLun_SOL_5(IStrategy): self.classifier.train_model(dataframe, model_name="1m_model") self.classifier.train_model(dataframe_1d, model_name="1d_model") """ - """ - model_name = "30m_model" - df = dataframe_30 + + model_name = "5m_model" + df = dataframe_5 if self.classifier.model is None: #self.classifier.train_model(df, model_name=model_name, data_file_path=model_name + '_feature_data.csv') self.classifier.load_model(model_name=model_name) @@ -129,14 +129,14 @@ class ChanLun_SOL_5(IStrategy): bottom_count = 0 for index in range(int(len(klc_list) * 0.8), len(klc_list)): klc = klc_list[index] - if self.classifier.predict(klc) > 0.4 and (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2): + if self.classifier.predict(klc) > 0.37 and (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2): features = klc.get_feature_data() - print(klc.end_time, klc.fx, self.classifier.predict(klc), features['klc_macd'], features['klc_macd_hist'], features['klc_rsi'], features['klc_macd_signal']) + print(klc.end_time, klc.fx, self.classifier.predict(klc), features['klc_volume_ratio'], features['klc_macd_hist'], features['klc_rsi']) bottom_avg += self.classifier.predict(klc) bottom_count += 1 - if self.classifier.predict(klc) > 0.35 and (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2): + if self.classifier.predict(klc) > 0.37 and (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2): features = klc.get_feature_data() - print(klc.end_time, klc.fx, self.classifier.predict(klc), features['klc_macd'], features['klc_macd_hist'], features['klc_rsi'], features['klc_macd_signal']) + print(klc.end_time, klc.fx, self.classifier.predict(klc), features['klc_volume_ratio'], features['klc_macd_hist'], features['klc_rsi']) top_avg += self.classifier.predict(klc) top_count += 1 if bottom_count > 0: @@ -145,7 +145,7 @@ class ChanLun_SOL_5(IStrategy): top_avg /= top_count print(bottom_avg, top_avg) print("-------------------------------------------------------------------------------") - """ + """ self.print_xgb(dataframe, "1m_model") self.print_xgb(dataframe_5, "5m_model") @@ -350,9 +350,9 @@ class ChanLun_SOL_5(IStrategy): cn3 = 'resample_{}_state'.format(self.get_ticker_indicator()*time) logger.info(f'{df[cn1][index]}, {df[cn2][index]}, {df[cn3][index]}') def add_indicators(self, df): - fast = 9 - slow = 24 - period = 14 + fast = 8 + slow = 16 + period = 6 macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) df['macd'] = macd['macd'] df['macdsignal'] = macd['macdsignal'] @@ -362,7 +362,17 @@ class ChanLun_SOL_5(IStrategy): df['ma30'] = ta.EMA(df, timeperiod=30) df['ma250'] = ta.MA(df, timeperiod=250) df['rsi'] = ta.RSI(df, timeperiod=14) + df['volume_ratio'] = self.cal_volume_ratio(df) return df + 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 local_print(self, df): fast = 7 slow = 14 diff --git a/web/app.py b/web/app.py index 08e1cb5..3dcb67a 100644 --- a/web/app.py +++ b/web/app.py @@ -12,7 +12,7 @@ import base64 import time import traceback from pytz import timezone - +import talib.abstract as ta # 添加父目录到系统路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -142,6 +142,7 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): ret_df.loc[klc_index] = df_copy.loc[index] klc_index += 1 """ + df = add_indicators(df) # 如果过滤后没有数据,返回None if len(df) == 0: print("过滤后无数据") @@ -154,7 +155,35 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): print(f"获取数据错误: {e}") traceback.print_exc() return None +def add_indicators(df): + fast = 8 + slow = 16 + period = 6 + macd = ta.MACD(df, fastperiod=fast, slowperiod=slow, signalperiod=period) + df['macd'] = macd['macd'] + df['macdsignal'] = macd['macdsignal'] + df['macdhist'] = macd['macdhist'] + df['ma5'] = (ta.MA(df, timeperiod=5)).fillna(0) + df['ma10'] = (ta.MA(df, timeperiod=10)).fillna(0) + df['ma30'] = (ta.EMA(df, timeperiod=30)).fillna(0) + df['ma250'] = (ta.MA(df, timeperiod=250)).fillna(0) + df['rsi'] = ta.RSI(df, timeperiod=14) + df['macd'] = df['macd'].fillna(0) + df['macdsignal'] = df['macdsignal'].fillna(0) + df['macdhist'] = df['macdhist'].fillna(0) + df['ma5'] = df['ma5'].fillna(0) + df['ma10'] = df['ma10'].fillna(0) + df['ma30'] = df['ma30'].fillna(0) + df['ma250'] = df['ma250'].fillna(0) + df['rsi'] = df['rsi'].fillna(0) + df['avg_volume'] = df['volume'].rolling(10).mean() + # 计算量比 + df['volume_ratio'] = df['volume'] / df['avg_volume'] + # 填充缺失值(前N根K线) + df['volume_ratio'] = df['volume_ratio'].fillna(1.0) + df['avg_volume'] = df['avg_volume'].fillna(0) + return df def calculate_macd(df): """计算MACD指标""" exp1 = df['close'].ewm(span=12, adjust=False).mean() @@ -182,7 +211,11 @@ def analyze_chan(df): zs_list = chan.calculate_zs(bi_list, seg_list) # 添加买卖点识别 buy_sell_points = identify_trade_points(bi_list, seg_list, zs_list) - + for bi in bi_list: + bi.cal_macdhist() + for bi in bi_list: + bi.cal_macd_div() + #print(bi.start_time, bi.macd_hist, bi.macd_div) # 提取K线分型信息 klc_fx_info = [] for klc in klc_list: @@ -411,7 +444,8 @@ def analyze(): 'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None, 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high, 'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None, - 'direction': convert_direction(bi.dir) + 'direction': convert_direction(bi.dir), + 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 } for bi in analysis_result['bi_list'] if bi.end_klc], 'seg_list': [{ 'start_time': seg.start_bi.start_klc.end_time if isinstance(seg.start_bi.start_klc.end_time, str) else seg.start_bi.start_klc.end_time.astimezone(client_tz).isoformat(), @@ -470,7 +504,8 @@ def analyze(): 'end_time': (bi.end_klc.end_time if isinstance(bi.end_klc.end_time, str) else bi.end_klc.end_time.astimezone(client_tz).isoformat()) if bi.end_klc else None, 'start_price': bi.start_klc.low if convert_direction(bi.dir) == 1 else bi.start_klc.high, 'end_price': bi.end_klc.high if convert_direction(bi.dir) == 1 else bi.end_klc.low if bi.end_klc else None, - 'direction': convert_direction(bi.dir) + 'direction': convert_direction(bi.dir), + 'macd_div': float(bi.macd_div) if hasattr(bi, 'macd_div') else 0 } for bi in element_analysis['bi_list'] if bi.end_klc] # 添加小周期K线数据 diff --git a/web/templates/index.html b/web/templates/index.html index c1c7ab8..f22e0db 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -442,6 +442,7 @@ 起始价格 结束价格 方向 + MACD背离值 @@ -1553,6 +1554,39 @@ color: bi.direction === 1 ? '#dc3545' : '#28a745', lineWidth: 1 }); + + // 在笔的末端添加macd_div值标记 + if (bi.macd_div && bi.macd_div !== 0) { + console.log(`添加macd_div标记: ${bi.macd_div.toFixed(2)}, 在时间点: ${endTime}`); + + const macdDivLabel = mainChart.addLineSeries({ + lastValueVisible: false, + priceLineVisible: false, + }); + + // 确定标记位置 + const markerPosition = bi.direction === 1 ? 'aboveBar' : 'belowBar'; + const labelValue = bi.direction === 1 ? endPrice + (endPrice * 0.005) : endPrice - (endPrice * 0.005); + const arrayShape = bi.direction === 1 ? 'arrowDown' : 'arrowUp'; + // 简化标记显示 + macdDivLabel.setData([ + { time: endTime, value: labelValue } + ]); + + // 使用不同方式添加文本标记 + const textColor = bi.macd_div > 0 ? '#dc3545' : '#28a745'; + const textSize = Math.min(14, Math.max(10, Math.abs(bi.macd_div) * 2)); + + macdDivLabel.setMarkers([ + { + time: endTime, + position: markerPosition, + color: textColor, + shape: arrayShape, + text: bi.macd_div.toFixed(2), + } + ]); + } } catch (e) { console.error('主周期笔处理出错:', e); } @@ -1599,6 +1633,40 @@ color: bi.direction === 1 ? '#9c27b0' : '#673ab7', lineWidth: 1 }); + + // 在笔的末端添加macd_div值标记 + if (bi.macd_div && bi.macd_div !== 0) { + console.log(`添加元素周期macd_div标记: ${bi.macd_div.toFixed(2)}, 在时间点: ${endTime}`); + + const macdDivLabel = mainChart.addLineSeries({ + lastValueVisible: false, + priceLineVisible: false, + }); + + // 确定标记位置 + const markerPosition = bi.direction === 1 ? 'aboveBar' : 'belowBar'; + const labelValue = bi.direction === 1 ? endPrice + (endPrice * 0.005) : endPrice - (endPrice * 0.005); + const arrayShape = bi.direction === 1 ? 'arrowDown' : 'arrowUp'; + // 简化标记显示 + macdDivLabel.setData([ + { time: endTime, value: labelValue } + ]); + + // 使用不同方式添加文本标记 + const textColor = bi.macd_div > 0 ? '#9c27b0' : '#673ab7'; + const textSize = Math.min(14, Math.max(10, Math.abs(bi.macd_div) * 2)); + + macdDivLabel.setMarkers([ + { + time: endTime, + position: markerPosition, + color: textColor, + shape: arrayShape, + text: bi.macd_div.toFixed(2), + transparent: true, + } + ]); + } } catch (e) { console.error('次周期笔处理出错:', e); } @@ -3153,7 +3221,8 @@ { data: 'end_time', render: formatTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, - { data: 'direction', render: formatDirection } + { data: 'direction', render: formatDirection }, + { data: 'macd_div', render: formatMacdValue } ] }); @@ -3633,7 +3702,8 @@ { data: 'end_time', render: formatTime }, { data: 'start_price', render: formatPrice }, { data: 'end_price', render: formatPrice }, - { data: 'direction', render: formatDirection } + { data: 'direction', render: formatDirection }, + { data: 'macd_div', render: formatMacdValue } ] });