Commit Graph
208 Commits
Author SHA1 Message Date
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
Gundy ad83a5f960 fix(realtime): 交易时段判断与落盘日期显式使用北京时间
quote_service/depth_service 的 _is_trading_hours 原用 naive
datetime.now() 取服务器本地时间; Docker 镜像 (python:slim) 默认
UTC, 北京 9:15-15:05 = UTC 1:15-7:05, 轮询窗口完全错开——Docker
部署的实时行情/五档监控在真实交易时段一次都不会跑。

- 新增 app/market_time.py: CN_TZ 固定 UTC+8 (A股无夏令时,
  不依赖系统 tzdata, Windows 桌面版同样可用)
- 两个服务的 _is_trading_hours / date.today() 全部切换
- monitor.py 的 as_of 日期同步对齐
- Dockerfile 加 ENV TZ=Asia/Shanghai 兜底 (日志时间戳)
2026-07-03 14:11:26 +08:00
Gundy 3d4d20de3b fix(auth): 仅在直连 peer 为内网时信任 X-Forwarded-For, 防伪造绕过认证
公网请求带 X-Forwarded-For: 127.0.0.1 可冒充内网, 绕过三道防线:
- 未设密码时的「仅本机/内网可访问」中间件闸门 (main.py)
- /api/auth/setup 的本机限制 (公网抢占设密码)
- 登录限流按 IP 计数 (轮换 XFF 无限暴破)

另修复限流器: 锁定过期后重置失败计数 (原先过期后每失败一次
立刻再锁 5 分钟), 并在条目超 1000 时清理过期记录防内存膨胀。
2026-07-03 14:11:26 +08:00
wshy f4ee8d4a9d Update README.md 2026-07-03 14:10:51 +08:00
shy3130 2cbdc99659 Merge PR #44: fix(docker) 修复 .env 相对路径覆盖导致每次重建容器丢数据 2026-07-03 14:07:55 +08:00
shy3130 c1a5a6c367 Revert "fix: 修复海外服务器部署后交易时段时区误判为休市"
This reverts commit f02a88ebaa.
2026-07-03 14:07:26 +08:00
Gundy 2cbcd53538 fix(theme): 连板梯队亮色可读性 — 卡片股票名与概念/行业标签
用户反馈亮色下连板梯队看不清, 根因是三处按暗底设计的写死颜色:
- 卡片股票名 text-rose-50/text-green-50 (近白) → 亮色深酒红/深绿
  (text-rose-900/emerald-900 + dark: 变体保持原暗色效果)
- 顶部概念/行业标签云: 亮黄[250,204,21]/亮蓝[96,165,250]文字元组
  按暗底设计 → TagStats 增加 textLight 深色阶 (黄700/蓝700),
  选中态亮色下不再用白字 (黄底白字不可读), 底色透明度同步调淡
- 梯队分组内的概念/行业小标签: 同样的黄/蓝 rgba 硬编码, 同方案修复

实测: 亮色下股票名/代码/标签全部清晰, 暗色截图对比无回归
2026-07-03 13:51:36 +08:00
Gundy abd17f53ca fix(theme): 亮色模式实测修正 — 看板雷达图/近白文字/图例颜色
用浏览器实测双主题后的修正:
- 看板情绪雷达 (SVG): 网格/中心渐变/描边从写死的深 slate 色改为
  hsl(var(--surface/elevated/border)) CSS 变量, 亮暗自动切换
  (亮色下原来是一块突兀的深色六边形)
- 回测统计卡: 中性值文字写死近白色 #f8fafc → inherit (亮色下不可见)
- ExtDimensionAnalysis 装饰图形 white/xx → foreground/xx
- 情绪页图表: 折线补 itemStyle, 图例色块与线色一致
  (原用 ECharts 默认调色板, 与线对不上, 双主题都存在的旧瑕疵)

实测覆盖: 看板/个股分析(K线)/市场情绪 亮暗双向切换 + 刷新持久化
(预渲染脚本生效, body 背景 #FAFAFA), 暗色无回归
2026-07-03 13:51:36 +08:00
Gundy 8abf7e338a feat(theme): 亮色主题基础设施 + 全部图表主题化
- lib/theme.ts: localStorage 持久化 + useTheme hook (本页/跨标签页
  同步) + ChartTheme 图表调色板 (ECharts/lightweight-charts 画布
  不吃 CSS 变量, 统一从这里取色)
- index.html: 去掉写死的 class=dark, 换预渲染内联脚本 (默认暗色,
  切过亮色则保持), 避免首屏闪烁
- Layout: 侧边栏底部亮/暗切换按钮 (设置入口上方)
- 图表主题化 (切换即时生效, 无需刷新):
  · EChartsIntraday / EChartsCandlestick / CandlestickChart
    (lightweight-charts applyOptions) / AnalysisKChart
  · 回测 4 图 (StrategyNav/FactorIC/FactorGroupNav/ReturnDistribution)
  · Sentiment 页双图
  序列颜色 (红涨绿跌/MA线) 双主题通用保持不变; 轴/网格/十字线/
  tooltip/信息栏背景按主题切换

CSS 变量亮色板 index.css 里本就有 (:root), 首次真正启用
2026-07-03 13:51:17 +08:00
wshy e7667fa3e9 Update README.md 2026-07-03 13:50:10 +08:00
wshy 114a52969c Update README.md 2026-07-03 13:43:41 +08:00
shy3130 574b163cb3 docs: README 与首次引导页增加"非 TickFlow 官方项目"声明
- README 顶部加醒目声明 badge(红+绿)+ 声明正文,功能介绍改为按页面分组的清单
- Onboarding 引导页新增 Step 0"使用前请知悉"声明步骤(个人开源/与官方无关/盈亏自负/不建议商用),原 4 步顺延为 5 步
2026-07-03 13:31:40 +08:00
shy3130 f02a88ebaa fix: 修复海外服务器部署后交易时段时区误判为休市
- 新增 backend/app/timezone.py 统一上海时区入口(ZoneInfo + UTC+8 兜底)
- quote_service / depth_service 的 _is_trading_hours() 改用显式 Asia/Shanghai
- Dockerfile 加 ENV TZ=Asia/Shanghai + tzdata,docker-compose.yml 加 TZ 环境变量
- 代码与容器双保险,解决裸 datetime.now() 在 UTC 容器下永远判为休市的问题
2026-07-03 13:31:33 +08:00
shy3130 7c51bf5054 refactor(docs): 策略提示词文档统一归入 prompts/,docs/ 专放使用指南
将 4 个策略运行时文档(strategy-guide/step1/step2/example)从 docs/
迁至 backend/app/strategy/prompts/,与运行时加载路径一致;docs/
回归「项目使用指南与配置说明」定位。

- prompts/ 现自包含 4 个 md,guide 内对 example 的相对链接可正确解析
- 更新 README 与前端 StrategyBuilderDialog 的引用路径
- docs/ 保留 deploy-password.md / screenshots / zhihu 等面向人类的文档

承接 #41 Docker 文档缺失修复
2026-07-03 11:03:24 +08:00
Gundy bb92e0ec05 fix(docker): 固定 DATA_DIR 容器路径, 防 .env 相对路径覆盖导致数据丢失
.env 里 DATA_DIR=./data(为开发模式设计)经 env_file 传入容器后,
覆盖了 Dockerfile 里的 ENV DATA_DIR=/app/data。config.py 的
_project_root() 按 backend/app/config.py 三层父目录推算项目根,
但 Docker 里 COPY backend/app ./app 抹掉了 backend 这层, 文件实际
落在 /app/app/config.py, 三层父目录数到了 /(而非 /app), 相对路径
./data 被解析成 /data —— 不在 docker-compose 挂载的 ./data:/app/data
范围内, 容器可写层不持久化, 每次 up --build 重建都会清空。

compose 的 environment 优先级高于 env_file, 显式固定 DATA_DIR 为
容器内绝对路径, 使其不受 .env 内容影响, 与 Dockerfile 原本的
「显式指定三个关键路径」意图保持一致。
2026-07-03 10:46:26 +08:00
wshy 38b5964987 Update README.md 2026-07-03 10:11:59 +08:00
shy3130 56276384e5 fix(strategy): 修复 Docker 镜像中 AI 策略提示词文档缺失
将运行时依赖的 3 个提示词文档(strategy-guide.md / step1 / step2)
从 docs/ 复制到 backend/app/strategy/prompts/,使其随 COPY backend/app
自动进入镜像;并将 GUIDE_PATH/_DOCS_DIR 改为相对当前文件的 prompts/,
路径在开发环境与 Docker 容器中解析一致。

修复 #41
2026-07-03 09:51:30 +08:00
wshy a6ad8665aa Update README.md 2026-07-02 22:29:30 +08:00
shy3130 085d50a744 Merge fix/strategy-delete-bugs: 自定义策略删除相关 bug 修复 2026-07-02 22:28:16 +08:00
shy3130 c7ad860dba fix: 修复自定义策略删除导致策略池清空及删除失败静默问题
- bug2(删除后策略全没): prune 仅在拉取成功且非空时执行,加载中/失败/空列表不碰池
  根因: 删除触发 engine.reload() 重扫所有文件,任一文件 import 失败被静默跳过,
  前端拿到残缺列表后 prune 把池中有效 ID 永久清除并写 localStorage
- bug1(自定义策略删不掉): handleDelete 不再静默吞错误,失败时显式提示并保持弹窗打开
- ai_save 守卫回归: 放宽前缀允许 ai_/custom_,保留 path traversal 字符白名单(安全核心未动)
  修复「AI 修改 custom 策略」因 strategy_id 非 ai_ 前缀被 400 拒绝
- 引擎加载失败可见化: _load_all 收集 load_errors,/api/screener/strategies 返回,
  前端 toast 提示具体失败文件,不再静默消失
- 同步补全 ai_generator _SYSTEM_PREFIX 三条铁律(与 bbc92c4 docs 一致)
2026-07-02 22:19:46 +08:00
shy3130 bbc92c45de docs: 优化策略提示词,新增文件与范围铁律约束
- step1/step2/guide 均补充不可触碰项目源码、不得放入内置策略目录的铁律
- 明确 AI 生成策略只属于 data/strategies/ai/,文件名/ID 用 ai_ 前缀
- 防止 AI 生成多文件、跨文件 import 或染指 backend/app/strategy/builtin/
2026-07-02 21:31:50 +08:00
shy3130 b203e07e22 chore: 更新 uv.lock 2026-07-02 17:46:31 +08:00
shy3130 d4dcbb1797 chore: 版本号升至 0.1.70 2026-07-02 17:45:30 +08:00
shy3130 e283d487ce feat: Free 档实时行情监控限制提示
实时行情开关区新增 Free 档限制提示「监控自选股前 5 只,
全市场监控需 Starter+」,位于交易时段状态上方,可手动关闭
(不持久化,刷新后恢复显示)。
2026-07-02 17:40:41 +08:00