修正了刷新图表缩放和位置不变的bug

This commit is contained in:
jackyu66git
2026-04-16 02:31:48 +08:00
parent 7815eada00
commit 04d8f73b94
3 changed files with 112 additions and 41 deletions
-12
View File
@@ -96,7 +96,6 @@ class ChanKLU:
self.trend = trend self.trend = trend
def set_separate_div(self, separate_div): def set_separate_div(self, separate_div):
self.separate_div = separate_div self.separate_div = separate_div
bb2633_status = self.check_bb2633()
if self.klc and self.klc.pre and self.klc.next: if self.klc and self.klc.pre and self.klc.next:
fx = self.check_fx_dir(self.klc.pre, self.klc.next) fx = self.check_fx_dir(self.klc.pre, self.klc.next)
if fx == Chan_FX_TYPE.TOP: if fx == Chan_FX_TYPE.TOP:
@@ -109,17 +108,6 @@ class ChanKLU:
self.separate_div = separate_div self.separate_div = separate_div
else: else:
self.separate_div = 0 self.separate_div = 0
if bb2633_status == 0:
self.separate_div = 0
def check_bb2633(self, threadhold=300):
#print(self.time, self.high, self.bb2633upper, self.low, self.bb2633lower)
if abs(self.high - self.bb2633upper) < threadhold:
#print(self.time, self.high, self.bb2633upper)
return 1
if abs(self.low - self.bb2633lower) < threadhold:
#print(self.time, self.low, self.bb2633lower)
return -1
return 0
def check_fx_dir(self, pre, next): def check_fx_dir(self, pre, next):
fx = Chan_FX_TYPE.UNKNOWN fx = Chan_FX_TYPE.UNKNOWN
if pre.klc_fx_type == Chan_KLC_FX.TOP1 or pre.klc_fx_type == Chan_KLC_FX.TOP2 or next.klc_fx_type == Chan_KLC_FX.TOP1 or next.klc_fx_type == Chan_KLC_FX.TOP2 or self.klc.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc.klc_fx_type == Chan_KLC_FX.TOP2: if pre.klc_fx_type == Chan_KLC_FX.TOP1 or pre.klc_fx_type == Chan_KLC_FX.TOP2 or next.klc_fx_type == Chan_KLC_FX.TOP1 or next.klc_fx_type == Chan_KLC_FX.TOP2 or self.klc.klc_fx_type == Chan_KLC_FX.TOP1 or self.klc.klc_fx_type == Chan_KLC_FX.TOP2:
+70 -23
View File
@@ -2052,12 +2052,7 @@
// 保存当前的可见范围(周期切换时不保留,避免范围越界) // 保存当前的可见范围(周期切换时不保留,避免范围越界)
if (!periodChanged && tvWidget && tvWidget.mainChart) { if (!periodChanged && tvWidget && tvWidget.mainChart) {
try { try {
const ts = tvWidget.mainChart.timeScale(); window._pendingRestoreView = captureChartViewState(tvWidget.mainChart);
window._pendingRestoreView = {
barSpacing: ts.options().barSpacing,
scrollPosition: ts.scrollPosition(),
rightOffset: ts.options().rightOffset
};
} catch (e) { } catch (e) {
window._pendingRestoreView = null; window._pendingRestoreView = null;
} }
@@ -2215,6 +2210,64 @@
} }
}); });
} }
function captureChartViewState(chart) {
if (!chart || !chart.timeScale) return null;
const ts = chart.timeScale();
const tsOptions = ts.options ? ts.options() : {};
return {
barSpacing: tsOptions.barSpacing,
rightOffset: tsOptions.rightOffset,
scrollPosition: ts.scrollPosition ? ts.scrollPosition() : null,
visibleRange: ts.getVisibleRange ? ts.getVisibleRange() : null,
logicalRange: ts.getVisibleLogicalRange ? ts.getVisibleLogicalRange() : null
};
}
function restoreChartViewState(charts, viewState) {
if (!viewState || !Array.isArray(charts) || charts.length === 0) return;
const validCharts = charts.filter(c => c && c.timeScale);
if (validCharts.length === 0) return;
validCharts.forEach(c => {
try {
const optionsPatch = {};
if (typeof viewState.barSpacing === 'number') optionsPatch.barSpacing = viewState.barSpacing;
if (typeof viewState.rightOffset === 'number') optionsPatch.rightOffset = viewState.rightOffset;
if (Object.keys(optionsPatch).length) {
c.timeScale().applyOptions(optionsPatch);
}
} catch (e) {}
});
let restored = false;
// 优先按逻辑范围恢复(对新数据更稳健)
if (viewState.logicalRange && viewState.logicalRange.from !== undefined && viewState.logicalRange.to !== undefined) {
validCharts.forEach(c => {
try {
c.timeScale().setVisibleLogicalRange(viewState.logicalRange);
restored = true;
} catch (e) {}
});
}
// 逻辑范围失败时,回退到时间可见范围
if (!restored && viewState.visibleRange && viewState.visibleRange.from !== undefined && viewState.visibleRange.to !== undefined) {
validCharts.forEach(c => {
try {
c.timeScale().setVisibleRange(viewState.visibleRange);
restored = true;
} catch (e) {}
});
}
// 最后回退到滚动位置
if (!restored && typeof viewState.scrollPosition === 'number') {
validCharts.forEach(c => {
try { c.timeScale().scrollToPosition(viewState.scrollPosition, false); } catch (e) {}
});
}
}
// 初始化图表 // 初始化图表
function initTradingView(symbol, timeframe) { function initTradingView(symbol, timeframe) {
try { try {
@@ -6634,9 +6687,12 @@
// 首先同步时间轴设置 // 首先同步时间轴设置
syncTimeScaleSettings(); syncTimeScaleSettings();
// 显示最近 200 根K线而非全部挤压(避免K线过多时重叠) // 仅在没有待恢复视图时,设置默认可见范围
const totalBars = candles ? candles.length : 0; const totalBars = candles ? candles.length : 0;
const visibleBarsCount = 200; const visibleBarsCount = 200;
const hasPendingRestoreView = !!window._pendingRestoreView;
if (!hasPendingRestoreView) {
// 显示最近 200 根K线而非全部挤压(避免K线过多时重叠)
if (totalBars > visibleBarsCount) { if (totalBars > visibleBarsCount) {
const rangeFrom = totalBars - visibleBarsCount; const rangeFrom = totalBars - visibleBarsCount;
const rangeTo = totalBars + 12; const rangeTo = totalBars + 12;
@@ -6644,6 +6700,7 @@
} else { } else {
mainChart.timeScale().fitContent(); mainChart.timeScale().fitContent();
} }
}
// 立即同步其他图表到主图表的范围 // 立即同步其他图表到主图表的范围
setTimeout(() => { setTimeout(() => {
@@ -6760,19 +6817,14 @@
if (showMacd && macdChart) allCharts.push(macdChart); if (showMacd && macdChart) allCharts.push(macdChart);
if (showMacd && chanMacdChart) allCharts.push(chanMacdChart); if (showMacd && chanMacdChart) allCharts.push(chanMacdChart);
// 检查是否有待恢复的视图(barSpacing + scrollPosition // 检查是否有待恢复的视图(缩放 + 位置
const pending = window._pendingRestoreView; const pending = window._pendingRestoreView;
window._pendingRestoreView = null; window._pendingRestoreView = null;
if (pending && pending.barSpacing) { if (pending) {
// 恢复刷新前的缩放和滚动位置 // 恢复刷新前的缩放和位置(优先可见范围/逻辑范围,最后回退到滚动位置
console.log('📌 恢复图表视图 - barSpacing:', pending.barSpacing, 'scrollPosition:', pending.scrollPosition); console.log('📌 恢复图表视图:', JSON.stringify(pending));
allCharts.forEach(c => { restoreChartViewState(allCharts, pending);
try {
c.timeScale().applyOptions({ barSpacing: pending.barSpacing });
c.timeScale().scrollToPosition(pending.scrollPosition, false);
} catch(e) {}
});
} else { } else {
// 无保存视图,正常同步主图到子图 // 无保存视图,正常同步主图到子图
const visibleRange = mainChart.timeScale().getVisibleRange(); const visibleRange = mainChart.timeScale().getVisibleRange();
@@ -8381,12 +8433,7 @@
// tvWidget 会在 initTradingView 内被重建,所以必须存到 window 上 // tvWidget 会在 initTradingView 内被重建,所以必须存到 window 上
if (tvWidget && tvWidget.mainChart) { if (tvWidget && tvWidget.mainChart) {
try { try {
const ts = tvWidget.mainChart.timeScale(); window._pendingRestoreView = captureChartViewState(tvWidget.mainChart);
window._pendingRestoreView = {
barSpacing: ts.options().barSpacing,
scrollPosition: ts.scrollPosition(),
rightOffset: ts.options().rightOffset
};
console.log('📌 保存图表视图:', JSON.stringify(window._pendingRestoreView)); console.log('📌 保存图表视图:', JSON.stringify(window._pendingRestoreView));
} catch (e) { } catch (e) {
console.warn('保存图表视图失败:', e); console.warn('保存图表视图失败:', e);
+36
View File
@@ -0,0 +1,36 @@
本级别没有背驰的,次级别背驰下跌后形成第三类卖点进而形成本级别的V型反转,小转大
大级别 判断趋势位置(是否进入末期)
中级别 识别第一类买卖点结构
小级别 精准入场与风控
短线操作策略
大级别1H/2H/4H,判断趋势方向,
上涨趋势
趋势启动第一次穿零轴后归零轴反弹,EMA52和黄白线无限接近零轴,MACD黄白线在零轴以上
下跌趋势
趋势启动第一次穿零轴后归零轴反弹,EMA52和黄白线无限接近零轴,MACD黄白线在零轴以下
MACD归零轴的两种情况,两者是或的关系,满足任意一种都是归零轴,归零轴的四种走势:
1. K线下跌或者上涨后触碰当前时间级别的EMA52附近
2. MACD的白线快线无限接近零轴,
3. MACD归零轴时,如果此时k线始终保持在EMA24附近,如果一直是EMA24之上之后出现反弹行情就会很大(最强反弹)这种反弹是2个时间级别同时归零轴形成的反弹,容易创新高新低,一般出现在强势行情。
4. K线先触碰EMA52,而MACD黄白线都未归零轴
归零轴完美形态
MACD归零轴反弹/反抽的完美形态是K线触碰EMA52附近,MACD的白线无限接近零轴之后出现上涨或下跌
中级别
识别EMA52和MACD黄白线归零轴,MACD能量柱形成背驰
小级别
识别最后下跌段MACD背驰和开始上涨K线形态,识别笔,精确定位第一类和第二类卖卖点
在小级别第一类或者第二类卖卖点开仓
平仓规则
大级别如果K线在EMA13/EMA7均线以上,根据倍数关系到相应的小周期观察顶底背离进行平仓操作,一般来说在大级别的1/4小级别观察背离顶底
比如1H归零轴反弹,K线一直运行在EMA7以上,那么到15M级别进行平仓操作,观察背离
趋势策略
1. 使用30分钟,5分钟,1分钟时间周期进行交易
2. 使用顶底分型和MACD背驰信号作为趋势衰竭信号,但是不作为开仓信号
3. 等待价格确认跌破EMA26K线跌破EMA26后2根确认,EMA13和EMA26交叉作为验证信号
4. 通过小级别5分钟和1分钟找到精确入场点
5. 止损定在最后一个顶底分型的高低点,止盈先设置1:2,如果出现反向分型和macd衰竭信号,先减半保本损
6.