主图增加笔/线段背驰与面积数字,并修正 SD99999 显示。

三周期分开关控制,只画数字不画图标;线段面积比沿用同向笔面积口径。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-09-10 04:33:40 +08:00
co-authored by Cursor
parent 6b72ba226b
commit 2cd50f1e01
11 changed files with 378 additions and 117 deletions
+216 -16
View File
@@ -82,6 +82,169 @@ 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);
if (isNaN(ts)) return;
var price = Number(item.end_price);
if (!isFinite(price)) price = Number(item.start_price);
if (!isFinite(price)) return;
var up = Number(item.direction) === 1;
out.push({
time: ts,
price: price,
text: text,
color: style.color,
above: up
});
}
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 '';
var abs = Math.abs(n);
if (abs >= 100) return n.toFixed(0);
if (abs >= 10) return n.toFixed(1);
return n.toFixed(2);
}
function pushAreaHistMarker(out, item, style) {
pushAreaTextLabel(out, item, style, formatMacdAreaText(item && item.macd_hist));
}
function collectAreaHistMarkers(biList, uncompletedBi, segList, uncompletedSeg, biStyle, segStyle, showBi, showSeg) {
var out = [];
if (showBi) {
(biList || []).forEach(function (bi) { pushAreaHistMarker(out, bi, biStyle); });
(uncompletedBi || []).forEach(function (bi) { pushAreaHistMarker(out, bi, biStyle); });
}
if (showSeg) {
(segList || []).forEach(function (seg) { pushAreaHistMarker(out, seg, segStyle); });
(uncompletedSeg || []).forEach(function (seg) { pushAreaHistMarker(out, seg, segStyle); });
}
return out;
}
function buildAreaHistMarkersFromData(data) {
var markers = [];
if (!data) return markers;
var showMainBi = $('#showMainBiArea').is(':checked');
var showMainSeg = $('#showMainSegArea').is(':checked');
if (showMainBi || showMainSeg) {
markers = markers.concat(collectAreaHistMarkers(
data.bi_list,
data.uncompleted_bi_list,
data.seg_list,
data.uncompleted_seg_list,
{ color: '#1565c0', size: 0.55 },
{ color: '#00838f', size: 0.65 },
showMainBi,
showMainSeg
));
}
var showElementBi = $('#showElementBiArea').is(':checked');
var showElementSeg = $('#showElementSegArea').is(':checked');
if (showElementBi || showElementSeg) {
markers = markers.concat(collectAreaHistMarkers(
data.element_bi_list,
data.element_uncompleted_bi_list,
data.element_seg_list,
data.element_uncompleted_seg_list,
{ color: '#3949ab', size: 0.5 },
{ color: '#5c6bc0', size: 0.6 },
showElementBi,
showElementSeg
));
}
var showSubSubBi = $('#showSubSubBiArea').is(':checked');
var showSubSubSeg = $('#showSubSubSegArea').is(':checked');
if (showSubSubBi || showSubSubSeg) {
markers = markers.concat(collectAreaHistMarkers(
data.sub_sub_bi_list,
data.sub_sub_uncompleted_bi_list,
data.sub_sub_seg_list,
data.sub_sub_uncompleted_seg_list,
{ color: '#2e7d32', size: 0.45 },
{ color: '#558b2f', size: 0.55 },
showSubSubBi,
showSubSubSeg
));
}
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;
@@ -163,23 +326,38 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
// LWC 4 无 priceScale 订阅:采样坐标变化(含增量 setData 后自动缩放)
var sampleSig = function () {
var boxes = window._fxBoxVerticals || [];
var labels = window._areaTextLabels || [];
var series = getMainPriceSeries();
if (!series || !boxes.length) return '0';
if (!series || (!boxes.length && !labels.length)) return '0';
var ts = mainChart.timeScale();
var a = boxes[0];
var b = boxes[boxes.length - 1];
return [
boxes.length,
quant(ts.timeToCoordinate(a.time)),
quant(series.priceToCoordinate(a.hi)),
quant(series.priceToCoordinate(a.lo)),
quant(ts.timeToCoordinate(b.time)),
quant(series.priceToCoordinate(b.hi)),
quant(series.priceToCoordinate(b.lo))
].join('|');
var parts = [boxes.length, labels.length];
if (boxes.length) {
var a = boxes[0];
var b = boxes[boxes.length - 1];
parts.push(
quant(ts.timeToCoordinate(a.time)),
quant(series.priceToCoordinate(a.hi)),
quant(series.priceToCoordinate(a.lo)),
quant(ts.timeToCoordinate(b.time)),
quant(series.priceToCoordinate(b.hi)),
quant(series.priceToCoordinate(b.lo))
);
}
if (labels.length) {
var la = labels[0];
var lb = labels[labels.length - 1];
parts.push(
quant(ts.timeToCoordinate(la.time)),
quant(series.priceToCoordinate(la.price)),
quant(ts.timeToCoordinate(lb.time)),
quant(series.priceToCoordinate(lb.price))
);
}
return parts.join('|');
};
var redraw = function () {
var boxes = window._fxBoxVerticals || [];
var labels = window._areaTextLabels || [];
var series = getMainPriceSeries();
var rect = mainChartContainer.getBoundingClientRect();
var dpr = window.devicePixelRatio || 1;
@@ -191,7 +369,7 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
if (!ctx2) return;
ctx2.setTransform(dpr, 0, 0, dpr, 0, 0);
ctx2.clearRect(0, 0, rect.width, rect.height);
if (!series || !boxes.length) {
if (!series || (!boxes.length && !labels.length)) {
lastSig = sampleSig();
return;
}
@@ -211,6 +389,19 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
ctx2.stroke();
}
ctx2.setLineDash([]);
if (labels.length) {
ctx2.font = '11px sans-serif';
ctx2.textAlign = 'center';
for (var li = 0; li < labels.length; li++) {
var lab = labels[li];
var lx = ts.timeToCoordinate(lab.time);
var ly = series.priceToCoordinate(lab.price);
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);
}
}
lastSig = sampleSig();
};
var scheduleRedraw = function () {
@@ -257,6 +448,7 @@ function syncFxBoxVerticalOverlay(mainChart, mainChartContainer) {
function chartTvRenderOverlays(ctx) {
window._fxBoxVerticals = [];
window._areaTextLabels = [];
var symbol = ctx.symbol;
var timeframe = ctx.timeframe;
var symbolConfig = ctx.symbolConfig;
@@ -1852,6 +2044,14 @@ function chartTvRenderOverlays(ctx) {
window.mainFxMarkers = [];
window.fxMarkers = [];
}
window._areaTextLabels = alignMarkersToCandles(
buildAreaDivMarkersFromData(currentData).concat(buildAreaHistMarkersFromData(currentData)),
candles
);
if (typeof window._redrawFxBoxVerticalOverlay === 'function') {
window._redrawFxBoxVerticalOverlay();
}
// 绘制小周期分型标记(含次次周期)
if (($('#showElementKlcFxType').is(':checked') && currentData.element_klc_fx_info && currentData.element_klc_fx_info.length > 0) ||
($('#showElementKluFxType').is(':checked') && currentData.element_klu_fx_info && currentData.element_klu_fx_info.length > 0) ||
@@ -2157,7 +2357,7 @@ function chartTvRenderOverlays(ctx) {
trendMarkersToUse = trendMarkersToUse.concat(subSubMarkers);
}
// 合并标记并设置
// 合并标记并设置(主图不画 U/穿零轴,只留背驰 SD/CD)
const combinedMarkers = [
...(window.mainFxMarkers || []),
...allElementFxMarkers,
@@ -2173,7 +2373,7 @@ function chartTvRenderOverlays(ctx) {
'合并设置', combinedMarkers.length, '个标记(主周期分型:',
(window.mainFxMarkers || []).length,
'个,小周期分型:', allElementFxMarkers.length,
'个,UnitTF:', (window.unittfMarkers || []).length,
'个,背驰:', (window.kluDivMarkersMain || []).length,
'个,BSP标记:', (window.bspMarkers || []).length,
'个,第四类标记:', (window.fastBspMarkers || []).length,
'个)'
@@ -2288,7 +2488,7 @@ function chartTvRenderOverlays(ctx) {
...(window.fastBspMarkers || [])
];
if (onlyMainAndU.length > 0) {
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, 'UnitTF:', (window.unittfMarkers || []).length, '');
console.log('仅设置', onlyMainAndU.length, '个主标记(主周期分型:', (window.mainFxMarkers || []).length, '背驰:', (window.kluDivMarkersMain || []).length, '');
// 根据当前主系列类型设置标记
const klineType2 = ($('#klineType').val() || (showOriginalKline ? 'candlestick' : 'line'));