feat: 第四类买卖点(B4/S4)融入缠论引擎与 web 展示

研究侧的 fast_bsp3 一直只活在 research/lib/ 里,web 端看不到,回测与目视
两条线对不上。这次把它搬进引擎,作为独立的第四类买卖点。

之所以单独立类而不是当作 B3/S3 的低滞后版:step30/31 显示引擎原生的
B3/S3 统计上呈逆势、显著亏损(胜率 27.4%、PF 0.66、t −18.76),而同一组
过滤器把 B4 从 PF 1.59 提到 2.26 却对它无效(0.66→0.71)。两者选的是
不同的交易群体,不是同一信号的早晚两版。

- chanlun/analysis/fast_bsp.py 原样搬入 find_fast_bsp3 与 build_htf_zones,
  另加 add_zone_ladder / htf_fx_timeline / attach_htf_agree
- research/lib/ 两个模块改为转发,所有 step 脚本导入不变,信号逐条比对一致
- 大级别上下文用 resample 从同一份 df 构建,不额外拉数据,因此与界面上选的
  周期和时间范围无关
- 前端三个复选框 + 过滤模式下拉;未过滤的原始信号用浅色,避免与主口径混淆

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-28 00:05:26 +08:00
co-authored by Cursor
parent 7e339d2a54
commit 9b72173285
17 changed files with 702 additions and 209 deletions
+59 -4
View File
@@ -852,6 +852,8 @@ function chartTvRenderOverlays(ctx) {
'BSP1_SELL': { color: '#00E676', text: 'S1', position: 'aboveBar', size: 0.5 },
'BSP2_SELL': { color: '#00B0FF', text: 'S2', position: 'aboveBar', size: 0.5 },
'BSP3_SELL': { color: '#8B4513', text: 'S3', position: 'aboveBar', size: 0.5 },
'BSP4_BUY': { color: '#FF6D00', text: 'B4', position: 'belowBar', size: 0.5 },
'BSP4_SELL': { color: '#0091EA', text: 'S4', position: 'aboveBar', size: 0.5 },
};
const getBspStyleKey = (bsp) => {
@@ -987,7 +989,57 @@ function chartTvRenderOverlays(ctx) {
// 关闭 BSP 显示时,清空全局 BSP 标记
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` 复选框
@@ -2113,7 +2165,8 @@ function chartTvRenderOverlays(ctx) {
...(window.kluDivMarkersElement || []),
...(window.kluDivMarkersSubSub || []),
...trendMarkersToUse,
...(window.bspMarkers || [])
...(window.bspMarkers || []),
...(window.fastBspMarkers || [])
];
if (combinedMarkers.length > 0) {
console.log(
@@ -2122,6 +2175,7 @@ function chartTvRenderOverlays(ctx) {
'个,小周期分型:', allElementFxMarkers.length,
'个,UnitTF:', (window.unittfMarkers || []).length,
'个,BSP标记:', (window.bspMarkers || []).length,
'个,第四类标记:', (window.fastBspMarkers || []).length,
'个)'
);
@@ -2223,14 +2277,15 @@ function chartTvRenderOverlays(ctx) {
// 这里的 onlyMainAndU 实际上是「最终要挂到主K线上」的一组标记
// 之前没有把 window.bspMarkers 合进去,导致上面已经合并了 BSP 标记,
// 但在这里再次调用 setMarkers 时把 BSP 覆盖掉了,从而前端看不到买卖点。
// 修复:把 BSP 标记一并合并进来。
// 修复:把 BSP 标记一并合并进来。第四类买卖点同理,两处都要带上。
const onlyMainAndU = [
...(window.mainFxMarkers || []),
...(window.kluDivMarkersMain || []),
...(window.kluDivMarkersElement || []),
...(window.kluDivMarkersSubSub || []),
...trendMarkersToUse,
...(window.bspMarkers || [])
...(window.bspMarkers || []),
...(window.fastBspMarkers || [])
];
if (onlyMainAndU.length > 0) {
console.log('仅设置', onlyMainAndU.length, '个主周期/UnitTF标记(主周期分型:', (window.mainFxMarkers || []).length, 'UnitTF:', (window.unittfMarkers || []).length, '');
+5
View File
@@ -649,6 +649,11 @@ $('#showElementBsp').change(function() {
updateChartDisplay();
});
// 第四类买卖点(B4/S4)显示开关与过滤模式
$('#showMainFastBsp, #showElementFastBsp, #showSubSubFastBsp, #fastBspFilterMode').change(function() {
updateChartDisplay();
});
// 在控制台输出当前显示状态
console.log('当前显示状态:', {
'showOriginalKline': $('#showOriginalKline').is(':checked'),