feat(ECR-008): 拆分主站 chart_tv.js 为多模块薄门面
行为冻结物理拆分;保留 initTradingView/dispose 对外 API;无打包器。node --check 全绿。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,564 @@
|
||||
/* chart_tv_indicators.js — volume / ATR / ChanMACD */
|
||||
|
||||
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 = useElementPeriod ?
|
||||
(currentData.element_macd || currentData.macd) : // 如果有次周期MACD数据则使用,否则使用主周期
|
||||
currentData.macd; // 主周期使用主周期MACD数据
|
||||
|
||||
console.log('MACD数据源选择:', 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: `SD${Number(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: `SD${Number(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: `SD${Number(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: `SD${Number(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: `SD${Number(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: `SD${Number(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;
|
||||
} catch (e) {
|
||||
console.warn('独立计算 KLU 背驰标记出错:', e);
|
||||
window.kluDivMarkersMain = [];
|
||||
window.kluDivMarkersElement = [];
|
||||
window.kluDivMarkersSubSub = [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user