feat(v0.2): 市场阶段与主线识别 + 因子挖掘全链路 + 数据层完善

- 市场环境: 新增情绪周期6阶段(冰点/启动/主升/高潮/退潮/修复, 连板梯队驱动,
  EMA平滑+2日确认+弱档否决, 平均段长9.7天)与概念/行业主线排名(涨停梯队聚合,
  可配置宽基/风格标签过滤); 市场环境页重构, regime 透明加列, 与5档state并存
- 挖掘: 因子与策略挖掘全链路(API/worker/进程锁/候选库/前端工作台/文档),
  周度调度默认关闭且永不自动发布
- 回测: 财务快照因子(点时口径), 批量回测预计算共享下期收益,
  信号路径矩阵列依赖展开修复(consecutive_limit_ups 缺列报错)
- 数据/性能: enriched 生成与预热治理, 重任务限流, 行情/K线缓存复用, 时区修复
- 测试: 后端全量 914 通过; GUI 黑盒验证截图存证 gui-test-screenshots/
This commit is contained in:
shy3130
2026-08-16 23:39:07 +08:00
parent eb869c7ad2
commit 697c27bb02
129 changed files with 20324 additions and 602 deletions
+23 -6
View File
@@ -25,6 +25,7 @@ import {
Star,
ScanSearch,
History,
Pickaxe,
FileText,
Settings,
Key,
@@ -79,6 +80,7 @@ const nav = [
{ to: '/watchlist', label: '自选', icon: Star },
{ to: '/screener', label: '策略', icon: ScanSearch },
{ to: '/backtest', label: '回测', icon: History },
{ to: '/mining', label: '挖掘', icon: Pickaxe },
{ to: '/stock-analysis', label: '个股分析', icon: TrendingUp },
{ to: '/limit-ladder', label: '连板梯队', icon: Flame },
{ to: '/concept-analysis', label: '概念分析', icon: Layers3 },
@@ -424,12 +426,11 @@ export function Layout() {
: (dataSources?.custom?.find(s => s.name === activeProvider)?.display_name || activeProvider)
const isCustomActive = activeProvider !== 'tickflow'
// 轮询触发记录总数 → 更新监控中心徽标 (每 15 秒)
// 轮询触发记录总数 → 更新监控中心徽标 (每 15 秒; 后台标签页由 SSE 事件驱动, 不轮询)
const alertsTotalQuery = useQuery({
queryKey: ['alerts-total'],
queryFn: () => api.alertsList({ days: 7, limit: 1 }),
refetchInterval: 15000,
refetchIntervalInBackground: true,
select: (data) => data.total,
})
// 只在拿到真实总数时同步徽标 (避免 data=undefined 时传 0 重置 lastSeen)
@@ -456,11 +457,27 @@ export function Layout() {
const navItems = savedOrder.length > 0
? (() => {
const byTo = new Map(allNav.map(n => [n.to, n]))
const ordered = savedOrder
const ordered = (savedOrder
.map(id => byTo.get(id) ?? byTo.get(`/analysis/${id}`))
.filter(Boolean)
const seen = new Set(ordered.map(n => n!.to))
return [...ordered as typeof allNav, ...allNav.filter(n => !seen.has(n.to))]
.filter(Boolean)) as typeof allNav
const seen = new Set(ordered.map(n => n.to))
const merged = [...ordered]
for (const item of allNav) {
if (seen.has(item.to)) continue
// 未保存过排序的新条目: 内置页插回默认位置(排在已保存的默认前驱之后),
// 分析/扩展菜单仍追加到末尾
const defaultIndex = nav.findIndex(n => n.to === item.to)
let anchor = -1
if (defaultIndex > 0) {
for (let i = defaultIndex - 1; i >= 0 && anchor < 0; i -= 1) {
anchor = merged.findIndex(n => n.to === nav[i].to)
}
}
if (anchor >= 0) merged.splice(anchor + 1, 0, item)
else if (defaultIndex >= 0) merged.unshift(item)
else merged.push(item)
}
return merged
})()
: allNav