Commit Graph
16 Commits
Author SHA1 Message Date
richardandClaude 4bb26713fc feat(backtest): clickable K-line popups for trade & picks rows
回测结果页三处标的/交易现在都能点开K线弹窗:

- 交易明细每行可点击,复用 TradeKlineModal 打开该笔交易的「交易回放」(买卖价线 + 持仓区间)。
- 选股分析每行(按标的聚合)新增 PicksSymbolKlineModal:展示该标的整个回测期的日K,逐笔标注买入/卖出箭头并显示成交价;同日同方向多笔合并为一个箭头,价格相同显示 ×n,不同则显示区间。
- 原两个弹窗状态合并为单个 union 覆盖层状态,类型上保证同一时刻只打开一个,消除各 opener 的手工互斥。
- 图表组件透传 visibleBars:'all',标的回放初始即适配显示整段区间,避免买卖点被默认尾窗遮挡。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-04 11:15:36 +08:00
richardandClaude 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
richardandClaude 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
richardandClaude 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
richardandClaude 0a11bb870e fix(kline): align intraday prefetch with rendering (live & effective days)
Rebase 到最新 main 后,按 review 提的两点把邻股预取与真实渲染路径对齐:

- 邻股预取当日最新分时补 live=true: 与 StockMultiDayIntradayChart 的 latest 同读
  实时源,盘中切股不回落分钟增量落盘的旧本地分区 (历史日期后端自行忽略 live)
- StockPanel 两处预取 days 改用 effectiveIntradayDays, 与多日图实际渲染周期一致,
  range 预取 queryKey 命中, 切换分时 tab 即时有数据
- 附带清理 rebase 冲突解析产生的残留右括号与冗余注释

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:25:06 +08:00
richardandClaude 6964e1b5c9 fix(kline): two regressions found in PR review
- EChartsCandlestick: 悬停重置 effect 依赖补上 symbol(_symbol) ——
  预取切股到同长度邻股时 data.length 不变, 原 [data.length] 不触发,
  hoverActiveRef 残留导致切股后「至今/周期」无悬停仍显示 (自愈于下次 mousemove)
- Watchlist: DimensionMembersDialog 的 onStockClick 补第三参 navList ——
  从成分弹窗打开个股时用成分列表作切股导航 (成员可能不在自选列表,
  原实现退回自选列表, 成员不在自选则导航按钮不显示)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:15:30 +08:00
richardandClaude 341b80e7ab refactor(kline): simplify round 2 — scope flex ratio, single-source guard and default
审查后清理, 无行为变更 (除下述作用域修正):

- StockPanel: 日K/分时并排的占宽从硬编码 flex-[1.4] 改为 dailyKlineFlex prop
  (默认 flex-1) —— 避免静默改变 TradeKlineModal 等其他消费方的并排布局;
  仅 StockPreviewDialog 显式传入 flex-[1.4]
- lib/kline.ts: klineMinuteRangeQueryOptions 内嵌「仅同 symbol」placeholderData 守卫,
  与 klineDailyQueryOptions 同源, 删除 StockMultiDayIntradayChart 的内联副本
- DEFAULT_INTRADAY_DAYS 单源化: StockPanel 默认值与弹窗 loadIntradayDays 回退共用
- StockPreviewDialog: prevSymbolRef 初始值改 null (首次挂载 view 本就是 daily);
  blur 复用 keydown 的 e.target(t) 替代重复 document.activeElement 查询

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:15:30 +08:00
richardandClaude 4d36a5ff20 fix(kline): blur focused control after arrow-key switching to drop stray focus ring
点过分时tab/外链等控件后方向键切股, 浏览器会给该已聚焦控件显示
focus-visible 默认蓝色 outline(应用未自定义 focus 样式, 即浏览器默认环)。
切股成功后 blur 掉当前聚焦元素, 一次覆盖弹窗内所有控件。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:14:14 +08:00
richardandClaude f3b916c049 feat(kline): prefetch multi-day intraday so switching keeps intraday tab instant
之前邻股只预取了日K/财务/单日分时(日K视图旁的分时图), 分时 tab 用的
多日分时(klineMinuteRange)没预取 → 分时 tab 切股仍要重新加载。

- lib/kline.ts: 新增 klineMinuteRangeQueryOptions, StockMultiDayIntradayChart 与预取共用
- StockPanel: 新增 intradayDays prop, 邻股预取补充 klineMinuteRange(days) + 最新分时
- StockPreviewDialog: 把当前 intradayDays 传给两个分支的 StockPanel, 保证 queryKey 命中

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:14:14 +08:00
richardandClaude 41a97888b6 fix(kline): widen dialog, balance chart split, keep intraday view on switching
- 弹窗 max-w 1100→1200, 日K/分时并排改 1.4:1 (日K为主视图),
  图表信息栏(悬停含「至今/周期」)有足够宽度单行容纳, 不再换行
- 修复: 分时 tab 下弹窗内切股会跳回日K —
  视图重置仅在弹窗首次打开(symbol 从 null 变非空)时发生, 切股保留当前视图

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:14:14 +08:00
richardandClaude 7038c7d627 refactor(kline): simplify after review — single-source queries, sort, and nav lists
审查后清理, 无行为变更:

- lib/kline.ts: 新增 klineMinuteQueryOptions, StockIntradayChart/StockMultiDayIntradayChart/
  StockPanel 邻股预取共用 (与 klineDailyQueryOptions 同风格单源)
- LimitUpLadder: sortLadderStocks 每 tick 只跑一次 (sortedTiers memo 供 TierGroup 与
  ladderNavItems 共用), resolveExtFields 提升为 memo (原来算 4 次)
- Dashboard: rankNav 复用 toNavItems 去掉 ! 断言; onNavigate 死条件简化为 alert: undefined
- Screener: 消费 ScreenerTable 传入的 navList (成员弹窗导航不再被丢弃), 移除重复 memo
- Watchlist: nav 列表仅在弹窗打开时构建 (sortedRows 随行情 tick 重建)
- ConceptAnalysis/IndustryAnalysis: 重复的 onStockClick 内联 lambda 抽为 handleStockClick
- StockPreviewDialog: 方向键与 ESC 同层级守卫 priceAlertDraft (点位弹窗打开时不切股)
- StockPanel: 去掉冗余 tick 别名; Monitor: 去掉冗余 as NavItem 断言

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:14:14 +08:00
richardandClaude 3258325daa feat(kline): add configurable external stock link
设置页新增「个股详情外链」: 可填 URL 模板(占位符 {code}/{market}/{symbol}),
信息条第一行右侧显示外链图标(新标签打开)。留空关闭。

- 新建 lib/stock-external-link.ts: load/save/build + scheme 白名单(http/https) + symbol 形状守卫
- storage.ts 新增 stockExternalTemplate kv

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:12:42 +08:00
richardandClaude 0f7c1dfe39 feat(kline): show since-prev-close change and period count on hover
悬停某根K线时, 图表自带信息栏额外显示「至今」(该K线昨收到最新收盘的涨跌幅)
与「周期 N」(到最新K线共多少根); 鼠标移出图表区仅隐藏该字段, 其余保持。

- updateAxisPointer 重写: 区分 悬停K线变化 与 竖虚线显隐, 分别控制信息栏重绘与副图 graphic
- 信息栏行高 height → min-height, 容纳「至今」字段不被裁剪

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:12:42 +08:00
richardandClaude ef66877529 feat(kline): prefetch neighbor stocks and stabilize dialog height
切股瞬间免 loading + 弹窗高度不抖动。

- 新建 lib/kline.ts: klineDailyQueryOptions 作为日K查询唯一权威配置,
  StockPanel(信息条)/StockDailyKChart(图表)/邻近预取三处共享同一 cache key
- StockPanel: 日K查询上提并新增 prefetchSymbols prop, 预取左右邻股日K+财务,
  日K到货后级联预取其默认选中日分时(klineMinute) —— 日K视图并排展示的分时图也免 loading
- StockInfoBar: 无数据时保持挂载(加载态占位), 按字段配置预留行高, 高度不塌陷
- useFinancials: 抽出 financialMetricsQueryOptions 供 prefetch 复用
- StockDailyKChart: 查询改走共享 options, 移除 onDataChange 上报链路

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:12:42 +08:00
richardandClaude ec95d71732 fix(dialog): dedup nav list so stock preview skips duplicate symbols
同一标的在榜单中多次出现(多概念/行业 leader、监控重复触发)时,
去重后保留首次出现, 避免切股与 n/N 计数空跳。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:11:54 +08:00
richardandClaude 571e828529 feat(kline): support in-dialog stock switching with arrow keys
K线弹窗支持在来源榜单内切股: 顶栏 ◀ n/N ▶ 按钮 + ←/→ 方向键(输入框/编辑器内让位),
首↔尾循环弱提示, 并给来源页当前预览行加高亮。

- StockPreviewDialog: 新增 navList/onNavigate props, 导出 NavItem/toNavItems;
  合并 ESC 与方向键监听; wrapMsg 弱提示浮层
- 9 个来源页面(自选/监控/选股/概念/行业/看板/连板/成分)构建 navList 并传 activeSymbol

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-02 13:11:54 +08:00