From b849ce8ec81f7970258444cce74a4123b571fdeb Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Thu, 24 Apr 2025 17:49:36 +0800 Subject: [PATCH] add changes --- ChanBI.py | 3 +- ChanEnum.py | 11 + ChanKLC.py | 13 +- ChanKLU.py | 9 + ChanLun.py | 113 +++-- ChanLun_Classifier.py | 22 +- __pycache__/ChanBI.cpython-312.pyc | Bin 6444 -> 6444 bytes __pycache__/ChanEnum.cpython-312.pyc | Bin 5877 -> 6292 bytes __pycache__/ChanKLC.cpython-312.pyc | Bin 19434 -> 19938 bytes __pycache__/ChanKLU.cpython-312.pyc | Bin 2134 -> 2593 bytes __pycache__/ChanLun.cpython-312.pyc | Bin 111772 -> 113893 bytes .../ChanLun_Classifier.cpython-312.pyc | Bin 17079 -> 18396 bytes web/app.py | 73 +--- web/templates/index.html | 406 ++++-------------- 14 files changed, 242 insertions(+), 408 deletions(-) diff --git a/ChanBI.py b/ChanBI.py index d8d3e78..34a70b8 100644 --- a/ChanBI.py +++ b/ChanBI.py @@ -53,6 +53,7 @@ class ChanBI(): 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 @@ -88,4 +89,4 @@ class ChanBI(): self.low = klc.low self.is_sure = False self.sure_time = None - #print(self.start_klc.start_time, klc.start_time, klc.fx, "This bi is extended") \ No newline at end of file + #print(self.start_time, klc.start_time, klc.fx, "This bi is extended") \ No newline at end of file diff --git a/ChanEnum.py b/ChanEnum.py index 93c2d78..1db70bb 100644 --- a/ChanEnum.py +++ b/ChanEnum.py @@ -40,6 +40,17 @@ class Chan_FX_TYPE(Enum): DOWN = auto() TT = auto() BB = auto() + PTOP = auto() + PBOTTOM = auto() + +class Chan_KLC_FX(Enum): + TOP1 = auto() + TOP2 = auto() + TOP3 = auto() + BOTTOM1 = auto() + BOTTOM2 = auto() + BOTTOM3 = auto() + UNKNOWN = auto() class Chan_BI_DIR(Enum): diff --git a/ChanKLC.py b/ChanKLC.py index 7c40a75..b70bc6c 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -1,7 +1,7 @@ import copy from typing import Dict, Optional -from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR +from ChanEnum import Chan_FX_TYPE, Chan_KLINE_DIR, Chan_BI_DIR, Chan_KLC_FX import ChanKLU import ChanCTime @@ -29,6 +29,10 @@ class ChanKLC(): self.bi_macdhist = 0 self.bi_macd_div = 0.0 self.bi = None + self.distance = 0 + self.klc_fx_type = Chan_KLC_FX.UNKNOWN + def set_klc_fx_type(self, klc_fx_type): + self.klc_fx_type = klc_fx_type def add_klu(self, klu): self.klus.append(klu) def set_end_klu(self, klu): @@ -123,6 +127,12 @@ class ChanKLC(): 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: @@ -164,6 +174,7 @@ class ChanKLC(): features['klc_pre_fx'] = (0 if self.pre.fx == Chan_FX_TYPE.UNKNOWN else 1 if self.pre.fx == Chan_FX_TYPE.TOP else 2) if self.pre else 0 #10 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 # New Add 20250422 #features['klc_macdhist'] = self.get_macdhist() #11 #features['klc_bi_macdhist'] = self.bi_macdhist #12 diff --git a/ChanKLU.py b/ChanKLU.py index 237dae5..681672c 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -21,6 +21,15 @@ class ChanKLU: def set_idx(self, idx): self.idx = idx self.index = idx + def set_indicators(self, dict): + self.macd = dict['macd'] + self.signal = dict['macdsignal'] + self.macdhist = dict['macdhist'] + self.ma5 = dict['ma5'] + self.ma10 = dict['ma10'] + self.ma30 = dict['ma30'] + self.ma250 = dict['ma250'] + self.rsi = dict['rsi'] def get_feature_data(self): features = dict() features['klu_close'] = self.close diff --git a/ChanLun.py b/ChanLun.py index dc29327..ad46946 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -1,6 +1,6 @@ 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 +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 from ChanKLU import ChanKLU from ChanKLC import ChanKLC from ChanBI import ChanBI @@ -88,6 +88,7 @@ class ChanLun(): df['macdhist'] = macd['macdhist'] return df def plot_dataframe(self, dataframe): + return self.get_klu_list(dataframe) klc_list = self.get_klc_list(dataframe) bi_list= self.cal_bi_list(klc_list) seg_list = self.get_seg_list(bi_list) @@ -529,7 +530,7 @@ class ChanLun(): fx = self.check_fx(klc) # Do nothing if fx == Chan_FX_TYPE.UNKNOWN: - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + klc.set_fx(fx) if len(bi_list) > 0: bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) @@ -541,16 +542,19 @@ class ChanLun(): 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_fx(Chan_FX_TYPE.TT) klc.set_state("20") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) - #print(klc.start_time, klc.fx, "二类买卖点Sell 1") + #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, "一类买卖点Sell 1") + 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.start_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]) @@ -558,8 +562,8 @@ class ChanLun(): # 不满足结合律的分型 if last_bottom.index + 4 > klc.index: if last_top.high > klc.high: - #print(klc.start_time, last_bottom.start_time, klc.fx, "中枢买卖点Sell 1") - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + #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 @@ -571,18 +575,21 @@ class ChanLun(): pre_last_bi.update_bi(klc) bi_list.remove(last_bi) pre_last_bi.set_next(None) - last_top.set_fx(Chan_FX_TYPE.UNKNOWN) + #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") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + klc.set_fx(Chan_FX_TYPE.PTOP) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) - #print(klc.start_time, last_bottom.start_time, klc.fx, "中枢买卖点Sell 2") + #print(klc.start_time, klc.fx, "无效分型") # 满足结合律 else: # New Temp TOP and last bottom confirmed ***** confirm last down bi(last bottom and last top) @@ -595,10 +602,13 @@ class ChanLun(): bi.add_klc(klc) bi_list.append(bi) last_top = klc + print(klc.start_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: @@ -606,29 +616,42 @@ class ChanLun(): 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") + #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.UNKNOWN) + #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) 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: @@ -637,16 +660,18 @@ class ChanLun(): 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_fx(Chan_FX_TYPE.BB) klc.set_state("-20") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) - #print(klc.start_time, klc.fx, "二类买卖点Buy 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, "一类买卖点Buy 1") + 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]) @@ -654,10 +679,10 @@ class ChanLun(): # 不满足结合律的分型 if last_top.index + 4 > klc.index: if last_bottom.low < klc.low: - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + #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") + #print(klc.start_time, klc.fx, "中枢买点Buy 1") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) # Found new bottom @@ -669,18 +694,21 @@ class ChanLun(): pre_last_bi.update_bi(klc) bi_list.remove(last_bi) pre_last_bi.set_next(None) - last_bottom.set_fx(Chan_FX_TYPE.UNKNOWN) + #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") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + #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, "中枢买卖点Buy 2") + #print(klc.start_time, klc.fx, "无效分型") # 满足结合律的分型 else: # New Temp Bottom and last top confirmed ***** confirm last up bi(last bottom and last top) @@ -693,9 +721,12 @@ class ChanLun(): 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: @@ -704,12 +735,14 @@ class ChanLun(): 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") + #print(klc.start_time, klc.fx, "二类买点Buy 2") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) # last_bottom = None @@ -717,14 +750,17 @@ class ChanLun(): if last_top: # 不满足结合律的分型 if last_top.index + 4 > klc.index: - klc.set_fx(Chan_FX_TYPE.UNKNOWN) + #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 @@ -733,8 +769,11 @@ class ChanLun(): bi_list.append(bi) bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) - #print(klc.start_time, 'Create first bottom') - #print(klc.time, klc.fx, klc.state) + 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" @@ -742,6 +781,7 @@ class ChanLun(): if klc.fx == Chan_FX_TYPE.BOTTOM: klc.state = "-10" #print(klc.time, klc.state) + """ return bi_list def get_zs_list(self, bi_list, seg_list): @@ -997,7 +1037,20 @@ class ChanLun(): def get_decimal(self, value): return Decimal("{:.2f}".format(value)) - + def add_indicators(self, 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) + df['ma10'] = ta.MA(df, timeperiod=10) + df['ma30'] = ta.EMA(df, timeperiod=30) + df['ma250'] = ta.MA(df, timeperiod=250) + df['rsi'] = ta.RSI(df, timeperiod=14) + return df def get_klc_list(self, dataframe): klu_list = self.get_klu_list(dataframe) klc_list = [] @@ -1075,14 +1128,8 @@ class ChanLun(): klu = ChanKLU(time_str, o, h, l, c, v) klu.set_idx(i) klu_list.append(klu) - klu.macd = item['macd'] - klu.signal = item['macdsignal'] - klu.macdhist = item['macdhist'] - klu.ma5 = item['ma5'] - klu.ma10 = item['ma10'] - klu.ma30 = item['ma30'] - klu.ma250 = item['ma250'] - klu.rsi = item['rsi'] + if True: + klu.set_indicators(item) return klu_list def get_bsp_list1(self, big_df): diff --git a/ChanLun_Classifier.py b/ChanLun_Classifier.py index 9261dcf..e088d5b 100644 --- a/ChanLun_Classifier.py +++ b/ChanLun_Classifier.py @@ -289,6 +289,13 @@ class ChanLunClassifier: bi_index = 0 sample_list = [] for klc in klc_list: + if klc.pre and klc.next: + if klc.high > klc.pre.high and klc.high > klc.next.high: + klc.set_fx(Chan_FX_TYPE.TOP) + elif klc.low < klc.pre.low and klc.low < klc.next.low: + klc.set_fx(Chan_FX_TYPE.BOTTOM) + else: + klc.set_fx(Chan_FX_TYPE.UNKNOWN) if klc.fx != Chan_FX_TYPE.UNKNOWN: sample_list.append(klc) for klc in sample_list: @@ -311,8 +318,8 @@ class ChanLunClassifier: # 判断这个bi是否赚钱(这里简单定义为:如果bi的结束价格高于起始价格,则标记为1,否则为0) # 这个标签定义可以根据实际需求修改 - seg = seg_list[bi_index] - if bi.start_klc.index == klc.index and bi.dir == Chan_BI_DIR.UP: + bi = bi_list[bi_index] + if bi.start_klc.index == klc.index: #if klc.index == seg.start_bi.start_klc.index and seg.dir == Chan_SEG_DIR.UP: label = 1 bi_index += 2 @@ -339,6 +346,13 @@ class ChanLunClassifier: bi_index = 0 sample_list = [] for klc in klc_list: + if klc.pre and klc.next: + if klc.high > klc.pre.high and klc.high > klc.next.high: + klc.set_fx(Chan_FX_TYPE.TOP) + elif klc.low < klc.pre.low and klc.low < klc.next.low: + klc.set_fx(Chan_FX_TYPE.BOTTOM) + else: + klc.set_fx(Chan_FX_TYPE.UNKNOWN) if klc.fx != Chan_FX_TYPE.UNKNOWN: sample_list.append(klc) for klc in sample_list: @@ -357,7 +371,7 @@ class ChanLunClassifier: else: feature_vec.append(0) seg = seg_list[bi_index] - if bi.start_klc.index == klc.index and bi.dir == Chan_BI_DIR.UP: + if bi.start_klc.index == klc.index: #if klc.index == seg.start_bi.start_klc.index and seg.dir == Chan_SEG_DIR.UP: label = 1 bi_index += 2 @@ -449,5 +463,5 @@ class ChanLunClassifier: return self.get_decimal(self.model.predict(dtest)[0]) def get_decimal(self, value): - return Decimal("{:.2f}".format(value)) + return Decimal("{:.4f}".format(value)) diff --git a/__pycache__/ChanBI.cpython-312.pyc b/__pycache__/ChanBI.cpython-312.pyc index 8b5d9cb6f44f82d612da3c3008529b74c2bf1eab..8e5f2e84e5b7d846817d96b62efcd9828410d290 100644 GIT binary patch delta 109 zcmZ2uw8n_fTQ&d3X7McSaWm=J;_LLfy6BFa`0NF?kg zl>RX6C(Vf1iC8R<{&I z;^^n|&*F(Sc}1<@{rpPZr%IlSbLl{qOP7t4jUtTTxFKa+NCyoFkoSuP)^ZkGM`O(m z{vfhqykauq=|WO#Q{i_{Q)~18*4srxRrB@cnLTuwyenLpp1tb|qq7h0nPl@K|41Oq za9qg2d9jQr@I?#4kXWm^LSKKX&lUSRQ~mv^UU0-}av5HTP33myj;!UH?XDfQG5;3~Vk6yyNmIs*jRMUTc99Xrs4IGT!+8TEAZ+um4 zjWlZ}N|ELA^$yAwRJY@-iNl){hh5(>cqpBN8s7<{7289!pa>yY$E~#E1g!f`8f|Dp zF!3*<4ezx1$LAwbJBA=SK$8-r9R{VRW)B(>y>$KZQ_x-|HFw&Mv6h`KuTl?tn+AEf zvYW=ZeE7ZZFh-o}>a2Z9< z4cOg>20c$tJT?|E8j(SP_xJKGu)aF|bL0}d_18%bdJuE)!{0z0>Q2Cz-YzYn1F;CN z^=h)j&VALtkww@ly(2B78mJh6s&1uugUX@g2aSEdSfgB_t_5`5CM T&(Uo^BvO59pQ9VphCcoUQ7-XP delta 783 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 P|1qCbnESCrR4Duxiwmw& diff --git a/__pycache__/ChanKLU.cpython-312.pyc b/__pycache__/ChanKLU.cpython-312.pyc index 2fe6c5eeea93195ddfb8a26d27e1e914f29ba38a..c2110842936c6f61a3d6e42786d9bb7d7c33130a 100644 GIT binary patch delta 574 zcmca6uuz2WG%qg~0}#yp%bDTIHj&SVQDtJZJXZ=!3qurJC95Xe#=1j{BIf#`#i>Qb z`dNv|+4=!R`B|ySC7WZJ5*a6}GK`*~AxFBN+2ULt5CRWLy$>~?b59HqB%1um8 zDb7sKOU#)Z!zRzjF*%6Mpq?cqGr6P~zT(u9_{=<@cw$L@QE`=+b4FsG zw@;`ZsyIk(aSV`XV7MV7F+*&kPX|j6=M6Eb88#QiG=OaG8xk@zqAp5+*t|D{#b>Zi zv<5OckX4JwBXo*N&QPBi3sk`cSKQ07c?WYD6Kjz?(4NUAZ2gRilaH`HWMrSbj9s5i z0Vr5xIQbg8T)G&L*~0LNNr+YSlNuAN-`R{76D0<026QXo~J0QS>l1k**PAQ7-I q$Pd3bY;yBcN^?@}ip+pqP?Qye!s`PwBO~K?CId!^PYgf;tN{SogMFO< delta 173 zcmZ1|a!r8mG%qg~0}xDk$(q5T#889 P66RHBl=#E|B*01l5TGu> diff --git a/__pycache__/ChanLun.cpython-312.pyc b/__pycache__/ChanLun.cpython-312.pyc index f3adc553ba1132453b94ada9db0b223d5d232ec4..2b45e4db3986f87c24a34fb8b023821f57f85098 100644 GIT binary patch delta 12629 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 delta 11624 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 diff --git a/__pycache__/ChanLun_Classifier.cpython-312.pyc b/__pycache__/ChanLun_Classifier.cpython-312.pyc index 2e4b553afda354341a7ee0593089a2ab79544afa..e364a86f2e7942d82df37ae221bcfa922e004837 100644 GIT binary patch delta 2273 zcmeH|Z%kWN6u{r@UtSCCV|@imTg%F5X}j_$#aRLj0yD-buow(qMwG1ls|?udlM?qj zvX~{CkN~?M0K=?_UnGk$hL{cK#J@AyG};QIc0`$3^n=lm&BQMY@t&4v!EK-XWa9hq zyXW3}?m6e4_s)5j&Y<;4q`Q%qr=iGmU8U)(fBHS$&!w_W6~65+#>~EieF}9Rpr|M< z(xDvC+Wqix&JL?IV<(T2QTis#F$^dXAko26}(*2g(zMm4OLkn@dJRwgib z&|zUNqKth+4W1ziMKxkN^zD1dX%gg?(`?j>>Y)6UJfff;lm$^#FDkcldCN+x5GAU}G)}X7 zv+snY_hkhoJRvy`DnxluX~J)43h{h7jfzRU2BSQ)jVPg3QVi9lZ@5`Y=RUC5;?vr+&@;*_iLI2pz3oU!J}?wXshZl_+uK@F z>gQXZX>IFhP04yjajrXvPr0o!7lSXjPic4;ITL0Gu*cJk-oZnjp0Ra<`D}Aizh^S? zT4dHA3%njk8VY6t(}CGzvEI4f*udOC!r*>8{O;(&=;hMo@}=@+?~*scJ(Xk&XAVyv zo)_0_T-?U3vXx0=(M-p5$80zjnTy0u&Yesc;b3GTa@nw4v{bZgS+XR!CwCmIiXXI9 zt+M`&tSx`s=3il}A)=9UBlDIulQ(YiuIRl<#=OSZ7zNZr3_xV&U6Hs&7V@7HtQO2vy_98{Fs)ezC!h;`r5uX7mgG zvf-%qCol`=`8&9x@rbrY8MrGTzcO&I51(uFplgfoH=aOf3p<-m>qk_GyGsFi_}2dd Dh>;)! delta 1330 zcma)+Ur19?9LLYOZg;zz&duFfI+y(B^=K3EV*L>c*HVGXS)ik^Ci3ZfpO^K)Dr66AUK+}}C(`#a~{-|xG< z>7}FRscj-B$3obc+-N#Dw0_w3c_~+Hgod?+U@v>Yw=5^*lmMafd``7MZMh_@B&3rj zOEojBm5M`@BxM89%yDv=w)CP_GpP>T4~NDL(sb0`Oo(Dr9dOp`U6hwz%cRnyS)eW~ z8k94d0FS~VjPNdMfl^t7z6zX)u*N4?Gg4uYmzaVj!PD|tg}J?uo#J2{guE^o_t|I( zbnu1n)hEzW7D=mW32EloEptxU)TR3QnoTX!W3@t$+PFcc#G&MA+3aIoaG=r-b&?ah zD;>~ZlMNo($u5=;R2KPm9}||tA1^DEABJj*;6shX)FRACf`Jl^$1S{ey&#koEZJuu zYIXV$LRt-4R;#gqO%oy5Cpe+QZ-)KLop8}_waU6GQG@SXMtvW3-W%vxEx;%LJ?e(* zRUrr0c1$s{+-Xx&!={EUI~qU;wA!Y#S`R6T3D>!%MmQXhxB&+w1E=A5qyqfGAgzL( z!M4DR)7>3Q<>vKvUFf=2^tvE8SrB}g8%){VukD^myXU4nW%qtH60zt94=0!Xf#cDV z+`Hk?$Z+Jrp0U06_r8!fL4S1*cOz?|j#@V7sFt4#^AlyIN_QE;+3q!Q(vsQcMSA5} zn-}Y`%8Zg8|8KLaNY~Ci6UDGk7CH8)lbzCR>orSa$(%rsR1akP{_TZ=3@;>qF(-KS zq&}Z0YI7aVT!$m8EdVRr)CEH;3b$fwm4}EiL;`UbaTM_caR~7g@fhJpj3co9$}>a{ z;yGdh&PEzd>)DUC^Ad$!!99_bQ|vaU@)vho7jA~{XogDgM7!pjVWdBL!)TqsquwCi zLS5}P+Ij0rt$|L%)paFh^X@$RSqe%!5$T&kYMo?#iSsGMtG-x_4vfTOE=t3&JMQP- zv*i;Kgo62SGj5`f;dVSmKf;%Iv-KTj3?Saa_WDL^*ckoRMgzv^_uX)(zKV{GOxGW! V^fzqYa4z>CPpiKXq=Nqoe*tbvIl}+| diff --git a/web/app.py b/web/app.py index af86b4a..540de34 100644 --- a/web/app.py +++ b/web/app.py @@ -128,14 +128,25 @@ def get_kl_data(symbol, timeframe, limit=1000, start_time=None, end_time=None): # 按时间排序 df = df.sort_values('timestamp') - + chan = ChanLun() + klc_list = chan.get_klc_list(df) + klc_index = 0 + df_copy = df.copy() + ret_df = pd.DataFrame(columns=['timestamp', 'open', 'high', 'low', 'close', 'volume', 'date']) + for index in range(0, len(df)): + if klc_index >= len(klc_list): + klc_index = len(klc_list) - 1 + klc = klc_list[klc_index] + if index == klc.start_klu.index: + ret_df.loc[klc_index] = df_copy.loc[index] + klc_index += 1 # 如果过滤后没有数据,返回None - if len(df) == 0: + if len(ret_df) == 0: print("过滤后无数据") return None - print(f"获取到总共 {len(df)} 条数据") - return df + print(f"获取到总共 {len(ret_df)} 条数据") + return ret_df except Exception as e: print(f"获取数据错误: {e}") @@ -166,9 +177,6 @@ def analyze_chan(df): seg_list = chan.get_seg_list(bi_list) zs_list = chan.calculate_zs(bi_list, seg_list) - # 获取笔中枢列表 - bi_zs_list = chan.get_bi_zs_list(bi_list) - # 添加买卖点识别 buy_sell_points = identify_trade_points(bi_list, seg_list, zs_list) @@ -177,7 +185,6 @@ def analyze_chan(df): 'bi_list': bi_list, 'seg_list': seg_list, 'zs_list': zs_list, - 'bi_zs_list': bi_zs_list, # 添加笔中枢数据 'trade_points': buy_sell_points } @@ -403,36 +410,16 @@ def analyze(): 'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None, 'zg': zs.zg, 'zd': zs.zd, - 'is_sure': zs.is_sure, # 添加中枢是否完成的标志 - 'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢 + 'is_sure': zs.is_sure # 添加中枢是否完成的标志 } for zs in analysis_result['zs_list'] if zs.end_klc], - # 添加笔中枢列表 - 'bi_zs_list': [{ - 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), - 'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None, - 'zg': zs.zg, - 'zd': zs.zd, - 'is_sure': zs.is_sure, # 添加中枢是否完成的标志 - 'type': 'BI_ZS' # 标记为笔中枢 - } for zs in analysis_result['bi_zs_list'] if zs.end_klc], # 添加未完成中枢列表 'uncompleted_zs_list': [{ 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), 'end_time': None, # 未完成中枢没有结束时间 'zg': zs.zg, 'zd': zs.zd, - 'is_sure': zs.is_sure, # 未完成中枢的is_sure为False - 'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢 + 'is_sure': zs.is_sure # 未完成中枢的is_sure为False } for zs in analysis_result['zs_list'] if not zs.is_sure], - # 添加未完成笔中枢列表 - 'uncompleted_bi_zs_list': [{ - 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), - 'end_time': None, # 未完成中枢没有结束时间 - 'zg': zs.zg, - 'zd': zs.zd, - 'is_sure': zs.is_sure, # 未完成中枢的is_sure为False - 'type': 'BI_ZS' # 标记为笔中枢 - } for zs in analysis_result['bi_zs_list'] if not zs.is_sure], 'trade_points': [{ 'type': point['type'], 'time': format_time_safely(point['time'], client_tz), @@ -477,40 +464,18 @@ def analyze(): 'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None, 'zg': zs.zg, 'zd': zs.zd, - 'is_sure': zs.is_sure, # 添加中枢是否完成的标志 - 'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢 + 'is_sure': zs.is_sure # 添加中枢是否完成的标志 } for zs in element_analysis['zs_list'] if zs.end_klc] - # 添加小周期笔中枢 - result['element_bi_zs_list'] = [{ - 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), - 'end_time': (zs.end_klc.end_time if isinstance(zs.end_klc.end_time, str) else zs.end_klc.end_time.astimezone(client_tz).isoformat()) if zs.end_klc else None, - 'zg': zs.zg, - 'zd': zs.zd, - 'is_sure': zs.is_sure, # 添加中枢是否完成的标志 - 'type': 'BI_ZS' # 标记为笔中枢 - } for zs in element_analysis['bi_zs_list'] if zs.end_klc] - # 添加小周期未完成中枢列表 result['element_uncompleted_zs_list'] = [{ 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), 'end_time': None, # 未完成中枢没有结束时间 'zg': zs.zg, 'zd': zs.zd, - 'is_sure': zs.is_sure, # 未完成中枢的is_sure为False - 'type': getattr(zs, 'type', 'SEG_ZS') # 中枢类型,默认为线段中枢 + 'is_sure': zs.is_sure # 未完成中枢的is_sure为False } for zs in element_analysis['zs_list'] if not zs.is_sure] - # 添加小周期未完成笔中枢列表 - result['element_uncompleted_bi_zs_list'] = [{ - 'start_time': zs.start_klc.start_time if isinstance(zs.start_klc.start_time, str) else zs.start_klc.start_time.astimezone(client_tz).isoformat(), - 'end_time': None, # 未完成中枢没有结束时间 - 'zg': zs.zg, - 'zd': zs.zd, - 'is_sure': zs.is_sure, # 未完成中枢的is_sure为False - 'type': 'BI_ZS' # 标记为笔中枢 - } for zs in element_analysis['bi_zs_list'] if not zs.is_sure] - result['element_trade_points'] = [{ 'type': point['type'], 'time': format_time_safely(point['time'], client_tz), diff --git a/web/templates/index.html b/web/templates/index.html index cb7eb05..d6a648d 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -196,16 +196,6 @@ font-weight: bold; } - .main-bi-zs { - color: #FF9800; - font-weight: bold; - } - - .element-bi-zs { - color: #9C27B0; - font-weight: bold; - } - /* 加载指示器旋转动画 */ @keyframes spin { 0% { transform: rotate(0deg); } @@ -308,14 +298,6 @@ -
- - -
-
- - -
@@ -335,14 +317,6 @@
-
- - -
-
- - -
@@ -542,14 +516,10 @@ mainSegSeries: [], mainZsSeries: [], mainUncompletedZsSeries: [], - mainBiZsSeries: [], // 添加主周期笔中枢系列 - mainUncompletedBiZsSeries: [], // 添加未完成主周期笔中枢系列 elementBiSeries: [], elementSegSeries: [], elementZsSeries: [], elementUncompletedZsSeries: [], - elementBiZsSeries: [], // 添加元素周期笔中枢系列 - elementUncompletedBiZsSeries: [], // 添加未完成元素周期笔中枢系列 tradePointSeries: [] }, state: { @@ -735,16 +705,6 @@ updateChartDisplay(); }); - // 添加笔中枢复选框变更事件 - $('#showMainBiZs').change(function() { - updateChartDisplay(); - }); - - // 添加未完成笔中枢复选框变更事件 - $('#showMainUncompletedBiZs').change(function() { - updateChartDisplay(); - }); - // 添加买卖点复选框变更事件 $('#showElementBi').change(function() { updateChartDisplay(); @@ -765,16 +725,6 @@ updateChartDisplay(); }); - // 添加笔中枢复选框变更事件 - $('#showElementBiZs').change(function() { - updateChartDisplay(); - }); - - // 添加未完成笔中枢复选框变更事件 - $('#showElementUncompletedBiZs').change(function() { - updateChartDisplay(); - }); - // 添加买卖点复选框变更事件 $('#showTradePoints').change(function() { updateChartDisplay(); @@ -946,39 +896,51 @@ }); function updateChartDisplay() { - // 获取复选框状态 - var showMainBi = $('#showMainBi').prop('checked'); - var showMainSeg = $('#showMainSeg').prop('checked'); - var showMainZs = $('#showMainZs').prop('checked'); - var showMainUncompletedZs = $('#showMainUncompletedZs').prop('checked'); - var showMainBiZs = $('#showMainBiZs').prop('checked'); - var showMainUncompletedBiZs = $('#showMainUncompletedBiZs').prop('checked'); - var showElementBi = $('#showElementBi').prop('checked'); - var showElementSeg = $('#showElementSeg').prop('checked'); - var showElementZs = $('#showElementZs').prop('checked'); - var showElementUncompletedZs = $('#showElementUncompletedZs').prop('checked'); - var showElementBiZs = $('#showElementBiZs').prop('checked'); - var showElementUncompletedBiZs = $('#showElementUncompletedBiZs').prop('checked'); - var showTradePoints = $('#showTradePoints').prop('checked'); - - // 调用显示/隐藏函数 - if (showMainBi) { drawBi(main_bi_list); } else { removeBi(); } - if (showMainSeg) { drawSeg(main_seg_list); } else { removeSeg(); } - if (showMainZs) { drawZones(main_zs_list); } else { removeZones(); } - if (showMainUncompletedZs) { drawUncompletedZones(main_uncompleted_zs_list); } else { removeUncompletedZones(); } - if (showMainBiZs) { drawBiZones(main_bi_zs_list); } else { removeBiZones(); } - if (showMainUncompletedBiZs) { drawUncompletedBiZones(main_uncompleted_bi_zs_list); } else { removeUncompletedBiZones(); } - - // 元素显示 - if (showElementBi) { drawBiOnElement(element_bi_list); } else { removeBiOnElement(); } - if (showElementSeg) { drawSegOnElement(element_seg_list); } else { removeSegOnElement(); } - if (showElementZs) { drawZonesOnElement(element_zs_list); } else { removeZonesOnElement(); } - if (showElementUncompletedZs) { drawUncompletedZonesOnElement(element_uncompleted_zs_list); } else { removeUncompletedZonesOnElement(); } - if (showElementBiZs) { drawBiZonesOnElement(element_bi_zs_list); } else { removeBiZonesOnElement(); } - if (showElementUncompletedBiZs) { drawUncompletedBiZonesOnElement(element_uncompleted_bi_zs_list); } else { removeUncompletedBiZonesOnElement(); } - - // 买卖点 - if (showTradePoints) { drawTradePoints(trade_points_list); } else { removeTradePoints(); } + if (currentData) { + // 保存当前的可见逻辑范围 + let logicalRange = null; + let visibleRange = null; + + if (tvWidget && tvWidget.mainChart) { + // 优先使用逻辑范围,这样在缩放级别上更准确 + logicalRange = tvWidget.mainChart.timeScale().getVisibleLogicalRange(); + // 备用方案,获取实际时间戳范围 + visibleRange = tvWidget.mainChart.timeScale().getVisibleRange(); + } + + console.log('更新图表显示'); + console.log('- 显示K线:', $('#showOriginalKline').is(':checked')); + console.log('- 显示笔:', $('#showMainBi').is(':checked')); + console.log('- 显示线段:', $('#showMainSeg').is(':checked')); + console.log('- 显示中枢:', $('#showMainZs').is(':checked')); + console.log('- 显示未完成中枢:', $('#showMainUncompletedZs').is(':checked')); + console.log('- 显示MACD:', $('#showMacd').is(':checked')); + console.log('- 显示买卖点:', $('#showTradePoints').is(':checked')); + + // 重新初始化图表,这将清除旧图形并重新绘制 + initTradingView($('#symbol').val(), $('#timeframe').val()); + + // 如果有保存的范围,恢复它 + setTimeout(() => { + if (tvWidget) { + if (logicalRange) { + // 应用保存的逻辑范围到所有图表 + tvWidget.mainChart.timeScale().setVisibleLogicalRange(logicalRange); + tvWidget.volumeChart.timeScale().setVisibleLogicalRange(logicalRange); + if (tvWidget.macdChart) { + tvWidget.macdChart.timeScale().setVisibleLogicalRange(logicalRange); + } + } else if (visibleRange) { + // 如果没有逻辑范围,使用时间戳范围 + tvWidget.mainChart.timeScale().setVisibleRange(visibleRange); + tvWidget.volumeChart.timeScale().setVisibleRange(visibleRange); + if (tvWidget.macdChart) { + tvWidget.macdChart.timeScale().setVisibleRange(visibleRange); + } + } + } + }, 100); + } } // 确保所有时间处理都使用UTC时间,包括表格数据显示 @@ -1076,8 +1038,8 @@ $('#refreshLoadingSpinner').hide(); // 保存当前数据 - currentData = data; - + currentData = data; + // 刷新图表 refreshChart(data); }, @@ -1116,14 +1078,10 @@ mainSegSeries: [], mainZsSeries: [], mainUncompletedZsSeries: [], - mainBiZsSeries: [], // 添加主周期笔中枢系列 - mainUncompletedBiZsSeries: [], // 添加未完成主周期笔中枢系列 elementBiSeries: [], elementSegSeries: [], elementZsSeries: [], elementUncompletedZsSeries: [], - elementBiZsSeries: [], // 添加元素周期笔中枢系列 - elementUncompletedBiZsSeries: [], // 添加未完成元素周期笔中枢系列 tradePointSeries: [] }, state: { @@ -1348,22 +1306,22 @@ if (currentData.macd && currentData.macd.macd && currentData.macd.macd[i] !== undefined) { macdData.push({ - time: timestamp, + time: timestamp, value: currentData.macd.macd[i] - }); - + }); + signalData.push({ - time: timestamp, + time: timestamp, value: currentData.macd.signal[i] }); // 设置直方图颜色 const histValue = currentData.macd.histogram[i]; histogramData.push({ - time: timestamp, + time: timestamp, value: histValue, color: histValue >= 0 ? 'rgba(220, 53, 69, 0.5)' : 'rgba(40, 167, 69, 0.5)' - }); + }); } } @@ -2624,7 +2582,7 @@ // 添加事件监听 addChartSyncEvents(mainChartContainer, mainChart); addChartSyncEvents(volumeChartContainer, volumeChart); - if (showMacd && macdChart) { + if (showMacd && macdChart) { addChartSyncEvents(macdChartContainer, macdChart); } @@ -2714,7 +2672,7 @@ if (isNaN(timeValue.getTime())) { console.error(`买卖点 #${i} 时间格式无效:`, tradePointsData[i].time); } - } catch (e) { + } catch (e) { console.error(`买卖点 #${i} 时间格式异常:`, e); } } else { @@ -3366,14 +3324,48 @@ } console.log('重绘分形元素 - 开始'); + console.log('- 显示笔:', $('#showMainBi').is(':checked')); + console.log('- 显示线段:', $('#showMainSeg').is(':checked')); + console.log('- 显示中枢:', $('#showMainZs').is(':checked')); // 保存当前的图表可见范围 const mainChart = tvWidget.mainChart; const visibleRange = mainChart.timeScale().getVisibleRange(); const logicalRange = mainChart.timeScale().getVisibleLogicalRange(); - // 调用显示所有元素函数 - displayAllElements(); + // 获取当前图表设置 + const showMacd = $('#showMacd').is(':checked'); + const showOriginalKline = $('#showOriginalKline').is(':checked'); + const showBi = $('#showMainBi').is(':checked'); + const showSeg = $('#showMainSeg').is(':checked'); + const showZs = $('#showMainZs').is(':checked'); + + // 保存原始K线和MACD数据,确保即使使用小级别,我们依然使用主周期的K线和MACD数据 + const originalKlineData = currentData.kline_data; + const originalMacdData = currentData.macd; + + // 重新初始化图表 - 这将清除所有系列并重新创建 + console.log('重新初始化图表...'); + + // 临时存储currentData中可能被修改的字段 + const tempCurrentData = { + kline_data: originalKlineData, + macd: originalMacdData + }; + + // 在重绘前确保K线和MACD数据不变 + if (currentData.original_kline_data && currentData.original_macd) { + // 如果已经保存了原始数据,恢复它们 + currentData.kline_data = currentData.original_kline_data; + currentData.macd = currentData.original_macd; + } else { + // 首次运行 - 保存原始数据 + currentData.original_kline_data = originalKlineData; + currentData.original_macd = originalMacdData; + } + + // 调用初始化函数 + initTradingView($('#symbol').val(), $('#timeframe').val()); // 恢复原始可见范围 setTimeout(() => { @@ -3486,14 +3478,12 @@ console.log('API返回数据:', data); console.log('K线数据点数:', data.kline_data ? data.kline_data.length : 0); - console.log('笔中枢数据点数:', data.bi_zs_list ? data.bi_zs_list.length : 0); // 检查是否包含小周期数据 if (data.element_timeframe) { console.log('小周期笔数据点数:', data.element_bi_list ? data.element_bi_list.length : 0); console.log('小周期线段数据点数:', data.element_seg_list ? data.element_seg_list.length : 0); console.log('小周期中枢数据点数:', data.element_zs_list ? data.element_zs_list.length : 0); - console.log('小周期笔中枢数据点数:', data.element_bi_zs_list ? data.element_bi_zs_list.length : 0); // 更新小周期选择器 $('#elementTimeframe').val(data.element_timeframe); @@ -3517,220 +3507,6 @@ // 更新表格数据 updateTables(data); } - - // 显示中枢 - function displayZones() { - // 主周期或元素周期中枢 - if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || - $('#showMainUncompletedZs').is(':checked') || $('#showElementUncompletedZs').is(':checked') || - $('#showMainBiZs').is(':checked') || $('#showElementBiZs').is(':checked') || - $('#showMainUncompletedBiZs').is(':checked') || $('#showElementUncompletedBiZs').is(':checked')) { - - console.log('绘制中枢 - 已启用'); - - // 绘制主周期线段中枢 - if ($('#showMainZs').is(':checked') && currentData.zs_list && currentData.zs_list.length > 0) { - console.log(`绘制主周期线段中枢数据,共${currentData.zs_list.length}条`); - drawZoneList(currentData.zs_list, '#F1C40F', 'SEG_ZS'); - } - - // 绘制主周期笔中枢 - if ($('#showMainBiZs').is(':checked') && currentData.bi_zs_list && currentData.bi_zs_list.length > 0) { - console.log(`绘制主周期笔中枢数据,共${currentData.bi_zs_list.length}条`); - // 添加检查笔中枢数据的详细日志 - if (currentData.bi_zs_list.length > 0) { - console.log('笔中枢数据示例:', currentData.bi_zs_list[0]); - } - drawZoneList(currentData.bi_zs_list, '#FF9800', 'BI_ZS'); - } else { - console.log('笔中枢数据不存在或勾选框未选中:', - '勾选状态=', $('#showMainBiZs').is(':checked'), - '数据存在=', !!(currentData.bi_zs_list), - '数据长度=', currentData.bi_zs_list ? currentData.bi_zs_list.length : 0); - } - - // 元素周期线段中枢 - if ($('#showElementZs').is(':checked') && currentData.element_zs_list && currentData.element_zs_list.length > 0) { - console.log(`绘制元素周期线段中枢数据,共${currentData.element_zs_list.length}条`); - drawZoneList(currentData.element_zs_list, '#3f51b5', 'SEG_ZS'); - } - - // 元素周期笔中枢 - if ($('#showElementBiZs').is(':checked') && currentData.element_bi_zs_list && currentData.element_bi_zs_list.length > 0) { - console.log(`绘制元素周期笔中枢数据,共${currentData.element_bi_zs_list.length}条`); - if (currentData.element_bi_zs_list.length > 0) { - console.log('元素笔中枢数据示例:', currentData.element_bi_zs_list[0]); - } - drawZoneList(currentData.element_bi_zs_list, '#9C27B0', 'BI_ZS'); - } else { - console.log('元素笔中枢数据不存在或勾选框未选中:', - '勾选状态=', $('#showElementBiZs').is(':checked'), - '数据存在=', !!(currentData.element_bi_zs_list), - '数据长度=', currentData.element_bi_zs_list ? currentData.element_bi_zs_list.length : 0); - } - - // 主周期未完成线段中枢 - if ($('#showMainUncompletedZs').is(':checked') && currentData.uncompleted_zs_list && currentData.uncompleted_zs_list.length > 0) { - console.log(`绘制主周期未完成线段中枢数据,共${currentData.uncompleted_zs_list.length}条`); - drawZoneList(currentData.uncompleted_zs_list, '#F1C40F', 'SEG_ZS', true); - } - - // 主周期未完成笔中枢 - if ($('#showMainUncompletedBiZs').is(':checked') && currentData.uncompleted_bi_zs_list && currentData.uncompleted_bi_zs_list.length > 0) { - console.log(`绘制主周期未完成笔中枢数据,共${currentData.uncompleted_bi_zs_list.length}条`); - drawZoneList(currentData.uncompleted_bi_zs_list, '#FF9800', 'BI_ZS', true); - } - - // 元素周期未完成线段中枢 - if ($('#showElementUncompletedZs').is(':checked') && currentData.element_uncompleted_zs_list && currentData.element_uncompleted_zs_list.length > 0) { - console.log(`绘制元素周期未完成线段中枢数据,共${currentData.element_uncompleted_zs_list.length}条`); - drawZoneList(currentData.element_uncompleted_zs_list, '#3f51b5', 'SEG_ZS', true); - } - - // 元素周期未完成笔中枢 - if ($('#showElementUncompletedBiZs').is(':checked') && currentData.element_uncompleted_bi_zs_list && currentData.element_uncompleted_bi_zs_list.length > 0) { - console.log(`绘制元素周期未完成笔中枢数据,共${currentData.element_uncompleted_bi_zs_list.length}条`); - drawZoneList(currentData.element_uncompleted_bi_zs_list, '#9C27B0', 'BI_ZS', true); - } - } else { - console.log('绘制中枢 - 已禁用'); - } - } - - // 绘制中枢列表函数 - function drawZoneList(zoneList, color, type, isUncompleted = false) { - zoneList.forEach(function(zs) { - try { - // 直接使用UTC时间戳(秒) - const startTime = Math.floor(new Date(zs.start_time).getTime() / 1000); - // 未完成中枢的结束时间设为当前K线的最后时间 - let endTime; - - if (isUncompleted) { - endTime = Math.floor(new Date(currentData.kline_data[currentData.kline_data.length-1].date).getTime() / 1000); - } else { - endTime = Math.floor(new Date(zs.end_time).getTime() / 1000); - } - - if (isNaN(startTime) || isNaN(endTime)) { - console.error(`${type}中枢时间转换错误:`, zs.start_time, zs.end_time); - return; - } - - const zg = parseFloat(zs.zg); // 中枢上沿 - const zd = parseFloat(zs.zd); // 中枢下沿 - - if (isNaN(zg) || isNaN(zd)) { - console.error(`${type}中枢价格转换错误:`, zs.zg, zs.zd); - return; - } - - // 创建中枢上边界 - const topSeries = mainChart.addLineSeries({ - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线 - lastValueVisible: false, - priceLineVisible: false, - }); - - topSeries.setData([ - { time: startTime, value: zg }, - { time: endTime, value: zg } - ]); - - // 为下边界创建另一条线 - const bottomSeries = mainChart.addLineSeries({ - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线 - lastValueVisible: false, - priceLineVisible: false, - }); - - bottomSeries.setData([ - { time: startTime, value: zd }, - { time: endTime, value: zd } - ]); - - // 添加左边界 - const leftSeries = mainChart.addLineSeries({ - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线 - lastValueVisible: false, - priceLineVisible: false, - }); - - leftSeries.setData([ - { time: startTime, value: zd }, - { time: startTime, value: zg } - ]); - - // 添加右边界 - const rightSeries = mainChart.addLineSeries({ - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0, // 未完成中枢用虚线 - lastValueVisible: false, - priceLineVisible: false, - }); - - rightSeries.setData([ - { time: endTime, value: zd }, - { time: endTime, value: zg } - ]); - - // 储存到相应的系列集合中 - if (type === 'BI_ZS') { - if (color === '#FF9800') { // 主周期笔中枢 - tvWidget.series.mainBiZsSeries.push({ - time: startTime, - value: zg, - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0 - }); - } else { // 元素周期笔中枢 - tvWidget.series.elementBiZsSeries.push({ - time: startTime, - value: zg, - color: color, - lineWidth: 1, - lineStyle: isUncompleted ? 2 : 0 - }); - } - } - } catch (e) { - console.error(`${type}中枢处理出错:`, e); - } - }); - } - - // 辅助函数,在updateTradingViewData或redrawFractalElements中调用 - function displayAllElements() { - console.log('调用displayAllElements,开始绘制所有图形元素...'); - - // 显示笔 - if ($('#showMainBi').is(':checked') || $('#showElementBi').is(':checked')) { - displayBi(); - } - - // 显示线段 - if ($('#showMainSeg').is(':checked') || $('#showElementSeg').is(':checked')) { - displaySeg(); - } - - // 显示中枢(包含线段中枢和笔中枢) - displayZones(); - - // 显示买卖点 - if ($('#showTradePoints').is(':checked')) { - displayTradePoints(); - } - - console.log('displayAllElements执行完成'); - } \ No newline at end of file