添加 StructureZone 结构价值区系统,支持多周期支撑/阻力分析
- 新增 ChanZone.py: 从笔中枢/线段中枢/EMA52 提取价格区,聚类评分 - ChanLun.py 新增 get_structure_zones() 方法 - web/app.py: 独立拉取多周期数据 + 缓存 + limit 传参避免全量传输 - web/index.html: 结构区勾选框 + K线数量输入 + 半透明填充区绘制 - tests/test_chan_zone.py: 24 个单元测试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d8069e977f
commit
ebcb3dce73
@@ -958,6 +958,11 @@
|
||||
<input class="form-check-input" type="checkbox" id="autoRefresh">
|
||||
<label class="form-check-label" for="autoRefresh">启用</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline me-2">
|
||||
<input class="form-check-input" type="checkbox" id="showMainStructureZone">
|
||||
<label class="form-check-label" for="showMainStructureZone">结构区</label>
|
||||
</div>
|
||||
<input type="number" id="zoneKlLines" class="form-control form-control-sm" value="1000" min="100" max="5000" step="100" style="width:80px;" title="结构区K线数量">
|
||||
<span id="nextRefreshTime" class="text-muted" style="display:none;font-size:0.85rem;"></span>
|
||||
<div id="refreshLoadingSpinner" class="loading-spinner ms-2" style="display:none;"></div>
|
||||
</div>
|
||||
@@ -1911,6 +1916,11 @@
|
||||
console.log('主BI中枢切换为:', $('#showMainBiZs').is(':checked'));
|
||||
updateChartDisplay();
|
||||
});
|
||||
// 结构价值区复选框变更事件
|
||||
$(document).on('change', '#showMainStructureZone', function() {
|
||||
console.log('结构区切换为:', $('#showMainStructureZone').is(':checked'));
|
||||
updateChartDisplay();
|
||||
});
|
||||
|
||||
// 添加趋势显示复选框变更事件(主/元素),变更后刷新主图
|
||||
$('#showMainTrend').change(function() {
|
||||
@@ -2179,7 +2189,8 @@
|
||||
sub_sub_timeframe: subSubTimeframe || undefined,
|
||||
start_time: startTimeMs,
|
||||
end_time: endTimeMs,
|
||||
elements_only: false
|
||||
elements_only: false,
|
||||
zone_kl_lines: parseInt($('#zoneKlLines').val()) || 1000
|
||||
},
|
||||
success: function(data) {
|
||||
// 隐藏加载图标
|
||||
@@ -4634,6 +4645,58 @@
|
||||
} catch (e) { console.error('次周期BI中枢处理出错:', e); }
|
||||
});
|
||||
}
|
||||
// 结构价值区绘制(半透明填充区 + 边框)
|
||||
if ($('#showMainStructureZone').is(':checked') && currentData.structure_zones && currentData.structure_zones.length > 0) {
|
||||
try {
|
||||
const kd = currentData.kline_data || [];
|
||||
if (kd.length > 0) {
|
||||
const chartStart = Math.floor(new Date(kd[0].date).getTime() / 1000);
|
||||
const chartEnd = Math.floor(new Date(kd[kd.length-1].date).getTime() / 1000);
|
||||
// 计算可见价格范围,过滤超出范围的区间
|
||||
let priceMin = Infinity, priceMax = -Infinity;
|
||||
kd.forEach(function(k) {
|
||||
const hi = parseFloat(k.high), lo = parseFloat(k.low);
|
||||
if (!isNaN(hi) && hi > priceMax) priceMax = hi;
|
||||
if (!isNaN(lo) && lo < priceMin) priceMin = lo;
|
||||
});
|
||||
const priceMargin = (priceMax - priceMin) * 0.05;
|
||||
priceMin -= priceMargin;
|
||||
priceMax += priceMargin;
|
||||
let drawnCount = 0;
|
||||
currentData.structure_zones.forEach(function(zone) {
|
||||
try {
|
||||
// 跳过完全超出可视价格范围的区间
|
||||
if (zone.upper < priceMin || zone.lower > priceMax) return;
|
||||
const fillColor = zone.zone_type === 'support' ? 'rgba(46, 204, 113, 0.08)' :
|
||||
zone.zone_type === 'resistance' ? 'rgba(231, 76, 60, 0.08)' :
|
||||
'rgba(149, 165, 166, 0.06)';
|
||||
const borderColor = zone.zone_type === 'support' ? 'rgba(46, 204, 113, 0.7)' :
|
||||
zone.zone_type === 'resistance' ? 'rgba(231, 76, 60, 0.7)' :
|
||||
'rgba(149, 165, 166, 0.6)';
|
||||
// 填充区:在上下边界之间画多条半透明线模拟填充
|
||||
const fillLines = 8;
|
||||
const step = (zone.upper - zone.lower) / (fillLines + 1);
|
||||
for (let fi = 1; fi <= fillLines; fi++) {
|
||||
const fy = zone.lower + step * fi;
|
||||
mainChart.addLineSeries({ color: fillColor, lineWidth: 2, lineStyle: 0, lastValueVisible: false, priceLineVisible: false })
|
||||
.setData([{ time: chartStart, value: fy }, { time: chartEnd, value: fy }]);
|
||||
}
|
||||
// 上边界(粗线)
|
||||
mainChart.addLineSeries({ color: borderColor, lineWidth: 2, lineStyle: 0, lastValueVisible: false, priceLineVisible: false })
|
||||
.setData([{ time: chartStart, value: zone.upper }, { time: chartEnd, value: zone.upper }]);
|
||||
// 下边界(粗线)
|
||||
mainChart.addLineSeries({ color: borderColor, lineWidth: 2, lineStyle: 0, lastValueVisible: false, priceLineVisible: false })
|
||||
.setData([{ time: chartStart, value: zone.lower }, { time: chartEnd, value: zone.lower }]);
|
||||
// 中心线(虚线)
|
||||
mainChart.addLineSeries({ color: borderColor, lineWidth: 1, lineStyle: 2, lastValueVisible: false, priceLineVisible: false })
|
||||
.setData([{ time: chartStart, value: zone.center }, { time: chartEnd, value: zone.center }]);
|
||||
drawnCount++;
|
||||
} catch (e) { console.error('结构区绘制出错:', e); }
|
||||
});
|
||||
console.log(`结构区: 共${currentData.structure_zones.length}个, 绘制${drawnCount}个 (可见价格范围: ${priceMin.toFixed(0)}-${priceMax.toFixed(0)})`);
|
||||
}
|
||||
} catch (e) { console.error('结构区整体绘制出错:', e); }
|
||||
}
|
||||
// 显示未完成中枢 - 分别处理主周期、次周期和次次周期
|
||||
if ($('#showMainZs').is(':checked') || $('#showElementZs').is(':checked') || $('#showSubSubZs').is(':checked') || $('#showSubSubBiZs').is(':checked')) {
|
||||
console.log('绘制未完成中枢 - 已启用');
|
||||
|
||||
Reference in New Issue
Block a user