自动刷新常态只拉 recent 尾部 K,每 1 分钟全量重算缠论;修复结构区缓存导入;默认指标/4h·1h·15m/近30天;同步 ECR-009 screener 相关改动。 Co-authored-by: Cursor <cursoragent@cursor.com>
240 lines
11 KiB
JavaScript
240 lines
11 KiB
JavaScript
/* chart_tv_finalize.js — time sync / bindSync / view restore / tooltip */
|
|
|
|
function chartTvFinalize(ctx) {
|
|
var symbol = ctx.symbol;
|
|
var timeframe = ctx.timeframe;
|
|
var symbolConfig = ctx.symbolConfig;
|
|
var useSubSubPeriod = ctx.useSubSubPeriod;
|
|
var useElementPeriod = ctx.useElementPeriod;
|
|
var klinePeriodLabel = ctx.klinePeriodLabel;
|
|
var candles = ctx.candles;
|
|
var klineDataSource = ctx.klineDataSource;
|
|
var container = ctx.container;
|
|
var showMacd = ctx.showMacd;
|
|
var showOriginalKline = ctx.showOriginalKline;
|
|
var mainChartContainer = ctx.mainChartContainer;
|
|
var volumeChartContainer = ctx.volumeChartContainer;
|
|
var atrChartContainer = ctx.atrChartContainer;
|
|
var macdChartContainer = ctx.macdChartContainer;
|
|
var chanMacdChartContainer = ctx.chanMacdChartContainer;
|
|
var mainChart = ctx.mainChart;
|
|
var volumeChart = ctx.volumeChart;
|
|
var atrChart = ctx.atrChart;
|
|
var macdChart = ctx.macdChart;
|
|
var chanMacdChart = ctx.chanMacdChart;
|
|
var createChartOptions = ctx.createChartOptions;
|
|
// 同步所有图表的时间轴配置
|
|
const hasPendingRestoreView = !!window._pendingRestoreView;
|
|
const pendingView = window._pendingRestoreView;
|
|
const syncTimeScaleSettings = () => {
|
|
const baseOptions = {
|
|
timeVisible: true,
|
|
secondsVisible: false,
|
|
borderColor: '#ddd',
|
|
lockVisibleTimeRangeOnResize: true,
|
|
// 关键:确保所有图表边缘行为完全一致
|
|
fixLeftEdge: false,
|
|
fixRightEdge: false,
|
|
// 确保时间刻度行为一致
|
|
ticksVisible: true,
|
|
minimumHeight: 0,
|
|
};
|
|
// 有待恢复视图时不要先写 barSpacing/rightOffset(会钉右缘导致图往右偏),
|
|
// 交给后面 setVisibleRange 一次锁定位置+缩放。
|
|
if (!pendingView) {
|
|
baseOptions.barSpacing = symbolConfig.type === 'a_stock' ? 6 : 10;
|
|
baseOptions.rightOffset = 12;
|
|
}
|
|
|
|
console.log('🔧 同步时间轴设置:', baseOptions);
|
|
|
|
// 应用相同的设置到所有图表
|
|
mainChart.timeScale().applyOptions(baseOptions);
|
|
volumeChart.timeScale().applyOptions(baseOptions);
|
|
atrChart.timeScale().applyOptions(baseOptions);
|
|
if (showMacd && macdChart) {
|
|
macdChart.timeScale().applyOptions(baseOptions);
|
|
}
|
|
};
|
|
|
|
// 首先同步时间轴设置
|
|
syncTimeScaleSettings();
|
|
|
|
// 仅在没有待恢复视图时,设置默认可见范围
|
|
const totalBars = candles ? candles.length : 0;
|
|
const visibleBarsCount = 200;
|
|
const allChartsNow = [mainChart, volumeChart, atrChart]
|
|
.concat(showMacd && macdChart ? [macdChart] : [])
|
|
.concat(showMacd && chanMacdChart ? [chanMacdChart] : []);
|
|
if (hasPendingRestoreView && pendingView) {
|
|
restoreChartViewState(allChartsNow, pendingView, { preferTime: true });
|
|
} else {
|
|
// 显示最近 200 根K线而非全部挤压(避免K线过多时重叠)
|
|
if (totalBars > visibleBarsCount) {
|
|
const rangeFrom = totalBars - visibleBarsCount;
|
|
const rangeTo = totalBars + 12;
|
|
mainChart.timeScale().setVisibleLogicalRange({ from: rangeFrom, to: rangeTo });
|
|
} else {
|
|
mainChart.timeScale().fitContent();
|
|
}
|
|
}
|
|
|
|
// 立即同步其他图表到主图表的范围(无 pending 时)
|
|
setTimeout(() => {
|
|
if (window._pendingRestoreView) {
|
|
restoreChartViewState(allChartsNow, window._pendingRestoreView, { preferTime: true });
|
|
return;
|
|
}
|
|
const logRange = mainChart.timeScale().getVisibleLogicalRange();
|
|
if (logRange) {
|
|
console.log('🔧 同步可见范围:', logRange);
|
|
volumeChart.timeScale().setVisibleLogicalRange(logRange);
|
|
atrChart.timeScale().setVisibleLogicalRange(logRange);
|
|
if (showMacd && macdChart) {
|
|
macdChart.timeScale().setVisibleLogicalRange(logRange);
|
|
}
|
|
if (showMacd && chanMacdChart) {
|
|
chanMacdChart.timeScale().setVisibleLogicalRange(logRange);
|
|
}
|
|
console.log('🔧 时间轴同步完成');
|
|
}
|
|
}, 50);
|
|
|
|
// 保存图表对象
|
|
tvWidget.mainChart = mainChart;
|
|
tvWidget.volumeChart = volumeChart;
|
|
tvWidget.atrChart = atrChart;
|
|
tvWidget.macdChart = macdChart;
|
|
tvWidget.chanMacdChart = chanMacdChart;
|
|
tvWidget.state.isInitialized = true;
|
|
// 注册窗口卸载时释放资源,避免GPU内存泄漏
|
|
window.onbeforeunload = function() {
|
|
try {
|
|
if (tvWidget && tvWidget.state && tvWidget.state.isInitialized) {
|
|
if (tvWidget.mainChart && typeof tvWidget.mainChart.remove === 'function') tvWidget.mainChart.remove();
|
|
if (tvWidget.volumeChart && typeof tvWidget.volumeChart.remove === 'function') tvWidget.volumeChart.remove();
|
|
if (tvWidget.macdChart && typeof tvWidget.macdChart.remove === 'function') tvWidget.macdChart.remove();
|
|
if (tvWidget.chanMacdChart && typeof tvWidget.chanMacdChart.remove === 'function') tvWidget.chanMacdChart.remove();
|
|
if (tvWidget.atrChart && typeof tvWidget.atrChart.remove === 'function') tvWidget.atrChart.remove();
|
|
}
|
|
} catch (e) {}
|
|
};
|
|
|
|
// 初始化默认均线/布林带配置(仅在首次初始化时)
|
|
if (!hasInitializedDefaultMAs && movingAverages.length === 0) {
|
|
console.log('初始化默认均线与布林带指标');
|
|
|
|
if (typeof maIdCounter !== 'number' || !Number.isFinite(maIdCounter)) {
|
|
maIdCounter = 0;
|
|
}
|
|
if (typeof bbIdCounter !== 'number' || !Number.isFinite(bbIdCounter)) {
|
|
bbIdCounter = 0;
|
|
}
|
|
|
|
const defaultMAs = [
|
|
{ type: 'EMA', length: 26, color: '#FF8C00', name: 'EMA26', visible: false }, // 橙色
|
|
{ type: 'EMA', length: 52, color: '#000000', name: 'EMA52', visible: true }, // 黑色 · 默认开
|
|
{ type: 'SMA', length: 30, color: '#1E90FF', name: 'MA30', visible: true }, // 蓝色 · 默认开
|
|
{ type: 'SMA', length: 250, color: '#800080', name: 'MA250', visible: true } // 紫色 · 默认开
|
|
];
|
|
|
|
defaultMAs.forEach(ma => {
|
|
const config = {
|
|
id: ++maIdCounter,
|
|
type: ma.type,
|
|
length: ma.length,
|
|
source: 'close',
|
|
smoothType: 'none',
|
|
smoothLength: 3,
|
|
lineWidth: 1, // 1px线宽
|
|
lineStyle: 0, // 实线
|
|
color: ma.color,
|
|
visible: ma.visible
|
|
};
|
|
|
|
movingAverages.push(config);
|
|
console.log(`添加默认${ma.name}:`, ma.color);
|
|
});
|
|
|
|
if (bollingerBands.length === 0) {
|
|
const defaultBB = {
|
|
id: ++bbIdCounter,
|
|
type: 'Bollinger Bands',
|
|
length: 20,
|
|
upperMultiplier: 2,
|
|
lowerMultiplier: 2,
|
|
source: 'close',
|
|
lineWidth: 1,
|
|
lineStyle: 0,
|
|
upperColor: '#ff6b6b',
|
|
middleColor: '#ffffff',
|
|
lowerColor: '#ff6b6b',
|
|
visible: false
|
|
};
|
|
bollingerBands.push(defaultBB);
|
|
console.log('添加默认布林带: BB(20, 2, 2)');
|
|
}
|
|
|
|
console.log('默认指标配置完成,当前均线数量', movingAverages.length, '布林带数量', bollingerBands.length);
|
|
hasInitializedDefaultMAs = true;
|
|
}
|
|
|
|
// 添加均线到图表
|
|
addMovingAveragesToChart(candles);
|
|
|
|
// 添加布林带到图表
|
|
addBollingerBandsToChart(candles);
|
|
|
|
// 更新技术指标面板显示
|
|
updateIndicatorPanel();
|
|
|
|
// 绑定同步事件
|
|
bindSyncEvents(mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, mainChart, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
|
|
|
|
// 最终确保所有图表时间轴对齐(同时恢复刷新前保存的缩放/位置)
|
|
setTimeout(() => {
|
|
const allCharts = [mainChart, volumeChart, atrChart];
|
|
if (showMacd && macdChart) allCharts.push(macdChart);
|
|
if (showMacd && chanMacdChart) allCharts.push(chanMacdChart);
|
|
|
|
// 检查是否有待恢复的视图(缩放 + 位置)
|
|
const pending = window._pendingRestoreView;
|
|
window._pendingRestoreView = null;
|
|
|
|
if (pending) {
|
|
// 恢复刷新前的缩放和位置(时间范围优先,避免数据滑动后逻辑索引错位)
|
|
console.log('📌 恢复图表视图:', JSON.stringify(pending));
|
|
restoreChartViewState(allCharts, pending, { preferTime: true });
|
|
} 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) {}
|
|
});
|
|
}
|
|
}
|
|
console.log('🔧 最终时间轴对齐完成');
|
|
}, 150);
|
|
// 只有在时间输入框都为空时才设置图表默认时间范围
|
|
if (!$('#start_time').val() && !$('#end_time').val()) {
|
|
setDefaultTimeRange();
|
|
}
|
|
|
|
// 添加买卖点提示
|
|
// 初始化 tooltip 与 U 显示状态
|
|
window.showUOnMain = $('#toggleUOnMain').is(':checked');
|
|
window.showUOnElement = $('#toggleUOnElement').is(':checked');
|
|
setupTooltip(mainChart, [], [], mainChartContainer, volumeChartContainer, atrChartContainer, macdChartContainer, chanMacdChartContainer, volumeChart, atrChart, macdChart, chanMacdChart, showMacd);
|
|
|
|
// 更新EMA52显示
|
|
if (currentData) {
|
|
updateEMA52Display(currentData);
|
|
}
|
|
|
|
}
|