自动刷新改用 tail update 与 scrollToPosition 恢复视窗,避免 setData 后跳到最右;拆分 chart_tv 模块并扩展 analyze/recent API。同步威科夫分析、pipeline 增量构建及相关策略与配置。 Co-authored-by: Cursor <cursoragent@cursor.com>
565 lines
30 KiB
JavaScript
565 lines
30 KiB
JavaScript
/* 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 = [];
|
||
}
|
||
}
|