feat(regime): 市场环境页全面增强 — 内存可控/开关/画像/美化/分批可配

后端
- 修复 /recompute 全量分支误调增量函数致 computed 恒 0: 改为 earliest~today
  走 run_regime_batch + upsert 强制覆盖
- 修复 /history 日期范围模式被 limit 默认 120 截断: limit 仅在未传 start/end 时生效
- _scan_enriched_fallback:
  · 修复 compute_limit_signals 漏传 instruments 参数致慢路径必抛异常
  · needed 白名单: 只算 regime 用到的列, 全量峰值 6.8GB→3.2GB
  · 分批 + warmup: 范围超 batch_days 时切片, 每批带 warmup 前缀算后 concat,
    全量峰值再降至 ~1.9GB; batch/warmup 参数从用户偏好读取
- _aggregate_daily: 逐日 filter 扫全表(O(N²))改为 polars 向量化 group_by
- 新增 enriched_date_set / earliest_enriched_date 辅助函数
- 盘后管道 regime 计算加开关(preferences.get_pipeline_regime_enabled), 默认关闭
- 新增偏好: regime_batch_days(默认60, 25~500) / regime_warmup_days(默认40, 35~90)
- 新增 API: PUT /preferences/pipeline-regime-enabled, PUT /preferences/regime-batch-params

前端
- 市场环境页时间范围控件: select 三档 → 4 档按钮组(1年/2年/自定义/全部)
  · 自定义弹窗手动输入天数(1~1000 钳制) + 快捷预设
  · 全部走 regimeCoverage 拿实际日期范围用 start/end 请求
- 市场环境页美化(对齐 Dashboard 设计语言): 渐变条头部/半透明卡片/SectionTitle/
  指标卡进度条/语义色 bull-bear
- 新建 RegimeConfigCard: 盘后自动计算开关 + 全量回填分批参数(每批天数/预热天数/
  快捷预设省内存·默认·更快)
- 数据页画像加市场环境卡片(独立 regimeCoverage 查询, 同步后刷新, 齿轮设置弹窗)
- 菜单排序设置 + 侧边栏 nav 加入市场环境, 默认排在复盘上面
- Layout 市场环境菜单 badge 改用琥珀胶囊(对齐 Settings 数据源标签)

验证
- 后端 582 passed; 真实数据全量回填产出 986 天, 内存峰值 ~1.9GB(原 6.8GB)
- 前端 tsc + pnpm build 通过
- 分批参数实测生效: batch=30 峰值 1896MB / batch=500 峰值 3772MB
This commit is contained in:
shy3130
2026-08-02 16:25:21 +08:00
parent 03f0ddebf0
commit 93558cf3b3
14 changed files with 712 additions and 107 deletions
+39
View File
@@ -418,6 +418,9 @@ def get_preferences() -> dict:
"pipeline_pull_a_share": preferences.get_pipeline_pull_a_share(),
"pipeline_pull_etf": preferences.get_pipeline_pull_etf(),
"pipeline_pull_index": preferences.get_pipeline_pull_index(),
"pipeline_regime_enabled": preferences.get_pipeline_regime_enabled(),
"regime_batch_days": preferences.get_regime_batch_days(),
"regime_warmup_days": preferences.get_regime_warmup_days(),
"pipeline_index_symbols": preferences.get_pipeline_index_symbols(),
"pipeline_schedule": preferences.get_pipeline_schedule(),
"instruments_schedule": preferences.get_instruments_schedule(),
@@ -830,6 +833,42 @@ def update_pipeline_pull_types(req: PipelinePullTypesIn) -> dict:
return preferences.set_pipeline_pull_types(cfg)
class PipelineRegimeEnabledIn(BaseModel):
"""盘后管道是否自动计算市场环境(regime)。"""
pipeline_regime_enabled: bool
@router.put("/preferences/pipeline-regime-enabled")
def update_pipeline_regime_enabled(req: PipelineRegimeEnabledIn) -> dict:
"""更新盘后管道 regime 自动计算开关。"""
from app.services import preferences
preferences.save({"pipeline_regime_enabled": bool(req.pipeline_regime_enabled)})
return {"pipeline_regime_enabled": preferences.get_pipeline_regime_enabled()}
class RegimeBatchParamsIn(BaseModel):
"""regime 全量回填分批参数(控制内存峰值)。"""
batch_days: int | None = None
warmup_days: int | None = None
@router.put("/preferences/regime-batch-params")
def update_regime_batch_params(req: RegimeBatchParamsIn) -> dict:
"""更新 regime 分批参数。仅在传入字段时保存对应项(支持部分更新)。"""
from app.services import preferences
updates: dict = {}
if req.batch_days is not None:
updates["regime_batch_days"] = req.batch_days
if req.warmup_days is not None:
updates["regime_warmup_days"] = req.warmup_days
if updates:
preferences.save(updates)
return {
"regime_batch_days": preferences.get_regime_batch_days(),
"regime_warmup_days": preferences.get_regime_warmup_days(),
}
class PipelineIndexSymbolsIn(BaseModel):
"""指数自定义拉取代码(逗号/换行/空格分隔,空串表示全量)。"""
symbols: str = ""