Web 加上资金面/情绪叠图,并收紧默认图面。

默认主/次/次次改为 45m/15m/5m、开始时间一周;SD/CD 按 hist 摆位置和箭头;去掉笔线段背驰与第四类勾选。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-09-12 02:25:44 +08:00
co-authored by Cursor
parent b301ad22c9
commit c22cd48f36
24 changed files with 1200 additions and 306 deletions
+32 -140
View File
@@ -82,11 +82,6 @@ var SUB_SUB_KLC_TREND_STYLE = {
UNKNOWN: { position: 'inBar', color: '#004d40', shape: 'square', size: 0.5 }
};
function parseAreaDivValue(v) {
var n = Number(v);
return (isFinite(n) && n !== 0) ? n : 0;
}
function pushAreaTextLabel(out, item, style, text) {
if (!item || !item.end_time || !text) return;
var ts = Math.floor(new Date(item.end_time).getTime() / 1000);
@@ -104,25 +99,6 @@ function pushAreaTextLabel(out, item, style, text) {
});
}
function pushAreaDivMarker(out, item, style) {
var div = parseAreaDivValue(item && item.macd_div);
if (!div) return;
pushAreaTextLabel(out, item, style, div.toFixed(2));
}
function collectAreaDivMarkers(biList, uncompletedBi, segList, uncompletedSeg, biStyle, segStyle, showBi, showSeg) {
var out = [];
if (showBi) {
(biList || []).forEach(function (bi) { pushAreaDivMarker(out, bi, biStyle); });
(uncompletedBi || []).forEach(function (bi) { pushAreaDivMarker(out, bi, biStyle); });
}
if (showSeg) {
(segList || []).forEach(function (seg) { pushAreaDivMarker(out, seg, segStyle); });
(uncompletedSeg || []).forEach(function (seg) { pushAreaDivMarker(out, seg, segStyle); });
}
return out;
}
function formatMacdAreaText(v) {
var n = Number(v);
if (!isFinite(n) || n === 0) return '';
@@ -197,54 +173,6 @@ function buildAreaHistMarkersFromData(data) {
return markers;
}
function buildAreaDivMarkersFromData(data) {
var markers = [];
if (!data) return markers;
var showMainBi = $('#showMainMacdDiv').is(':checked');
var showMainSeg = $('#showMainSegMacdDiv').is(':checked');
if (showMainBi || showMainSeg) {
markers = markers.concat(collectAreaDivMarkers(
data.bi_list,
data.uncompleted_bi_list,
data.seg_list,
data.uncompleted_seg_list,
{ color: '#e53935', size: 0.6 },
{ color: '#fb8c00', size: 0.7 },
showMainBi,
showMainSeg
));
}
var showElementBi = $('#showElementMacdDiv').is(':checked');
var showElementSeg = $('#showElementSegMacdDiv').is(':checked');
if (showElementBi || showElementSeg) {
markers = markers.concat(collectAreaDivMarkers(
data.element_bi_list,
data.element_uncompleted_bi_list,
data.element_seg_list,
data.element_uncompleted_seg_list,
{ color: '#8e24aa', size: 0.55 },
{ color: '#5e35b1', size: 0.65 },
showElementBi,
showElementSeg
));
}
var showSubSubBi = $('#showSubSubMacdDiv').is(':checked');
var showSubSubSeg = $('#showSubSubSegMacdDiv').is(':checked');
if (showSubSubBi || showSubSubSeg) {
markers = markers.concat(collectAreaDivMarkers(
data.sub_sub_bi_list,
data.sub_sub_uncompleted_bi_list,
data.sub_sub_seg_list,
data.sub_sub_uncompleted_seg_list,
{ color: '#00897b', size: 0.5 },
{ color: '#00695c', size: 0.6 },
showSubSubBi,
showSubSubSeg
));
}
return markers;
}
function buildKlcTrendMarker(timeAligned, trendRaw, palette) {
var kind = normalizeKlcTrendRaw(trendRaw);
var style = palette[kind] || palette.UNKNOWN || palette.FLAT;
@@ -299,6 +227,21 @@ function getMainPriceSeries() {
s.lineSeries || s.areaSeries || s.baselineSeries || null;
}
function plotLeftOffset(chart) {
try {
var left = chart && chart.priceScale && chart.priceScale('left');
if (!left) return 0;
var visible = true;
try {
var opts = left.options && left.options();
if (opts && opts.visible === false) return 0;
} catch (e) {}
return (typeof left.width === 'function' ? left.width() : 0) || 0;
} catch (e) {
return 0;
}
}
function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
if (!mainChart || !mainChartContainer) return;
if (typeof window._fxBoxOverlayCleanup === 'function') {
@@ -330,7 +273,7 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
var series = getMainPriceSeries();
if (!series || (!boxes.length && !labels.length)) return '0';
var ts = mainChart.timeScale();
var parts = [boxes.length, labels.length];
var parts = [boxes.length, labels.length, quant(plotLeftOffset(mainChart))];
if (boxes.length) {
var a = boxes[0];
var b = boxes[boxes.length - 1];
@@ -374,18 +317,20 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
return;
}
var ts = mainChart.timeScale();
var x0 = plotLeftOffset(mainChart);
for (var i = 0; i < boxes.length; i++) {
var box = boxes[i];
var x = ts.timeToCoordinate(box.time);
var y1 = series.priceToCoordinate(box.hi);
var y2 = series.priceToCoordinate(box.lo);
if (x == null || y1 == null || y2 == null) continue;
var px = Math.round(x + x0) + 0.5;
ctx2.beginPath();
ctx2.strokeStyle = box.color;
ctx2.lineWidth = 1;
ctx2.setLineDash([4, 3]);
ctx2.moveTo(Math.round(x) + 0.5, y1);
ctx2.lineTo(Math.round(x) + 0.5, y2);
ctx2.moveTo(px, y1);
ctx2.lineTo(px, y2);
ctx2.stroke();
}
ctx2.setLineDash([]);
@@ -399,7 +344,7 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
if (lx == null || ly == null) continue;
ctx2.fillStyle = lab.color;
ctx2.textBaseline = lab.above ? 'bottom' : 'top';
ctx2.fillText(lab.text, Math.round(lx), lab.above ? ly - 3 : ly + 3);
ctx2.fillText(lab.text, Math.round(lx + x0), lab.above ? ly - 3 : ly + 3);
}
}
lastSig = sampleSig();
@@ -1182,56 +1127,6 @@ function chartTvRenderOverlays(ctx) {
window.bspMarkers = [];
}
// 第四类买卖点(B4/S4):中枢突破回抽后当根入场,位置同 B3/S3 但早 7~8 根。
// 与 BSP 分开收集,因为它数量远多于 B1/B2/B3,混在一个开关里图会糊掉。
if ($('#showMainFastBsp').is(':checked') || $('#showElementFastBsp').is(':checked') || $('#showSubSubFastBsp').is(':checked')) {
// 深色 = 区间套(大级别分型同向) + 中枢顺向推进都满足;浅色 = 未通过过滤
const FAST_BSP_STYLE = {
'BUY': { strong: '#FF6D00', weak: '#FFCC80', text: 'B4', position: 'belowBar' },
'SELL': { strong: '#0091EA', weak: '#81D4FA', text: 'S4', position: 'aboveBar' },
};
const onlyFiltered = ($('#fastBspFilterMode').val() || 'all') === 'filtered';
const allFastBspMarkers = [];
const collectFastBsp = function(list, prefix, label) {
(list || []).forEach(function(bsp) {
try {
const ts = Math.floor(new Date(bsp.time).getTime() / 1000);
if (isNaN(ts)) return;
const style = FAST_BSP_STYLE[(bsp.dir || '').toUpperCase()];
if (!style) return;
const passed = !!(bsp.htf_agree && bsp.ladder_ok);
if (onlyFiltered && !passed) return;
allFastBspMarkers.push({
time: ts,
position: style.position,
color: passed ? style.strong : style.weak,
text: prefix + (passed ? style.text : style.text.toLowerCase()),
size: passed ? 2 : 1
});
} catch (e) {
console.error(label + '第四类买卖点处理出错:', e);
}
});
};
if ($('#showMainFastBsp').is(':checked')) {
collectFastBsp(currentData.fast_bsp_list, '', '主周期');
}
if ($('#showElementFastBsp').is(':checked')) {
collectFastBsp(currentData.element_fast_bsp_list, 'e', '次周期');
}
if ($('#showSubSubFastBsp').is(':checked')) {
collectFastBsp(currentData.sub_sub_fast_bsp_list, 's', '次次周期');
}
allFastBspMarkers.sort((a, b) => a.time - b.time);
window.fastBspMarkers = allFastBspMarkers;
console.log(`绘制第四类买卖点,共${allFastBspMarkers.length}个标记(${onlyFiltered ? '仅过滤后' : '全部'}`);
} else {
window.fastBspMarkers = [];
}
// 添加买卖点标记(旧版,保留兼容)
// 这里为了与主面板上的「买卖点」开关保持一致,
// 同时响应顶部的 `#showMainBsp` 复选框
@@ -1807,7 +1702,7 @@ function chartTvRenderOverlays(ctx) {
lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
title: '次周期布林上轨'
title: '次布林上轨'
});
elementUpperBandSeries.setData(elementUpperBandData);
@@ -1818,7 +1713,7 @@ function chartTvRenderOverlays(ctx) {
lineStyle: 2, // 虚线
lastValueVisible: false,
priceLineVisible: false,
title: '次周期布林下轨'
title: '次布林下轨'
});
elementLowerBandSeries.setData(elementLowerBandData);
@@ -1828,7 +1723,7 @@ function chartTvRenderOverlays(ctx) {
lineWidth: 1,
lastValueVisible: false,
priceLineVisible: false,
title: '次周期布林中轨'
title: '次布林中轨'
});
elementMiddleBandSeries.setData(elementMiddleBandData);
@@ -1943,7 +1838,7 @@ function chartTvRenderOverlays(ctx) {
const fxMarker = {
time: timestamp,
tooltip: `<div style="color: ${strengthColor}; font-weight: bold;">
周期${fx.is_bottom ? '底分型' : '顶分型'}(合): ${fx.fx_type}<br>
${fx.is_bottom ? '底分型' : '顶分型'}(合): ${fx.fx_type}<br>
强度分数: ${fx.fx_strength}分<br>
强度等级: ${fx.fx_strength_level}<br>
是否强分型: ${fx.is_strong_fx ? '是' : '否'}<br>
@@ -2007,7 +1902,7 @@ function chartTvRenderOverlays(ctx) {
const fxMarker = {
time: timestamp,
tooltip: `<div style="color: ${strengthColor}; font-weight: bold;">
周期${fx.is_bottom ? '底分型' : '顶分型'}(原): ${fx.fx_type}<br>
${fx.is_bottom ? '底分型' : '顶分型'}(原): ${fx.fx_type}<br>
强度分数: ${fx.fx_strength}分<br>
强度等级: ${fx.fx_strength_level}<br>
是否强分型: ${fx.is_strong_fx ? '是' : '否'}<br>
@@ -2045,7 +1940,7 @@ function chartTvRenderOverlays(ctx) {
window.fxMarkers = [];
}
window._areaTextLabels = alignMarkersToCandles(
buildAreaDivMarkersFromData(currentData).concat(buildAreaHistMarkersFromData(currentData)),
buildAreaHistMarkersFromData(currentData),
candles
);
if (typeof window._redrawFxBoxVerticalOverlay === 'function') {
@@ -2140,7 +2035,7 @@ function chartTvRenderOverlays(ctx) {
const elementFxMarker = {
time: timestamp,
tooltip: `<div style="color: ${strengthColor}; font-weight: bold;">
小周期${fx.is_bottom ? '底分型' : '顶分型'}(合): ${fx.fx_type}<br>
${fx.is_bottom ? '底分型' : '顶分型'}(合): ${fx.fx_type}<br>
强度分数: ${fx.fx_strength}分<br>
强度等级: ${fx.fx_strength_level}<br>
是否强分型: ${fx.is_strong_fx ? '是' : '否'}<br>
@@ -2196,7 +2091,7 @@ function chartTvRenderOverlays(ctx) {
const elementFxMarker = {
time: timestamp,
tooltip: `<div style="color: ${strengthColor}; font-weight: bold;">
小周期${fx.is_bottom ? '底分型' : '顶分型'}(原): ${fx.fx_type}<br>
${fx.is_bottom ? '底分型' : '顶分型'}(原): ${fx.fx_type}<br>
强度分数: ${fx.fx_strength}分<br>
强度等级: ${fx.fx_strength_level}<br>
是否强分型: ${fx.is_strong_fx ? '是' : '否'}<br>
@@ -2365,8 +2260,7 @@ function chartTvRenderOverlays(ctx) {
...(window.kluDivMarkersElement || []),
...(window.kluDivMarkersSubSub || []),
...trendMarkersToUse,
...(window.bspMarkers || []),
...(window.fastBspMarkers || [])
...(window.bspMarkers || [])
];
if (combinedMarkers.length > 0) {
console.log(
@@ -2375,7 +2269,6 @@ function chartTvRenderOverlays(ctx) {
'个,小周期分型:', allElementFxMarkers.length,
'个,背驰:', (window.kluDivMarkersMain || []).length,
'个,BSP标记:', (window.bspMarkers || []).length,
'个,第四类标记:', (window.fastBspMarkers || []).length,
'个)'
);
@@ -2477,15 +2370,14 @@ function chartTvRenderOverlays(ctx) {
// 这里的 onlyMainAndU 实际上是「最终要挂到主K线上」的一组标记
// 之前没有把 window.bspMarkers 合进去,导致上面已经合并了 BSP 标记,
// 但在这里再次调用 setMarkers 时把 BSP 覆盖掉了,从而前端看不到买卖点。
// 修复:把 BSP 标记一并合并进来。第四类买卖点同理,两处都要带上。
// 修复:把 BSP 标记一并合并进来。两处都要带上,否则后面这次 setMarkers 会把买卖点盖掉
const onlyMainAndU = [
...(window.mainFxMarkers || []),
...(window.kluDivMarkersMain || []),
...(window.kluDivMarkersElement || []),
...(window.kluDivMarkersSubSub || []),
...trendMarkersToUse,
...(window.bspMarkers || []),
...(window.fastBspMarkers || [])
...(window.bspMarkers || [])
];
if (onlyMainAndU.length > 0) {
console.log('仅设置', onlyMainAndU.length, '个主图标记(主周期分型:', (window.mainFxMarkers || []).length, ',背驰:', (window.kluDivMarkersMain || []).length, '');