shy3130
8d8b66c7f8
fix(kline): 分钟批量补全识别前部空洞, 重启跨开盘后分时可自愈
...
盘中重启/停机跨开盘后, 本地当日分钟K只剩重启时刻起的连续尾巴。
缺口判定只检查相邻K间隔, 连续尾巴被判"仅尾部落后"走增量 — 增量
锚定本地最新时间, 永远不会回看缺的开头, 上午的洞永久残留, 自选
分时只显示几根K。
- _has_holes 增加前部洞判定: 当日首根 > 开盘+6min 容差即视为洞,
归入全天重拉 (取到即落盘, 一次自愈)。仅根数不足的分支才判定,
稳态零额外请求; 停牌晚开票命中亦幂等无害
- 全量分钟服务健康时不再压制空洞类补拉 (服务增量补不了洞),
纯尾部落后的压制维持不变
实测: 服务器重启窗口后策略页(本地为空)走全天拉取显示完整、
自选页(有连续尾巴)被增量锁死只显示 11 根, 修复后统一自愈。
2026-09-09 12:13:04 +08:00
wshy
3db7f38446
Merge pull request #281 from 0112020179/codex/fix-enriched-refresh-memory
...
fix(oom): 降低补算内存峰值并统一重任务并发控制
2026-09-09 11:36:22 +08:00
0112020179
771a95a6d1
fix(oom): 降低补算内存峰值并统一重任务并发控制
2026-09-09 11:09:48 +08:00
wshy
5ffe43d42d
Merge pull request #280 from kevin9327/fix/backtest-stream-date-400
...
fix(backtest): SSE 端点非法 start/end 返回 400 而不是 500
2026-09-09 09:04:52 +08:00
wshy
efd9f820d4
Merge pull request #279 from kevin9327/fix/screener-run-all-as-of
...
fix(screener): run_all 的 as_of 与 /custom、/preset 同口径校验 (数字 as_of 会写坏策略缓存)
2026-09-09 09:04:46 +08:00
wshy
6eb1508fd8
Merge pull request #278 from kevin9327/fix/alerts-query-bounds
...
fix(alerts): 触发记录查询的 days/limit 补上范围约束 (limit=-1 静默丢记录)
2026-09-09 09:04:40 +08:00
wshy
fe1410532f
Merge pull request #275 from kevin9327/fix/factor-delete-fail-closed
...
fix(factors): 删除因子被拒绝时定义已被删掉 (删除路径未 fail-closed)
2026-09-09 09:04:22 +08:00
kevin9327 and Claude Opus 5
68005c92e6
fix(alerts): 触发记录查询的 days/limit 补上范围约束
...
GET /api/alerts 的 days、limit 是裸 int,没有任何约束,越界值不报错而是
静默返回错误结果:
- days=-1 → cutoff 被推到未来,200 但 alerts 为空(total 仍是 3)
- limit=-1 → out[:limit] 变成负数切片,3 条记录只返回 2 条,
静默丢掉最旧一条,调用方无从察觉
- limit=0 → 200 但 alerts 为空
同类列表端点已有正确写法:abnormal.py 用 `limit: int = Query(500, ge=1,
le=2000)`,rps.py 用 `days: int = Query(12, ge=7, le=30)`,越界直接 422。
本次把 /api/alerts 补齐到同一口径,上下限取存储侧保留策略本身
(alert_store.MAX_DAYS / MAX_RECORDS),不新增常量,超出保留窗口的请求
本来也没有可返回的数据。
前端 alertsList 实际只传 days=7、limit=1/10/500,全部落在新区间内;
新增的 tests/test_alerts_query_bounds.py 同时锁住这些合法取值仍返回 200
且记录条数不变。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-09-09 07:37:17 +09:00
kevin9327 and Claude Opus 5
fd5d883c8f
fix(screener): run_all 的 as_of 与 /custom、/preset 同口径校验
...
/api/screener/run_all 收的是无类型 dict,as_of 只在 isinstance(str) 时才
解析,其余类型原样透传,造成两个问题:
1. 非法字符串("not-a-date"、"2026-13-01")抛未捕获 ValueError → 500;
2. JSON 数字({"as_of": 2026})不进解析分支,直接被当作日期用下去。
实测:{"as_of": 2026} 返回 200,响应 as_of="2026",
strategy_cache.json 也写入 as_of="2026"({"as_of": 20260904} → "20260904")。
其它入口写进同一份缓存的是 "2026-09-04",cached-summary /
cached-result 都按 as_of 字符串比对,格式不一致的缓存条目永远失配。
同一份 as_of 在 /api/screener/custom 和 /api/screener/preset 上走
Pydantic 模型(CustomRequest / PresetRequest 的 `as_of: Optional[date]`),
非法字符串和数字都会被 422 拦下;只有 run_all 这一处漏了。本次把 run_all
补齐到同一口径:非字符串直接 400,非法字符串 400「日期格式错误」。
补 tests/test_screener_run_all_as_of.py,同时锁住合法 as_of 仍然照常执行、
缓存仍写 ISO 日期,以及不传 as_of 时回退到 latest_date()。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-09-09 07:36:21 +09:00
kevin9327 and Claude Opus 5
d2931aaadb
fix(backtest): SSE 端点非法 start/end 返回 400 而不是 500
...
strategy/stream、optimize/stream、walkforward/stream 三个端点直接裸调
date.fromisoformat(start/end),前端传入 "not-a-date"、"2026-13-01"、
"2026/09/04" 之类的值会抛未捕获 ValueError,客户端只拿到 500 和
Internal Server Error,看不到是哪个参数不合法。
仓库内同类入口已有正确写法:signals.py 的 /intraday/replay 把
date.fromisoformat 包在 try 里返回 400「日期格式错误」,mining.py 的
MiningRunRequest 用 field_validator 走 422。本次只把 backtest 这三处
补齐到同一口径,不改其它行为。
补 tests/test_backtest_stream_date_guard.py:三个端点 × 非法 start/end
断言 400;另外用服务端范围保护(backtest_range_guard)让事件流立即收尾,
断言合法日期照旧返回 200,证明不是把入口收窄。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com >
2026-09-09 07:35:32 +09:00
kevin9327
a49c8b3b15
fix(factors): 删除因子被拒时定义已被删掉
...
DELETE /api/factors/custom/{id} 的 404 守卫把 store.delete_one 当成存在性
探测, 但它会真的 unlink 文件并返回 True。当定义在盘上却没进注册表时
(load_into_registry 对注册失败的定义只告警跳过, 如复合因子的成员已被强制
删除), get_factor 为 None, 短路求值会走到 delete_one, 定义随即被删除。
随后的引用检查再返回 409「该因子仍有引用, 拒绝删除」—— 接口声称拒绝, 定义
却已经没了; 用户改带 force=true 重试只会得到 404, 定义无法恢复。
把 fail-closed 的引用检查移到存在性判定之前, 保证任何拒绝路径都不改磁盘。
2026-09-09 07:20:16 +09:00
kevin9327
e349f2ba9b
fix(settings): 端点测速在清单缓存未预热时 500
...
_endpoints_cache 初值是 {"ts": 0.0, "data": None}, "data" 键存在, 所以
dict.get("data", {}) 的默认值不生效, 返回的是 None。进程启动后到第一次
GET /api/settings/endpoints 之前, POST /api/settings/test_endpoint 不带
rounds 会在 None.get("testRounds", 5) 上抛 AttributeError → HTTP 500。
前端 endpoints 查询有 5 分钟 staleTime, 后端重启后用户再点测速正好落在
这个窗口: 前端不会重新拉清单, 后端缓存却已清空。
改为先 `or {}` 兜底再取 testRounds, 保持原有默认 5 轮语义。
2026-09-09 07:12:54 +09:00
0112020179
a8bb7a25bf
fix(kline): 压缩详情响应并限制无权限分钟回退
2026-09-08 18:09:59 +08:00
shy3130
908b385501
feat(ext-data): 拉取接口支持 API Key 鉴权 + 扩展数据弹窗加宽可滚动
...
后端:
- PullConfig 新增 auth (none/bearer/header/query, 与自定义行情源
AuthConfig 同口径); 拉取/测试/回补共用 _request_json, 鉴权注入
只有一套口径, 用户自定义 Headers 仍优先于 UA 标识头
- Key 本体存 secrets_store (data/user_data/secrets.json, 0600),
不落 config.json; EXT_{ID}_API_KEY 环境变量兜底; 配置了鉴权但
未设 Key 时 fail-closed 拒绝请求
- 新增 GET/PUT /api/ext-data/{id}/api-key (GET 只返回脱敏值);
删除配置时清理残留 Key; 历史 config.json 无 auth 字段读为 None
行为不变; PUT /pull 不带 auth 时沿用现有鉴权
前端:
- 拉取面板新增"接口鉴权"区块: 方式下拉 + 请求头名/参数名 + Key
密码框 (输入新 Key 覆盖, 清空保存删除), 随保存/测试/开关一起生效
- SettingsModal 加 width prop, 扩展数据设置弹窗与 EditExtDialog
加宽至 max-w-2xl; SettingsModal 加 max-h-[88vh] + 内容区独立
滚动, 矮视口下不再被截断
2026-09-07 22:54:05 +08:00
shy3130
f8a23b7ce5
fix(api): quote_service 缺失时 quote-interval 兜底返回默认档位 ( #261 )
...
PUT /preferences/quote-interval 的 if not qs 分支错误调用
qs.get_min_interval(),此时 qs 必为 None,触发 AttributeError → 500。
改为返回硬编码默认值 6.0,与 GET 接口兜底写法及
QuoteService.DEFAULT_INTERVAL 保持一致,并新增复现测试。
2026-09-07 22:01:12 +08:00
shy3130
f03bc38a16
feat(screener): run_all 渐进式返回, 快策略先出、慢策略后台补算
...
策略页全量 run_all 需 ~2 分钟, 期间卡片全空。现在按历史耗时升序执行,
首返时限 (strategy_run_all_first_return_s, 默认 15s) 内算完的策略随
响应返回, 慢策略转后台继续算并逐个写入策略缓存, 前端轮询
cached-summary 逐个点亮卡片数字 (未出的显示脉冲占位)。
后端:
- services/strategy_run_queue: 单飞 daemon 工作线程 + handle 状态。
相同 key (资产/周期/日期/策略集) 且未完成的请求搭车现有执行不重算;
已完成的重跑即新执行。后端全局同时只跑一个 run_all, 补上前端
防重入之外的第二道 Numba 并发防线
- run_all 渐进分支 (仅日线 + summary_only): 逐策略增量写缓存 (同日
按 sid 合并), 收尾整体重写保持旧口径; 结果带 computed_at 时间戳;
分钟周期与明细请求保持整段阻塞不变
- 历史耗时落盘 user_data/strategy_run_timings.json, 次日起快策略自动
排前; cached-summary 透传 computed_at
前端:
- 请求通用 30s 超时 + 慢接口豁免清单 (run_all/run/backtest/factor 等
300s), 避免一个挂起请求占满 HTTP/1.1 连接拖死全站
- Screener: 收到 pending 后 summaryQuery 每 2s 轮询, 以
computed_at >= started_at 判新 (防同日旧缓存冒充), 8 分钟兜底;
StrategyCard 三种尺寸新增 computing 脉冲占位
验证: 新增 13 测试 (排序/落盘/搭车/串行化/端点行为/旧路径兼容),
全量套件 1762 passed; pnpm build 通过。
2026-09-07 15:25:54 +08:00
shy3130
ddb265b0f8
feat(ext-data): 出站请求默认携带 tsp 标识头
...
扩展数据的三个出站点 (定时/手动拉取与历史回补、内置预设拉取、URL 探测
测试) 统一经 outbound_headers() 注入 User-Agent: tsp/<版本> 与
X-TSP-Client: tick-stock-panel, 服务端 (如 tickflow-hub) 可据此识别
本项目的请求来源。
用户在拉取配置里显式设置的同名请求头优先 (大小写不敏感匹配, 不重复
发送), 需要特定 UA 的数据源不受影响。
验证: pytest 22/22 (含 3 个新用例: 默认标识头/用户头优先/实际请求携带);
逐文件 ruff 与 HEAD 对比零新增。
2026-09-06 23:16:27 +08:00
shy3130
cd9e20610f
feat(ext-data): 时序表历史回补 — date_param 按日拉取 + tickflow-hub 404 契约
...
人气排行等 timeseries 扩展表此前只能从开启拉取之日起逐日累积。拉取配置
新增日期参数名 (date_param), 接口支持按日查询时即可历史回补: 按本地交易日
逐日请求 ?{date_param}=YYYY-MM-DD 写入对应分区, 幂等 (已有分区跳过),
单次上限 120 天, 429 限流退避重试 + 连续限流中止可续补。
适配 tickflow-hub PR#19 (/exports、/fuyao-rank 按日历史截面):
- 该日无快照返回 404 → 计入 empty 跳过, 不进失败清单 (主循环与 429
退避重试两条路径均覆盖)
- 防污染契约: 响应行 date 与请求日期不一致 (接口忽略日期参数) 时
fail-closed 拒写该分区, 避免当日值污染整个历史时序
- 修复调度器 next_run 计算的 UTC 未导入 NameError
前端拉取面板新增日期参数名配置与回补入口; 回补落盘的历史数据立即进入
信号/因子/回测按日对齐通道 (PIT)。
验证: pytest tests/test_ext_backfill.py 16 通过 (含 404/429/幂等/防污染),
ext 全套 144 通过; pnpm build 通过。
2026-09-06 22:35:08 +08:00
wshy
f275edafeb
Merge pull request #256 from SummerCaptain/feat/recap-push-gate
...
feat(preferences): 复盘推送 review_push_mode 门控
2026-09-06 20:53:29 +08:00
wshy
f0c0fd3bf8
Merge pull request #255 from SummerCaptain/feat/ai-strategy-draft-gate
...
feat(strategy): AI 策略保存为 research_only 草稿 + publish 端点
2026-09-06 20:53:25 +08:00
shy3130
e3a2fbce13
feat(ext-data): 扩展表字段接入信号与因子(数值因子/评分 + string 归属筛选)
...
数值字段(int/float) → 信号+因子双通道:
- ext_factors: 帧组装时 join 扩展列并注册 kind=base 因子(分组「扩展数据」),
时序模式按 (symbol,交易日) 精确对齐无未来函数, 快照模式仅当日单日帧
注入(历史帧跳过防未来函数)
- registry.all_factors 惰性同步(配置目录签名幂等, 以注册表为权威增删);
custom_signals.allowed_fields 自动并入 → 信号下拉/因子库/AI提示词/检验
同一份清单; factor 补算入口按需注入
- 失效链: 上传/拉取/配置变更自动清扩展帧缓存+策略缓存, API层补
repo.clear_cache; 写入后下一次计算立即生效
- 列名保留中文(预设表字段名), 非ASCII数值字段只进信号不注册因子
(DSL标识符ASCII-only)
string 字段(概念/行业归属) → 仅信号条件通道:
- 运算符 包含(contains,字面量匹配非正则)/等于/不等于, 右值为字符串字面量,
可与数值条件混合(强势板块归属 AND 热度阈值)
- 前端信号编辑器按字段类型切换运算符与右值输入; /options 暴露
stringFields; AI 提示词含字符串字段清单与 contains 用法
- string 不注册为因子(数值口径), 空值不误报
测试: test_ext_factors 18个(PIT对齐/跨日不泄露/快照门控/写入失效/
contains字面量/中文列名端到端等); 存量因子计数测试补 data/ 运行时隔离
夹具(黄金断言不依赖本机扩展表); 受影响回归148个全过; pnpm build 通过;
ruff 对齐 main 基线
2026-09-06 18:27:33 +08:00
shy3130
716897f41a
feat(signals): 信号库新增盘中信号(分钟K特征)
...
- intraday_features: 会话对齐特征帧构造器(vwap/涨跌幅/1·3·5分钟放量比/
日内与开盘30分钟高低点距离, 滚动窗口不跨午休, 只用已收盘bar防未来函数)
- custom_signals: timeframe=daily|intraday 双 schema, 盘中条件支持
cross_up/cross_down 穿越算子; 输出为当日条件上升沿(首bar不触发,
null特征判false绝不误报)
- 旧4个分时穿越信号列名零迁移(评估器回映射历史列名, 存量监控规则不动)
- 引擎单点注入 csgi_ 列: 监控/分钟策略/分钟回测共用同一构造器;
日线策略引用盘中信号显式报错
- 回放验证 API /api/custom-signals/intraday/replay(本地历史分钟K重放,
区间≤60天标的≤200, 先验证再配置监控)
- 能力门槛: 分钟K能力(订阅池)或全量分钟能力(本地分区); 回放仅需本地历史
验证: 新增16个测试(特征数值/边界/旧4信号黄金等价/引擎注入/回放端点),
受影响回归109个全过, ruff对齐基线, docs/features.md 同步
2026-09-06 18:26:29 +08:00
sc
9d55c237ed
feat(preferences): review_push_mode 端点与前端开关配套
...
- settings: GET 返回 review_push_mode, PUT review-push 支持 mode 入参
- market_recap: push 移入 SaveReportRequest body(显式外发)
- Review 页: 推送触发方式 auto/manual 开关
- 门控测试: 手动/定时两条路径的 manual/auto 推送断言
2026-09-06 16:20:42 +08:00
sc
e6c7fa4a43
feat(strategy): 策略池 AI 草稿分区与发布闸前端配套
...
- 后端 publish 失败由 ValueError 改为 HTTPException(500) 返回给前端
- 策略池 AI 标签新增「草稿」分区, 展示 research_only 策略并提供发布按钮
- 导入/保存 AI 策略为草稿时提示发布后可用, onSavedId 对草稿态跳过入池校验
2026-09-06 16:05:02 +08:00
sc
905fb8846c
feat(preferences): 复盘推送 review_push_mode 门控
2026-09-06 13:41:52 +08:00
sc
f6fc22fef1
feat(strategy): AI 策略保存为 research_only 草稿 + publish 端点
2026-09-06 13:41:51 +08:00
kevin9327
e12e0c7d02
fix(watchlist): 自选导入上传分块读取, 越限即拒绝而非读完再拒
...
import-csv / import-image 原先 `await file.read()` 之后才比较长度, 上限只在
整个文件进入内存之后生效, 与 issue #204 修复前的扩展数据上传同类。新增
_read_upload_capped 分块读取, 累计超过上限的那一块立即返回 400 并停止读取,
状态码与文案不变; 内存占用不超过上限 + 一块。
测试: 新增 5 例 (含「越限后不再继续读取」的字节计数断言); test_watchlist_csv
的上传 mock 改为像真实 UploadFile 一样读尽返回 b""。
2026-09-05 21:11:44 +09:00
shy3130
2ff909cd36
fix(notifications): #244 维护者补丁 — 移除预埋类型并修正全角标点
...
- 移除 api.ts 中与通知无关的 MainCostEstimate/StockLevels.main_cost 类型(对应后端字段在本仓库不存在, 属跨 PR 预埋, 全仓库零引用)
- settings.py 5 处 PR 新增字符串的全角逗号改半角, RUF001 恢复到 main 基线(28 ≤ 29)
- 验证: 通知测试 48 passed, 前端 build 通过
2026-09-05 17:16:58 +08:00
wshy
c1cad36449
Merge pull request #244 from yushenglin01/feat/notification-channels
...
feat(notifications): add custom webhook and email channels
2026-09-05 17:14:25 +08:00
shy3130
e0cd625ef4
feat(platform): 因子平台与因子↔策略双向联动 v0.2.3
...
- 因子平台: /factors 一级页(检验/因子库/编辑器/组合/挖掘), DSL 公式因子(25 算子点选、双语字段、我的因子模板、脏公式守卫), 版本与生命周期, 自动挖掘 L1 统计筛选
- 因子↔策略四条桥: 触发器 Zap 快建因子条件信号、因子一键生成排名策略、自定义信号 AI 提示词接入因子分组、策略回测因子归因(胜/败单入场信号日因子均值, 独立 tab, 双语因子名)
- 回测: 统计卡新增盈亏比(≥1 红/<1 绿), 蒙卡回撤合并为中位/95% 双值卡(自适应字号), 高级设置基础过滤与策略编辑器参数对齐(5 组区间)
- 信号库独立页 /signals(原设置 tab 迁出), 持仓提醒入导航; 挖掘并入因子页第 5 tab, /mining 旧链接重定向
- 研究线配套: 因子目录 61→77(评分/矩阵双内核), stats_v2(Newey-West/BH-FDR/DSR), enriched 管道与异动/报价服务配套调整
- 文档: README 导航与特性表、features.md 因子平台章节、操作说明书 9.2、factor-platform-plan 执行状态与 §5、二开文档桥接说明; 交流与支持节改版
- 版本 0.2.2 → 0.2.3; 后端全量 1625 passed(1 例环境性跳过), 前端 build 通过
2026-09-05 15:41:15 +08:00
yushenglin
5289cde13e
feat(notifications): add custom webhook and email channels
2026-09-04 16:37:06 +08:00
0112020179
8721bd2e83
perf(kline): update detail chart with latest row
2026-09-03 17:35:16 +08:00
shy3130
41205b197c
chore: follow-ups for PR #231/#233 reviews
...
- monitor_rules: raise from None in remind_date validation (B904)
- lots: log warning on asset-type fallback to stock (fail-open traceability)
- matrix: demote turnover NaN-column notice to debug
2026-09-03 12:45:24 +08:00
richard and Claude
adc7ab52a9
feat(lots): holdings-reminder registry with auto stop/profit & expiry monitoring
...
新增「持仓提醒」页(/lots),登记买入批次(个股/ETF),每批自动生成并同步两条
监控规则: lot_{id}_p(type=price 止盈止损)与 lot_{id}_d(type=date 到期提醒)。
- 核心监控引擎新增 date 规则类型: 纯日历窗口、每个交易日仅在首个轮询评估一次、
按天 cooldown、跨天清理过期键; 消息按触发当天显示"N天后到期/今日到期"。
- 批次派生规则继承默认 webhook 渠道; 监控中心只读展示(批次/批次托管徽标),
通用 monitor-rules 接口对带 lot_id 的规则写/删返回 409, 避免与批次页脱节。
- 通知正文统一在缺省时追加触发现价与涨跌幅, 并避免与引擎自带引语重复。
- 后端分层: strategy/lots.py 域(校验/存储/批次→规则纯映射) + api/lots.py 薄路由
(写锁/校验先行/级联删规则/复用 _sync_engine 重载); services/fs_utils.py 原子写。
- 前端: api/queryKeys 契约、DateShortcuts、Lots 页(数值输入可留空、加载/出错态、
文案打磨)、Monitor 日期提醒/批次徽标; RuleEditor 不手工建 date(由持仓页生成)。
- 记账/加仓减仓属"交易口径", 不在本改动(issue #230 ); 到期提醒按自然日窗口,
休市/节假日顺延的交易日历口径待 issue 定夺。
验证: 新增 tests/test_date_rule.py + tests/test_lots.py 全绿(20), 监控族回归通过,
ruff 新文件干净, pnpm build 通过。
Co-Authored-By: Claude <noreply@anthropic.com >
2026-09-02 20:20:38 +08:00
wshy
a28f9e852f
Merge pull request #228 from thinkbuf/feat/watchlist-import-csv-groups
...
feat(watchlist): batch import CSV / pasted codes into groups
2026-09-02 15:55:18 +08:00
richard and Claude
e9f5c606b6
feat(watchlist): batch import CSV / pasted codes into groups
...
自选页新增批量导入:支持 CSV/TXT 文件与粘贴证券代码两种来源,解析出
候选并在弹窗内确认后按 M:N 分组一次性写入(目标分组可多选或就地新建,
默认只勾新增标的,已在自选的可并入所选分组)。
Co-Authored-By: Claude <noreply@anthropic.com >
2026-09-02 14:48:32 +08:00
richard and Claude
40fea3236e
feat(monitor): add webhook test message button
...
Add a test button to the push-notification settings so users can verify Feishu and WeCom group-webhook configs after saving.
Backend: POST /api/settings/preferences/webhook-test reads the saved webhook URL/secret and sends a one-shot test message (max_attempts=1 to skip production retry backoff). All failure modes return HTTP 200 + {ok:false, detail} so the UI renders a single path.
Frontend: api.sendTestWebhook(); TestSendButton/TestResult components shared by both channels; success feedback auto-dismisses after 2s.
Tests: 7 cases covering both channels, failure modes, and single-attempt behavior.
Co-Authored-By: Claude <noreply@anthropic.com >
2026-09-02 13:36:06 +08:00
shy3130
e8c870af73
Merge branch 'pr-219' into merge/pr-221-220-219
2026-09-01 22:20:01 +08:00
shy3130
a9c00fd567
Merge branch 'pr-221' into merge/pr-221-220-219
2026-09-01 22:20:01 +08:00
shy3130
95cd99ab8c
merge: 同步 main (辐射本地读 + 传输压缩/网络设置) 到全量分钟分支
...
冲突两处均为 minute-refresh status 字段两侧各自新增, 两者全保留
(healthy + provider/provider_effective/repair_only)。
2026-09-01 13:05:21 +08:00
shy3130
bc9461da9a
feat(net): 分时/日K批量响应可选 gzip 压缩与网络设置
...
- _gzip_payload: 偏好开启 + Accept-Encoding 协商 + 超 1KB 才压
(level 6); datetime 序列化与非压缩路径一致, 保证 since 增量
字符串字典序合并不受影响
- minute-batch / daily-batch 端点各自独立偏好键, 逐请求即时生效;
实测分时 9.1MB→1.28MB (86%), 日K 736KB→156KB (79%)
- 超时设置 tab 更名网络设置, 卡内新增压缩区: 大开关 OR 联动
(任一子开即亮, 全关才灭, 点击全开/全关) + 分时/日K两个子开关
- preferences.save() 加线程锁: 修复并行 PUT read-modify-write
互相覆盖 (总开关批量关不齐的根因), 存量偏好写入一并受益
- 测试: 双端点压缩/关闭直通/无协商头直通/偏好默认 + 并行 save 竞态
2026-09-01 13:04:00 +08:00
shy3130
010c484289
feat(data): 全量分钟 (full_minute) 数据集开放插件/自定义源接入
...
像其他能力一样可路由: 声明 full_minute 数据集并在设置页路由, 即接入
盘中全市场分钟落盘服务 (与 TickFlow Expert 同一能力键 intraday.universe)。
- policy: _DATASET_CAP_MAP 增补 full_minute → INTRADAY_UNIVERSE, 自定义源
声明且被路由时自动补授能力, 服务门控对两类源统一口径
- capabilities 注册表: full_minute 从不可路由 (field=None) 改为
full_minute_data_provider 偏好路由; preferences/settings API 收发新字段
- 插件契约: get_intraday_batch (修复轮, 未实现回退 get_minute 当日窗口) +
get_intraday_latest (稳态增量, 可选; 未实现降级仅修复轮, 节奏下限 60s)
- YAML 声明式源: full_minute 数据集与 minute 同形, loader 白名单放行,
源编辑器 UI 可配置; 仅修复轮语义
- kline_sync 边界: fetch_intraday_custom_batch / _fetch_intraday_custom_latest,
帧过北京墙钟守卫, 与 TickFlow 边界函数同纪律
- minute_refresh: 删「自定义分钟源让位」逻辑, 改按路由取数;
status 增 provider / provider_effective / repair_only
- 前端: 监控页卡片按能力驱动, 数据源页路由矩阵, api.ts 类型
- 测试: 门控/路由/降级/增广新增用例, 后端 1332 全过
- 文档: plugin-development / custom-data-source / configuration / features /
CONTRIBUTING 契约细化 (AI 可照文档接入)
2026-08-31 22:53:24 +08:00
shy3130
be799abfa0
fix(intraday): since 增量时区崩溃 — 客户端直传原始时间串, 服务端归一 aware 输入
...
客户端 toISOString 生成带 Z 的 UTC, Python 3.12 解析为 aware datetime,
与行内 naive 北京时间比较 TypeError (第二轮轮询 500), 且换算差 8 小时。
- 客户端: since 直接回传最旧最后一根的原始 datetime 字符串 (同格式同时区,
字典序即时间序), 消灭一切换算
- 服务端: fromisoformat 后 aware 输入先转北京墙钟再去 tzinfo, 防御旧客户端
2026-08-31 22:01:46 +08:00
shy3130
ed2f81c312
feat(realtime): 首用门禁 — 本地无数据时禁止开启实时行情
...
日K/enriched 均空 (与看板首用弹窗同口径) 时开启实时行情返回 409 并给
同步指引, 不落偏好。展示/监控/梯队全部依赖本地数据底座, 空数据下轮询
只是空转耗配额且缺 instruments 维表。只拦开启不影响关闭, 已开后清库
下次重开自然被拦。
2026-08-31 21:45:07 +08:00
shy3130
320969497a
perf(intraday): 分时链路增量传输 + SSE 续画 + 视口感知
...
- minute-batch 支持 since 增量响应: 只回 >= since 的K (含动态最后一根),
客户端按 (symbol, datetime) upsert 合并, 轮询流量从全天序列降到每轮一两根
- 自选分时 SSE 续画: enriched tick 前端续写分钟序列最后一根/滚动追加新K
(纯视图叠加不写缓存, 服务端定版值始终权威); tick 新鲜时轮询降为 30s 兜底
- 视口感知: 从虚拟器直读渲染中 symbol 集合 (可见+overscan), 数据层只拉视口内;
since 只按本轮请求集合取最旧尾部, 视口外落后不放大窗口; 集合变化 300ms
防抖补拉; 剔除指数保持端点契约
- since 过滤专项测试: 含 since 当根 / 早于全部K / 无 since 三态
2026-08-31 21:45:06 +08:00
shy3130
869c49bb0b
fix(kline): 分钟增量起点改为最后一根本身 — 形成中的动态K需重拉覆盖
2026-08-31 20:54:56 +08:00
shy3130
b78ba9940d
perf(kline): 分钟批量补拉取到即落盘 + 尾部增量窗口
...
- 补拉结果经 _write_minute_partition upsert 落盘 (持仓库写锁, 与全量分钟
服务/盘后同步同一纪律), 下一轮命中本地, 大自选从每轮全天重拉降为增量
- 完整性判定由 0.9 根数比例改为 期望-2 根: 比例阈值会让持久化数据在 90%
处冻结尾巴
- 分类三态: fresh 直读本地零请求 / 尾部落后从最后一根+1min 增量拉 /
中间有洞 (间距非 1min/午休 91min) 退回全天重拉回填, 防增量窗口漏数据
- 落盘失败降级为仅返回本轮数据 (持久化是优化而非正确性前提)
2026-08-31 20:53:23 +08:00
shy3130
df5cae143e
feat(minute): 自选分时接入全量分钟本地供给 (prefer_local + freshness 判定)
...
- MinuteRefreshService.is_healthy(): 偏好开 + 线程活 + 最近一轮距今
≤ max(2×间隔, 30s)。last_round_at 只在取数+落盘成功后更新, 连续失败
自动超时判不健康, 冷启动修复轮期间天然不健康
- /api/kline/minute-batch 加 prefer_local 标志: 服务健康时股票 incomplete
不再批量补拉, 本地有多少给多少 (服务下一轮落盘补全; 停牌票补拉也是空,
无损); ETF 不在全量分钟 universe 内维持补拉; 不健康回落现状兜底。
响应加 full_minute_local 标记
- 大自选 (数百上千只) 不再每 6s 轮询持续打批量分钟接口
- 4 个新用例: 健康跳过股票补拉/ETF 仍补, 不健康回落, 无标志不受影响,
is_healthy 三条件边界
2026-08-31 17:10:02 +08:00
shy3130
3c6ed99922
feat(index): 指数收敛为固定核心四只 + fuyao 指数快照接入
...
- 新建 app/services/index_const.py 单一权威: 展示层固定四只核心指数
(上证/深证成指/创业板/科创综指), quote_service/overview/
market_overview_builder/sector_monitor 四处重复定义全部收敛
- fuyao 插件接入指数快照端点 (/api/a-share-index/prices/snapshot):
client 增加 index_snapshot (批量上限 627 码), provider 实现可选协议
get_realtime_indices — .BJ 后缀先行过滤 (未知代码整批 1002 连坐),
volume 不做股转手; 修复 fuyao 路由下指数冻结在日K兜底的 bug
- quote_service 自定义源分支鸭子类型调用 get_realtime_indices 补拉指数,
请求清单 = 核心四只 + 启用指数监控规则标的; 未实现的源走日K兜底
- TickFlow 分支指数固定按码显式拉取, 移除 CN_Index 全量 universe 与
mode core/all 分支; 指数落盘固定 merge 不截断
- 指数偏好全套下线: realtime_index_symbols / sidebar_index_symbols /
indices_nav_pinned / realtime_pull_index / realtime_index_mode
- /api/index/list 与 /search (全指数浏览搜索) 删除; daily/minute 保留
供指数详情页, sync_instruments/sync_daily 保留供数据页
- sector_monitor 指数标的恒可监控, catalog 签名不再依赖指数偏好
- 新增 7 个用例: 指数快照映射/.BJ 过滤/软失败 + 自定义源指数补充链路
(含监控规则并入/无协议源静默/指数失败软降级)
2026-08-31 16:50:21 +08:00
shy3130
e287a55e82
fix(capabilities): redetect 同步刷新 app.state 快照并升级 tickflow SDK 0.1.25
...
- /api/capabilities/redetect 重新探测后同步写回 app.state.capabilities,
minute_refresh 等门控服务即时读到新档位, 不再需要重启
- tickflow 0.1.24 缺 intraday_universe 方法导致 Expert 档全量分钟探测失败,
升级到 0.1.25
- 全量分钟轮次异常与模式补充 warning 日志, 便于线上诊断
2026-08-31 16:50:12 +08:00