From d8f2269a49e21cb3d9330c1a2ab0b2084372686a Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Sat, 28 Feb 2026 19:27:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=AF=86=E5=88=AB=E7=AC=94?= =?UTF-8?q?=E7=A0=B4=E5=9D=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChanKLC.py | 1 + TF_DF.py | 289 +++++++++++++++++++++++++++++++++++++++ web/templates/index.html | 24 ++-- 3 files changed, 302 insertions(+), 12 deletions(-) diff --git a/ChanKLC.py b/ChanKLC.py index 2c97c58..4439f00 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -316,6 +316,7 @@ class ChanKLC(): #if ema_name: #print(self.end_time, ema_name, self.ema_status[ema_name]['semantic'], hist_div) #self.cal_bb_out() + #print(self.pre.start_time, self.next.end_time, self.klc_fx_type) def add_klu(self, klu): self.klu_list.append(klu) def set_end_klu(self, klu): diff --git a/TF_DF.py b/TF_DF.py index e2907a3..64c0ad8 100644 --- a/TF_DF.py +++ b/TF_DF.py @@ -891,6 +891,293 @@ class TF_DF(): return seg_list def cal_bi_list(self, klc_list): + bi_list = [] + last_top = None + last_bottom = None + bi_klc_min = 4 + for klc in klc_list: + fx = self.check_fx(klc) + if fx == Chan_FX_TYPE.TOP: + if last_bottom: + if self.check_top_fx(last_bottom, klc) == False: + fx = Chan_FX_TYPE.UNKNOWN + if fx == Chan_FX_TYPE.BOTTOM: + if last_top: + if self.check_bottom_fx(last_top, klc) == False: + #print(klc.end_time, last_top.end_time, "---") + fx = Chan_FX_TYPE.UNKNOWN + # Do nothing + if fx == Chan_FX_TYPE.UNKNOWN: + if len(bi_list) > 0: + bi_list[-1].add_klc(klc) + #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: + #print(klc.end_time, "Top 7, 1", last_bi.start_time, klc.high, last_bi.high) + #klc.klc_fx_type = Chan_KLC_FX.TOP7 + """ + 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: + #print(klc.end_time, "Bottom 8, 2", last_bi.start_time) + #klc.klc_fx_type = Chan_KLC_FX.BOTTOM8 + """ + 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: + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + #print(klc.end_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) + self.check_fx_pattern(klc) + #print(klc.end_time, klc.fx, "一类卖点Sell 1") + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + else: + # 不满足结合律的分型 + if last_bottom.index + bi_klc_min > 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没有意义,前面的UKNOWN已经出现TOP7 + else: + if last_top.index + bi_klc_min < 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) + 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) + self.check_fx_pattern(klc) + 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") + # 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 = 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]) + else: + klc.set_fx(Chan_FX_TYPE.TT) + #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 + bi_klc_min > 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]) + # 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.add_klc(klc) + klc.set_bi(bi_list[-1]) + #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 5") + #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: + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + #print(last_bottom.start_time, last_bottom.end_time, "--------------------------------1") + #print(klc.end_time, klc.fx, "二类买点Buy 1") + else: + # A new bottom found + last_bottom = klc + #print(klc.end_time, klc.fx, bi_list[-1].dir, "Last Bottom Change 1") + klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM1) + self.check_fx_pattern(klc) + #print(klc.end_time, klc.fx, "一类买点Buy 1") + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + else: + # 不满足结合律的分型 + if last_top.index + bi_klc_min > klc.index: + if last_bottom.low < klc.low: + #print(klc.end_time, klc.fx, "中枢买点Buy 1") + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + # Found new bottom没有意义,上面UNKNOWN的时候已经是笔破坏了 + else: + #print(klc.end_time, last_bottom.end_time, "Found a new bottom") + if last_bottom.index + bi_klc_min < 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 = 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") + #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) + 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) + self.check_fx_pattern(klc) + bi_list[-1].add_klc(klc) + klc.set_bi(bi_list[-1]) + #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 + bi_klc_min > 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) + 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") + return bi_list + def cal_bi_list1(self, klc_list): bi_list = [] last_top = None last_bottom = None @@ -914,6 +1201,7 @@ class TF_DF(): 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: + print("fx=unknown, 1") 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) @@ -928,6 +1216,7 @@ class TF_DF(): klc.set_bi(bi) #print(klc.start_time, bi.start_time, bi.end_time, bi.dir, bi.high, bi.low, bi.is_sure) else: + print("fx=unknown, 2") 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) diff --git a/web/templates/index.html b/web/templates/index.html index 73d0c37..9d169f1 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -1243,7 +1243,7 @@ r.close, r.ema5, r.ema10, - r.ema24, + r.ema26, r.ema52, `` ]); @@ -1284,7 +1284,7 @@ trendDetailTable.row.add([ new Date(row.timestamp).toLocaleString('zh-CN', { timeZone: data.timezone }), row.open, row.high, row.low, row.close, row.volume, - row.ema5, row.ema10, row.ema24, row.ema52 + row.ema5, row.ema10, row.ema26, row.ema52 ]); }); trendDetailTable.draw(); @@ -1317,7 +1317,7 @@ // 关闭均线的价格线与最后值标签,仅保留K线的当前价格虚线 const ema5 = chart.addLineSeries({ color: '#ff0000', lineWidth: 2, lastValueVisible: false, priceLineVisible: false }); const ema10 = chart.addLineSeries({ color: '#2962FF', lineWidth: 2, lastValueVisible: false, priceLineVisible: false }); - const ema24 = chart.addLineSeries({ color: '#008000', lineWidth: 2, lastValueVisible: false, priceLineVisible: false }); + const ema26 = chart.addLineSeries({ color: '#008000', lineWidth: 2, lastValueVisible: false, priceLineVisible: false }); const ema52 = chart.addLineSeries({ color: '#800080', lineWidth: 2, lastValueVisible: false, priceLineVisible: false }); const k = (data.kline_data || []).map(r => ({ @@ -1347,7 +1347,7 @@ }; ema5.setData(sanitizeMA('ema5')); ema10.setData(sanitizeMA('ema10')); - ema24.setData(sanitizeMA('ema24')); + ema26.setData(sanitizeMA('ema26')); ema52.setData(sanitizeMA('ema52')); // 趋势线(使用返回的拟合参数) @@ -1400,12 +1400,12 @@ function classifyStageFrontend(kline, directionHint) { const close = kline.map(r => Number(r.close)); - const ema24 = computeEMA(close, 24); + const ema26 = computeEMA(close, 26); const ema52 = computeEMA(close, 52); const last = close[close.length-1]; - const e24 = ema24[ema24.length-1]; + const e26 = ema26[ema26.length-1]; const e52 = ema52[ema52.length-1]; - const s24 = slope(ema24, 20); + const s26 = slope(ema26, 20); const s52 = slope(ema52, 30); const dist52 = (e52 && isFinite(e52)) ? (last - e52)/e52 : 0; const { hist } = computeMACDSeries(close); @@ -1417,8 +1417,8 @@ let direction = directionHint; if (!direction) { - if (e24 > e52 && s24 > 0 && s52 > 0) direction = 'bull'; - else if (e24 < e52 && s24 < 0 && s52 < 0) direction = 'bear'; + if (e26 > e52 && s26 > 0 && s52 > 0) direction = 'bull'; + else if (e26 < e52 && s26 < 0 && s52 < 0) direction = 'bear'; else direction = 'sideways'; } @@ -1426,11 +1426,11 @@ const ad = Math.abs(dist52); if (direction === 'bull') { if (ad < 0.03 && growth > 0) stage = 'early'; - else if (ad < 0.10 && (growth >= 0 || s24 > 0)) stage = 'mid'; + else if (ad < 0.10 && (growth >= 0 || s26 > 0)) stage = 'mid'; else stage = 'late'; } else if (direction === 'bear') { if (ad < 0.03 && growth > 0) stage = 'early'; - else if (ad < 0.10 && (growth >= 0 || s24 < 0)) stage = 'mid'; + else if (ad < 0.10 && (growth >= 0 || s26 < 0)) stage = 'mid'; else stage = 'late'; } else { stage = 'early'; @@ -6035,7 +6035,7 @@ const defaultMAs = [ { type: 'EMA', length: 52, color: '#800080', name: 'EMA52' }, // 紫色 - { type: 'EMA', length: 24, color: '#008000', name: 'EMA24' }, // 深绿色 + { type: 'EMA', length: 26, color: '#008000', name: 'EMA26' }, // 深绿色 { type: 'EMA', length: 104, color: '#FF8C00', name: 'EMA104' }, // 橙色 { type: 'EMA', length: 156, color: '#1E90FF', name: 'EMA156' }, // 蓝色 { type: 'EMA', length: 208, color: '#1F004F', name: 'EMA312' } // 蓝色