wshy
|
0bc5ff1831
|
fix(strategy): 加载边界归一化 META params, 修复非标准格式导致 /api/strategies 500 (fixes #68) (#72)
custom/AI 策略的 META["params"] 可能是 dict / list[str] 等非标准格式 (LLM 偶发
漂移 / 用户手改). 加载时 _load_file 只 setdefault("params", []) 不校验格式,
坏策略照样进内存, 在 _strategy_detail() 的 {p["id"]: p["default"] for p in params}
处抛 TypeError, 一个坏策略拖垮整个 /api/strategies 列表 500. 回测链路
backtest/strategy.py:589 的 _normalize_params 同样会崩.
在 _load_file 加载边界新增 _normalize_param_defs(), 把任意格式统一成标准
list[dict]: dict 按 key 作 id 转换, list[str] 每项作 id, 不可识别项丢弃并 warning,
整体异常降级为空 list. 三类来源 (builtin/custom/ai) 都走 _load_file, 单点覆盖.
降级而非崩溃: 策略仍可见可用, 只是设置面板无参数滑块.
新增 11 个测试覆盖归一化各分支 + _strategy_detail 不再 500 的回归断言.
|
2026-07-08 12:34:08 +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 |
|
Lytem
|
9c730e4b69
|
fix: 修复因子回测分层错误问题 (#63)
* fix: force LF for uv.lock
* Fix factor backtest grouping stability
|
2026-07-08 12:11:03 +08:00 |
|
im47cn
|
3a5f3e05b6
|
feat(backtest): 稳健性指标 Sortino + 蒙特卡罗回撤分位 + per-trade 明细 (#67)
* feat(backtest): 新增稳健性指标 Sortino + 蒙特卡罗回撤分位 + per-trade 明细
回测原本只有 Sharpe/Calmar/最大回撤, 缺防过拟合视角。本 PR 补三类:
1. Sortino 比率: 用下行偏差 (MAR=0 目标半方差) 替代总标准差, 只惩罚负
收益波动。无亏损时约定返回 None (不虚报 0/inf)。各 stats 函数用与自身
Sharpe 相同的收益基准 (逐笔 or 日频)。
2. 蒙特卡罗最大回撤分位: 对逐笔收益有放回重抽样 1000 次, 估计回撤分布,
报 P50(中位) 与 P95(95% 置信最坏)。回答"仅因成交顺序运气回撤能有多坏",
单次样本内回撤看不到这个。固定种子 (42) 保证可复现/可测。
3. per-trade 明细: best/worst/median_pnl/avg_holding_days, 补 _calc_stats
与 _calc_portfolio_stats 原本缺失的逐笔视角。
三个 stats 函数 (_calc_stats / _calc_independent_candidate_result /
_calc_portfolio_stats) 全部接入, 用共享静态 helper (_sortino_ratio /
_mc_drawdown_percentiles / _per_trade_block) 避免重复。纯 additive, 既有
字段不变, 空交易安全。
前端 StrategyBacktest 结果区新增 索提诺 / 蒙卡回撤(中位) / 蒙卡回撤(95%最坏)
三个指标卡 (stats 为 Record<string,any>, 无需改类型)。
新增 tests/backtest/test_robustness_metrics.py 11 用例: Sortino 手算校验/
无下行 None/优于 Sharpe/样本不足; 蒙卡确定性/P95≤P50≤0/全正零回撤/样本不足;
_calc_stats 与 portfolio 字段集成 + 空交易安全。
注: upstream main 既有 test_trailing_take_profit_exits_after_activation
失败与本 PR 无关 (改动前后一致复现)。
* test+fix(backtest): 子代理审查修复 — 补 full 主路径覆盖 + finite 护栏 + 内存上限
子代理审查发现的缺口, 本次补全:
覆盖 (测试):
- [关键] full 模式主路径 _calc_independent_candidate_result 原无集成测试,
漏拼 sortino/mc 字典展开会导致前端指标卡空值却测试全绿。新增该分支断言。
- MC 确定性测试从"两次相等"升级为钉死快照值 (p50=-0.0976/p95=-0.2108),
一旦有人把种子改成系统熵立即红。
- p95 断言从恒真的 p95<=p50 (percentile 单调性数学恒成立, 无信息) 改为
p50<0 且 p95<p95 严格更差, 真正验证数值。
- 补全盈利交易 → sortino 集成层为 None 的用例 (原仅 helper 层覆盖)。
健壮性 (实现):
- 三个 helper 入口 finite 过滤: inf/nan 收益会让 cumprod 传播 nan → 分位 nan
→ asdict 直出非法 JSON (项目 _safe 只洗 rows 不洗 stats)。剔除后与纯净输入
结果一致 (加测试守护)。
- MC 内存护栏: full 模式可数千笔, n_sims×n×8B×4 数组瞬时可达数百 MB。
按 2M 单元上限压降模拟次数 (小样本 n_sims=1000 不变, 快照稳定)。
注: 逐笔年化口径 (_calc_stats 对 pnls ×√252) 落在死路径 simulate() (无 app
调用方) 且复刻既有 Sharpe, 不在本 PR 范围。两条活路径 (portfolio/independent)
均用日频基准, ×√252 正确。
|
2026-07-08 12:10:05 +08:00 |
|
wshy
|
0121a5583f
|
fix(strategy): 策略详情合并触发器 override, 修复买卖信号 tag 保存后回显丢失 (#71)
_strategy_detail 原直接返回策略源文件默认值 s.entry_signals / s.exit_signals,
没有像 stop_loss / max_hold_days 那样合并 overrides, 导致用户在策略卡片弹窗里
新选的买入/卖出触发器 tag 虽已保存进 strategy_overrides JSON, 但再次打开时
回显的还是源文件默认值.
保存落盘 / 回测 / 监控链路均正常 (它们各自有 _effective_signals 合并逻辑),
只有 GET /api/strategies/{id} 这条详情链路漏了 merge.
新增 4 个回归测试覆盖 _strategy_detail 的触发器合并契约.
|
2026-07-08 11:27:59 +08:00 |
|
wshy
|
3820b35921
|
fix(watchlist): 自选页 enriched 改为 LEFT JOIN, 修复不在缓存 universe 的自选股被静默丢弃 (#70)
后端 watchlist_enriched 原以 enriched 缓存为主表执行 inner filter
(df_e.filter(is_in(stock_symbols))), 方向反了: 不在缓存 universe 里的自选
标的 (新股/冷门股/新用户未同步) 被整行丢弃. 改为以自选列表为主表 LEFT JOIN
enriched, 缺失标的指标为 null, 前端已有 '—' 占位渲染兜底. ETF 分支同理.
前端 Watchlist 顶部胶囊区原 hiddenCount = allSymbols - sortedRows, 把 '数据
未返回' 误算为 '被筛选隐藏'. 拆分为两个口径:
- hiddenCount: rows.length - sortedRows.length (真正被筛选条件隐藏)
- pendingCount: sortedRows 中 close 为 null 的行数 (指标未就绪)
并新增 '待数据 N' 灰色提示与原 '已过滤 N' 区分.
新增 4 个回归测试覆盖核心契约.
|
2026-07-08 11:06:56 +08:00 |
|
wshy
|
5b2282f8cb
|
feat(ext-data): 新增扩展数据支持 URL 创建并调整设置弹窗 tab (#64)
- 新建扩展数据弹窗加入 URL/文件/手动 三种接入方式, URL 模式支持探测字段、预览、保存拉取配置并可选立即导入或定时拉取
- 后端新增 /api/ext-data/detect-url 探测接口, 自动识别 JSON 行数组路径、字段类型与 symbol/code 候选列
- 抽出 infer_fields_from_df / detect_symbol_candidates / apply_config_mapping 公共逻辑, 文件上传与 URL 探测复用
- 拉取写入支持仅含 code 的数据, 按 code_map 自动生成标准 symbol
- 设置弹窗 tab 顺序调整为 拉取/推送/上传, 默认显示拉取
|
2026-07-07 18:04:49 +08:00 |
|
shy3130
|
f8914c9140
|
feat: 新增企业微信推送、个股分析页重构、价位算法优化及多处交互改进 (v0.1.82)
- 推送: 新增企业微信群机器人通道(与飞书并列), 监控告警/复盘报告多渠道分发
- 个股分析: 历史报告改为右侧常驻栏(展示全部股票报告), 进页面自动恢复上次选股
- 价位算法: 成交密集区改用换手率衰减模型(国内主流筹码分布); 缺口回补判定修正(必须完全穿越缺口)
- 回测: 回撤止盈改纯峰值口径(与 trailing_stop 一致); 佣金/印花税/滑点合并一行; 建仓口径加问号气泡说明
- 分时图: 自选列表迷你分时图加渐变填充(对齐个股对话框风格)
- 图表: 个股分析日K图右侧标签适配浅色主题; 价位标签与下方文字行双向 hover 高亮联动
|
2026-07-07 17:56:25 +08:00 |
|
shy3130
|
03e99bd05f
|
feat(stock-sdk): 取数入口补充 INFO 日志, 便于确认切换后实际走 stock-sdk
|
2026-07-07 13:07:59 +08:00 |
|
wshy
|
bfc4168612
|
Update README.md
|
2026-07-07 12:43:18 +08:00 |
|
shy3130
|
9b22030166
|
fix: 数据源切换按 datasets 动态分流, 修复切换到 stock-sdk 后分钟K仍走 tickflow
- DataSources.tsx switchProvider 非 tickflow 分支原只传 4 个字段(漏 minute_data_provider),
后端 exclude_none 保留旧值导致 minute 永远停在 tickflow。改为按源声明的 datasets
动态构建: 支持的数据集切到该源, 不支持的显式写 tickflow(消除 UI/后台不一致)
- README License 段补充 stock-sdk (ISC) 协议声明
|
2026-07-07 12:39:23 +08:00 |
|
shy3130
|
fb7cf921a9
|
feat: 看板刷新按钮改为后端缓存重载
原「刷新」只重新请求前端数据(overview.refetch), 不动后端缓存, 跨天
残留时拿到的是旧数据。改为「重载」:
- 新增 POST /api/data/refresh-cache 端点: clear_cache + refresh_cache
重建 enriched 内存缓存, 同时清 overview 聚合缓存 + screener TTL 缓存
- 看板按钮先调后端重建缓存, 再刷新看板数据
- 与设置页「清理并刷新」(清前端 react-query)互补, 这个重建后端 Polars 缓存
|
2026-07-07 11:43:39 +08:00 |
|
shy3130
|
a688690f15
|
fix: 盘前维表同步后刷新 enriched 缓存, 修复跨天连板梯队不准
根因: 服务跨天不重启时, enriched 内存缓存残留前一交易日数据, 连板梯队/
选股读到旧的涨跌停信号。盘前维表 (instruments) 含当日精确涨跌停价, 但同步
完成后未触发缓存重建。
修复: run_instruments_sync 完成后 (rows > 0), 执行 clear_cache + refresh_cache,
让 enriched 缓存基于最新维表 (含当日 limit_up/down) 重建。与设置页「清理并刷新」
按钮同等效果, 但盘前自动执行, 无需手动操作。
|
2026-07-07 11:10:17 +08:00 |
|
shy3130
|
9217efa1da
|
docs: README 添加交流群二维码
- 新增「💬 交流群」板块(免责声明上方)
- 二维码文件重命名为英文 community-qr-code.jpg
|
2026-07-07 10:48:58 +08:00 |
|
wshy
|
c48e9d2d5b
|
Rename 1c178a8b893dde63fce74b2e898ce087.jpg to 交流群二维码
|
2026-07-07 10:20:24 +08:00 |
|
wshy
|
83a70da7be
|
Add files via upload
|
2026-07-07 10:06:24 +08:00 |
|
 wshyandshy3130
|
3656fedc62
|
fix(docker): 镜像内置 stock-sdk 插件依赖(Node + node_modules) (#59)
Docker 运行时镜像(python:3.11-slim)既无 node 也无 node_modules,
导致 stock-sdk 插件 availability() 恒返回 false, 用户反馈打成 docker 后无法安装。
多阶段构建:
- 新增 stocksdk-builder 阶段(node:20-bookworm-slim)npm ci 装依赖
- runtime 阶段 apt 装 nodejs(满足 engines>=18, 自带全部动态依赖)
- COPY --from 将 vendored node_modules 拷到与 bridge.mjs 同目录
(/app/app/plugins/stocksdk/node_modules), 命中 bridge.mjs loadSDK() 第一候选路径
用户 docker pull 后开箱即用, 无需进容器手动 npm install。
同步更新 plugin.yaml install_hint 文案与相关文档。
Co-authored-by: shy3130 <shy3130@users.noreply.github.com>
|
2026-07-06 22:26:43 +08:00 |
|
shy3130
|
9f78fdebd4
|
style: 主题切换只保留图标, 与设置同行
左侧菜单底部: 主题切换(纯图标)+ 设置同一行排列, 更紧凑。
原来上下两行各占满宽, 现在左边小图标按钮 + 右边设置占满剩余空间。
|
2026-07-06 17:45:25 +08:00 |
|
shy3130
|
9ef9b4e6ec
|
chore: bump to 0.1.81
|
2026-07-06 17:42:19 +08:00 |
|
Aliang
|
7627df9b60
|
fix(stock-analysis): 修复带鱼屏布局留白
|
2026-07-06 17:30:09 +08:00 |
|
 Jinfeng SunandClaude Fable 5
|
b0d1f2f742
|
feat: 自选/个股分析/K线查询接入 ETF (#58)
* feat: 自选/个股分析/K线查询接入 ETF
数据层此前已完成 ETF 同步与存储 (instruments_etf / kline_etf_* /
adj_factor_etf), 但查询侧仍只走股票路径。本次把已有的 ETF 读取能力
接到面向用户的接口上:
- repository: 新增 get_etf_symbol_set / resolve_asset_type 按 symbol
判定资产类型; get_minute / get_minute_batch / latest_minute_date
增加 asset_type 参数切换 ETF 分钟K存储
- kline API: instruments/search 增加 asset_types 参数 (默认 stock,
既有调用方行为不变), 结果附 asset_type; instruments/names 合并
ETF 名称; /daily /minute /minute-batch 按资产类型分流
- watchlist API: /enriched 合并 ETF enriched 缓存行, 名称补齐支持 ETF
- 个股分析: /levels 与 AI 分析按资产类型分流, ETF 无财务数据走
已有兜底提示
- 前端: 自选搜索框传 asset_types=stock,etf 并显示 ETF 标记
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix: 修复 ETF 接入的审查发现问题
多智能体代码审查确认的 9 处问题修复:
- watchlist /enriched: 仅自选实际含 ETF 时才加载 ETF enriched 缓存,
避免无 ETF 用户在缓存冷启动时触发全量懒加载阻塞请求; 恢复股票
enriched 预热期间 all-or-nothing 旧契约 (不再返回只有 ETF 的部分结果);
as_of 取股票/ETF 两类缓存中较旧者, 不再把旧 ETF 行标成股票缓存日期
- repository: refresh_cache 失效 _etf_enriched_cache, 盘后管道跑完
ETF 行不再停留在旧日期; get_etf_symbol_set / get_index_symbol_set
增加 memo (随 instruments 缓存失效), 热路径不再每请求重建全量集合;
新增 get_name_map 统一股票+ETF 名称解析, 收敛三处重复合并逻辑
- kline /daily: 实时蜡烛注入改为资产感知, ETF 开启实时拉取时同样
注入今日 bar (未开启时由"非今日不注入"守卫自然跳过)
- kline search: 空关键词早退提前到数据处理之前; symbol 列 dtype
归一到 Utf8, 防两份缓存来源 dtype 不一致导致 concat SchemaError
已知限制 (不在本次范围): kline_etf_minute 目前无盘后同步写入方,
ETF 分时依赖"本地缺失 → TickFlow 实时补拉"路径, 与其他未同步分钟K
标的行为一致。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
2026-07-06 17:26:40 +08:00 |
|
Aliang
|
bb562cbdff
|
feat(ai): 更新炸鸡中转站 API 地址
|
2026-07-06 17:18:09 +08:00 |
|
shy3130
|
966d94ab03
|
feat: 数据源插件化架构 + stock-sdk 首个插件
将可选数据源改为插件化架构: 插件代码放 backend/app/plugins/<name>/,
用户在设置页点击安装依赖, 主仓库不背运行时依赖(nodejs 等)。
架构:
- loader.py 新增 _load_builtin_plugins() 扫描 plugins/ 目录, 通过
plugin.yaml 清单动态发现并注册插件(委托自检 + 优雅降级)
- runtime 字段支持 node/python, 安装/卸载分别用 npm/pip
- 现有 tickflow + YAML 自定义源逻辑 100% 保留, 插件是叠加层
stock-sdk 插件 (plugins/stocksdk/):
- 原始实现来自 @forrany 的 PR #57, 迁移到插件化架构, 署名保留
- Node 桥接 (bridge.py/mjs) + Python provider, 覆盖日K/除权/分钟/实时
- 设置页主列表直接显示: 未装灰显+安装按钮, 装完可切换/卸载
配套:
- instrument_sync: 通用增强, 任何 provider 都能提供标的维表
- install/uninstall: uv 优先回退 pip, 容错坏 uv.toml + 国内镜像
- 10 例单测全过, tsc 零错误
|
2026-07-06 17:00:56 +08:00 |
|
shy3130
|
cc697e8ea4
|
fix: 分时图跟随实时行情自动轮询
实时行情开启后(realtimeRunning), 自选列表分时图自动按 15s 轮询刷新,
无需用户单独开启 minute_intraday_refresh 开关。
|
2026-07-06 17:00:55 +08:00 |
|
shy3130
|
1cb19872db
|
feat: 增加自选列表分时小图
|
2026-07-06 11:10:36 +08:00 |
|
weikejia
|
a86da75eff
|
feat: 看板涨幅榜/跌幅榜/成交额榜/活跃榜/领涨领跌概念个股可点击查看弹窗
|
2026-07-06 10:39:18 +08:00 |
|
lytem28
|
3ac35a816a
|
feat: make builtin strategy filters configurable
|
2026-07-06 10:26:16 +08:00 |
|
wshy
|
cfa749fab4
|
Update README.md
|
2026-07-06 09:58:02 +08:00 |
|
wshy
|
420e0ccd14
|
Update README.md
|
2026-07-06 09:57:30 +08:00 |
|
shy3130
|
f7b56ec3c8
|
docs: 自定义数据源新增 AI 映射 prompt 模板
|
2026-07-05 23:16:18 +08:00 |
|
shy3130
|
887e404fc4
|
Merge branch 'feat/tickflow-rate-limits'
|
2026-07-05 23:04:35 +08:00 |
|
shy3130
|
044479dedd
|
fix: 后端版本号同步到 0.1.80
|
2026-07-05 23:03:43 +08:00 |
|
wshy
|
491a61a0e2
|
Merge pull request #55 from shy3130/feat/tickflow-rate-limits
feat: 自定义数据源扩展 + 限频集中化 (v0.1.80)
|
2026-07-05 22:58:21 +08:00 |
|
shy3130
|
fa08981851
|
feat: 自定义数据源扩展 + 限频集中化 (v0.1.80)
|
2026-07-05 22:57:46 +08:00 |
|
 wshyandshy3130
|
e524ea4893
|
perf: 指标计算移出启动关键路径, 后台异步预热 (#51)
启动时 instruments/index/ETF 同步刷新(<0.1s), enriched 的 compute_indicators
(107万行, 低配Linux 50s+) 推 daemon 线程异步完成。应用秒级 ready, 前端不再
报 ECONNREFUSED。预热期间上层走空表降级, 完成后自动替换缓存。
- repository: _enriched_warming 标志 + warmup 线程 + 懒加载守卫
- main: refresh_cache(background=True) + app.state.indicators_ready
- data API: status 端点加 indicators_ready 字段
- 前端: Data 页指标计算中提示 + 加快轮询
Closes #47
Co-authored-by: shy3130 <shy3130@users.noreply.github.com>
|
2026-07-04 21:55:03 +08:00 |
|
wshy
|
7827d480a5
|
Update README.md
|
2026-07-04 18:51:42 +08:00 |
|
wshy
|
a52f1b35f4
|
Update README.md
|
2026-07-04 18:47:14 +08:00 |
|
 wshyandshy3130
|
a23be3822a
|
chore: add cache startup diagnostics (#49)
Co-authored-by: shy3130 <shy3130@users.noreply.github.com>
|
2026-07-04 18:42:49 +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 |
|
im47cn
|
1fd7e84785
|
feat(backtest): 成本模型拆分为佣金+印花税(仅卖出)+滑点 (#46)
* feat(backtest): 成本模型拆分为佣金+印花税(仅卖出)+滑点
MatcherConfig 新增 commission_pct/stamp_tax_pct 及 buy_cost_pct()/
sell_cost_pct() 方法, 撮合三处现场统一改用方法计算。
印花税单边(仅卖出)是 A 股与美股的本质差异: 旧的 fees_pct 双边模型
把印花税摊到买卖两腿, 会高估买入成本、低估卖出成本。拆分后买入腿=
佣金+滑点, 卖出腿=佣金+印花税+滑点。
commission_pct 未设时回退 fees_pct, 印花税未设时为 0, 完全向后兼容,
既有 12 个 portfolio 测试不变。strategy/api(含 SSE + 缓存键)全链透传。
新增 tests/backtest/test_cost_model.py 覆盖向后兼容/拆分/印花税仅卖出/
优先级/撮合传导 5 个用例。
* fix(backtest): 打通成本模型前端链路并修复 cancel 缓存键
子代理审查发现 PR3 半成品缺陷, 本次补全:
C1 (正确性): /strategy/cancel 的 _make_job_key 漏传 commission_pct/
stamp_tax_pct, 一旦用户设成本参数, cancel 算出的 job_key 与 stream 失配
导致取消静默失败。补 None-aware 解析对齐两侧口径。
C2 (完整性): 前端 SSE/sync 链未透传新字段, 新成本模型从 UI 够不到。
- backtestTask.ts / api.ts: 类型 + query 透传 commission_pct/stamp_tax_pct
- StrategyBacktest.tsx: 佣金映射到 commission_pct, 新增印花税(千分之)输入,
默认 1 (A股千1), 映射 stamp_tax_pct; 状态持久化
- storage.ts: stampTax 类型声明
测试加固:
- test_stamp_tax_only_deducts_on_sell_leg: 去掉硬编码 shares=9900,
改为断言两次运行 shares 相等 (守护买入腿 sizing 不受卖出成本污染)
+ 从结果反推卖出市值校验差额
- 新增 commission_pct=0.0 非 None 边界 (防 falsy 回退)
- 新增 job_key 区分成本参数的回归测试 (守护 C1)
后端 26 测试全绿; 前端 tsc 仅剩既有 baseUrl deprecation (非本次引入)。
|
2026-07-03 23:15:32 +08:00 |
|
shy3130
|
5a46bf915e
|
Merge fix/desktop-launch: 修复 console=False 下桌面客户端打不开
|
2026-07-03 22:41:59 +08:00 |
|
shy3130
|
6a74fa9963
|
fix(desktop): 修复 console=False 下 exe 打不开
PyInstaller windowed 启动器 (runw.exe, console=False) 不分配控制台,
sys.stdout/stderr 为 None。desktop.py / main.py 的 logging.basicConfig
默认建 StreamHandler(sys.stderr), stderr 为 None 时首次写日志调用
None.write() 抛 AttributeError, 崩在导入早期, 进程被 OS 直接终止,
try/except 都拦不住 → 用户看到「双击一闪退出、查无日志」。
修复:
1. _guard_streams() - 启动最早期把缺失的 stdout/stderr 换成丢弃写入的
_NullStream, 让 logging/reconfigure/print 全部安全落地 (windowed 标准修法)
2. _setup_logging() - 追加 FileHandler 到 data/desktop.log, console=False
下不再吞日志, 事后可查
3. _run_uvmicorn() - except Exception 捕获后端导入/运行崩溃并写 traceback,
不再静默死亡 (主线程只报「超时」却查无原因)
4. main() - except Exception 顶层兜底: 崩溃时 logger.exception + 弹原生
MessageBox 显示完整堆栈, 让用户能截图反馈
验证: console=False 包本地能正常打开 (修复前打不开)
|
2026-07-03 22:02:13 +08:00 |
|
shy3130
|
17188e16db
|
docs: 补传缺失的使用文档(configuration/deployment/features/strategy)
|
2026-07-03 15:23:24 +08:00 |
|
wshy
|
106ff628c6
|
Update README.md
|
2026-07-03 14:39:49 +08:00 |
|
shy3130
|
60dec142ea
|
Merge PR #45: feat 亮色主题 — 明暗切换 + 全部图表主题化
|
2026-07-03 14:12:13 +08:00 |
|
Gundy
|
1d8b93cc42
|
fix: 一批低危问题修复
- alert_store: 删除 delete_one 里不可达的 return count 死代码;
list_recent/count 持锁读, 防与 prune/clear 整文件重写并发读到截断内容
- ai_generator: import 校验从黑名单改为白名单 (仅 polars/__future__),
黑名单挡不住 ctypes/importlib/builtins/pickle 等未列出模块;
_extract_meta 改用 ast.literal_eval 替代 compile+eval
- frontend api.ts: request() 合并调用方 headers (此前被整体覆盖丢弃)
- VERSION 同步至 v0.1.70 (与 pyproject/package.json 一致)
|
2026-07-03 14:11:27 +08:00 |
|
Gundy
|
a05927baf2
|
fix(repo): parquet 原子写入 + 分区读-改-写加锁
- 所有分区写入改为「写 .tmp → rename 替换」: 直接 write_parquet
到最终路径, 进程被 kill (dev.sh 清端口用 kill -9) 或断电时会留下
半截文件, 之后 scan_parquet glob 整条读链路报错。.tmp 后缀不匹配
*.parquet glob, 不会被扫描误读。
- merge/flush/_write_daily_partition 加 _write_lock 序列化:
实时轮询线程、手动 refresh、盘后管道并发对同一分区做读-改-写
会互相覆盖丢数据。
|
2026-07-03 14:11:26 +08:00 |
|
Gundy
|
9452351edb
|
fix(sse): 每个 SSE 连接独立订阅者队列, 修复多客户端告警丢失
此前行情/告警/五档/复盘四通道共用服务级 Event + pending 列表,
pop 是「取走」语义: 开多个标签页或设备时, 告警只会被先醒来的那条
连接消费, 其余连接永远收不到; 共享 Event 的 clear/wait 也存在互相
吞信号的竞态。
- 新增 QuoteSubscriber: 每连接独立 Event + 队列, 生产侧广播
- /api/intraday/stream 改为 subscribe → wait/pop → finally
unsubscribe, 单事件等待替代原先每轮 4 个 to_thread 任务
(被 cancel 的 to_thread 线程实际仍在跑, 多客户端会耗尽线程池)
- 背压上限不变 (告警 1000 / 复盘 200, 每订阅者独立)
|
2026-07-03 14:11:26 +08:00 |
|
Gundy
|
4e332c42a6
|
fix(monitor): 规则表并发迭代加快照, 防评估线程与 API 线程竞态
evaluate()/has_rule_type() 跑在行情轮询线程, 直接迭代 self._rules
时若 API 线程并发 add_rule/remove_rule 会抛 RuntimeError
(dictionary changed size during iteration), 该轮全部告警被上层
except 吞掉静默丢失。统一 list() 快照后迭代; set_rules 改为构建
完整 dict 后原子替换, 避免读到半装载状态; remove_rule 重建
_last_fire 同理。
|
2026-07-03 14:11:26 +08:00 |
|
Gundy
|
3f17e58908
|
fix(quotes): change_pct fallback 统一小数制; enable() 补返回值; 拉取加锁
- change_pct: API ext 缺失时的 fallback 原先乘 100 (百分制), 与
ext.change_pct 的小数制 (0.0366=3.66%) 及 enriched 全项目约定
(pipeline.py 小数制) 相差 100 倍, 会导致策略过滤大面积误命中、
监控消息显示 366% 之类的异常值。两处 fallback 去掉 *100。
- enable(): docstring 承诺返回 bool, 成功路径此前隐式返回 None,
补 return True。
- _fetch_quotes: 加 _fetch_lock 串行化, 防手动 POST /refresh 与
后台轮询线程并发写同一批 parquet/缓存互相覆盖。
|
2026-07-03 14:11:26 +08:00 |
|