dsh-astock
A股港股量化工作台 —— DeepSeek Harness 插件。
当前版本 0.9.0 · npm · 版本记录 · 提交历史
在侧边栏底部提供独立的「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:
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 区块:
[{"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 |
每手股数从行情里逐股读取,不写死。策略页会明示当前适用哪套规则,并提供「套用港股默认费率」一键填入。
港股的三个限制
- 无复权数据。腾讯对港股不提供复权价(
qfq/hfq/不复权返回的数据完全相同),本插件拿到的港股 K 线是不复权的。除权除息日会有价格跳空,长期收益会略被低估。回测结果页会明确提示这一点。
- 无财务数据。东方财富那份财务报只覆盖 A 股(港股
SECUCODE=00700.HK 返回「返回数据为空」)。切到港股的公司数据页会给出这句说明,而不是一张空表。
- 成交额单位不同。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 函数,它返回的一句话会显示在明细最前面:
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 两套入口。
在「公司数据」页填入 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 不可用)。
测试
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,足以模拟。
安装
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 新代码即可:
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。
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
修「小米产品发布会之前卖出」这类需求跑不通
拿线上真实请求复现过,两头都出了问题:
- 模型根本没搜(
searched=false、suggestedEvents=[]),却在说明里声称
「列表里已经有 2026-09-07「小米产品发布会」,已登记为自定义事件」,然后写了
EVCUS(-1)——策略指向一个不存在的日期。原因是提示词只说「列表里没有的事件才需要联网查」,
没写「不许假设它已经存在」。
- 即使它真的写错了,也没有任何机制拦住。
三处修复:
- 提示词加硬规则:
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 一起发布。