修正了刷新图表缩放和位置不变的bug
This commit is contained in:
-12
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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. 等待价格确认跌破EMA26,K线跌破EMA26后2根确认,EMA13和EMA26交叉作为验证信号
|
||||||
|
4. 通过小级别5分钟和1分钟找到精确入场点
|
||||||
|
5. 止损定在最后一个顶底分型的高低点,止盈先设置1:2,如果出现反向分型和macd衰竭信号,先减半保本损
|
||||||
|
6.
|
||||||
Reference in New Issue
Block a user