/* main.js */
$(document).ready(function() {
// 初始化技术指标下拉菜单
initIndicatorDropdown();
// 设置默认的筛选时间(最近7天)
const now = new Date();
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
// 添加页面滚动事件监听器,清除十字线延长线
$(window).on('scroll', function() {
try {
// 清除所有十字线延长线,防止它们跟着页面滚动
const existingVolumeLines = document.querySelectorAll('.volume-crosshair-line');
existingVolumeLines.forEach(line => line.remove());
const existingAtrLines = document.querySelectorAll('.atr-crosshair-line');
existingAtrLines.forEach(line => line.remove());
const existingMacdLines = document.querySelectorAll('.macd-crosshair-line');
existingMacdLines.forEach(line => line.remove());
const existingChanMacdLines = document.querySelectorAll('.chanmacd-crosshair-line');
existingChanMacdLines.forEach(line => line.remove());
} catch (e) {
console.debug('清除滚动中的十字线时出错:', e);
}
});
});
// ====== ChanMACD图表相关函数 ======
// 清除ChanMACD标注
function clearChanMacdMarkers() {
// 清除所有系列的标记
if (tvWidget.series.chanMacdLineSeries) {
tvWidget.series.chanMacdLineSeries.setMarkers([]);
}
if (tvWidget.series.chanMacdSignalSeries) {
tvWidget.series.chanMacdSignalSeries.setMarkers([]);
}
if (tvWidget.series.chanMacdHistSeries) {
tvWidget.series.chanMacdHistSeries.setMarkers([]);
}
// 清空全局UnitTF标记,避免旧数据残留影响主图合并
window.unittfMarkers = [];
}
// 添加所有ChanMACD标记
function addAllChanMacdMarkers(segList, unittfList, histsetList, stateMarkers) {
const macdMarkers = [];
const signalMarkers = [];
const histMarkers = [];
const boundaryMarkers = [];
const uTooltipMarkers = [];
// 添加段标记到MACD线
console.log('处理段标记,段数量:', segList.length);
segList.forEach((seg, index) => {
console.log(`段${index}:`, {
start_time: seg.start_time,
end_time: seg.end_time,
seg_dir: seg.seg_dir,
has_start: !!seg.start_time,
has_end: !!seg.end_time
});
if (!seg.start_time) {
console.log(`段${index}没有开始时间,跳过`);
return;
}
const startTime = new Date(seg.start_time).getTime() / 1000;
console.log(`段${index}开始时间戳:`, startTime);
macdMarkers.push({
time: startTime,
position: 'aboveBar',
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
shape: 'square',
text: `S${index}`,
size: 0.5
});
if (seg.end_time) {
const endTime = new Date(seg.end_time).getTime() / 1000;
console.log(`段${index}结束时间戳:`, endTime);
macdMarkers.push({
time: endTime,
position: 'aboveBar',
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
shape: 'square',
text: `S${index}E`,
size: 0.5
});
}
});
console.log('生成的段标记数量:', macdMarkers.length);
// 添加UnitTF标记(用于U)
console.log('DEBUG: U 源数据条数:', Array.isArray(unittfList) ? unittfList.length : 'not array');
unittfList.forEach((unittf, index) => {
if (!unittf.start_time || unittf.invalid) return;
const startTime = new Date(unittf.start_time).getTime() / 1000;
if (index === 0) {
console.log('DEBUG: U0 示例:', unittf);
}
const startMarker = {
time: startTime,
position: unittf.dir > 0 ? 'aboveBar' : 'belowBar',
color: unittf.dir > 0 ? '#ff9800' : '#9c27b0',
shape: 'circle',
text: `U${index}`,
size: 0.5
};
signalMarkers.push(startMarker);
// tooltip(开始)
uTooltipMarkers.push({
time: startTime,
tooltip: `
UnitTF(${unittf.dir > 0 ? '正区' : '负区'}) 开始
峰值: ${unittf.peak_abs ?? '-'} 长度: ${unittf.length ?? '-'}
类型: ${unittf.start_type ?? '-'}
时间: ${unittf.start_time}
`
});
if (unittf.end_time) {
const endTime = new Date(unittf.end_time).getTime() / 1000;
const endMarker = {
time: endTime,
position: unittf.dir > 0 ? 'aboveBar' : 'belowBar',
color: unittf.dir > 0 ? '#ff9800' : '#9c27b0',
shape: 'circle',
text: `U${index}E`,
size: 0.5
};
signalMarkers.push(endMarker);
// tooltip(结束)
uTooltipMarkers.push({
time: endTime,
tooltip: `
UnitTF(${unittf.dir > 0 ? '正区' : '负区'}) 结束
峰值: ${unittf.peak_abs ?? '-'} 长度: ${unittf.length ?? '-'}
类型: ${unittf.end_type ?? '-'}
时间: ${unittf.end_time}
`
});
}
});
// 识别"U 结束与新 U 开始同一根K"的边界,并显示合成标记(即使前一个U是 invalid 也显示边界)
for (let i = 0; i + 1 < unittfList.length; i++) {
const cur = unittfList[i];
const nxt = unittfList[i + 1];
if (!cur.end_time || !nxt.start_time) continue;
const tEnd = new Date(cur.end_time).getTime();
const tStart = new Date(nxt.start_time).getTime();
if (!isNaN(tEnd) && tEnd === tStart) {
const ts = Math.floor(tEnd / 1000);
const color = nxt.dir > 0 ? '#ffb74d' : '#ba68c8';
const marker = {
time: ts,
position: nxt.dir > 0 ? 'aboveBar' : 'belowBar',
color: color,
shape: 'square',
text: 'U↔',
size: 0.6
};
boundaryMarkers.push(marker);
uTooltipMarkers.push({
time: ts,
tooltip: `\n U 结束 + 新 U 开始 (边界)
\n 结束方向: ${cur.dir > 0 ? '正区' : '负区'} → 新方向: ${nxt.dir > 0 ? '正区' : '负区'}
\n 时间: ${nxt.start_time}\n
`
});
}
}
// 添加HistSet标记到Histogram
histsetList.forEach((histset, index) => {
if (!histset.start_time) return;
const startTime = new Date(histset.start_time).getTime() / 1000;
if (false) {
histMarkers.push({
time: startTime,
position: histset.histset_dir === 'ABOVE' ? 'aboveBar' : 'belowBar',
color: histset.histset_dir === 'ABOVE' ? '#4caf50' : '#f44336',
shape: 'arrowUp',
text: `H${index}`,
size: 0.5
});
if (histset.end_time) {
const endTime = new Date(histset.end_time).getTime() / 1000;
histMarkers.push({
time: endTime,
position: histset.histset_dir === 'ABOVE' ? 'aboveBar' : 'belowBar',
color: histset.histset_dir === 'ABOVE' ? '#4caf50' : '#f44336',
shape: 'arrowDown',
text: `H${index}E`,
size: 0.5
});
}
}
});
// 设置所有标记
console.log('设置段标记到图表,标记数量:', macdMarkers.length);
if (tvWidget.series.chanMacdLineSeries && macdMarkers.length > 0) {
tvWidget.series.chanMacdLineSeries.setMarkers(macdMarkers);
console.log('✅ 段标记已设置到chanMacdLineSeries');
} else {
console.log('⚠️ 无法设置段标记:', {
hasSeries: !!tvWidget.series.chanMacdLineSeries,
markersLength: macdMarkers.length
});
}
// 保存到全局,供主图与分型一起统一合并绘制(仅在开关开启时)
console.log('DEBUG: U 标记数量:', signalMarkers.length);
const allowUMerge = (window.showUOnMain && window.showUOnElement);
window.unittfMarkers = allowUMerge ? [...signalMarkers, ...boundaryMarkers] : [];
if (uTooltipMarkers.length > 0) {
if (window.fxMarkers) {
window.fxMarkers = [ ...window.fxMarkers, ...uTooltipMarkers ];
} else {
window.fxMarkers = uTooltipMarkers;
}
}
// 同时在ChanMACD的Signal子图上标注U
if (tvWidget.series.chanMacdSignalSeries && (signalMarkers.length > 0 || boundaryMarkers.length > 0)) {
tvWidget.series.chanMacdSignalSeries.setMarkers([...signalMarkers, ...boundaryMarkers]);
}
if (tvWidget.series.chanMacdHistSeries && histMarkers.length > 0) {
tvWidget.series.chanMacdHistSeries.setMarkers(histMarkers);
}
// 添加状态标记
if (stateMarkers) {
addStateMarkers(stateMarkers);
}
}
// 添加状态标记
function addStateMarkers(stateMarkers) {
const stateMarkersList = [];
const stateTooltips = [];
// 调试信息
console.log('DEBUG: 状态标记数据:', stateMarkers);
console.log('DEBUG: 高位列表长度:', stateMarkers.high_position_list ? stateMarkers.high_position_list.length : 0);
console.log('DEBUG: 低位列表长度:', stateMarkers.low_position_list ? stateMarkers.low_position_list.length : 0);
console.log('DEBUG: 高位空列表长度:', stateMarkers.high_empty_list ? stateMarkers.high_empty_list.length : 0);
console.log('DEBUG: 低位空列表长度:', stateMarkers.low_empty_list ? stateMarkers.low_empty_list.length : 0);
// HP/HPE:仅使用高位术语(正负两侧统一展示为HP/HPE)
const hpHeTemp = [];
let hpIdx = 0; // 高位峰值计数
let heIdx = 0; // 高位空(HPE)计数
(stateMarkers.high_position_list || []).forEach(m => {
if (!m.time) return;
const t = new Date(m.time).getTime()/1000;
const color = '#e91e63';
hpHeTemp.push({ t, position: 'belowBar', color, text: `HP${hpIdx}` });
stateTooltips.push({
time: t,
tooltip: `HP${hpIdx} 峰值
MACD:${(m.macd??'').toFixed?.(4)||m.macd}
SIGNAL:${(m.signal??'').toFixed?.(4)||m.signal}
HIST:${(m.macdhist??'').toFixed?.(4)||m.macdhist}
`
});
hpIdx++;
});
(stateMarkers.low_position_list || []).forEach(m => {
if (!m.time) return;
const t = new Date(m.time).getTime()/1000;
const color = '#4caf50';
// 低位峰值也统一标记为 HP(按需求不使用 LP)
hpHeTemp.push({ t, position: 'aboveBar', color, text: `HP${hpIdx}` });
stateTooltips.push({
time: t,
tooltip: `HP${hpIdx} 峰值(正区)
MACD:${(m.macd??'').toFixed?.(4)||m.macd}
SIGNAL:${(m.signal??'').toFixed?.(4)||m.signal}
HIST:${(m.macdhist??'').toFixed?.(4)||m.macdhist}
`
});
hpIdx++;
});
(stateMarkers.high_empty_list || []).forEach(m => {
if (!m.time) return;
const t = new Date(m.time).getTime()/1000;
const color = '#ff9800';
hpHeTemp.push({ t, position: 'belowBar', color, text: `HPE${heIdx}` });
stateTooltips.push({
time: t,
tooltip: `HPE${heIdx} 黄白交叉
MACD:${(m.macd??'').toFixed?.(4)||m.macd}
SIGNAL:${(m.signal??'').toFixed?.(4)||m.signal}
HIST:${(m.macdhist??'').toFixed?.(4)||m.macdhist}
`
});
heIdx++;
});
(stateMarkers.low_empty_list || []).forEach(m => {
if (!m.time) return;
const t = new Date(m.time).getTime()/1000;
const color = '#17a2b8';
// 低位空也统一标记为 HPE(按需求不使用 LPE)
hpHeTemp.push({ t, position: 'aboveBar', color, text: `HPE${heIdx}` });
stateTooltips.push({
time: t,
tooltip: `HPE${heIdx} 黄白交叉(正区)
MACD:${(m.macd??'').toFixed?.(4)||m.macd}
SIGNAL:${(m.signal??'').toFixed?.(4)||m.signal}
HIST:${(m.macdhist??'').toFixed?.(4)||m.macdhist}
`
});
heIdx++;
});
hpHeTemp.sort((a,b)=>a.t-b.t).forEach(it => {
stateMarkersList.push({
time: it.t,
position: it.position,
color: it.color,
shape: 'diamond',
text: it.text,
size: 0.65
});
});
// 设置状态标记到 MACD 线
if (tvWidget.series.chanMacdLineSeries && stateMarkersList.length > 0) {
tvWidget.series.chanMacdLineSeries.setMarkers(stateMarkersList);
console.log('✅ 状态标记已设置到MACD线,数量:', stateMarkersList.length);
}
// 将状态标记的 tooltip 合并入全局,主图悬浮可见
if (stateTooltips.length > 0) {
if (window.fxMarkers) {
window.fxMarkers = [...window.fxMarkers, ...stateTooltips];
} else {
window.fxMarkers = stateTooltips;
}
}
}
// 保留原函数用于向后兼容(但不使用)
function addChanMacdSegMarkers(segList) {
const markers = [];
segList.forEach((seg, index) => {
if (!seg.start_time) return;
const startTime = new Date(seg.start_time).getTime() / 1000;
if (false){
// 添加起点标记
markers.push({
time: startTime,
position: 'aboveBar',
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
shape: 'square',
text: `S${index}`,
size: 0.5
});
// 如果有结束时间,添加结束标记
if (seg.end_time) {
const endTime = new Date(seg.end_time).getTime() / 1000;
markers.push({
time: endTime,
position: 'aboveBar',
color: seg.seg_dir === 'ABOVE' ? '#e91e63' : '#4caf50',
shape: 'square',
text: `S${index}E`,
size: 0.5
});
}
}
});
// 设置标记到MACD线上
if (tvWidget.series.chanMacdLineSeries && markers.length > 0) {
tvWidget.series.chanMacdLineSeries.setMarkers(markers);
}
}
// 添加UnitTF标注
function addChanMacdUnitTFMarkers(unittfList) {
const markers = [];
unittfList.forEach((unittf, index) => {
if (!unittf.start_time || unittf.invalid) return;
const startTime = new Date(unittf.start_time).getTime() / 1000;
// 添加起点标记
markers.push({
time: startTime,
position: 'belowBar',
color: unittf.dir > 0 ? '#ff9800' : '#9c27b0',
shape: 'circle',
text: `U${index}`,
size: 0.5
});
// 如果有结束时间,添加结束标记
if (unittf.end_time) {
const endTime = new Date(unittf.end_time).getTime() / 1000;
markers.push({
time: endTime,
position: 'belowBar',
color: unittf.dir > 0 ? '#ff9800' : '#9c27b0',
shape: 'circle',
text: `U${index}E`,
size: 0.5
});
}
});
// 设置标记到信号线上
if (tvWidget.series.chanMacdSignalSeries && markers.length > 0) {
tvWidget.series.chanMacdSignalSeries.setMarkers(markers);
}
}
// 添加HistSet标注
function addChanMacdHistSetMarkers(histsetList) {
const markers = [];
histsetList.forEach((histset, index) => {
if (!histset.start_time) return;
const startTime = new Date(histset.start_time).getTime() / 1000;
// 添加起点标记
markers.push({
time: startTime,
position: histset.histset_dir === 'ABOVE' ? 'aboveBar' : 'belowBar',
color: histset.histset_dir === 'ABOVE' ? '#4caf50' : '#f44336',
shape: 'arrowUp',
text: `H${index}`,
size: 0.5
});
// 如果有结束时间,添加结束标记
if (histset.end_time) {
const endTime = new Date(histset.end_time).getTime() / 1000;
markers.push({
time: endTime,
position: histset.histset_dir === 'ABOVE' ? 'aboveBar' : 'belowBar',
color: histset.histset_dir === 'ABOVE' ? '#4caf50' : '#f44336',
shape: 'arrowDown',
text: `H${index}E`,
size: 0.5
});
}
});
// 设置标记到柱状图上
if (tvWidget.series.chanMacdHistSeries && markers.length > 0) {
tvWidget.series.chanMacdHistSeries.setMarkers(markers);
}
}