跳转至

用 dsh 构建市场投研助手

本章演示如何使用 dsh 构建市场投研助手。我们先让 dsh 整理 A 股市场数据,再把基于 20 日和 60 日均线的判断方法写成规则,由它生成信号、完成回测并制作日报。最后,我们让它对比通富微电和紫金矿业,生成一份带日 K 图、资金流向、板块对照和短期判断的个股报告。

实测使用独立工作区 a-share-research,全程采用标准模式。历史数据整理于 2026 年 8 月 19 日,盘中任务运行于 8 月 20 日。工作区记录了 Python 3.9.6,dsh Web 的准确版本号没有保留。最终产物分布在 data/signals/backtest/reports/。行情日期、技术指标和回测数字取自现有文件或运行记录,指数说明与公开报道另附来源。

行情接口会变化,历史数据也需要持续复核。文中的短期判断只在给定数据和条件下成立。

接入并整理市场行情

用一句普通需求开始

“A 股大盘”没有唯一对应的指数。上证指数知名度高,但只反映沪市。要观察整个市场,还需要同时看深市、不同市值层次和主要板块。

新建文件夹 a-share-research,将它添加为工作区,再在标准模式下新建会话。第一条消息先说明要跟踪什么、希望得到哪些结果,并要求暂时不要分析行情。安装方式和数据接口交给 dsh 根据当前环境处理。

提示词内容:

我想做一个每天跟踪 A 股大盘的投研助手。先帮我确定用哪些指数能够代表 A 股整体走势,再获取相应的历史日线并整理到当前工作区。请说明数据来源和最新日期,暂时不要分析行情。

完成这轮任务后,dsh 列出了九个指数,并明确这一步没有分析行情。图 9-1 截取了它给出的指数选择表。

首次任务完成后给出的指数选择

这九个指数分别覆盖全市场、沪深市场、市值层次和主要板块。后面的信号规则以中证全指为主,其余指数用于横向观察。dsh 读取最终 CSV 和复检报告,生成图 9-2 所示的结果卡,方便核对指数范围和数据日期。

九个指数及最终历史数据范围

九个指数及最终历史数据范围

中证全指是本章的主要观察对象。中证指数公司 2026 年 7 月 31 日发布的指数资料显示,其样本空间包括上交所、深交所和北交所符合条件的股票与存托凭证。北证 50 因本次接口没有返回历史数据而未单列。本次工作区也没有可用的 Wind 数据权限,因此没有纳入万得全 A,也没有用相似代码代替。

让 dsh 处理依赖和接口故障

工作区起初为空,Python 环境也没有 pandas、requests 和 akshare。dsh 先创建虚拟环境并安装依赖。取数时,akshare 的指数映射请求失败,东方财富接口随后多次连接失败。dsh 转而使用能够访问的数据源,并把获取和修补步骤保存成脚本。

本次日线主源是腾讯财经。1990 至 1992 年的上证指数异常段使用同花顺数据替换,四个中证系列指数使用中证指数官网数据补齐或重建。新浪财经只用于近期数据抽查。九份 data/raw/*.csv 的字段统一为 date、open、high、low、close、volume,“data/combined_close.csv”保存按日期对齐的收盘价。

复检后的九份 CSV 合计 44,987 行,最新日期为 2026 年 8 月 19 日,其中中证全指有 3,656 行。早期日志和元数据中的行数没有随修复后的 CSV 更新,下面统一以当前 CSV 和复检报告为准。

检查数据并核对修复

第一轮取数结束时,dsh 回复称数据已经通过完整性检查。进入分析前,再让它检查一遍日期和数值:

提示词内容:

帮我检查一下刚获取的行情数据,看看日期和数值有没有重复、缺失或明显异常,并把检查结果保存下来。先不要分析走势。

这条提示没有写明“只检查、不修改”,dsh 检查后直接修复了数据。处理正式数据时,应先让它列出异常和来源,确认后再决定是否修改。文件已经改变,下一轮只让它重新整理报告:

提示词内容:

报告里有些历史原因没有注明来源。请重新整理检查报告,只保留数据能够直接证明的结论。需要解释原因的地方补充可核验来源,否则标成“无法核验”。另外把“发现的问题、采取的修复、修复后的复检结果”分开写清楚,不要再修改数据。

检查发现,2015 年 3 月 27 日有八个指数的最低价误填为最高价。中证全指在 2018 年 8 月 3 日至 24 日连续 16 个交易日出现相同收盘值 4666.50,另缺少 2018 年 10 月 23 日和 24 日两条记录。dsh 用同花顺与中证指数官网交叉核对后完成修复,并读取“data/validation_report.md”生成图 9-3,汇总四项主要异常在修复前后的变化。

行情数据修复后的复检结果

图 9-3 显示,上述异常已经修复,主规则所用的中证全指可以进入信号计算。深证成指的官方资料把 1994 年 7 月 20 日列为基日,本次数据却包含更早的延伸记录,其生成方式无法核验。后面的规则不使用这段数据。

把分析方法变成信号规则

行情准备好后,先用中证全指的 20 日和 60 日均线观察趋势。最初只确定了“上穿”和“下穿”,均线类型、窗口边界、等号处理和检验区间还没有说明。这些条件需要先写清楚,再交给代码实现。

提示词内容:

接下来以中证全指作为主要观察对象。我想用 20 日和 60 日均线判断趋势,短期均线上穿长期均线记为向上信号,下穿记为向下信号。先不要写代码,帮我把这条想法变成可以历史检验的明确规则。如果还有适合日线数据的辅助分析方法,也列出来供我选择,但不要直接混进这条规则。有需要我决定的细节直接问我。

dsh 随后在界面中询问均线类型、统计口径、是否过滤信号和历史区间。图 9-4 显示第一个问题。这些选择会影响信号日期或统计结果,需要由用户决定。

dsh 询问均线的计算方式

本次选择简单移动平均(SMA)、不过滤穿越、使用全部历史,并先统计信号和状态。dsh 将规则保存到“docs/ma20_60_trend_rule.md”,再读取该文件生成图 9-5,把公式和四项决定放在一起,方便核对。

从规则文件整理出的 20/60 日均线核对卡

这里有三处计算细节。MA60 需要 60 个交易日,第 60 日只用于确定初始状态。MA20 与 MA60 相等时不算穿越。金叉要求前一日 MA20 <= MA60 且当日 MA20 > MA60,死叉反向判断。辅助指标没有加入主规则。

规则文件中的 R9 写的是 T+1 开盘执行,后面的回测改用 T+1 收盘。本次没有覆盖规则文件,因此该文件不能作为回测的最终规则说明。后文以回测提示词和报告中的 T+1 收盘口径为准。读者复现时,应先统一规则文件与回测假设。

生成信号清单和图

信号规则确认后,再让 dsh 编码:

提示词内容:

就按刚才定下的主规则实现,先不加入其他辅助指标。生成全部历史信号清单,再画一张最近三年的走势图,标出中证全指、MA20、MA60 和信号点,保存到“signals”目录。完成后告诉我信号总数、当前状态和最近五次信号。只描述规则结果,不给投资建议。

“scripts/signal_ma20_60.py”生成“signals/signal_history.csv”、“signals/summary.txt”和最近三年的信号图。检验区间为 2011 年 10 月 31 日至 2026 年 8 月 19 日,共 3,597 个交易日,产生 72 个信号,金叉与死叉各 36 次。最近三年的图中有 7 个金叉和 8 个死叉。

最近三年的中证全指、均线和穿越信号

图中的三角形只标出均线穿越。均线存在滞后,彼此靠近时还可能连续反转。仅凭这张图无法判断策略是否盈利,下面继续做回测。

用历史数据检验信号

本次回测只做多头。金叉后的次日收盘进入,死叉后的次日收盘退出,其余时间空仓,不计成本和滑点。发送的提示词如下:

提示词内容:

现在用历史数据检验这套信号。向上信号后从次日收盘开始模拟持有,向下信号后从次日收盘结束,其他时间空仓,不计成本和滑点。请和同期持续跟踪中证全指比较,统计累计净值、上涨段占比和最大回撤,生成回测报告和净值曲线,保存到“backtest”目录,并说明结果的局限。

第一次报告理解错了“上涨段占比”,统计的是持仓期间上涨交易日所占的比例。实际想看的是,所有完整持有段中有多少段收益为正,于是又补了一条:

提示词内容:

我说的“上涨段占比”是每个完整持有区间收益为正的比例,不是持仓期间上涨交易日的比例。请按这个口径补算持有段数、正收益段数和占比,并列出收益最差的三段,更新报告和结果汇总。其他回测假设保持不变。

dsh 读取修正后的“backtest/backtest_report.md”和“backtest/results_summary.md”,生成图 9-7。修正后共得到 36 个完整持有段,其中 12 段收益为正,“上涨段占比”为 33.3%。图中并列展示策略与持续持有的净值、年化收益率和最大回撤。策略的期末净值、年化收益率和最大回撤均未优于持续持有基准。

双均线回测的主要指标与最差三段

净值曲线更直观。策略只在规则处于多头状态时持有,因此红线在空仓期间保持水平。蓝线表示持续跟踪指数的基准。样本末端,策略净值低于起点,也落后于基准。

双均线策略与持续持有的净值曲线

双均线策略与持续持有的净值曲线

回测脚本能够运行,但这套策略在当前样本中的结果并不好。回测未计交易成本和滑点,空仓收益按 0 计算。样本只覆盖一个指数、一组参数和 36 个完整持有段,也没有进行样本外检验。中证全指的指数点位本身不是可直接买卖的资产,本次回测结果也不代表任何跟踪产品的实际收益,历史结果不能外推到未来。

定时监控并生成分析报告

先把日报的数据边界守住

接下来让 dsh 把日报任务写入“scripts/daily_report.py”。这个脚本更新历史日线,汇总九个指数的最新表现,并计算中证全指的 MA20、MA60、当前状态和最近信号。手动核对日报后,再创建“scripts/intraday_monitor.py”,用会话中的后台进程测试盘中报告能否连续生成。

提示词内容:

把每天的例行动作固定成“daily_report.py”:更新现有行情,汇总九个指数的最新表现,再按已经确定的规则计算中证全指的 MA20、MA60 和当前信号状态,在“reports”目录生成当天的分析报告。先手动运行一次,把报告全文给我看。没有新交易日就如实说明,只写数据事实和规则结果,不给操作建议。

第一次手动运行发生在 8 月 19 日晚,报告正常采用当天收盘。8 月 20 日 10:42 再运行时,接口已返回盘中数值,脚本却把它写入日线文件并称作“收盘”。同一天连续运行,数值还会继续变化。若不清理,这些盘中记录会进入均线和回测。

提示词内容:

现在这份报告把盘中价当成了收盘价。请按北京时间判断,收盘前运行时忽略当天尚未完成的日线,只使用最近一个完整交易日。收盘后才更新当天数据。清理刚写入的 2026-08-20 盘中记录,恢复相关数据和汇总文件,并撤销刚才对“fetch_index_daily.py”的改动。然后重新运行日报,生成时间注明北京时间。

dsh 清除九份 CSV 中的 8 月 20 日盘中记录并重跑日报。北京时间 10:55,图 9-9 中的最新完整交易日仍是 8 月 19 日,说明日报已经恢复到完整交易日口径。提示词还要求撤销“fetch_index_daily.py”的改动,这张图无法证明该操作已经完成,仍需检查脚本差异。15:00 后仍需核对接口返回日期和更新时间,再决定是否纳入当天数据。

日报在盘中只采用最近完整交易日

验证一次临时定时监控

日报只使用完整日线。为了确认盘中查询不会改写日线文件,我们随后刷新一次盘中行情,并在运行后检查 data/raw/。下面是实测时发送的提示词。这段话本身没有明确禁止写入。

提示词内容:

刷新一下今天 A 股大盘的盘中行情,告诉我九个指数目前的涨跌情况,并画一张中证全指今天的分时图保存下来。注明北京时间和数据来源。

dsh 返回九个指数的实时快照,并保存中证全指分时图。本次查询没有写入 data/raw/,也没有把盘中变化判定为正式日线信号。随后,我们让它启动一个有明确结束时间的后台进程:

提示词内容:

请创建一个临时盯盘任务:从现在起到今天 11:30,每隔 5 分钟通过 headless 模式刷新九个指数的盘中行情,更新中证全指分时图,并生成一份带北京时间的简短分析报告。每次结果分别保存到“reports/intraday”,不要写入日线数据,也不要把盘中变化判定为正式日线信号。11:30 后停止运行,并告诉我任务配置、日志位置和清理方法。

脚本“scripts/intraday_monitor.py”使用纯 API 请求和 Matplotlib 的 Agg 后端运行。日志显示,后台进程在 11:06、11:10、11:15、11:20 和 11:24 分别生成一轮快照,并在 11:30 触发停止条件后退出。dsh 根据快照文件和日志生成图 9-10,汇总运行时间和数据边界。

临时监控生成五轮快照并到点停止

临时监控生成五轮快照并到点停止

dsh 把提示词中的 headless 实现为不打开浏览器窗口的 Python 进程,并用 Matplotlib 的无界面后端绘图。它没有调用 dsh Web 的 schedule、dsh CLI 的 headless profile,也没有配置系统 cron。日志记录了五轮快照,进程也在 11:30 退出。本次测试只验证了连续运行和按时停止。长期无人值守还需要测试调度方式、失败重试和告警。

把市场监控延伸为个股分析

前面的指数任务已经完成数据整理,并通过会话中的临时后台进程生成了五轮盘中快照。接下来选择两只不同板块的股票,让 dsh 生成一份个股对比报告:

提示词内容:

前面的指数分析保留。现在比较通富微电(002156)和紫金矿业(601899):直接查询两只股票近一年的日 K 行情、成交量和资金流向,结合所属板块走势以及可核验的近期公告和新闻,生成一份近期走势对比报告。注明数据来源、统计口径和北京时间。

dsh 最终生成“reports/stock_compare_002156_601899_20260820_v2.md”。报告包括大盘和行业对照、ETF 表现、5/20/60/120 日涨跌、MA20、MA60、MACD、RSI、ATR 和成交量变化。

个股日 K 与成交额来自腾讯财经,资金流采用新浪财经口径,行业指数来自中证指数官网,公告转载和新闻逐条保留链接。行情与指标计算截止到 2026 年 8 月 19 日收盘,公开信息检索截止到 8 月 20 日报告生成时。

这份报告内容较多,直接截取 Markdown 不利于阅读。为了把规则状态、短期判断和日 K 走势放在同一组页面中,我们让 dsh 继续生成两张静态 HTML 摘要页:

提示词内容:

读取“reports/stock_compare_002156_601899_20260820_v2.md”,为通富微电和紫金矿业生成两张静态 HTML 页面,方便在浏览器中阅读和导出图片。

第一张展示两只股票的 20/60 日均线规则状态、未来 5 至 20 个交易日的短期判断、主要依据以及判断改变条件。第二张展示两只股票最近约 120 个交易日的日 K、MA20 和 MA60,并标出收盘价和当前规则状态。

只使用报告中已有的行情、指标和公开信息,不修改数字。将事实、规则状态、短期推断和改变条件分开呈现,并注明行情截止时间和“研究示例,不构成投资建议”。页面使用白色背景,减少装饰,导出图片后文字和图表仍要清晰。

dsh 根据详细报告生成了两张 HTML 页面。图 9-11 汇总规则状态、短期判断和改变条件,图 9-12 展示日 K 与均线位置。页面没有改变报告中的行情和指标,图中的短期判断属于 dsh 根据这些材料作出的条件推断。

两只股票的规则状态、短期判断及改变条件

两只股票的规则状态、短期判断及改变条件

图 9-11 对通富微电给出“短期反弹,波动较大”的判断。近 5 日涨幅转正,MACD 柱开始改善。报告收录的业绩预增公告转载和通富微电在2025 年年度报告中披露的 AMD 战略合作关系,构成事件背景。它的 20/60 日均线仍为空头,ATR 占收盘价 8.4%,当前回升尚未形成上涨趋势。转强条件是放量站上 MA60 的 67.05,并等待 20/60 日均线重新金叉。

图 9-11 对紫金矿业给出“短期上涨,多头延续”的判断。其 20/60 日均线保持多头,收盘价仍在 MA60 和 MA120 上方。有色指数与 ETF 的近 20 日涨幅、个股近 20 日资金净流入均为正。短线先看价格能否重新站上 MA20 的 33.04,并伴随成交量回升。若价格跌破近 20 日低点 30.97 或 MA60 的 30.02,这一判断失效。

两只股票最近约 120 个交易日的日 K 与均线

两只股票最近约 120 个交易日的日 K 与均线

图中的两项短期判断以报告生成后的 5 至 20 个交易日为观察期。以后每个交易日收盘后,都要重新取数、复算指标并核对最新公告。报告只用于整理研究线索,不构成交易建议。