diff --git a/ChanKLC.py b/ChanKLC.py index a140f1d..7dcafcb 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -372,14 +372,14 @@ class ChanKLC(): end_time = self.next.end_time high = self.high low = self.pre.low if self.pre.low < self.next.low else self.next.low - if self.next.close < self.pre.low: + if self.next.close < self.pre.low or True: display = True elif self.fx == Chan_FX_TYPE.BOTTOM: start_time = self.pre.end_time end_time = self.next.end_time high = self.pre.high if self.pre.high > self.next.high else self.next.high low = self.low - if self.next.close > self.pre.high: + if self.next.close > self.pre.high or True: display = True if high > 0 and self.next.end_time and display: #print(start_time, end_time, high, low) diff --git a/K线动能理论.txt b/K线动能理论.txt index 09e598a..a01758e 100644 --- a/K线动能理论.txt +++ b/K线动能理论.txt @@ -39,6 +39,8 @@ MACD归零轴的两种情况,两者是或的关系,满足任意一种都是 3. MACD归零轴时,如果此时k线始终保持在EMA24附近,如果一直是EMA24之上之后出现反弹行情就会很大(最强反弹)这种反弹是2个时间级别同时归零轴形成的反弹,容易创新高新低,一般出现在强势行情。 4. K线先触碰EMA52,而MACD黄白线都未归零轴 +MACD归零轴反弹/反抽的完美形态是K线触碰EMA52附近,MACD的白线无限接近零轴之后出现上涨或下跌 + 高位空 当MACD的黄白线远离零轴运行时,与零轴有一定的距离,形成了零轴的高危形态。随着K线出现缓慢上涨或者下跌,或者盘整,MACD的能量柱出现衰减,同时能量柱与MACD黄白线形成空间夹角,随着能量柱越来越小,夹角越来越大形成高位空。这种容易形成回调下跌,特别是导致次一级的MACD穿越零轴 @@ -55,6 +57,7 @@ MACD黄白线和零轴的几种形态: 当MACD黄白线处于高位,随着K线出现缓慢上涨或者横盘整理,MACD黄白线保持高位出现平滑横盘走势,此时,MACD的能量柱出现衰减变化,同时能量柱和黄白线之间形成一定的空间夹脚,随着能量柱的不断衰减就导致黄白线和能量柱之间的空间夹脚越来越大,因此就形成高位空 归零轴 当MACD黄白线在高位,驱动K线上涨的能量所产生的加速度小于或者等于零,K线减速上涨或者下跌,能量变化越来越小,能量柱呈现出一根比一根短的排列方式 + 穿零轴 同时满足以下两个条件 1. 在某个时间级别,K线的价格或者指数有效击穿当前时间级别的EMA52 diff --git a/TF_DF.py b/TF_DF.py index 28e0910..48691f9 100644 --- a/TF_DF.py +++ b/TF_DF.py @@ -205,6 +205,19 @@ class TF_DF(): #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM") return Chan_FX_TYPE.BOTTOM return Chan_FX_TYPE.UNKNOWN + def check_fx2(self, klc): + if klc.pre and klc.next: + if klc.high > klc.pre.close and klc.close > klc.next.close and klc.close > klc.pre.close and klc.close > klc.next.close: + #if (klc.close > klc.ema52 or klc.next.close > klc.next.ema52) and klc.macd > 0: + klc.set_fx(Chan_FX_TYPE.TOP) + #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "TOP") + return Chan_FX_TYPE.TOP + elif klc.low < klc.pre.close and klc.close < klc.next.close and klc.close < klc.pre.close and klc.close < klc.next.close: + #if (klc.close < klc.ema52 or klc.next.close < klc.next.ema52) and klc.macd < 0: + klc.set_fx(Chan_FX_TYPE.BOTTOM) + #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time, klc.macd, klc.state, klc.fx, "BOTTOM") + return Chan_FX_TYPE.BOTTOM + return Chan_FX_TYPE.UNKNOWN def check_fx_pattern(self, klc): klu_list = klc.pre.klu_list + klc.klu_list + klc.next.klu_list diff --git a/web/templates/index.html b/web/templates/index.html index 1635605..1bcd6d1 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -2049,72 +2049,24 @@ const periodChanged = (curPeriod !== _lastKlinePeriod); _lastKlinePeriod = curPeriod; - // 保存当前的可见逻辑范围(周期切换时不保留,避免范围越界) - let logicalRange = null; - let visibleRange = null; - + // 保存当前的可见范围(周期切换时不保留,避免范围越界) if (!periodChanged && tvWidget && tvWidget.mainChart) { - logicalRange = tvWidget.mainChart.timeScale().getVisibleLogicalRange(); - visibleRange = tvWidget.mainChart.timeScale().getVisibleRange(); + try { + const ts = tvWidget.mainChart.timeScale(); + window._pendingRestoreView = { + barSpacing: ts.options().barSpacing, + scrollPosition: ts.scrollPosition(), + rightOffset: ts.options().rightOffset + }; + } catch (e) { + window._pendingRestoreView = null; + } } console.log('更新图表显示'); - console.log('BI中枢输入状态:', { - main: $('#showMainBiZs').is(':checked'), - element: $('#showElementBiZs').is(':checked') - }); - console.log('- 显示K线:', $('#showOriginalKline').is(':checked')); - console.log('- 显示笔:', $('#showMainBi').is(':checked')); - console.log('- 显示线段:', $('#showMainSeg').is(':checked')); - console.log('- 显示中枢:', $('#showMainZs').is(':checked')); - console.log('- 显示BI中枢(主):', $('#showMainBiZs').is(':checked')); - console.log('- 显示BI中枢(次):', $('#showElementBiZs').is(':checked')); - console.log('- 显示MACD:', true); - console.log('- 显示成交量:', false); - console.log('- 显示分型类型:', $('#showKlcFxType').is(':checked')); - console.log('- 显示小周期分型:', $('#showElementKlcFxType').is(':checked')); - console.log('- 显示KLU分型:', $('#showKluFxType').is(':checked')); - console.log('- 显示小周期KLU分型:', $('#showElementKluFxType').is(':checked')); - console.log('- 显示买卖点:', $('#showTradePoints').is(':checked')); - console.log('- 显示原始K线:', $('#showOriginalKline').is(':checked')); - console.log('- 显示主周期布林带:', $('#showMainBollinger').is(':checked')); - console.log('- 显示次周期布林带:', $('#showElementBollinger').is(':checked')); - // 重新初始化图表,这将清除旧图形并重新绘制 + // 重新初始化图表(initTradingView 内部会在最终同步时读取 _pendingRestoreView) initTradingView($('#symbol').val(), $('#timeframe').val()); - - // 如果有保存的范围,恢复它 - setTimeout(() => { - if (tvWidget) { - if (logicalRange) { - // 应用保存的逻辑范围到所有图表 - tvWidget.mainChart.timeScale().setVisibleLogicalRange(logicalRange); - tvWidget.volumeChart.timeScale().setVisibleLogicalRange(logicalRange); - if (tvWidget.atrChart) { - tvWidget.atrChart.timeScale().setVisibleLogicalRange(logicalRange); - } - if (tvWidget.macdChart) { - tvWidget.macdChart.timeScale().setVisibleLogicalRange(logicalRange); - } - if (tvWidget.chanMacdChart) { - tvWidget.chanMacdChart.timeScale().setVisibleLogicalRange(logicalRange); - } - } else if (visibleRange) { - // 如果没有逻辑范围,使用时间戳范围 - tvWidget.mainChart.timeScale().setVisibleRange(visibleRange); - tvWidget.volumeChart.timeScale().setVisibleRange(visibleRange); - if (tvWidget.atrChart) { - tvWidget.atrChart.timeScale().setVisibleRange(visibleRange); - } - if (tvWidget.macdChart) { - tvWidget.macdChart.timeScale().setVisibleRange(visibleRange); - } - if (tvWidget.chanMacdChart) { - tvWidget.chanMacdChart.timeScale().setVisibleRange(visibleRange); - } - } - } - }, 100); } } // 确保所有时间处理都使用UTC时间,包括表格数据显示 @@ -6742,11 +6694,11 @@ } const defaultMAs = [ - { type: 'EMA', length: 52, color: '#800080', name: 'EMA52' }, // 紫色 - { 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' } // 蓝色 + { type: 'EMA', length: 13, color: '#800080', name: 'EMA13', visible: true }, // 紫色 + { type: 'EMA', length: 26, color: '#000000', name: 'EMA26', visible: true }, // 黑色 + { type: 'EMA', length: 52, color: '#FF8C00', name: 'EMA52', visible: false }, // 橙色 + { type: 'EMA', length: 104, color: '#1E90FF', name: 'EMA104', visible: false }, // 蓝色 + { type: 'EMA', length: 156, color: '#F700FF', name: 'EMA156', visible: false } // 粉色 ]; defaultMAs.forEach(ma => { @@ -6760,7 +6712,7 @@ lineWidth: 1, // 1px线宽 lineStyle: 0, // 实线 color: ma.color, - visible: true + visible: ma.visible }; movingAverages.push(config); @@ -6775,7 +6727,7 @@ upperMultiplier: 2, lowerMultiplier: 2, source: 'close', - lineWidth: 2, + lineWidth: 1, lineStyle: 0, upperColor: '#ff6b6b', middleColor: '#ffffff', @@ -6802,24 +6754,39 @@ // 绑定同步事件 bindSyncEvents(mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, mainChart, volumeChart, atrChart, macdChart, chanMacdChart, showMacd); - // 最终确保所有图表时间轴对齐 + // 最终确保所有图表时间轴对齐(同时恢复刷新前保存的缩放/位置) setTimeout(() => { - const visibleRange = mainChart.timeScale().getVisibleRange(); - if (visibleRange) { - console.log('🔧 最终同步可见范围:', visibleRange); - - // 强制重新设置所有图表的可见范围 - volumeChart.timeScale().setVisibleRange(visibleRange); - atrChart.timeScale().setVisibleRange(visibleRange); - if (showMacd && macdChart) { - macdChart.timeScale().setVisibleRange(visibleRange); + const allCharts = [mainChart, volumeChart, atrChart]; + if (showMacd && macdChart) allCharts.push(macdChart); + if (showMacd && chanMacdChart) allCharts.push(chanMacdChart); + + // 检查是否有待恢复的视图(barSpacing + scrollPosition) + const pending = window._pendingRestoreView; + window._pendingRestoreView = null; + + if (pending && pending.barSpacing) { + // 恢复刷新前的缩放和滚动位置 + console.log('📌 恢复图表视图 - barSpacing:', pending.barSpacing, 'scrollPosition:', pending.scrollPosition); + allCharts.forEach(c => { + try { + c.timeScale().applyOptions({ barSpacing: pending.barSpacing }); + c.timeScale().scrollToPosition(pending.scrollPosition, false); + } catch(e) {} + }); + } else { + // 无保存视图,正常同步主图到子图 + const visibleRange = mainChart.timeScale().getVisibleRange(); + if (visibleRange) { + console.log('🔧 最终同步可见范围:', visibleRange); + [volumeChart, atrChart].concat( + showMacd && macdChart ? [macdChart] : [], + showMacd && chanMacdChart ? [chanMacdChart] : [] + ).forEach(c => { + try { c.timeScale().setVisibleRange(visibleRange); } catch(e) {} + }); } - if (showMacd && chanMacdChart) { - chanMacdChart.timeScale().setVisibleRange(visibleRange); - } - - console.log('🔧 最终时间轴对齐完成'); } + console.log('🔧 最终时间轴对齐完成'); }, 150); // 只有在时间输入框都为空时才设置图表默认时间范围 if (!$('#start_time').val() && !$('#end_time').val()) { @@ -8410,14 +8377,28 @@ $('#elementTimeframe').val(data.element_timeframe); } + // 保存当前缩放(barSpacing)和滚动位置(scrollPosition)到 window + // tvWidget 会在 initTradingView 内被重建,所以必须存到 window 上 + if (tvWidget && tvWidget.mainChart) { + try { + const ts = tvWidget.mainChart.timeScale(); + window._pendingRestoreView = { + barSpacing: ts.options().barSpacing, + scrollPosition: ts.scrollPosition(), + rightOffset: ts.options().rightOffset + }; + console.log('📌 保存图表视图:', JSON.stringify(window._pendingRestoreView)); + } catch (e) { + console.warn('保存图表视图失败:', e); + window._pendingRestoreView = null; + } + } + initTradingView($('#symbol').val(), $('#timeframe').val()); // 更新表格数据 updateTables(data); - // 只有在图表已初始化且调用了updateTradingViewData时才不需要重复调用EMA52显示 - // 如果图表未初始化,调用了initTradingView,那么EMA52显示已经在initTradingView中处理了 - // 但为了确保在所有情况下都能正确显示,这里统一调用一次 if (currentData && currentData.ema52_dict) { updateEMA52Display(currentData); } @@ -10574,13 +10555,13 @@