Skip to content

dsh-quant-assistant

Verified

dsh-quant-assistant · v1.0.1 · MIT · Web UI

把本地 DSH 一键转化为 A 股量化助手:因子检索、策略构建、可交易级回测、选股、仓位与止损、盘后自动增量同步,全部本地、零额外运行时。

Install

dsh plugin add dsh-quant-assistant

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

dsh-quant-assistant

把本地 DSH 一键变成 A 股量化助手:因子检索 → 策略组合 → 可交易级回测 → 选股 → 仓位。

全部本地运行,零原生依赖(纯 Node ≥ 22 + node:sqlite),不联网也能用已同步的数据。

免责声明:本工具只做历史数据的统计分析与回测,不提供投资建议、不预测股价、不推荐个股。 回测结果是对历史的统计描述,不构成对未来收益的任何承诺。

安装

dsh plugin --profile web add dsh-quant-assistant

装完重启 DSH,然后:

  1. 工具自动就绪(31 个 quant_*)
  2. 切到「量化助手」preset,获得量化工作流与四个技能
  3. 第一次使用时先同步数据(见下)

快速开始

在会话里直接说人话即可:

先同步沪深 300 成分股的近 5 年行情
用 20 日动量做个策略,每月调仓,持有 20 只,跑一下 2019 年至今的回测
把这次回测导出成报告

助手会按这个流程走:

quant_data_status          → 看数据有没有、新不新
quant_factor_search        → 把"20 日动量"落到具体公式并念给你确认
quant_strategy_compose     → 组合并校验,返回口径提醒
quant_strategy_save        → 确认后保存(新增版本,不覆盖)
quant_backtest_run         → 物化面板 + 逐日模拟
quant_backtest_analyze     → 分年度收益、回撤区间、费用占比
quant_export + present     → 落成 Markdown/CSV 并交给你

保持数据新鲜

数据不会自己变新。两种方式:

手动:说一句"同步一下最新行情"。

自动(推荐):说「开启每天收盘后自动同步数据」。助手会先向你说明代价, 你同意后才会开启:

  • 每天在指定时刻(默认 18:30)把本地已有的股票增量补到当天
  • 只同步本地已有的股票,不会自动扩大到全市场;单次有硬上限(默认 3000 只)
  • 开启后不再逐次询问 —— 所以开启是一次长期授权,助手不会替你决定
  • DSH 没在运行时不会执行,但下次启动会补跑被错过的那一次
  • 每天真正新增的只有 1 个交易日,所以走的是按股票各自算起点的增量, 不是每天重拉全市场
  • 非交易日会先探 3 只确认"今天有没有新数据",没有就一次请求都不发
  • 想关掉随时说「关闭自动同步」

开启后可以问「自动同步什么状态」,会看到开关、下次执行时间、上次结果与授权记录。

新鲜度按工作日算

quant_data_status 的 stale 用工作日判定(跳过周末)—— 否则每个周一早上都会报"过期 3 天",那是噪声不是信号。

已知近似:不识别节假日(不维护交易所日历),长假仍会算成若干工作日。

界面

装好后不需要额外操作,六类结果会自动渲染成卡片(都在会话流里,不占额外面板):

卡片 内容
回测结果 手写 SVG 净值曲线 + 总收益/年化/夏普/回撤/波动/Calmar/胜率/换手 + 基准对比 + 可折叠的口径声明
数据状态 覆盖范围、新鲜度(工作日/自然日)、自动同步状态、面板落后提示、可折叠的面板清单
数据同步 耗时、入库根数、离线事件表生效条数、失败明细;需确认时显示规模与耗时
选股结果 候选名单 + 拒绝原因计数(ST/停牌/次新)+ 免责声明
自动同步 开关、时刻、下次执行、上次结果、授权记录;当前进程没有定时器时会如实说明
导出交付物 文件清单 + 字节数 + 落点(会话工作目录 / 数据目录)

外加一个设置页(侧边栏设置 → 量化助手):数据路径、低内存选项、保持数据新鲜、 导出交付物、查看/清理指引、免责声明、已知限制。

卡片是工具结果的纯函数:数据经 presentationMeta 随工具结果一起送达, 不订阅、不轮询、不需要额外的服务。所有卡片按 320px 宽度设计,窄屏不溢出。

导出交付物

说「把这次回测导出成报告」,助手会调用 quant_export:

kind 要什么 产出
backtest backtestId Markdown 报告(指标 + 费用 + 分因子 + 口径声明 + 完整净值表),format:"both" 另给净值 CSV
select 与 quant_select_stocks 同参数 候选名单(带拒绝原因与"不是推荐"声明)
holdings 可选 cash 持仓估值
panel factorId 因子面板明细 CSV(带股票名)
data_status — 数据仓库状态
  • 默认落在会话工作目录的 quant-exports/ 下 —— 你能在项目里找到,也能直接打开
  • CSV 带 UTF-8 BOM,Excel 打开中文列名不乱码
  • 报告自带口径声明与免责声明
  • 导出后助手会调用 present 声明为交付物(只把路径写在回复里不算交付)

为什么要落盘:长会话里工具结果会在 8KB 处被压缩裁剪,回测的完整净值曲线随时可能 从上下文里消失。要引用的结论先落盘,是唯一可靠的做法。

31 个工具

域 工具
因子(4) quant_factor_list quant_factor_search quant_factor_preview quant_factor_create
策略(6) quant_strategy_compose quant_strategy_save quant_strategy_load quant_strategy_list quant_strategy_search quant_strategy_versions
回测(3) quant_backtest_run quant_backtest_analyze quant_backtest_get
选股(1) quant_select_stocks
优化(1) quant_optimize_parameters
仓位(3) quant_save_holdings quant_portfolio_status quant_save_position_rules
日历(2) quant_trading_calendar quant_resolve_date
行情财务(6) quant_quote quant_kline_history quant_financial_summary quant_quarterly_financials quant_industry_stocks quant_index_constituents
仓库(3) quant_data_status quant_data_sync quant_data_cleanup
自动同步(1) quant_autosync
导出(1) quant_export

回测口径

这是本项目最花功夫的部分 —— 能算得出来的数字很多,算得对的不多。

  • T 日收盘出信号、T+1 开盘成交
  • 涨停不买、跌停不卖、停牌不交易,判定用不复权原始价(用复权价会算错涨跌停幅度)
  • 涨跌停幅度分板块:主板 10%、创业板/科创板 20%、北交所 30%、ST 5%
  • T+1 制度(当日买入不可卖出)
  • 100 股整手(部分卖出按整手取整,清仓可卖零股)
  • 佣金(最低 5 元)/ 印花税(仅卖出)/ 过户费 / 滑点
  • 停牌持仓按最后已知收盘价估值(按 0 计会产生假跳变)
  • 因子与收益算在复权价上,否则动量类因子会在除权日带上分红跳空

结果里一定会带口径声明

回测结果包含 caveats 字段,逐条说明这次计算建立在什么假设上:

  • ST 状态使用当前快照,非历史逐日状态
  • 上市天数用自然日近似(未按交易日历计数)
  • 日频无法表达盘中路径;止损类规则使用显式假设
  • 有 N 个持仓日在停牌中,按最后已知收盘价估值
  • 基准是区间内等权买入持有代理(v1 未接入指数行情)

这些不是免责套话,是理解数字的前提。助手会主动转述它们。

数据来源

数据 来源
日线行情 东财 push2his 为主,新浪兜底(已与 BaoStock 独立源逐字节比对)
复权事件 东财分红 + 腾讯事件元数据 + 随包发布的离线修正表
基本面 东财数据中心(报告期快照 + 公告日生效)

已知覆盖边界(八条)

  1. 退市股的复权事件不全
  2. 同日多笔分配可能被来源合并(实测 300750 在 2024-04-30:东财记 2.011,实际 5.01)
  3. 存在所有源都漏记的分配(实测 300750 在 2025-01-24)
  4. 个别标的原始价序列存在一日错位风险(实测 601899 在 2018-06-25/26)
  5. 停牌与涨跌停按日线判定,无法表达盘中路径
  6. 基本面不做财报重述回溯
  7. 北交所代码的市场归属待复核

完整版见 DATA-SOURCES.md。

审批护栏

高风险操作会先被拒绝,助手必须向你说明规模与耗时、取得同意后才能继续:

触发条件 工具
全市场同步(> 200 只或未指定列表) quant_data_sync
回测区间 > 5 年 quant_backtest_run
参数组合 > 100 组 quant_optimize_parameters
删除全部行情 quant_data_cleanup
开启自动同步(长期授权) quant_autosync {action:"enable"}

护栏是单调否决:每次按当次参数判断,不会"记住上次批准"。 用户同意的是"这次全市场同步",不是"以后任何全市场同步"。

唯一的例外是"开启自动同步":它拦的不是"这一次很重",而是"以后每天都会发生、 而且没人再问"。自动任务没有弹窗通道,所以同意必须在配置时取得 —— 护栏就是那道门。

数据存放与清理

数据全部在 $DSH_HOME/quant/(可用 DSH_QUANT_DATA_DIR 或插件行的 dataDir 覆盖):

$DSH_HOME/quant/quant.db      仓库(行情 / 复权事件 / 面板 / 策略 / 回测留档 / 自动同步状态)
  • 不上传任何内容。网络只用于同步,不用于计算。
  • 内存受限的机器可以设 DSH_QUANT_TEMP_STORE=file: 全市场重因子的峰值 RSS 从约 1.6GB 降到约 0.42GB,代价是耗时 +18% (需要可写的临时目录,插件会自己在仓库同级建 .sqlite-tmp/)。

卸载

# ① 卸载前先删掉本包投递的 preset(DSH 正在运行时做,工具可用)
#    也可以在会话里说「清理量化助手的 preset」
quant_data_cleanup {scope: "preset"}

# ② 移除插件
dsh plugin --profile web remove dsh-quant-assistant
会被清掉 被保留
31 个工具与护栏 数据($DSH_HOME/quant/)—— 卸载不删用户数据
preset 目录(若你先做了 ①) 自动同步的授权记录(撤销授权不该由清理数据代劳)

为什么 preset 要手动(卸载前)清理 —— 平台限制,如实说明

dsh plugin remove 不启动 DSH,所以插件的清理代码在卸载时根本没有机会执行。 DSH 也不把 package.json 的变化纳入 HMR 观察范围(实测改了它也不重载成分)。

原先实现的"卸载时清理"其实是靠 任何 unload 时都触发 effect —— 包括 DSH 正常关闭。 于是出现一个更糟的 bug:向 dsh web 发 SIGTERM,preset 就被删了(下个启动会重新物化,自愈,但语义错了)。

现在的行为是区分这两件事:

动作 preset
DSH 正常关闭(SIGTERM / 退出) 保留
quant_data_cleanup {scope:"preset"} 删除
dsh plugin remove 删除前如果你没做①,会剩下一个目录 —— 见下面

卸载后若 preset 目录还在,手动删掉即可(它引用的是 DSH 自带的包,不会让 DSH 启不来, 但技能会引用已不存在的 quant_* 工具,会让人困惑):

rm -rf "$DSH_HOME/.agent-presets/quant-assistant"

只别把它当成卸载没做干净——这是平台的钩子缺失,本插件补不上这一层,README 只能告诉你怎么办。 它只删元数据标记为本包的那一个目录,绝不碰别人的 preset。

技能

preset 自带四个技能,助手会按需加载:

技能 内容
quant-sources 数据来源、八条覆盖边界、复权口径、没有专用工具时如何用 web_fetch 兜底
quant-workflow 标准流程,以及"数据过期/自动同步""用户拒绝保存""护栏拦截""结果导出"四条分支
quant-discipline 合规边界与结果表述规范(怎么把数字讲对)
quant-session 会话机制:goal(长任务的判据与完成纪律)、subagent_fork(样本外独立复核,必须在看到答案前分叉)、压缩的代价与"先落盘再依赖"

已知限制(v1 有意不做)

  • 贝叶斯优化、IC/IR 与分层回测等研究套件
  • 新闻 / 研报 / 资金流的专用抓取工具(改用 quant-sources 技能 + web_fetch)
  • 指数行情:回测基准目前是等权买入持有代理,结果里会写明
  • PTC 批量编排(DSH_TOOLS_MODE 是进程级的,第三方插件不应改动它)
  • 仓位与止损规则是声明性配置,不会自动下单,也不会在回测里隐式生效
  • 自动同步只跳过周末,不识别节假日(不维护交易所日历); 非交易日会空跑一次,但已被探测压到 3 次请求
  • 自动同步需要 DSH 在那个时刻正在运行;关机/休眠不会执行,只能靠下次启动补跑
  • headless profile(patchReload: startup)不会自动挂载 timer 服务, 此时定时不会触发;手动补跑与状态读写照常可用,插件会如实说明而不是假装会跑

开发

npm test                      # 122 个单测
node spike/dsl-smoke.mjs      # 109 个内置因子全部可编译
node spike/verify-factors.mjs # 因子值 vs qlib/pandas 真值(106,263 点)
node spike/tools-smoke.mjs    # 31 个工具逐个真调用
node spike/plugin-apply-smoke.mjs   # 安装副作用与卸载清理
node spike/plugin-real-ctx-smoke.mjs # 真实 Cordis 上下文装载(不启动 DSH 进程)
node spike/p2-smoke.mjs       # 回测端到端 + 不变式

spike/out/browser/verify-cards.mjs 是真实浏览器验证(隔离实例 + puppeteer + 真 React + 真主题 CSS),28 条断言,失败非 0 退出。它抓到过 4 个只有真浏览器/看图才能发现的缺陷:

  • chip('已开启', 'down') 传字符串而非令牌 → color: down 是非法 CSS,静默回退成继承色
  • 同步卡把"规模估算"渲染成"同步中…"(estimateOnly 没有 ms 字段,卡片却拿它当"运行中")
  • 回测卡 4 个指标在 320px 下挤成 3+1(不溢出,所以溢出断言全绿 —— 断言盲区)
  • 验证脚本自己的截图文件名在撒谎(按数组下标取,插了新用例后整体错位)

现场与复现步骤见 spike/out/browser/README.md,里面也记了一条教训: 不要把一次工具拒绝推断成自己的能力结论 —— read_image 当初被拒绝, 原因是模型路由配置没声明 input: [text, image],不是模型不能读图。

plugin-real-ctx-smoke.mjs 值得单独说一句:它在一个裸 Cordis Context + 真实 TimerService 里用 ctx.plugin() 把插件装载一遍(这正是 DSH loader 装载 bundle 行的路径), 所以它抓得到手写假 ctx 永远抓不到的东西。它上线就抓到一个装不上级别的缺陷:

Cordis 把插件 apply 的返回值当成 effect 解释 —— 返回普通对象直接抛 Invalid effect。

原来的 apply 返回诊断报告,于是插件在真实 DSH 里根本装不上。 现在拆成 apply(返回 void)+ setup(返回报告),并在 tests/plugin.test.mjs 里加了回归守卫。

架构与设计依据见 ARCHITECTURE.md(P5 的定时同步与导出见 §6.5 / §6.6), 阶段计划见 PLAN.md,与 AIFUND5/qlib 的同参数对照见 spike/GATE-FINDINGS.md。

许可

MIT