Files
Chan/web/static/js/app/chart_tv_indicators.js
T
jackyu66gitandCursor 2cd50f1e01 主图增加笔/线段背驰与面积数字,并修正 SD99999 显示。
三周期分开关控制,只画数字不画图标;线段面积比沿用同向笔面积口径。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-10 04:33:40 +08:00

573 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* chart_tv_indicators.js — volume / ATR / ChanMACD */
function formatSdMarkerText(separateDiv) {
const n = Number(separateDiv);
return `SD${n === 99999 ? 0 : n}`;
}
function chartTvRenderIndicators(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;
// 转换成交量数据 - 与K线周期一致
let volumes = [];
const volumeDataSource = useSubSubPeriod ? (currentData.sub_sub_kline_data || []) : (useElementPeriod ? currentData.element_kline_data : currentData.kline_data);
console.log('成交量数据源选择:', klinePeriodLabel);
console.log('成交量数据长度:', volumeDataSource.length);
if (volumeDataSource && Array.isArray(volumeDataSource)) {
volumes = volumeDataSource.map(kline => {
// 使用与K线和MACD完全相同的时间戳计算方式
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
return {
time: timestamp,
value: parseFloat(kline.volume),
color: parseFloat(kline.close) >= parseFloat(kline.open) ? 'rgba(40, 167, 69, 0.5)' : 'rgba(220, 53, 69, 0.5)',
};
});
console.log('处理后的成交量数据点数:', volumes.length);
}
// 添加成交量图表
const volumeSeries = volumeChart.addHistogramSeries({
color: '#26a69a',
priceFormat: {
type: 'volume',
},
title: '成交量',
});
volumeSeries.setData(volumes);
tvWidget.series.volumeSeries = volumeSeries;
// 添加ATR图表
const atrLineSeries = atrChart.addLineSeries({
color: '#FF9800',
lineWidth: 2,
title: 'ATR',
lastValueVisible: false,
priceLineVisible: false,
});
// 准备ATR数据
const atrData = [];
const atrKlineDataSource = useSubSubPeriod ? (currentData.sub_sub_kline_data || []) : (useElementPeriod ? currentData.element_kline_data : currentData.kline_data);
const atrDataSource = useSubSubPeriod ? (currentData.sub_sub_atr || currentData.atr) : (useElementPeriod ? (currentData.element_atr || currentData.atr) : currentData.atr);
console.log('ATR数据源选择:', klinePeriodLabel);
console.log('ATR数据长度:', atrDataSource ? atrDataSource.length : 0);
console.log('K线数据长度:', atrKlineDataSource ? atrKlineDataSource.length : 0);
if (atrDataSource && Array.isArray(atrDataSource) && atrKlineDataSource && Array.isArray(atrKlineDataSource)) {
// 关键修复:为每个K线时间点都创建ATR数据点,包括没有ATR值的前期数据
for (let i = 0; i < atrKlineDataSource.length; i++) {
const kline = atrKlineDataSource[i];
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
// 为每个时间点都添加数据以保持时间轴对齐,但ATR为0时不显示
if (atrDataSource[i] !== undefined) {
if (atrDataSource[i] > 0) {
// ATR有效值,正常显示
atrData.push({
time: timestamp,
value: atrDataSource[i]
});
} else {
// ATR为0,添加时间点但不显示线条(使用undefined作为value
atrData.push({
time: timestamp,
value: undefined
});
}
}
}
console.log('处理后的ATR数据点数:', atrData.length);
console.log('ATR数据样本:', atrData.slice(0, 5));
}
console.log('处理后的ATR数据点数:', atrData.length);
atrLineSeries.setData(atrData);
tvWidget.series.atrLineSeries = atrLineSeries;
// 旧 MACD 图已移除,不再绘制(保留占位但彻底禁用)
if (FEATURES.legacyMacd && showMacd && currentData.macd && currentData.kline_data && Array.isArray(currentData.kline_data)) {
// 创建MACD线
const macdLineSeries = macdChart.addLineSeries({
color: '#2962FF',
lineWidth: 1,
title: 'MACD',
lastValueVisible: false, // 禁用最后值标签,防止遮挡
priceLineVisible: false, // 禁用价格线
});
// 创建信号线
const signalLineSeries = macdChart.addLineSeries({
color: '#FF6B6B',
lineWidth: 1,
title: 'Signal',
lastValueVisible: false, // 禁用最后值标签,防止遮挡
priceLineVisible: false, // 禁用价格线
});
// 创建直方图
const histogramSeries = macdChart.addHistogramSeries({
color: '#26a69a',
title: 'Histogram',
priceFormat: {
type: 'price',
precision: 4,
},
});
// 提取MACD数据 - 使用和K线数据相同的时间处理逻辑
const macdData = [];
const signalData = [];
const histogramData = [];
// 使用与K线数据相同的数据源来确保时间对齐
const klineDataSource = useSubSubPeriod ? (currentData.sub_sub_kline_data || []) : (useElementPeriod ? currentData.element_kline_data : currentData.kline_data);
const macdDataSource = useSubSubPeriod
? (currentData.sub_sub_macd || currentData.macd)
: (useElementPeriod ? (currentData.element_macd || currentData.macd) : currentData.macd);
console.log('MACD数据源选择:', useSubSubPeriod ? '次次周期' : (useElementPeriod ? '次周期' : '主周期'));
console.log('K线数据长度:', klineDataSource.length);
console.log('MACD数据:', macdDataSource);
for (let i = 0; i < klineDataSource.length; i++) {
const kline = klineDataSource[i];
// 使用与K线完全相同的时间戳计算方式
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
if (macdDataSource && macdDataSource.macd && macdDataSource.macd[i] !== undefined) {
macdData.push({
time: timestamp,
value: macdDataSource.macd[i]
});
signalData.push({
time: timestamp,
value: macdDataSource.signal[i]
});
// 设置直方图颜色
const histValue = macdDataSource.histogram[i];
histogramData.push({
time: timestamp,
value: histValue,
color: histValue >= 0 ? 'rgba(40, 167, 69, 0.5)' : 'rgba(220, 53, 69, 0.5)'
});
}
}
console.log('处理后的MACD数据点数:', macdData.length);
macdLineSeries.setData(macdData);
signalLineSeries.setData(signalData);
histogramSeries.setData(histogramData);
tvWidget.series.macdLineSeries = macdLineSeries;
tvWidget.series.signalLineSeries = signalLineSeries;
tvWidget.series.histogramSeries = histogramSeries;
}
// 添加ChanMACD图表
console.log('ChanMACD图表创建条件检查:', {
showMacd: showMacd,
chanMacdChart: !!chanMacdChart,
hasMacd: !!currentData.macd,
hasKlineData: !!currentData.kline_data,
isArray: Array.isArray(currentData.kline_data)
});
// 在创建 ChanMACD 前,确保一次性同步 U 显示开关到全局(默认不显示)
if (typeof window.showUOnMain === 'undefined') {
window.showUOnMain = $('#toggleUOnMain').is(':checked');
}
if (typeof window.showUOnElement === 'undefined') {
window.showUOnElement = $('#toggleUOnElement').is(':checked');
}
if (typeof window.showUOnSubSub === 'undefined') {
window.showUOnSubSub = $('#toggleUOnSubSub').is(':checked');
}
if (showMacd && chanMacdChart && ((useSubSubPeriod && currentData.sub_sub_macd) || (useElementPeriod && currentData.element_macd) || currentData.macd) && (useSubSubPeriod ? (currentData.sub_sub_kline_data || []) : (useElementPeriod ? currentData.element_kline_data : currentData.kline_data))) {
console.log('✅ 开始创建 ChanMACD 系列');
// 创建ChanMACD线系列
const chanMacdLineSeries = chanMacdChart.addLineSeries({
color: '#2962FF',
lineWidth: 1,
title: 'ChanMACD',
lastValueVisible: false,
priceLineVisible: false,
});
// 创建ChanMACD信号线系列
const chanMacdSignalSeries = chanMacdChart.addLineSeries({
color: '#FF6B6B',
lineWidth: 1,
title: 'ChanSignal',
lastValueVisible: false,
priceLineVisible: false,
});
// 创建ChanMACD柱状图系列
const chanMacdHistSeries = chanMacdChart.addHistogramSeries({
color: '#26a69a',
title: 'ChanHistogram',
priceFormat: {
type: 'price',
precision: 4,
},
});
// 设置ChanMACD图表的字体大小
chanMacdChart.applyOptions({
layout: {
fontSize: 10, // 设置更小的字体大小
},
rightPriceScale: {
fontSize: 10, // 设置右侧价格轴的字体大小
},
timeScale: {
fontSize: 10, // 设置时间轴的字体大小
},
});
// 使用与主图一致的数据源(小周期开启时使用小周期MACD与K线)
const klineDataSource = useSubSubPeriod ? (currentData.sub_sub_kline_data || []) : (useElementPeriod ? currentData.element_kline_data : currentData.kline_data);
const macdDataSource = useSubSubPeriod ? (currentData.sub_sub_macd || currentData.macd) : (useElementPeriod ? (currentData.element_macd || currentData.macd) : currentData.macd);
// 准备ChanMACD数据
const chanMacdData = [];
const chanSignalData = [];
const chanHistData = [];
console.log('ChanMACD数据源检查:', {
klineDataSourceLength: klineDataSource.length,
macdDataSource: !!macdDataSource,
macdLength: macdDataSource ? macdDataSource.macd.length : 0
});
console.log('ChanMACD数据源检查:', {
klineDataSourceLength: klineDataSource.length,
macdDataSource: !!macdDataSource,
macdLength: macdDataSource ? macdDataSource.macd.length : 0
});
for (let i = 0; i < klineDataSource.length; i++) {
const kline = klineDataSource[i];
if (kline && kline.date &&
i < macdDataSource.macd.length &&
macdDataSource.macd[i] !== null && macdDataSource.macd[i] !== undefined) {
// 使用与K线完全相同的时间戳计算方式
const timestamp = Math.floor(new Date(kline.date).getTime() / 1000);
chanMacdData.push({
time: timestamp,
value: macdDataSource.macd[i]
});
chanSignalData.push({
time: timestamp,
value: macdDataSource.signal[i]
});
chanHistData.push({
time: timestamp,
value: macdDataSource.histogram[i],
color: macdDataSource.histogram[i] >= 0 ? 'rgba(40, 167, 69, 0.5)' : 'rgba(220, 53, 69, 0.5)'
});
}
}
console.log('ChanMACD数据处理完成:', {
chanMacdDataLength: chanMacdData.length,
chanSignalDataLength: chanSignalData.length,
chanHistDataLength: chanHistData.length,
sampleData: chanMacdData.length > 0 ? chanMacdData[0] : null
});
// 设置ChanMACD数据
console.log('ChanMACD数据长度:', chanMacdData.length, chanSignalData.length, chanHistData.length);
if (chanMacdData.length > 0) {
chanMacdLineSeries.setData(chanMacdData);
chanMacdSignalSeries.setData(chanSignalData);
chanMacdHistSeries.setData(chanHistData);
console.log('✅ ChanMACD数据设置成功');
} else {
console.warn('⚠️ ChanMACD数据为空,无法设置数据');
}
// 保存到tvWidget
tvWidget.series.chanMacdLineSeries = chanMacdLineSeries;
tvWidget.series.chanMacdSignalSeries = chanMacdSignalSeries;
tvWidget.series.chanMacdHistSeries = chanMacdHistSeries;
console.log('✅ ChanMACD图表系列已保存到tvWidget');
// 添加ChanMACD分析标注
// 根据主/次周期开关与各自的"显示U"独立控制
const cm = useSubSubPeriod ? (currentData.sub_sub_chan_macd || currentData.chan_macd) : (useElementPeriod ? (currentData.element_chan_macd || currentData.chan_macd) : currentData.chan_macd);
// 默认不显示,必须用户勾选对应复选框
const allowU = useSubSubPeriod ? !!window.showUOnSubSub : (useElementPeriod ? !!window.showUOnElement : !!window.showUOnMain);
if (cm && allowU) {
console.log('添加ChanMACD分析标注:', {
segListLength: cm.seg_list ? cm.seg_list.length : 0,
unittfListLength: cm.unittf_list ? cm.unittf_list.length : 0,
histsetListLength: cm.histset_list ? cm.histset_list.length : 0
});
// 详细检查段数据
if (cm.seg_list && cm.seg_list.length > 0) {
console.log('段数据详情:', cm.seg_list.slice(0, 3)); // 显示前3个段
} else {
console.log('⚠️ 段数据为空或不存在');
}
addAllChanMacdMarkers(
cm.seg_list || [],
cm.unittf_list || [],
cm.histset_list || [],
{
high_position_list: cm.high_position_list || [],
high_empty_list: cm.high_empty_list || [],
low_position_list: cm.low_position_list || [],
low_empty_list: cm.low_empty_list || [],
return_zero_list: cm.return_zero_list || [],
cross0_up_list: cm.cross0_up_list || [],
cross0_down_list: cm.cross0_down_list || []
}
);
// 同时从主/次周期的 klu_list 提取 SD/CD 标记,分别使用不同样式
try {
const mainCm = currentData.chan_macd || {};
const elementCm = currentData.element_chan_macd || {};
const mainMarkers = [];
const elementMarkers = [];
// 基于时间构建 MACD 值映射,便于按时间快速获取对应的 MACD 值
const buildMacdTimeMap = (macdObj, klineArr) => {
const map = new Map();
if (!macdObj || !klineArr || !Array.isArray(klineArr)) return map;
for (let i = 0; i < klineArr.length; i++) {
const k = klineArr[i];
if (!k || !k.date) continue;
const t = Math.floor(new Date(k.date).getTime() / 1000);
const val = (macdObj.macd && macdObj.macd[i] !== undefined && macdObj.macd[i] !== null) ? macdObj.macd[i] : null;
map.set(t, val);
}
return map;
};
const mainMacdMap = buildMacdTimeMap(currentData.macd, currentData.kline_data);
const elementMacdMap = buildMacdTimeMap(
(currentData.element_macd || currentData.macd),
(currentData.element_kline_data || currentData.kline_data)
);
// 主周期 U 标记(蓝/橙,与原样式一致)
if (window.showUOnMain && Array.isArray(mainCm.klu_list)) {
mainCm.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
const macdVal = mainMacdMap.get(ts);
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
mainMarkers.push({ time: ts, position: posSd, color: '#03a9f4', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
const macdVal = mainMacdMap.get(ts);
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
mainMarkers.push({ time: ts, position: posCd, color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
mainMarkers.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
// 次周期 U 标记(使用不同配色以区分)
if (window.showUOnElement && Array.isArray(elementCm.klu_list)) {
elementCm.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
const macdVal = elementMacdMap.get(ts);
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
elementMarkers.push({ time: ts, position: posSd, color: '#9c27b0', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
const macdVal = elementMacdMap.get(ts);
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
elementMarkers.push({ time: ts, position: posCd, color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
elementMarkers.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
const subSubCm = currentData.sub_sub_chan_macd || {};
const subSubMarkers = [];
const subSubMacdMap = buildMacdTimeMap(currentData.macd, currentData.kline_data);
if (window.showUOnSubSub && Array.isArray(subSubCm.klu_list)) {
subSubCm.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
const macdVal = subSubMacdMap.get(ts);
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
subSubMarkers.push({ time: ts, position: posSd, color: '#00897b', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
const macdVal = subSubMacdMap.get(ts);
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
subSubMarkers.push({ time: ts, position: posCd, color: '#26a69a', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
subSubMarkers.push({ time: ts, position: 'belowBar', color: '#00695c', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
window.kluDivMarkersSubSub = subSubMarkers;
// 保存到全局,供主图合并标记使用
window.kluDivMarkersMain = mainMarkers;
window.kluDivMarkersElement = elementMarkers;
} catch (e) {
console.warn('处理 KLU 背驰标记出错:', e);
window.kluDivMarkersMain = [];
window.kluDivMarkersElement = [];
window.kluDivMarkersSubSub = [];
}
} else {
console.log('⚠️ 没有ChanMACD分析数据');
// 无数据时清空本次的 KLU 背驰标记
window.kluDivMarkersMain = [];
window.kluDivMarkersElement = [];
window.kluDivMarkersSubSub = [];
}
} else {
console.log('⚠️ ChanMACD图表创建条件不满足');
}
// 独立于当前显示周期:计算主/次周期 SD/CD 标记(用于主图合并显示)
try {
const mainCmAll = currentData.chan_macd || {};
const elementCmAll = currentData.element_chan_macd || {};
const mainMarkersAll = [];
const elementMarkersAll = [];
// 构建 MACD 时间映射,用于依据 MACD 正负决定 SD/CD 的显示上下位置
const buildMacdTimeMapAll = (macdObj, klineArr) => {
const map = new Map();
if (!macdObj || !klineArr || !Array.isArray(klineArr)) return map;
for (let i = 0; i < klineArr.length; i++) {
const k = klineArr[i];
if (!k || !k.date) continue;
const t = Math.floor(new Date(k.date).getTime() / 1000);
const val = (macdObj.macd && macdObj.macd[i] !== undefined && macdObj.macd[i] !== null) ? macdObj.macd[i] : null;
map.set(t, val);
}
return map;
};
const mainMacdMapAll = buildMacdTimeMapAll(currentData.macd, currentData.kline_data);
const elementMacdMapAll = buildMacdTimeMapAll(
(currentData.element_macd || currentData.macd),
(currentData.element_kline_data || currentData.kline_data)
);
if ((typeof window.showUOnMain === 'undefined' ? false : window.showUOnMain) && Array.isArray(mainCmAll.klu_list)) {
mainCmAll.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
const macdVal = mainMacdMapAll.get(ts);
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
mainMarkersAll.push({ time: ts, position: posSd, color: '#03a9f4', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
const macdVal = mainMacdMapAll.get(ts);
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
mainMarkersAll.push({ time: ts, position: posCd, color: '#ff9800', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
mainMarkersAll.push({ time: ts, position: 'belowBar', color: '#8bc34a', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
if ((typeof window.showUOnElement === 'undefined' ? false : window.showUOnElement) && Array.isArray(elementCmAll.klu_list)) {
elementCmAll.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
const macdVal = elementMacdMapAll.get(ts);
const posSd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'aboveBar';
elementMarkersAll.push({ time: ts, position: posSd, color: '#9c27b0', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
const macdVal = elementMacdMapAll.get(ts);
const posCd = (macdVal > 0) ? 'aboveBar' : (macdVal < 0) ? 'belowBar' : 'belowBar';
elementMarkersAll.push({ time: ts, position: posCd, color: '#4caf50', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
elementMarkersAll.push({ time: ts, position: 'belowBar', color: '#009688', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
window.kluDivMarkersMain = mainMarkersAll;
window.kluDivMarkersElement = elementMarkersAll;
const subSubCmAll = currentData.sub_sub_chan_macd || {};
const subSubMarkersAll = [];
if (window.showUOnSubSub && Array.isArray(subSubCmAll.klu_list)) {
subSubCmAll.klu_list.forEach((item) => {
if (!item || !item.time) return;
const ts = Math.floor(new Date(item.time).getTime() / 1000);
if (isNaN(ts)) return;
if (Number(item.separate_div) > 0) {
subSubMarkersAll.push({ time: ts, position: 'aboveBar', color: '#00897b', shape: 'arrowUp', text: formatSdMarkerText(item.separate_div), size: 0.6 });
}
if (item.continue_div === true) {
subSubMarkersAll.push({ time: ts, position: 'belowBar', color: '#26a69a', shape: 'arrowDown', text: 'CD', size: 0.6 });
}
if (item.near0_return && Number(item.near0_return) > 0) {
subSubMarkersAll.push({ time: ts, position: 'belowBar', color: '#00695c', shape: 'circle', text: `${Number(item.near0_return)}`, size: 0.6 });
}
});
}
window.kluDivMarkersSubSub = subSubMarkersAll;
if (typeof refreshUnittfOverlayFromData === 'function') {
refreshUnittfOverlayFromData(currentData);
}
} catch (e) {
console.warn('独立计算 KLU 背驰标记出错:', e);
window.kluDivMarkersMain = [];
window.kluDivMarkersElement = [];
window.kluDivMarkersSubSub = [];
}
}