Commit Graph
8 Commits
Author SHA1 Message Date
shy3130 365f1ccbfb feat(minute-k): 分钟K同步流式落盘 + 段大小可配 + 回测成交修复 + 卡死超时调整
1. 分钟K流式落盘 (修复1年全市场卡死)
   - sync_minute_batch 加 on_segment 回调,每段拉完立即写盘
   - 抽出 _write_minute_partition 公共函数,消除重复
   - 内存峰值从全量(~25GB)降到单段,避免 OOM

2. 段大小可配 (默认20交易日,范围[5,30])
   - 新增 minute_sync_segment_days 偏好项
   - 「单次获取」与「获取1年」共用此分段设置
   - 前端设置弹窗新增分段大小步进器

3. 回测分钟K精确成交修复 (从未成功跑通过)
   - _resolve_minute_fill 改接受 DataFrame,避免 to_numpy() 退化为
     object 数组后字段名索引抛 IndexError
   - _load_minute_for_fills 用 partition_by 向量化分组替换逐行循环
   - 新增 test_minute_fill.py 回归测试 (6 用例)

4. 卡死 watchdog 超时阈值按任务类型区分
   - 普通任务 600s → 1200s;分钟K长任务 → 1800s
   - create(timeout_s=) 支持按 job 存阈值,reap_stale 读 job 自身值
   - 修复分钟K正常拉取被误杀导致僵尸线程继续写盘的问题

5. UI 优化
   - 设置弹窗按 自动同步/手动获取/清空 三区块分组
   - 「单次获取」改为按分段大小拉一段 (天数=分段设置)
   - 状态文案: 盘后自动同步 → 自动同步已开启/已关闭
2026-07-12 14:23:22 +08:00
shy3130 fc85d53bee feat(minute-k): 分钟K重做 — 前复权 + 向前扩展 + 清空 + 单独拉取 + 进度
- kline_sync: adjust="forward" 前复权; sync_minute_batch 按月分段(57天/段)
  突破 count=10000 上限; on_chunk_done 回调带日期段标签
- kline.py: 删除 extend_minute_history; 新增 sync_minute_single(单股拉取落库)
  + clear_minute(仅清分钟K二次确认) + sync_minute 可选 days/extend 参数
- preferences/settings: 默认 5 天上限 30 (恢复原始); 超时强制释放 _heavy_run_lock
- MinuteSyncConfig: 自动同步(5天/关闭) + 往前获取(单次拉满) + 获取1年(分段)
  + 清空按钮 + 进度回调 onJobStart
- StockIntradayChart: 单股获取改用 syncMinuteSingle (落库)
- daily_pipeline: _minute_chunk_progress 兼容 seg_label 参数
2026-07-12 11:04:34 +08:00
Jinfeng SunandClaude Opus 4.8 e5a94c42d5 feat: ETF 支持(选股 / 回测 / 监控) (#61)
* feat(screener): 选股引擎支持 ETF

- 12 个内置策略打 asset_types 白名单 + strategy_supports_asset;涨停类
  (连板/断板反包)仅股票,其余 10 个技术类对 ETF 开放
- ScreenerService(repo, asset_type) 分流取数,ETF 复用 kline_etf_enriched,
  跳过股票专用历史缓存与涨停信号;进程级 _history_cache key 含 asset_type
- API /run、/run_preset 透传 asset_type;/strategies 按资产过滤;
  股票专有策略在 ETF 下返回空
- 新增 enriched_dirname(asset_type) 共享 helper;get_enriched_latest_asset
  增 refresh 参数(供轮询线程避免冷缓存同步重算)
- 前端「策略」页加 股票/ETF 切换,ETF 走实时单跑(空日期→用 ETF 自身最新日);
  QK.screenerStrategies 按 asset_type keyed
- 测试:test_screener_etf.py

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(backtest): 回测支持 ETF(个股/因子/策略组合)

- 三条回测路径 + 共用 BacktestEngine 面板加载按 asset_type 路由到
  kline_etf_enriched(复用 enriched_dirname);PanelCache key 隔离资产;
  ETF 跳过股票专用 get_enriched_range 缓存
- 面板 compute_all/名称 JOIN 按 asset_type 取维表(get_instruments_asset),
  修复 ETF 策略回测用错股票维表致名称为空/涨停信号算错
- BacktestConfig/FactorConfig/StrategyBacktestConfig 增 asset_type
- 三个回测 API + SSE stream 透传 asset_type;_make_job_key 纳入 asset_type
  (修复 stream 与 cancel job_key 不对齐致取消失效的回归)
- 前端策略组合页/因子页加 股票/ETF 切换,标的搜索与策略列表跟随资产;
  assetType 持久化
- 测试:test_backtest_etf.py(含 job_key 一致性回归);既有回测测试替身同步

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(monitor): 监控规则支持 ETF

- engine.evaluate(df, asset_type) 按规则 asset_type 分轮评估;quote_service
  增开 ETF 评估轮(用 ETF enriched 快照),股票轮不受影响、不重置其策略结果
- ETF 评估轮独立 try(异常不丢弃已算出的股票告警)+ refresh=False(不在轮询
  线程触发 ETF 冷缓存同步重算)
- ETF 版历史加载器(main.py 注入)+ 按规则 asset_type 选加载器
- _strategy_pools 按 (sid, asset_type) 键,避免同策略股票/ETF 规则互相覆盖
- name_map 仅在有 ETF 规则时补 ETF 维表, setdefault 保股票名优先
- RuleModel/normalize 增 asset_type(默认 stock,持久化往返)
- 前端 RuleEditor 加 股票/ETF 选择,策略列表与标的搜索跟随资产
- 测试:test_monitor_etf.py

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(etf): 前端 API 绑定透传 asset_type + 文档

- api.ts: screener/backtest 绑定加 assetType 参数,MonitorRule 类型加 asset_type
- docs/features.md: 标注选股/回测/监控的 ETF 支持范围与前提

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(reliability): 管道并发/原子写/能力探测/监控告警多处加固

后端可靠性专项修复(均带回归测试, backend 全套 64 passed):

并发与数据完整性:
- 盘后管道单飞: JobStore.create() 去重纳入 pending∨running, 关闭"两次快速点击"
  并发双跑窗口; 新增 _heavy_run_lock 执行槽挡住 reap 后僵尸线程并发写 parquet
- adj_factor/minute 全部改走原子写(tmp+replace), 消除 kill/断电致 all.parquet 损坏
- 分块拉取失败聚合 WARNING 可见化(不再静默当成功); 复权失败标的会保持旧价已提示

能力探测:
- 周期重探(60min)热更新 app.state.capabilities, 付费 Key 过期/续费无需重启即可见
- 瞬时探测失败(超时/连接/5xx, 按 _is_transient 判定)不降级、保留旧付费档;
  真 401/无权限仍正常降级回落 free-api

监控告警:
- 评估仅在连续竞价(9:30-11:30/13:00-15:00)+ 快照当日新鲜度下进行, 避开集合竞价/
  收盘后陈旧价与节假日误告警
- scope=sector fail-closed(validate 拒绝新建 + _apply_scope 返回空), 修复板块规则
  对全市场刷屏
- 飞书 webhook 加退避重试并移到独立线程池 fire-and-forget, 不再阻塞行情轮询线程

单标的新鲜度: 新增 repo.symbols_lagging() 检测掉队标的并 WARNING + 计入 job 结果

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:12:29 +08:00
shy3130 fa08981851 feat: 自定义数据源扩展 + 限频集中化 (v0.1.80) 2026-07-05 22:57:46 +08:00
wshyandshy3130 57f417e6eb fix(sync): 修复同步卡死在26%及启动期视图注册崩溃 (#47) (#48)
三处根因:

1. 限流逻辑失效 (kline_sync.py ×3, index_sync.py ×2)
   条件 `len(chunks) > rpm` 在 free 档恒为 False (56 chunks < 60 rpm),
   导致节流 sleep 永不执行, 请求密集打出触发服务端限流 → 卡死。
   改为始终按 interval (60/rpm) 节流。

2. 卡死看门狗无法自愈 (pipeline_jobs.py, api/pipeline.py)
   原超时检查只在再次点「同步」时触发, 等待中永不回收。
   抽出 JobStore.reap_stale() (STALE_JOB_TIMEOUT_S=600), 在
   /run 和 /jobs/{id} 轮询端点都调用 — 前端每秒轮询, 10分钟后
   必定自动回收卡死 job, UI 不再永久停在 26%。

3. 启动期视图注册异常捕获不足 (repository.py)
   _register_views 只捕获 duckdb.IOException, 跨版本/平台空目录
   可能抛 CatalogException 等炸掉 lifespan。放宽到 Exception。

Co-authored-by: shy3130 <shy3130@users.noreply.github.com>
2026-07-04 15:53:50 +08:00
shy3130 a72445d782 fix(pipeline): 修复盘后管道除权因子拉取范围逻辑
daily_pipeline 除权因子改用日K范围回填 (实时增量模式兜底最近数日),
移除 max_date/incremental 依赖;kline_sync 修复 _normalize_adj_factor
重复列重命名崩溃, 新增 fetch_adj_factor_single 单股拉取;
kline.py fallback 改用 compute_enriched 应用复权;
index_sync 指数/ETF 日K同步增加批次进度回调;
同步指数日K步骤输出详细日志。
2026-06-29 17:17:44 +08:00
shy3130 2acaa38a2b feat: release v0.1.60 2026-06-27 23:23:34 +08:00
shy3130 7369358d08 Initial release v0.1.19 2026-06-18 17:18:23 +08:00