Skills Plugins MCP Prompt Model 导航 博客 资讯 我的中心

dsh-astock

A 股 / 港股量化工作台:自选股、K 线图、公司数据、策略配置与回测,为 DeepSeek Harness 提供行情研究能力。

hzy1522 @hzy1522 ⬇ 2 ★ 2 main

安装

dsh plugin --profile web add github:hzy1522/dsh-astock
下载安装清单

需要可复现安装时,可在仓库后追加 #commit 固定提交。

A 股 / 港股量化工作台:自选股、K 线图、公司数据、策略配置与回测,为 DeepSeek Harness 提供行情研究能力。

该插件未提供要点说明,请参考仓库 README。

  1. 安装并启动 DeepSeek Harness:npx @deepseek-ai/dsh web
  2. 在终端执行上面的安装命令(CLI 会解析插件并核验来源)
  3. 用 dsh plugins list 确认已安装,必要时重启 Harness 生效

插件以当前 dsh 进程的权限运行,安装时可能执行代码。请先通读仓库源码与许可证,确认无破坏性命令与越权访问;本站只做索引,不对第三方插件安全性作担保。

代码仓库github.com/hzy1522/dsh-astock
许可证MIT
主要语言main
下载量2
GitHub 星标2
最近推送2026-09-14
收录日期2026-09-19
分类工具与能力

事实信息来自公开插件目录快照(2026-10-01),介绍文案由本站再加工。

以下为插件仓库 README 全文(原始内容,由公开目录抓取整理)。

# dsh-astock

A股港股量化工作台 —— DeepSeek Harness 插件。

**当前版本 `0.9.0`** · [npm](https://www.npmjs.com/package/dsh-astock) · [版本记录](#版本记录) · [提交历史](https://github.com/hzy1522/dsh-astock/commits/main)

在侧边栏底部提供独立的「A股港股」整页:**A 股与港股**的自选股管理、K线图、公司财务数据、策略配置与回测。

> 包名 `dsh-astock` 里的 `a` 是历史遗留(最初只做 A 股),安装命令与 `id` 都依赖它,因此保持不变;界面与文档里显示的是 `A股港股`。

## ⚠️ 免责声明

**本插件仅用于技术学习与量化研究,不构成任何投资建议、要约或收益承诺。** 不提供选股推荐与买卖时机提示,也不代客理财。任何据此做出的投资决策由使用者自行判断并承担全部后果。

- **数据**:行情、K线、财务数据均来自第三方公开接口,可能存在错误、延迟、缺失或因接口变更而中断,不对准确性、完整性、及时性作任何保证。港股数据来自腾讯且未经复权,除权除息日会出现价格跳空。
- **回测**:基于历史数据,不能代表未来表现。结果受策略过拟合、幸存者偏差、参数与区间选择影响;交易成本、滑点、涨跌停与流动性均为近似模型,与真实成交存在差异。**历史收益不预示未来收益。**
- **AI 生成**:生成的策略代码仅供参考、未经审核,可能有逻辑错误或隐含风险,请自行阅读并验证后再使用。
- **责任**:使用者应自行核实数据与结果,并自行承担使用本插件所产生的任何直接或间接损失。插件作者不对任何投资损失承担责任。
- **合规**:请遵守所在地区法律法规及所使用数据源的服务条款,禁止用于内幕交易、市场操纵等违法用途。

界面里有两处对应措施:**首次打开会要求确认完整条款**(确认后落盘,条款有实质修改时会重新提示),**底部常驻一条精简声明**(即使确认弹窗因接口故障没能出现,这条也始终可见)。回测结果与 AI 生成处也各有对应提示。

## 功能

| 模块 | 内容 |
| --- | --- |
| 自选股 | 支持 **A 股与港股**(可混排):按代码 / 名称 / 拼音首字母搜索(`600519`、`茅台`、`GZMT`、`00700`),增删、切换;落盘保存 |
| K线 | 手写 SVG 蜡烛图(阳线空心红 / 阴线实心绿)、MA5/10/20/60、成交量副图、十字光标逐根读数;日/周/月 × 前/后/不复权 × 1/3/5/10 年;60–500 根缩放 + 平移 |
| 公司数据 | 实时快照(现价/涨跌/开高低/成交额/换手/PE/PB/总市值/流通市值/**每手股数**/**币种**)+ 16 期财务(EPS/BPS/ROE/毛利率,仅 A 股)+ **公司动态**(说明会 / 财报预约披露 / 除权除息 / 自定义事件)+ **AI 查动态并自动填入**(可接自定义 HTTP 数据源) |
| 策略 | 9 个参数化模板(双均线、MACD、RSI、布林带、唐奇安突破、均线多头、**相对强弱 RS**、**大盘趋势+相对强弱**、**说明会前后**)+ 表达式编辑器 + JavaScript 编辑器 + **和 AI 多轮对话改策略(可出表达式或 JS、可联网查证)** |
| 回测 | **按市场自动切换规则**(A 股 T+1/涨跌停/100 股一手;港股 T+0/无涨跌停/每手逐股不同/印花税双边);佣金(5元起)/印花税/其他费率/滑点;总收益/年化/最大回撤/夏普/卡玛/胜率/盈亏比;资金曲线 + 沪深300 对比;支持**按日期区间回测**与年份快捷档位 |
| 交易明细 | 点击买入日期向下展开**决策证据**:逐条条件的 ✓/✗ 与两侧实际数值、实际触发项加重显示、交叉给出前一根、策略自述 `why`;拿不到证据时如实说明并提供一键 AI 改写 |
| 免责 | **首次强制确认**完整条款(落盘,版本变更时重新提示)+ **底部常驻**精简声明;回测结果与 AI 生成处各有提示 |

## 策略:三种入口

策略页顶部可切换 **表达式 / JavaScript** 两种模式;页面最上方还有一个 **用文字描述让 AI 生成** 的入口。三者共用同一套指标实现与同一个回测引擎——JS 侧的指标函数只是把值包装成 AST 节点交给表达式求值器,所以**同一个策略在两种模式下必然得到完全相同的回测结果**(测试里有交叉验证断言)。

### 入口一:和 AI 多轮对话(推荐)

策略页底部是**对话区**:写一句话点「发送」,然后一直追问下去。

> 我:20 日均线上穿 60 日均线时买入,跌破 20 日均线时卖出。
> AI:先用双均线金叉做了一版。`已更新为表达式策略,已通过试运行校验`
> 我:再加一个放量过滤,成交量要高于 20 日均量。
> AI:加上了。`已更新为表达式策略,已通过试运行校验`
> 我:如果持有不到 5 天就别卖。
> AI:这个需求表达式表达不了,需要 JavaScript 模式,因为要记住持有了几天。

每一轮 AI 都会给出**完整的新策略**并直接填进编辑器,所以看完就能点回测。要点:

- **按当前模式产出**:你在表达式模式就让它写表达式,在 JS 模式就写 JS。它做不到时会自己换模式并说明原因(比如「需要记住持有天数」只能写 JS)。
- **对话上下文一起带上**:每一轮都把之前的对话与当前策略发给模型,所以可以用「再加个」「改成」「把止损换成」这种自然的追问,不用重复描述。
- **想改哪一版都能退回去**:表达式与 JS 各留一版,「↺ 换回上一版」一键切回。
- **只回答、不给代码也是合法的一轮**:需求不明确时它会反问,编辑器不动,你接着答就行。
- **发送后立刻试运行**。拿到策略就地跑一遍当前 K 线(表达式按表达式解析、JS 按 JS 执行),语法错、运行错、信号长度不对当场写在气泡下面。试运行失败时策略仍会填进去,方便你手动修。
- **失败不丢输入**:请求失败时你那句话会留在输入框里,直接重试即可。

> 表达式模式还有个额外好处:表达式里的比较(`C > MA(20)`)会被系统**逐条拆出真假与两侧数值**,
> 交易明细里的证据比 JS 模式更细。所以简单逻辑优先用表达式。
- **把真实事件日期一并交给模型,必要时还会自己联网查**。你自己算不出「哪天开会」「那天是不是交易日」,模型同样算不出,所以宿主会先把该股的真实事件列表(见[因子](#已接入公司事件可回测))塞进提示词,并明确要求:只能用这些日期、禁止自己推算日历、禁止写死日期字符串。列表里没有的(比如产品发布会),它可以自己用 `web_search` 去查,查到的日期要经你确认才会生效。比如直接写:

  > 公司开业绩说明会的前一个交易日卖出,会后的第二个交易日买入。

  它会生成 `sell: EVMEET(-1), buy: EVMEET(2)`,用哪场会、哪天、隔几个交易日,全由真实数据决定。
  生成完成后提示里会写「已把该股 N 条真实事件日期交给模型」——这句话就是「有没有真实依据」的凭证。

模型调用走宿主的 `llm` 服务,用 `agentDefaultModel` 的当前选型,因此**不需要在插件里配置任何 API Key**。若宿主未挂载 `llm` 服务或没有默认模型,会给出明确提示。
对话只保存在当前页面状态里(不落盘),切换股票时会清空;点「清空对话」可以随时重新开始。

### 入口二:表达式

买入 / 卖出条件各写一行,例如:

```
买入:CROSS(MA(5),MA(20)) AND RSI(14)<70
卖出:CROSS(MA(20),MA(5)) OR C>BOLL_UP(20,2)
```

- **变量**:`C` `O` `H` `L` `V`(收/开/高/低/量),别名 `CLOSE` `OPEN` `HIGH` `LOW` `VOL`
- **函数**:`MA(n)` `EMA(n)` `SUM(n)` `STD(n)`、`RSI(n)`、`DIF()` `DEA()` `MACD()`、`BOLL_UP(n,k)` `BOLL_MID(n)` `BOLL_LOW(n,k)`、`HHV(n)` `LLV(n)` `REF(x,n)` `CROSS(a,b)` `ABS` `MAX` `MIN`
- **运算符**:`+ - * / > < >= <= == != AND OR NOT`(或 `&& || !`)、括号
- 函数名与 `AND/OR/NOT` 大小写不敏感;`MA(5)` 等价于 `MA(C,5)`

表达式由本包自带的词法 / 语法分析器求值,**不使用 `eval`**。每个节点求值为整段区间的序列,一次算完全程。

### 入口三:JavaScript

写任意 JS——循环、变量、状态机、多分支都可以。必须 `return { buy, sell }`,两个数组长度都要等于 `C.length`:

```js
const fast = MA(C, 5)
const slow = MA(C, 20)
const volUp = GT(V, MA(V, 20))
const buy = new Array(C.length).fill(0)
const sell = new Array(C.length).fill(0)
let inPosition = false
let holdDays = 0
for (let i = 0; i < C.length; i++) {
  if (!inPosition && CROSS(fast, slow)[i] && volUp[i]) { inPosition = true; holdDays = 0; buy[i] = 1; continue }
  if (inPosition) {
    holdDays++
    if (holdDays >= 5 && LT(C, slow)[i]) { inPosition = false; sell[i] = 1 }  // 至少持有 5 日
  }
}
// 可选:自己说明第 i 根为什么买卖,会显示在交易明细最前面
function why(i, side) {
  return side === 'buy' ? '5 日线上穿 20 日线且放量' : '持有满 5 日后收盘跌破 20 日线'
}
return { buy, sell, why }
```

可用(都是与 K 线等长的数组,索引可直接用):

- **序列**:`C` `O` `H` `L` `V`,以及总根数 `N`
- **指标**:`MA(x,n)` `EMA(x,n)` `SUM(x,n)` `STD(x,n)` `HHV(x,n)` `LLV(x,n)` `REF(x,n)`、`RSI(n)`、`DIF()` `DEA()` `MACD()`、`BOLL_UP(p,k)` `BOLL_MID(p)` `BOLL_LOW(p,k)`、`ABS/MAX/MIN`
- **布尔组合**(返回 `1/0` 序列,`null` 当假):`CROSS(a,b)` `GT` `LT` `GTE` `LTE` `AND` `OR` `NOT`
- 也支持 `MA(5)` 这类省略序列的写法(默认用收盘价;`HHV` 默认最高价、`LLV` 默认最低价)

**想让交易明细给出证据,买卖条件就用上面这组布尔函数组合**(`AND(CROSS(fast, slow), volUp)`),
系统才能逐条还原「信号日哪一条成立、两侧数值是多少、这次到底踩中了哪几条」;
如果直接写 `C[i] > slow[i]` 这类原生比较,运行期不留下任何可读的中间结构,界面只能给指标快照 +
一条一键 AI 改写的出路。也可以额外 `return` 一个 `why(i, side)`,用自己的话补一句原因。

错误会分类提示:语法错误、运行异常、返回值类型不对、信号数组长度不匹配——四种都是可读的具体信息,不是一句「失败」。

> ⚠️ 策略代码在**浏览器**里执行。避免死循环(如 `while(true)`):主线程被占满会让界面卡住,刷新页面即可恢复。
> 模板按钮在两个模式下都会填入对应写法——JS 模式下填的是等价 JS 代码,可直接改。

## 因子

策略不只能用 K 线。因子分两类,**界面上也明确分区**——因为有一类数据只存在于「当下」,拿来回测就是造假。

### 已接入:市场情绪(可回测)

全部由「个股 K 线 + 沪深300 指数」算出,不依赖任何不稳定的第三方源:

| 因子 | 含义 |
| --- | --- |
| `BENCH` | 沪深300 收盘价序列(已按日期对齐到当前标的) |
| `RS(n)` | **相对强弱** = 个股 n 日涨幅 ÷ 指数同期涨幅。`>1` 表示跑赢大盘 |
| `IDXRET(n)` | 指数 n 日涨幅 |
| `IDXMA(n)` | 指数 n 日均线 |
| `IDXDEV(n)` | 指数偏离 n 日均线的比例,正数表示在均线上方 |
| `IDXVOL(n)` | 指数年化波动率(越高越不安全) |
| `BETA(n)` | 个股对指数的滚动 Beta,`>1` 表示比大盘波动更大 |

表达式模式与 JS 模式**都能用**(两者共用同一套实现,不会漂移)。策略页会显示这些因子的**当前读数**。

内置两个模板演示:**相对强弱 RS 择时**、**大盘趋势 + 相对强弱**(要求个股与大盘同时处于上升趋势、且个股跑赢大盘才买)。

> **前视偏差的处理**:指数序列只做**前向填充**,开头没有对应交易日的位置保持 `null`,刻意不用未来值回填开头——否则早期的因子值会偷偷用上后面的数据。指数取不到时策略会**明确报错**,而不是静默算出一堆 null。

### 已接入:公司事件(可回测)

「财报披露后第二个交易日买入」「说明会前一个交易日卖出」这类想法,缺的从来不是写代码的
能力,而是**真实日期**——「哪天开会」「哪天披露」「那天是不是交易日」,模型凭空算不出来。
所以这三类日期直接从公开数据里取,交易日换算按真实 K 线做:

| 因子 | 含义 | 日期来源 |
| --- | --- | --- |
| `EV(n)` | 全部事件 | — |
| `EVMEET(n)` | 业绩说明会 / 股东大会 / 路演 | 公告正文里的「会议召开时间」(公告日在前) |
| `EVREP(n)` | 财报预约披露 | 交易所预约披露时间表 |
| `EVDIV(n)` | 除权除息 | 分红实施公告(公告日在前) |
| `EVCUS(n)` | **自定义事件**(AI 联网检索 / 手工添加) | 用户逐个确认,标为「未核实」 |

`n` 是**相对该事件的第 n 个交易日**:`-1` = 事件前一个交易日,`0` = 事件当日,
`2` = 事件后第二个交易日。事件当天不是交易日(公告常在周末或盘后发)时按**之后第一个
交易日**算。策略只写 `n`,日历由引擎算。

**港股同样可用**(`EVMEET` / `EVREP`):

| 港股事件 | 来源 | 因子 |
| --- | --- | --- |
| **董事会会议召开日期** | 港交所公告(提前公布哪天开会审批业绩) | `EVREP(n)` |
| 股东周年大会 / 股东特别大会通告 | 港交所公告 | `EVMEET(n)` |
| 业绩公告 | 公告日即业绩日,只能用于 `n >= 0` | `EVREP(n)` |

港股公告是繁体、日期还常写成中文数字(「謹訂於**二零二六年五月十三日**」),所以日期解析
同时支持阿拉伯数字与中文数字,并覆盖「將於…**召開**董事會會議」「謹訂於…**舉行**股東週年大會」
这些写法。

**三类事件都是事先公开过的日期**,所以「提前于事件」的条件是合规的:

- 说明会、股东大会、除权除息都有**公告日在前**;
- 财报用**预约披露日**而不是实际披露日——预约时间表是交易所期初公布的,用它做「财报前
  卖出」没问题;用实际披露日等于提前知道了财报哪天出。
- 引擎还有一道硬保护:如果那一根 K 线当天事件**还没公告**,该条件直接不触发。所以
  「当天才公告的说明会」不会被提前埋伏——这一条有测试专门守着。

「公司数据」页有**公司动态**表,列出该股全部真实事件日期(日期 / 类型 / 内容 / 何时公开),
你能一眼看到策略手里有什么牌。内置模板 **说明会前后** 演示这套因子。

**AI 生成**会把该股的真实事件列表直接塞进提示词,并明确要求:只能用这些日期、禁止自己
推算日历、禁止写死日期字符串;生成结果里也会告诉你「已把该股 N 条真实事件日期交给模型」。

#### 数据源里没有的日期:让 AI 自己联网查

产品发布会这类日期,交易所数据里就是没有。这时 AI 会**自己联网查证**:宿主给它挂了
`web_search` / `web_fetch` 两个工具,它可以搜多轮、也可以打开公告页确认,最多四轮
(最后一轮不再给工具,逼它落笔写代码)。

查到之后它**不能直接把日期写进策略**,而是输出一个 `events` 区块:

```events
[{"date":"2026-09-09","title":"秋季新品发布会","announcedAt":"2026-08-01","source":"https://…","evidence":"原文写明 9 月 9 日召开"}]
```

插件解析出来后,在策略页列成**候选事件**,必须由你点一下「确认加入」才会生效:

- 自定义事件单独一类,**只由 `EVCUS(n)` 引用**,不会悄悄改变 `EV` / `EVMEET` 的语义;
- 表里标成**未核实**并给出出处链接——交易所数据和模型检索结果永远分得清;
- 日期不合法(不是 `YYYY-MM-DD`、或不是真实存在的日期)的条目**直接丢掉**——宁可少一条,也不让编的日期进来;
- 公告日未知时按「事先已知」处理,界面会明确写出来:**那是个建模假设,不是事实**。

#### 两条检索路径(宿主挂了就用宿主的,坏了用内置的)

宿主 `web` 服务的搜索 provider **可能是坏的**——实测本机部署每次调用都报
`WEB_PROVIDER_ERROR: DeepSeek returned no web_search_tool_result blocks`。所以检索是两级的:

| 顺序 | 来源 | 覆盖范围 |
| --- | --- | --- |
| 1 | 宿主 `web.search`(部署配置的搜索提供方) | 通用网页 |
| 2 | **内置财经资讯检索** | 东方财富站内搜索:财经新闻 + 公告 |

宿主第一次失败后,本轮不再重试它,直接走内置检索;两条路都不通时如实报错,并且
**不再继续给模型工具**(省掉后面几轮空转)。界面上会写明这次用的是哪条路、宿主报了什么错、
以及去**设置 → 插件 → 插件配置 → Web search** 改 Endpoint 就能修好宿主搜索。

**宿主还会替模型先查一遍**:需求里出现「发布会 / 说明会 / 股东大会 / 业绩 / 财报 / 除权」这类
事件词时,宿主先拿股票名 + 需求去检索一次(约 0.5 秒,10 分钟内同查询走缓存),
把结果原样摆进提示词,并要求模型**只能从中取真实日期、找不到就说找不到**。
这样做是因为「等模型自己决定去搜」不可靠——实测它会直接声称「列表里已经有这个事件」,
然后写出一个指向不存在日期的策略。

内置检索的结果会标注来源(`内置财经资讯检索·东方财富站内搜索:媒体新闻与公告`),
不冒充通用搜索。对选股策略来说这部分内容恰好最相关——会议、业绩、发布会都在公告与新闻里。
抓网页(`web_fetch`)同样先走宿主服务,不可用时退回插件直连取正文。

宿主没挂 `web` 服务时不会假装查过:返回值里写明「未挂载 web 服务,本次没有联网查证」,
界面照原样显示。`/astock/api/health` 里的 `webSearch` 字段就是这项能力的状态。

#### 取不到时不会装没事

会议日期只能从**公告正文**里读,而正文接口(`np-cnotice-stock`)在密集请求下会直接
**ECONNRESET**(实测过)。所以:

- 正文按 `art_code` 把**解析结果落盘缓存**(`announcements.json`):一条公告里的会议日期
  不会变,抓过一次就不再打扰上游,上游抖动也不会让已有的事件凭空消失;
- 拉取改为**小批量并发 + 重试 + 批间隔**,降低触发限流的概率;
- 仍然失败的部分会写进 `errors` 并在界面上说明「部分事件没取到:…」,**绝不悄悄变少**。
  财报预约披露与除权除息走另一个域名,不受影响,所以限流时事件列表不会全空;
- 失败后 **60 秒内不再请求正文**(退避):被限流时反复切股票/刷新只会把封禁越敲越久;
- 界面上有 **↻ 重新加载事件日期** 与 **手动添加** 两条出路,不用干等。

> 实测该正文接口在密集请求后会按 IP 限流,返回 `ECONNRESET`,通常几十分钟到几小时恢复;
> 恢复后解析结果会永久落盘,之后不再请求。

#### 兜底一:自己填

上游读不到、或数据源里根本没有的日期(自家公司发布会、行业展会),「公司数据」页有
**手动添加**:选日期、写名称,立刻成为自定义事件,策略里用 `EVCUS(n)` 引用。
它和 AI 检索的结果一起标成「未核实」,来源分列显示。

> 一只股票如果没有可用事件(例如港股最近 200 条公告里没有会议类文件),界面会说明原因,
> 用到事件因子的策略会直接报错而不是静默算出「没有信号」。给 AI 的提示词里也会明确写上
> 「这只股票没有事件数据,禁止使用 EV/EVMEET/EVREP/EVDIV」,免得它生成一个一跑就报错的策略。

## 公司数据页:让 AI 去查动态,查到就填表

「公司数据」页有独立的 AI 对话区,和策略页那个是两回事:这里不写策略,只查**这家公司的动态**
(发布会、业绩说明会、股东大会、财报披露、分红除权……),查到的日期**直接填进事件表**。

> 我:小米近三年的产品发布会都有哪些?
> AI:查到 2026 年 9 月 7 日有一场秋季旗舰新品发布会…… `已自动填入 1 条事件(标为未核实,可删除)`

- **自动填入**:宿主直接从 AI 的输出里解析 `events` 区块并写进自定义事件(同一天同标题只留一条),
  客户端只管把最新表铺上去。表里标**未核实**、带来源链接、随时可删;策略里用 `EVCUS(n)` 引用。
- **只问情况不会乱写**:AI 没给出日期时,表里不会多出任何东西。
- **换了股票就重开对话**:对话是跟着股票的。

### 外部数据源:接你自己的接口

除了联网检索,「外部数据源」区可以配 HTTP 接口,配完 AI 就能把它当工具调用:

| 字段 | 说明 |
| --- | --- |
| 名称 | 工具名(模型看到的是 `ds_<名称>`) |
| URL 模板 | 支持 `{code}` `{name}` `{query}` 占位符,例如 `https://api.example.com/ann?code={code}` |
| 说明 | 原样注入系统提示词——**把某个 MCP / skill 的用法写在这里**,AI 就知道该怎么用你的数据源 |

启用的数据源会作为工具交给模型,调用结果(含失败原因)都会回灌给它;停用的不会出现。

#### 为什么不是「直接调用宿主注册的 MCP 工具」

用运行时探针在真实 Host 进程里查过,结论是**做不到**:

```
插件上下文 ctx.get('tools')  →  只有 register / schemas / get,没有 execute
agent 上下文 ctx.get('tools') →  有 execute(但那是 agent 作用域,插件借不到)
ctx.get('skills').list()     →  0 条(skill 注册在 agent 作用域,插件侧看不到)
```

也就是说,宿主里注册的工具(包括 MCP 服务器提供的)**在插件作用域内不可执行**,skill 目录
也读不到。这是作用域设计,不是 bug。所以插件这边给的是自己能掌控的两条路:
**把数据源配成 HTTP 接口**,或**把用法写进「说明」**(MCP 若同时提供 HTTP 端点,直接填进来即可)。
界面上也照实写明了这一点,不假装支持。

> 安全:harness 自带的核心工具(`bash` / 读写文件 / `web_*` 之外的东西)不会被暴露给这个助手;
> 插件只把它自己配置的数据源交给模型。

### 规划中

| 阶段 | 因子 | 可回测 |
| --- | --- | --- |
| 行业 | 所属行业、**成分股自算行业指数**、个股 vs 行业相对强度 | ✅ |
| 公司动态 | 公告密度序列;最新公告列表 | 密度✅ / 列表仅当下 |
| 市场热度 | 人气榜、行业实时排名、当日资金流 | ❌ **仅展示** |

市场热度这类数据**没有历史**,所以只能做信息面板并标注「不参与回测」,绝不能当因子用。

## 港股

搜索、行情、K线、回测都支持港股(代码 5 位,如 `00700` 腾讯控股、`00005` 汇丰控股)。A 股与港股可混在同一个自选股列表里。

**但港股不是「换个数据源」那么简单**——A 股与港股的交易规则不同,直接套用会算错。回测引擎按标的自动切换:

| | A 股 | 港股 |
| --- | --- | --- |
| 交割 | **T+1**(当日买入不可卖) | **T+0**(当日可回转) |
| 涨跌停 | ±10%(创业板/科创板 ±20%,北交所 ±30%) | **无涨跌停** |
| 每手股数 | 恒为 100 | **逐股不同**:腾讯 100、小米 200、汇丰 400、长和 500 |
| 印花税 | 卖出单边 0.05% | **买卖双边各 0.1%** |
| 币种 | CNY | HKD |

每手股数**从行情里逐股读取**,不写死。策略页会明示当前适用哪套规则,并提供「套用港股默认费率」一键填入。

### 港股的三个限制

1. **无复权数据**。腾讯对港股不提供复权价(`qfq`/`hfq`/不复权返回的数据完全相同),本插件拿到的港股 K 线是**不复权**的。除权除息日会有价格跳空,长期收益会略被低估。回测结果页会明确提示这一点。
2. **无财务数据**。东方财富那份财务报只覆盖 A 股(港股 `SECUCODE=00700.HK` 返回「返回数据为空」)。切到港股的公司数据页会给出这句说明,而不是一张空表。
3. **成交额单位不同**。A 股行情接口给的是万元,港股给的是元,已在解析层归一化。同样,港股行情没有换手率与市净率,界面显示 `—` 而非 0。

## 回测区间

策略页最上方是一条常驻操作条(滚动时也贴在顶部):

```
[开始回测]   起始[2023-09-11] ~ 结束[        ]
快捷区间  近1年  近3年  近5年  近10年  全部
          已加载 780 根:2023-08-25 ~ 2026-09-11(选更长的档位会自动多取数据)
          所选区间命中 780 根 K 线。
```

- **默认三年**:起始日默认为三年前的今天,结束日留空(取到最新)。可以直接改日期,也可以点档位。
- **年份快捷档位**:原生日期选择器只能按月步进,翻到几年前要点很多下。这里的 `近1/3/5/10年` 是一键跳年。
- **档位会自动补数据**:选了比已加载范围更长的档位(比如只加载了 3 年却点「近10年」),会连带把 K 线年数调大并重新取数,而不是给你一个被静默裁剪的区间。
- **区间是在已加载的 K 线上按日期裁剪**,切换瞬时完成,不会重新取数。
- 所选区间不足 30 根会直接报错并说明原因;起始日早于已加载数据时会提示需要更多数据,而不会静默算出一段不完整的区间。
- 结果页会写明实际使用的区间(`2026-01-05 ~ 2026-06-30(116 根)`),以及是否发生过裁剪。

## 交易明细:为什么买 / 为什么卖

点击交易明细里的**买入日期**,会在下方展开这笔交易的**决策证据**:

```
▾ 2026-07-15   1204.86   2026-08-20   1298.50   +7.66%   26
    为什么买
      信号日 2026-07-14(收盘决定信号) → 成交日 2026-07-15(开盘 @1204.86)
      策略自述原因(why 钩子返回)
        第 486 根:5 日线上穿 20 日线,成交量高过 20 日均量
      按你代码里的条件函数自动拆解(✓ 成立、✗ 不成立;加重显示的是本次信号的实际触发项)
      ✓  全部条件成立(AND)        下列条件必须同时成立
        ✓  CROSS(MA(C, 5), MA(C, 20))   今 1202.47 vs 1197.29 / 前 1195.80 vs 1197.29
        ✓  V > MA(V, 20)                今 86321.00 vs 41120.00
        ✗  RSI(14) > 200                今 41.8833 vs 200
```

**为什么信号日在成交日前一天**:信号在收盘产生、次日开盘成交(无未来函数),所以要解释「为什么」,必须回到信号那一根去看。

### 证据不是猜的:条件函数会被逐个记录

JS 策略有循环和状态机,静态分析确实给不出可信归因——但它在**计算层面**是收敛的:每一个买卖条件都是策略库里
`GT / LT / GTE / LTE / CROSS / AND / OR / NOT` 这些函数的组合。所以库函数在返回时会记下自己的身份、操作数与结果,
信号日就能把整条链路反过来还原出来:

- **逐条列出**每条条件**当时成立还是不成立**(不成立的一样列出来,不会只挑成立的讲);
- 每条都比较**两侧的实际数值**,交叉类条件额外给出**前一根**(只看当根证明不了上穿);
- **加重显示本次信号实际触发的那几条**:`AND` 的每个分支都是触发源,`OR` 里没成立的分支不会被算进去;
- 缩进表示条件之间的嵌套关系。

### 两种模式能给出的东西不一样

| 模式 | 能给出的解释 |
| --- | --- |
| **表达式** | **逐条件拆解**:把顶层的 `AND` / `OR` 摊平成一条条条件,各自标注 ✓/✗ 并附上比较两侧的具体数值。**不成立的条件也会如实列出**。 |
| **JavaScript(用条件函数组合)** | 与表达式模式**同一套证据**:逐条真假 + 两侧数值 + 实际触发项,还能额外显示策略自己写的 `why(i, side)` 说明。 |
| **JavaScript(直接写 `C[i] > MA(C, 20)[i]` 等原生比较)** | **无法自动归因**——系统拿不到条件函数留下的标记。界面会如实说明这一点并给出信号日的**指标快照**(C / MA5 / MA20 / MA60 / RSI14 / MACD / 有指数时的 RS、IDXDEV),同时提供**一键让 AI 改写成可归因版本**(保持买卖逻辑不变,只换成条件函数 + `why`),原代码留在「策略配置」页可随时换回。 |

这个区别是本质的:原生 JS 比较在运行期不留下任何可读取的中间结构,静态分析也给不出可信的归因。
**与其编一个看起来像原因的理由,不如说清「这里只能给快照」并给出一条能真正拿到证据的路。**

### `why(i, side)`:让策略自己说话

条件函数的证据链说明「哪些条件成立了」,但**这条策略为什么要看这几个条件**只有写代码的人说得准。
所以代码可以额外 `return` 一个 `why` 函数,它返回的一句话会显示在明细最前面:

```js
function why(i, side) {
  if (side === 'buy') return '5 日线上穿 20 日线,成交量高过 20 日均量,且大盘在 60 日线上方'
  return '5 日线下穿 20 日线,或收盘跌破 20 日线'
}
return { buy, sell, why }
```

AI 生成的策略会被要求同时给出条件函数组合与 `why` 说明,所以生成的策略开箱就有完整证据。

## 回测的真实性约定

按 A 股口径(港股按上表切换):

- **无未来函数**:信号在收盘产生,在**次日开盘**成交。
- **T+1**:买入当日不可卖出。因卖出也走次日开盘,实际最短持有为 2 个交易日,比真实 T+1 略保守。
- **涨跌停**:按板块推断限幅(主板 10% / 创业板·科创板 20% / 北交所 30%)。开盘处于涨停价则买不进,跌停价则卖不出。**ST 股的 ±5% 无法从代码推断,未建模**,会对 ST 偏乐观。港股无此限制。
- **成本**:佣金费率可调、5 元起收;印花税买卖费率分开设置(A 股卖出单边、港股双边);其他费率(过户费等)可调;滑点默认千分之一。
- **整手**:委托量按该股的每手股数取整(A 股 100,港股逐股不同)。资金不足 1 手时不会建仓,界面会明确提示原因。
- **前复权口径**:A 股默认使用前复权价,收益连续性正确,但绝对价位与历史成交价不一致。
- 回测结束仍有持仓时不强制平仓,按最后一根收盘价计入权益(界面会提示)。

## 数据源

默认**全部免密钥**。K线与行情以腾讯为主源、新浪为备源,搜索与财务用东方财富。

- K线:`web.ifzq.gtimg.cn`(按年区间分页)→ 备源 `money.finance.sina.com.cn`
- 行情:`qt.gtimg.cn`(**GBK 编码**,按响应头 charset 解码)
- 搜索:`searchapi.eastmoney.com`;失败时回退为纯代码录入
- 财务:`datacenter-web.eastmoney.com`
- 事件:港交所/交易所公告(正文日期)、预约披露时间表、分红实施公告
- 资讯检索(AI 联网查证的兜底):`search-api-web.eastmoney.com`

### 同花顺官方数据(可选,需要自己的 API Key)

同花顺官方有一套面向 AI Agent 的 A 股数据服务:**同一个 API Key,提供 MCP 与 REST 两套入口**。

| 项 | 值 |
| --- | --- |
| 官网 / 文档 |  ·  |
| API Key |  免费签发 |
| REST | `https://fuyao.aicubes.cn/api/...`,请求头 `X-api-key` |
| 托管 MCP(HTTP) | `https://fuyao.aicubes.cn/mcp/a-share`、`/mcp/a-share-index`、`/mcp/meta` 等 6 个 |
| 限流 | 不限制累计调用次数(异常并发会 429 / `code=4001`) |

在「公司数据」页填入 Key 之后:

- AI 助手会多出 `ths_corporate_actions`(**除权除息 / 分红送股**)、`ths_prices_snapshot`(行情)、
  `ths_hot_stocks`(热榜)、`ths_dragon_tiger`(龙虎榜)四个工具;
- **除权除息事件**在东方财富那份表为空时,自动改用同花顺官方复权事件兜底;
- Key 只存在本地(`$DSH_ASTOCK_HOME/hithink.json`),接口里只回掩码,不回明文。

> **为什么插件走 REST 而不是直接连 MCP**:宿主里注册的 MCP 工具在**插件作用域内不可执行**
> (插件拿到的 `tools` 服务只有 `register/schemas/get`,**没有 `execute`**,见下文实测)。
> 好在这套服务两套入口共用同一个 Key、同一批数据,走 REST 等价且更简单。
> 当前覆盖 **A 股**,港股不在它的范围内。

## 配置

| 环境变量 | 作用 | 默认 |
| --- | --- | --- |
| `DSH_ASTOCK_HOME` | 状态落盘目录 | `$DSH_HOME/astock`,再退回 `~/.dsh/astock` |

目录里有四个 JSON:`watchlist.json`(自选股)、`disclaimer.json`(免责声明确认)、
`events.json`(自定义事件)、`announcements.json`(公告正文解析缓存,可随时删除,删了会重新抓)。

## 结构

```
package.json             dsh.bundle.patch + dsh.client.platform,exports["./client"]
cordis.patch.yml         插入 id=astock name=dsh-astock
lib/index.js             Host 半边:10 条 /astock/api/* 路由 + 自选股/免责/自定义事件落盘 + 事件抓取 + 联网查证工具循环
client/client.js         客户端 bundle:__ModuleLoader__ 工厂块,仅 require('react')
tests/
  engine.test.mjs        表达式引擎 + 回测引擎(纯离线,读 fixtures)
  host.test.mjs          Host 路由(真实上游 + mock llm)
  client.test.mjs        客户端 bundle(真实执行组件 + 数据流)
  fixtures/              两份真实日线数据,供离线测试使用
```

客户端与 Host 通过 HTTP 路由通信(`host.call` 是动态插件专有机制,正式 bundle 不可用)。

## 测试

```bash
pnpm test                 # 三套全跑
node tests/engine.test.mjs   # 策略与回测引擎(纯离线,43 项)
node tests/host.test.mjs     # Host 半边:真实上游 + mock llm + 港股 + 自选股混排 + 免责声明 + 事件路由 + 港股事件解析 + 联网查证工具循环 + 多轮对话与表达式输出 + 检索兜底与预查 + 公司数据对话 + 同花顺数据(280 项)
node tests/client.test.mjs   # 客户端 bundle:真实执行组件 + 数据流 + 策略模式 + AI 入口 + 港股规则 + 回测区间与档位 + 免责弹窗 + 情绪因子 + 事件因子与自定义事件 + 交易证据链 + AI 对话与表达式模式 + 公司数据对话与数据源管理 + 同花顺 Key(274 项)
```

`host.test.mjs` 与 `client.test.mjs` 需要网络(要打腾讯/东财的真实接口)。`engine.test.mjs` 完全离线,只读 `tests/fixtures/`。

`client.test.mjs` 用自建 hook 运行时而非 react-dom:宿主里 react 是 18、react-dom 是 19,版本不匹配;本插件只用到 `createElement` / `useState` / `useEffect`,足以模拟。

## 安装

```bash
dsh plugin --profile web add -w <本目录绝对路径>
```

`-w` 是必需的:profile 自身的 `pnpm-workspace.yaml` 把它标记为 workspace root。

首次安装后需重启 `dsh web`(bundle 列表只在启动时读取)。

### 改代码后要不要重启?

**分两半,结论不同:**

| 改动的文件 | 生效方式 |
| --- | --- |
| `client/client.js` | **热更新,不用重启**。harness 的 `@deepseek-ai/dsh-client-hmr` 会轮询 bundle 文件,内容变了就通过 `/plugins/events` SSE 推送新版本,页面自动换掉旧 bundle(必要时刷新一次页面)。 |
| `lib/index.js`(Host 半边) | 需要重启 `dsh web`。Host 插件在启动时装载。 |

判断某次改动是否已经被服务:从 `/plugins/events` 取当前图,找到本包条目的 `url`,抓下来 grep 新代码即可:

```bash
curl -s --max-time 5 http://127.0.0.1:3080/plugins/events \
  | sed -n 's/^data: //p' | head -1 \
  | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{const g=JSON.parse(s).graph;console.log(g.entries.find(e=>e.id==='dsh-astock').url)})"
```

## 版本记录

每个版本对应一次 GitHub 提交与一次 npm 发布。完整提交历史见
[commits](https://github.com/hzy1522/dsh-astock/commits/main)。

### 0.9.0

**接入同花顺官方数据(REST API,凭一个 Key)**

调研确认:同花顺官方(HiThink-Tech)发布了面向 AI Agent 的金融数据服务
`HiThink-Tech/Financial-API`,**同一个 API Key 同时提供 MCP 与 REST 两套入口**:
6 个托管 MCP 端点(`https://fuyao.aicubes.cn/mcp/*`,HTTP 传输)与一组 REST 接口
(`https://fuyao.aicubes.cn/api/...`,请求头 `X-api-key`),不限制累计调用次数。

由于插件作用域执行不了宿主注册的 MCP 工具(实测),插件改走**同一份数据的 REST 入口**:

- 「公司数据」页新增「同花顺官方数据」:粘贴 Key → 保存 / 清除 / 测试连接;Key 本地落盘、只回掩码。
- 配好后 AI 助手多出四个工具:`ths_corporate_actions`(除权除息/分红送股)、
  `ths_prices_snapshot`、`ths_hot_stocks`、`ths_dragon_tiger`。
- **除权除息事件**在东方财富为空时用同花顺官方复权事件兜底(A 股)。
- 代码 → `thscode` 的交易所后缀映射(沪/深/北),港股明确报「不在覆盖范围」。

测试:host 262 → 280 项(后缀映射、Key 掩码与清除、未配置不暴露工具、配置后注入工具、
假 Key 被如实拒绝、除权兜底),client 264 → 274 项(Key 表单、掩码、测试连接、清除)。
合计 597 项全通过。

### 0.8.0

**公司数据页接入 AI 对话:查到动态直接填进表;并接自定义数据源**

- 「公司数据」页新增 AI 对话区:查发布会 / 说明会 / 股东大会 / 财报 / 分红等公司动态,
  查到的事件**由宿主直接写进事件表**(标未核实、带来源、可删除,同日同标题去重),
  策略里用 `EVCUS(n)` 引用。只问情况、没给出日期时不会往表里塞东西;换股票自动重开对话。
- 新增「外部数据源」:可配 HTTP 接口(URL 模板支持 `{code}` `{name}` `{query}`),
  启用的数据源会作为工具交给模型,调用结果(含失败原因)回灌;「说明」字段原样注入提示词。
- **实测结论(运行时探针)**:插件上下文里的 `tools` 服务只有 `register/schemas/get`,
  **没有 `execute`**;`skills.list()` 在插件侧返回 0 条——也就是说**插件无法调用宿主注册的
  工具(含 MCP 服务器)或读取 skill 目录**,这是作用域设计。因此走「HTTP 数据源 + 说明文本」
  这条自己能掌控的路,界面上也照实写明,不假装支持。
- 安全:只把插件自己配置的数据源交给模型,harness 自带核心工具不暴露。

测试:host 238 → 262 项(数据源增删改查、模板替换、数据源当工具调用并回灌、说明注入、
自动填表与去重、停用不暴露、只问答不写表),client 241 → 264 项(对话区、自动填表提示、
数据源增删、注册表限制说明)。合计 569 项全通过。

### 0.7.2

**修「小米产品发布会之前卖出」这类需求跑不通**

拿线上真实请求复现过,两头都出了问题:

1. **模型根本没搜**(`searched=false`、`suggestedEvents=[]`),却在说明里声称
   「列表里已经有 2026-09-07「小米产品发布会」,已登记为自定义事件」,然后写了
   `EVCUS(-1)`——策略指向一个**不存在**的日期。原因是提示词只说「列表里没有的事件才需要联网查」,
   没写「不许假设它已经存在」。
2. 即使它真的写错了,也没有任何机制拦住。

三处修复:

- **提示词加硬规则**:`EVCUS` 只能引用用户已确认的自定义事件;事件列表里没有的,
  不许假设存在、不许跳过检索;**绝不允许声称「列表里已经有某个事件」,除非它真的在列表里**。
- **输出后校验 + 强制补一轮**:策略里用到了 `EVMEET/EVREP/EVDIV/EVCUS`(或事件全空的 `EV`)
  而对应事件不存在时,宿主不采信,补一轮带明确指令的对话(「先联网查证并输出 events 区块,
  查不到就明说」)。只有**既给出候选事件、又确实搜过**才算合规——凭记忆写的日期一样不可信。
- **宿主替模型先查一遍**:事件类需求先自己做一次新闻检索(内置财经资讯检索),
  把结果摆进提示词。不再把可靠性押在「模型会不会去调工具」上。
- 兜底也给足出路:确实查不到时,界面上写明三条路——再追问一次 / 公司数据页手动添加 /
  改成不依赖事件因子;候选事件区也标注「不点确认就直接回测会报错」。

测试:host 218 → 238 项(假设事件的检测、强制补一轮、预查块与命中缓存、非事件需求不预查),
client 240 → 241 项(查不到事件时的三条出路)。合计 522 项全通过。

### 0.7.1

**修「AI 的 web_search 用不了」:检索加了一层内置兜底**

用运行时探针在真实 Host 进程里查过,结论很明确:

- `llm` 的工具调用**完全正常**——模型会正确发出 `web_search` 的流式工具调用;
- 坏的是宿主 `web.search`:每次调用都返回
  `WEB_PROVIDER_ERROR: DeepSeek returned no web_search_tool_result blocks`,
  它配的搜索 Endpoint(`open.feedcoopapi.com/search_api/global_search/messages`)没返回原生搜索结果。

插件这边能做的是**不把整件事卡在宿主的配置上**:

- 宿主搜索失败后自动改用**内置财经资讯检索**(东方财富站内搜索,覆盖财经新闻与公告),
  实测能搜到「贵州茅台召开半年度业绩说明会」这类带日期的新闻,正是策略需要的东西;
- 宿主 provider 失败一次后本轮不再重试;两条路都不通就如实报错并停止再给工具;
- 界面写明这次用的是哪条路、宿主的原始报错,以及修好宿主搜索的路径
  (设置 → 插件 → 插件配置 → Web search → Endpoint);
- `web_fetch` 同样加了插件直连的兜底。

测试:host 208 → 218 项(内置检索结构、宿主可用时走宿主、provider 报错时退回内置、
本轮不再重试、两条路都不通),client 237 → 240 项(检索来源与修复路径提示)。合计 501 项全通过。

### 0.7.0

**AI 改成多轮对话,并且能产出表达式策略**

原来只有「一句话 → 一次生成 → 填进编辑器」,想再改只能重说一遍;而且只出 JS。
现在策略页底部是对话区:

- **多轮对话**:每一轮都把之前的对话与当前策略一起发给模型,所以「再加个放量过滤」
  「把止损换成跌破 30 日线」这样追问就行。AI 每轮给出**完整的新策略**并直接填进编辑器。
- **支持表达式模式**:按你当前的模式产出——表达式模式要求输出
  `{"buy":"…","sell":"…"}`,JS 模式要求输出函数体。需求确实表达不了时(要记持有天数、
  多步状态),它会改用另一种模式**并说明原因**,返回值和界面都如实显示实际产出的是哪种。
- **只回答不给代码也是合法一轮**:需求不明确时它会反问,编辑器不动。
- **两模式各留一版可回退**:`↺ 换回上一版表达式` / `↺ 换回上一版代码`。
- **失败不丢输入**:请求失败时那句话回到输入框,并在对话里留一条失败气泡。
- 表达式模式下的事件因子(`EVCUS(-1)`)也能用;交易明细里同样写明命中的真实事件——
  这一条原来只对 JS 生效,现在表达式模式也补上了。

注意:AI 产出的策略**每一轮都会覆盖编辑器**(旧版可回退)。模型偶尔忘记加围栏代码块时,
只要整段看着就是函数体也会被认成代码。

测试:host 187 → 208 项(表达式块解析、对话历史传递与裁剪、反问轮、模型换模式时如实回报、
无围栏纯代码识别),client 211 → 237 项(多轮历史、表达式应用与试运行校验、反问不改编辑器、
清空对话、回退上一版、表达式模式的事件因子证据)。合计 488 项全通过。

### 0.6.3

**限流退避 + 手动添加事件(上游靠不住时的两条出路)**

东财的公告正文接口(`np-cnotice-stock`)在密集请求后会按 IP 限流,返回 `ECONNRESET`。
实测该接口没有可替代的镜像域名(`np-anotice-stock` 的 content 路径返回空 body),所以:

- 失败后 **60 秒内不再请求正文**——被限流时反复切股票/刷新只会把封禁越敲越久;
- 「公司数据」页新增**手动添加事件**:选日期、写名称,即时成为自定义事件(标为手工来源、
  未核实),与 AI 检索走同一条保存路径;
- 配合 0.6.2 的「取数出错不写缓存」与「↻ 重新加载」,限流期间不再被锁死。

测试:host 185 → 187 项,client 206 → 211 项。合计 441 项全通过。

### 0.6.2

**修「限流后要等 30 分钟才恢复」**

事件列表有 30 分钟进程内缓存,但取数出错时也写了缓存——于是上游限流一次,
「缺了会议事件」的结果就被锁住半小时,上游恢复了用户也看不到。改成**只有成功才进缓存**。

同时「公司数据」页新增 **↻ 重新加载事件日期** 按钮,取数失败会直接显示原因,
不用切股票来触发重取。

测试:client 204 → 206 项。合计 434 项全通过。

### 0.6.1

**港股也能用事件因子了(修「港股没事件数据」)**

0.6.0 上线后实测:港股股票的事件列表是空的,AI 生成的事件策略一跑就报
「事件因子需要公司事件数据」。原因是港股公告的标题写法、日期写法都与 A 股不同,
原来的解析一条都没匹配上:

- 新增港股来源:**「董事会会议召开日期」公告=提前公布的业绩日**(港股版的预约披露),
  归到 `EVREP(n)`;**股东周年大会 / 股东特别大会通告**归到 `EVMEET(n)`;
  **业绩公告**作为兜底(公告日即业绩日,只能用于 `n >= 0`,不会变成未来函数)。
- 日期解析支持**中文数字**(「謹訂於二零二六年五月十三日」→ 2026-05-13),
  并覆盖「將於…**召開**董事會會議」这类港式写法。
- 同一天的财报事件去重时**优先保留更早公布的那条**(预告日),
  这样 `EVREP(-1)` 这种提前量才是有依据的。

**修「上游限流导致事件悄悄变少」**

公告正文接口在密集请求下会直接 ECONNRESET。原来的代码把它 `.catch(() => null)` 吞掉了,
表现就是「事件列表莫名变少、界面还说一切正常」。现在:

- 正文解析结果按 `art_code` **落盘缓存**(`announcements.json`),抓过就不再请求;
- 小批量并发 + 重试 + 批间隔,降低触发限流的概率;
- 失败的部分写进 `errors` 并在界面说明「部分事件没取到:…」,绝不静默变少。

**修「模型不知道没有事件数据」**

原来没有事件时只回报一个 0,模型照样按常识写 `EVMEET(-1)`。现在这种情况会在提示词里
明确写上「这只股票没有事件数据,**禁止**使用 EV/EVMEET/EVREP/EVDIV」,并给出两条出路
(联网查到就用 `EVCUS`、否则生成不依赖事件的替代策略)。客户端的试运行也会把
**待确认的候选事件**一并算进去,不再出现「明明确认后能用、却先报一次错」。

测试:host 158 → 185 项(中文数字与繁体日期提取、港股真实事件、候选事件校验、无事件提示词),
client 201 → 204 项(无事件时的原因显示与预警、试运行带上候选事件)。合计 432 项全通过。
新增测试钩子 `__test`(仅供离线测试调用纯函数,不是公开 API)。

### 0.6.0

**AI 可以自己联网查资料了(发布会这类交易所数据里没有的日期)**

0.5.0 给了模型真实的交易所事件日期,但那里面没有「产品发布会」。0.6.0 让模型自己上网查:

- 宿主给模型挂 `web_search` / `web_fetch` 两个工具,**最多四轮**工具调用(最后一轮不给工具,
  逼它落笔写代码)。工具调用是流式分片给出的,插件按 index 拼回完整参数。
- 模型查到的日期**不能直接写进策略**,必须输出 `events` 区块;插件解析后列成**候选事件**,
  由用户点「确认加入」才成为自定义事件。日期不合法的条目直接丢弃。
- 自定义事件单独一类,**只由 `EVCUS(n)` 引用**,不会悄悄改变 `EV`/`EVMEET` 的语义;
  表中标注**未核实**并附出处链接,与交易所数据严格分开。
- 宿主没挂 `web` 服务时静默降级,但会如实回报「未挂载 web 服务,本次没有联网查证」;
  `health.webSearch` 暴露这项能力的状态。
- 新增 `/astock/api/custom-events`(覆盖式保存)与 `events.json` 落盘;事件表支持逐条删除。

测试:host 127 → 158 项(多轮工具调用、分片参数拼接、候选事件解析与非法日期丢弃、搜索失败的降级、
没有 web 服务时的降级、自定义事件的去重与来源区分),client 181 → 201 项(自定义事件渲染与删除、
`EVCUS` 语义、自定义事件不混进 `EVMEET`、候选事件的确认流程)。合计 401 项全通过。

### 0.5.0

**交易明细的「为什么买 / 为什么卖」改为真正的证据链(针对「还是没给出实际证明」的反馈)**

之前 JS 模式的交易明细只能给一张指标快照,没有任何东西能证明「这笔交易到底踩中了哪一条条件」——
信号日的 MA5 甚至可能是向下的,快照本身无法解释信号。0.5.0 把这件事做实:

- **JS 策略库现在会记录触发链路**:每个条件函数(`GT` / `LT` / `GTE` / `LTE` / `CROSS` / `AND` / `OR` / `NOT`)
  返回时都会记下自己的身份、操作数与结果(标记不可枚举,不影响任何计算结果,两种模式的指标仍然同源)。
  信号日据此把整条链路反向还原:
  - 逐条列出**每条条件当时成立还是不成立**(不成立的照样列,不挑着说);
  - 每条附**两侧实际数值**,交叉类条件额外给出**前一根**(只看当根证明不了上穿);
  - **加重显示本次信号实际触发的那几条**:`AND` 的每个分支都是触发源,`OR` 里没成立的分支不算;
  - 缩进表示嵌套关系。
- **新增 `why(i, side)` 钩子**:策略可以自己 `return` 一个函数说明第 i 根为什么买卖,
  这段话显示在明细最前面——「为什么要看这几个条件」只有写代码的人说得准。
- **原生比较(`C[i] > slow[i]`)仍然无法归因**,此时界面**明确说「无法自动归因」**并给出三条出路,
  其中一条是新增的**一键让 AI 改写成可归因版本**(保持买卖逻辑不变,改成条件函数组合 + `why`)。
  改写会覆盖编辑器内容,因此旧代码自动留档,「策略配置」页可一键换回。
- **AI 生成的策略**被要求必须用条件函数组合并附 `why` 说明,开箱即有完整证据。
- 策略编辑页与 README 都写清了「怎么写才有证据」。

**新增:公司事件因子(真实日期,可回测)**

- 新增 `/astock/api/events`:抓三类**事先公开**的真实日期——说明会 / 股东大会(逐条去公告正文里
  提取「会议召开时间」,提取不到或日期不合常理就**宁可漏掉也不编**)、财报**预约**披露日、
  除权除息日(带公告日)。
- 新增 `EV(n)` / `EVMEET(n)` / `EVREP(n)` / `EVDIV(n)`:`n` 是相对该事件的第 n 个交易日,
  交易日换算按真实 K 线做(事件日不是交易日就顺延到之后第一个交易日)。
- **硬保护**:那一根 K 线当天事件还没公告时,条件直接不触发——「当天才公告的说明会」不会被提前埋伏。
- 「公司数据」页新增**公司动态**表;新增模板**说明会前后**;交易明细里会写明「命中 2026-08-21 说明会」。
- **AI 生成**把该股真实事件列表塞进提示词,并要求只能用这些日期、禁止推算日历;返回值里带
  `events` 条数,界面显示「已把该股 N 条真实事件日期交给模型」。

测试:client 139 → 181 项(证据链、`AND`/`OR`/`NOT` 节点、实际触发项、`OR` 假分支不误标、原生比较兜底、
一键改写与换回;事件日映射、非交易日顺延、当天公告不许提前埋伏、事件缺失时的可读错误),
host 103 → 127 项(AI 提示词要求条件函数与 `why`、要求用事件因子且不许编日期、事件路由的真实性与日期合理性)。
合计 351 项全通过。

### 0.4.1

**文档发布:让 npm 页面同步 README**

- 纯文档版本,**无代码变更**。npm 上已发布的版本不能修改,`0.4.0` 的 README 里没有版本记录,
  这个版本只为把最新文档同步到 npm 页面。
- 之所以单独发一版而不是等下次代码改动:npm 页面的 README 是给使用者看的第一手材料,
  版本记录留在那里才有意义。

### 0.4.0

**交易明细说明「为什么买 / 为什么卖」**

- 点击交易行向下展开决策证据。表达式模式把顶层 `AND` / `OR` 摊平成一条条条件,各自标注 `✓` / `✗` 并附上比较两侧的具体数值;**不成立的条件也如实列出**,不挑着说。
- 引擎开始记录信号发生的 bar(成交在次日开盘,所以信号在前一根),解释回到信号那一根而不是成交那一根。
- JS 模式无法定位到具体语句,界面**如实说明**并改为给出信号日的指标快照——与其编一个像原因的理由,不如说清这里只能给快照。

### 0.3.1

**修复:AI 生成策略总是报「模型没有返回任何内容」**

- 根因:思考 token 与正文 token 共用 `maxTokens`。默认选型带 `reasoningEffort: high`,2048 的预算被思考全部吃掉,模型**还没开始写代码就被截断**(实测 `reasoningTokens=2048`、`text-delta=0`、`finish=max-tokens`)。
- 不再透传 `reasoningEffort`;`maxTokens` 2048 → 16384。
- 错误信息改为说清「**为什么**没有内容」,区分截断 / 其他 finish 原因 / 流意外结束,并给出可操作建议。

### 0.3.0

**市场情绪因子(第一阶段)**

- 新增 `BENCH`、`RS(n)`、`IDXRET(n)`、`IDXMA(n)`、`IDXDEV(n)`、`IDXVOL(n)`、`BETA(n)`,全部由「个股 K 线 + 沪深300」算出,**完全可回测**。
- 表达式与 JS 两种模式共用同一套实现(只在求值器里写一次),不会漂移。
- 指数序列只做前向填充,开头保持 `null`,不用未来值回填——避免前视偏差。
- 策略页新增因子读数面板;新增「相对强弱 RS 择时」「大盘趋势 + 相对强弱」两个模板。

### 0.2.0

**免责声明 + 改名 + 首次发布到 npm**

- 首次打开强制确认完整条款(落盘,条款版本变更时重新提示)+ 底部常驻声明;回测结果与 AI 生成处各有提示。
- 显示名从「A股量化工作台」改为「**A股港股量化工作台**」,侧边栏标签改为「A股港股」——此前已支持港股,旧名字不再准确。
- 移除 `private`,补 `publishConfig`(本机默认 registry 是只读镜像,不显式指定会发布失败)。

### 0.1.0

**首个版本**

- **A 股与港股**:自选股(可混排、落盘)、手写 SVG K 线图、公司财务数据。
- **三种策略入口**:6 个参数化模板 / 表达式(手写词法 + 语法分析,**不使用 `eval`**)/ JavaScript(`new Function`,支持循环与状态机)。
- **回测引擎**:按市场自动切换规则(A 股 T+1、涨跌停、100 股一手、印花税卖出单边;港股 T+0、无涨跌停、每手逐股读取、印花税双边),支持按日期区间回测。
- 无未来函数(信号收盘产生、次日开盘成交)、佣金/印花税/过户费/滑点、整手约束。

> 说明:`0.1.0` 发布时显示名仍是「A股量化工作台」,改名的提交(`42f99d4`)随 `0.2.0` 一起发布。

数据来源:公开的 DeepSeek Harness 插件目录与各插件 GitHub 仓库。本站为独立第三方目录,与 DeepSeek、幻方(High-Flyer)及插件作者均无隶属或背书关系。

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。