@dshtrading/connector-tiger 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,247 @@
1
- # @dshtrading/connector-template
1
+ # @dshtrading/connector-tiger
2
2
 
3
- **本包是脚手架源,不是可安装的连接器**:不入任何 bundle 依赖,仅在 `pnpm -r build/test`
4
- 基线内保证可编译、冒烟测试绿。真实接入用生成器:
3
+ 老虎证券(Tiger Trade)连接器:**港股(hk)行情 + 交易双面**。上游是老虎**券商 OpenAPI 网关**
4
+ (`openapi.itiger.com/gateway`,RSA 签名鉴权),实现 `@dshtrading/api` 的
5
+ `MarketDataService` / `TradeService` 两个契约,注册服务键 `tradingHkMarketData` /
6
+ `tradingHkTrade`,注册 5 个 `hk_*` 工具。被 hk bundle 以 `workspace:*` 依赖
7
+ (`packages/hk/package.json:35`)。
5
8
 
6
- ```sh
7
- node scripts/new-connector.mjs --slug bybit --title Bybit --market crypto
8
- ```
9
+ **行情面零跨源回落**:tiger 行情两法只打老虎网关,上游失败时**直接上报结构化错误**
10
+ (`src/rest.ts:114-141`、`143-177`),不改打任何第二数据源;腾讯公开端点只能由用户显式选择
11
+ `connector-tencent` 得到。详见「上游端点」「周期与契约」与「合规记录」。
12
+
13
+ ## 状态
14
+
15
+ **未实证**:本包没有已提交的真实券商请求记录(无原始响应、无真机错误码分布)。上游路径、
16
+ 签名规范与字段映射全部取自源码(下文逐条给出行号),**未经真机核对**。已提交的相关材料只有两类:
17
+
18
+ - `spikes/impl-service-seam-gate/NET-VERIFY.md`(2026-09-01,第 36 行):明确记录**未**
19
+ 对各家券商网关做真实下单验证,该轮唯一真实网络对象是 OKX;本包的服务缝三态由离线
20
+ `test/trade-gate.test.ts` 覆盖,不是出网证据。
21
+ - `spikes/verify-connectors-suite/EVIDENCE.md`(2026-08-31,第 37-39 行):当时的静态评审把
22
+ tiger 交易面判为「本地 stub:`placeOrder` 不发网络请求、静默返回假 `status:'new'`」。
23
+ 该结论对应 commit `b80000e`;当前源码(`src/rest.ts:220-269`)确实 POST 网关、且在回执缺
24
+ 真实订单 id 时抛 `TRADING_EXCHANGE_ERROR`(第 243-256 行),与那时已不一致——但**现状同样
25
+ 没有实证**,本 README 不据此声明可用。
26
+
27
+ 本仓 `spikes/` 下没有 `impl-tiger*` 目录,也没有任何针对老虎端点的探针脚本。
28
+
29
+ ## 配置
30
+
31
+ 字段名与默认值见 `src/index.ts:51-59`。
32
+
33
+ | 字段 | 默认 | 说明 |
34
+ |---|---|---|
35
+ | `enabled` | `true` | 总开关(`src/index.ts:52`) |
36
+ | `env` | `'demo'` | 运行环境标签(`src/index.ts:53`)。它**只进订单意图台账的作用域**(`src/index.ts:118-122` 的 `environment: options.config.env`),**不改变请求地址**——见「已知边界」 |
37
+ | `dryRun` | `true` | 强制模拟:为 true 时即使请求 `dryRun:false` 也只回本地模拟回执(`src/index.ts:140`) |
38
+ | `liveTrading` | `false` | 实盘请求的**非权威镜像**;真实放行需 `@dshtrading/authority` 平面的人工签署授权(`liveTradingEnabled()`,`src/index.ts:133`、`177`) |
39
+ | `tigerId` | 无默认(可选) | Tiger ID(`src/index.ts:56`)。**缺省时 rest 层用字面量 `'demo'` 占位**(`src/rest.ts:72`),不报错;也可由环境变量 `TIGER_ID` 回落(`src/rest.ts:65`)。设置页自 2026-10-15 起不再提供该字段——它是非密账户标识,家在 Config |
40
+ | `privateKeyRef` | `'TIGER_PRIVATE_KEY'` | RSA 私钥(PEM)的**环境变量名**(BYOK,`src/index.ts:57`),用于 RSA2 签名;设置中心 `dshtrading.credentials.tiger.privateKey` 优先级更高 |
41
+ | `accountId` | 无默认(可选) | Tiger 账户 ID(`src/index.ts:58`)。缺省时不发 `account` 业务字段(`src/rest.ts:183-188`、`209`、`230`、`276`);也可由环境变量 `TIGER_ACCOUNT_ID` 回落(`src/rest.ts:67`) |
42
+
43
+ 交易安全三件套在本包的落点:
44
+
45
+ - **dry-run 缺省**:工具参数 `dryRun` 默认 `true`(`src/index.ts:271`),工具执行里
46
+ `args.dryRun !== false` 即走模拟分支(`src/index.ts:280`);服务内 `order.dryRun ?? true`
47
+ (`src/index.ts:132`)。
48
+ - **liveTrading 必须显式开启且非权威**:工具层再查一次(`src/index.ts:302-308`),服务缝
49
+ 内第一步也查(`src/index.ts:133-139`);撤单与下单同门槛(`src/index.ts:177-182`)。
50
+ - **base 审批闸门**:`hk_place_order` / `hk_cancel_order` 命中
51
+ `LIVE_ACTION_GATE_PATTERN`(`packages/base/src/index.ts:63`),无人在场时 ask 降级为 deny
52
+ (`packages/base/src/index.ts:20-24`)。
53
+ - **凭证解析面**:RSA 私钥的设置中心键 `dshtrading.credentials.tiger.privateKey`(router
54
+ `getCredential`)优先、`privateKeyRef` → `process.env` 兜底,由 `resolvePrivateKey` 统一解析
55
+ (`src/index.ts:220-223`);数据面行与 preset 行**都**走它(`src/dataplane.ts:32-37`),所以面板
56
+ 填的 PEM 在 GUI 行情面同样生效。值只交给 rest 客户端,不落日志、不进台账身份(`src/index.ts:121`)。
57
+
58
+ ## 接线
59
+
60
+ - **数据面行**:`packages/hk/cordis.patch.yml:42-45` → 行 id `dsh-trading-hk-dataplane-tiger`,
61
+ `name: '@dshtrading/connector-tiger/dataplane'`,行 config 只写 `enabled: true`。
62
+ `src/dataplane.ts` 重导出 `Config`(第 10 行)让 loader 补全 schema 默认值,在
63
+ `tradingHkMarketData` isolate realm 内构造行情服务(第 34-35 行)并 `register('hk', 'tiger', …)`
64
+ 进 `tradingMarketDataRegistry`(第 36 行);无注册表的老部署回退直接构造(第 30-33 行)。
65
+ **该文件不解析也不注册 `tradingTradeRegistry`**(全文件无此键)——交易服务只在 preset 面可用。
66
+ - **preset 行**:`packages/hk/assets/preset/hk-trader/agent.cordis.yml:93-105` → 组 id
67
+ `dsh-trading-hk-connector-tiger-group`,`isolate: {tradingHkMarketData, tradingHkTrade}`
68
+ (第 96-98 行),行 id `dsh-trading-hk-connector-tiger`,`name: '@dshtrading/connector-tiger'`,
69
+ 行 config `enabled: true` / `dryRun: true` / `liveTrading: false`(第 102-105 行)。
70
+ 该行的 `apply` 直接构造行情与交易两个服务(`src/index.ts:228-229`),不经注册表。
71
+ - **provider 词汇**:`packages/router/src/index.ts:81`(`PROVIDER_VOCABULARY` 的 `'tiger'`)。
72
+ - **设置页显示名**:`PROVIDER_LABELS` 的 `tiger` 条目
73
+ (`packages/client-ui-settings/src/client/trading-settings-controller.ts:69`:`type: 'commercial'`、
74
+ `env: 'TIGER_ID'`、`markets: ['hk']`)与 `packages/client-ui-settings/src/client/locales.ts`
75
+ 的 `'provider.tiger'` 条目(zh `Tiger Trade (老虎证券)` / en `Tiger Trade`)。凭据卡片字段只剩
76
+ `privateKey`(`trading-settings-controller.ts:168`,`secret: true`,由本包 `resolvePrivateKey`
77
+ 消费);`tigerId` 已撤(2026-10-15)——它是非密账户标识,家在 `Config.tigerId`
78
+ (`src/index.ts:56`,rest 层另有 `TIGER_ID` 回落,`src/rest.ts:65`)。**`markets` 与本包实现面一致**:
79
+ 本包唯一的 `MARKET = 'hk'`(`src/dataplane.ts:25`),唯一挂载处是 `packages/hk/cordis.patch.yml:42-45`。
80
+ - **bundle 依赖**:`packages/hk/package.json:35` 的 `@dshtrading/connector-tiger: workspace:*`。
81
+ - **激活条件**:`routeAllows()` 要求 `enabled` 且
82
+ `tradingMarketRouter.activeProvider('hk') === 'tiger'`(`src/index.ts:209-214`)。
83
+
84
+ ## 上游端点
85
+
86
+ - 主基地址:`process.env.TIGER_API_URL || 'https://openapi.itiger.com/gateway'`(`src/rest.ts:64`),
87
+ 也可由 `TigerRestOptions.baseUrl` 覆盖。全部业务调用都是**同一个 POST 端点**,靠 `method`
88
+ 字段区分(`src/rest.ts:76-99`,参数字段 `tiger_id` / `method` / `charset` / `sign_type: 'RSA2'` /
89
+ `timestamp` / `version` / `biz_content` / `sign`)。
90
+ - **没有第二基地址**:上面这个网关地址是本连接器唯一的出网目标;腾讯公开端点只能由用户显式
91
+ 选择 `connector-tencent` 得到,其边界见该包 README。
92
+
93
+ | 用途 | method / 端点 | 源码 |
94
+ |---|---|---|
95
+ | 实时报价 | `POST 网关` `method=quote_real_time`(`biz_content.symbols=[sym]`) | `src/rest.ts:120` |
96
+ | K 线 | `POST 网关` `method=kline_quote`(`symbols` / `period` / `limit`) | `src/rest.ts:149` |
97
+ | 账户资产 | `POST 网关` `method=user_asset`(`account`) | `src/rest.ts:188` |
98
+ | 持仓 | `POST 网关` `method=positions`(`account`) | `src/rest.ts:209` |
99
+ | 下单 | `POST 网关` `method=trade_order`(`account` / `symbol` / `action`(BUY\|SELL) / `orderType`(MKT\|LMT) / `totalQuantity` / `limitPrice`) | `src/rest.ts:236` |
100
+ | 撤单 | `POST 网关` `method=cancel_order`(`account` / `orderId`) | `src/rest.ts:278` |
101
+
102
+ ## 周期与契约
9
103
 
10
- 生成后按 [docs/guides/connector-playbook.md](../../docs/guides/connector-playbook.md) 的填写顺序
11
- (src/rest.ts 头部清单 7 项)实现 TODO。每个 TODO 的参照段都在 connector-okx
12
- (packages/connector-okx/),它是本仓第一个真实 TradeService 连接器。
104
+ - **周期词汇 8 个**:`1m/5m/15m/30m/1h/1d/1w/1M`(`src/rest.ts:24`),但**没有到上游的映射表**:
105
+ `period: interval` 原样透传(`src/rest.ts:149`)——本包不产生 `TRADING_UNSUPPORTED_INTERVAL`,
106
+ 非法周期由上游拒绝。工具 enum 与词汇同集合(`src/index.ts:252`)。
107
+ - **非日线周期没有本地兜底**:`interval` 只进 `period` 字段(`src/rest.ts:149`),上游不支持即
108
+ 失败上报;本包不再有写死周期的回落路径(旧腾讯日线回落分支已随 fail-closed 改动删除)。
109
+ - **`closeTime` 恒按一天**:K 线映射把每根 bar 的 `closeTime` 写成 `openTime + 86400000 - 1`
110
+ (`src/rest.ts:175`),与周期无关——非日线周期下它不代表周期末。
111
+ - **符号形态**:`toTigerSymbol`(`src/rest.ts:26-37`)——含 `.` 的取点前段为 venue symbol;
112
+ 裸 5 位数字补成 `<code>.HK`;**4 位 `0700.HK` 不补零**(与 futu 的 `normalizeHkSymbol`、
113
+ longbridge 的 `toLongbridgeSymbol` 不同);空串 → `TRADING_INVALID_ARGUMENT`(第 28 行)。
114
+ - **签名规范**:`generateTigerSignature`(`src/rest.ts:39-46`)——参与字段按 key 字典序拼
115
+ `k=v&…`,剔除空值与 `sign` 自身(第 40 行),RSA-SHA256 + base64。
116
+ - **不发 venue client key**:本连接器**不发**官方 `user_mark`(类头注 `src/index.ts:99-107`:
117
+ 本包发的是 camelCase 键,与官方 snake_case 业务参数尚未逐字对齐,按「不猜字段」纪律不新增);
118
+ 实盘幂等完全由本地意图台账保证。
119
+ - **错误映射**:签名失败 → `TRADING_AUTH_FAILED`(`src/rest.ts:90`);HTTP 非 2xx →
120
+ `TRADING_UPSTREAM_ERROR`(第 101 行);请求抛错 → `TRADING_NETWORK`(第 106-110 行);
121
+ 缺 privateKey(资产与交易面)→ `TRADING_AUTH_FAILED`(第 181、203、222、273 行);
122
+ 行情两法业务码非 0 → `TRADING_EXCHANGE_ERROR`(第 123、152 行);`getTicker` 回 code 0
123
+ 但没有可用的有限正数报价行 → `TRADING_EXCHANGE_ERROR`(第 127-133 行);`getKlines`
124
+ 回包形状不可识别(`data` 非数组,或没有 `items` 数组)→ `TRADING_EXCHANGE_ERROR`
125
+ (第 156-158、161-167 行);下单无 `data` 或无真实订单 id → `TRADING_EXCHANGE_ERROR`
126
+ (第 245、252 行,**不编 id**);单笔订单查询 → `TRADING_NOT_IMPLEMENTED`
127
+ (`src/index.ts:192-196`,显式拒绝而非返回编造订单)。
128
+ - **行情两法错误直报(零跨源回落)**:`getTicker` / `getKlines` 不做 `try/catch`
129
+ (`src/rest.ts:114-141`、`143-177`),签名失败、HTTP 非 2xx、请求抛错、业务码非 0 全部按上面的
130
+ 映射上报;没有任何回落分支,因此也不存在「回执里的行情到底来自哪个源」这个问题。
131
+ - **无本地超时**:`requestJson` 不设 `AbortController`/超时(`src/rest.ts:71-112`),
132
+ 上游无响应时请求没有连接器侧上限(与 futu 的 10 s 不同)。
133
+ - **服务类字段用 TS 编译期 `private`**(禁 ECMAScript `#`,cordis realm 代理会炸)。
13
134
 
14
- ## 结构
135
+ ## 能力面
15
136
 
16
- - `src/rest.ts` —— 错误载体(TradingServiceError)+ 通用请求管线(超时/错误映射/
17
- 模拟盘头/注入 fetch)直接可用;签名与端点 TODO。
18
- - `src/index.ts` —— Config(enabled/env/dryRun/liveTrading + 三 ref 凭证组)、
19
- 闸门三态判定、凭证解析(BYOK + 环境变量回落)、MarketDataService/TradeService 骨架、
20
- 7 个市场前缀工具注册(duplicate-safe)、apply(enabled=false 静默退出)。
21
- - `test/template.test.ts` —— 结构冒烟 7 用例。
137
+ 行情面服务方法(`src/index.ts:64-93`):
22
138
 
23
- ## Token
139
+ | 服务方法 | 状态 |
140
+ |---|---|
141
+ | `getTicker` | 有(`src/index.ts:76-78`;上游失败即抛错,不回落,`src/rest.ts:114-141`) |
142
+ | `getKlines` | 有(`src/index.ts:80-82`;上游失败即抛错,不回落,`src/rest.ts:143-177`) |
143
+ | `subscribeTicker` | 有(快照轮询,缺省 5000 ms、下限 1000 ms,`src/index.ts:84-92`) |
144
+ | `listInstruments` / `getTickers` / `getOrderbook` / `getFundamentals` / `getDerivatives` / `getRecentTrades` | 缺席 |
24
145
 
25
- `tiger`(slug)/ `Tiger`(title)/ `TIGER`(ref 前缀)/
26
- `crypto`(工具前缀)/ `Crypto`(服务键 infix)。token 未展开也可编译;
27
- 生成器保证全部替换。
146
+ 交易面服务方法(`src/index.ts:95-205`):
28
147
 
29
- ## 注意(勿改的坑)
148
+ | 服务方法 | 状态 | 有对应工具 |
149
+ |---|---|---|
150
+ | `placeOrder` | 有(三态闸门 + 意图台账,`src/index.ts:129-172`) | `hk_place_order` |
151
+ | `cancelOrder` | 有(与下单同门槛,`src/index.ts:174-184`) | `hk_cancel_order` |
152
+ | `getBalance` | 有(`src/index.ts:125-127`) | `hk_get_balance` |
153
+ | `getPositions` | 有(POST `method=positions`,`src/index.ts:198-200`) | **无工具** |
154
+ | `getOrders` | 恒返回 `[]`(`src/index.ts:202-204`) | 无 |
155
+ | `getOrder` | **显式抛 `TRADING_NOT_IMPLEMENTED`**(`src/index.ts:191-196`:本切片没有已核对的订单状态契约,不返回编造订单) | 无 |
156
+ | `getBalances` / `listOpenOrders` / `listTradeFills` / `setLeverage` | 缺席 | 无 |
157
+
158
+ 注册的工具 5 个(`src/index.ts:231-343`,同名已存在则跳过,第 233-235 行):
159
+
160
+ | 工具 | 注册位置 | 说明 |
161
+ |---|---|---|
162
+ | `hk_get_ticker` | `src/index.ts:237-245` | 最新价与成交量;上游失败即抛错,没有回落来源 |
163
+ | `hk_get_klines` | `src/index.ts:247-260` | interval 用 8 词汇枚举,缺省 `1d`;limit 缺省 100 |
164
+ | `hk_place_order` | `src/index.ts:262-320` | `dryRun !== false` 时回本地模拟回执(`sim-tiger-` 前缀);否则要求 `liveTrading` |
165
+ | `hk_cancel_order` | `src/index.ts:322-331` | 参数名 `ordId`;实盘动作,同样需 `liveTrading` 且 `dryRun=false` |
166
+ | `hk_get_balance` | `src/index.ts:333-342` | 账户余额(返回形状与 `AccountBalance` 契约不一致,见「已知边界」) |
167
+
168
+ 注意本包**没有任何工具名带 `crypto` 前缀**:模板残留(旧 README 的 Token 段)写的是
169
+ `crypto`/`Crypto`,实际工具前缀是 `hk`、服务键 infix 是 `Hk`(`src/index.ts:61-62`、`238`)。
170
+
171
+ ## 已知边界
172
+
173
+ - **未实证项**:全部端点、签名是否被上游接受、真机错误码分布,均未经真机核对。
174
+ `spikes/impl-service-seam-gate/NET-VERIFY.md:36` 明确记录未对券商网关做真实下单验证。
175
+ - **行情失败即失败**:`getTicker` / `getKlines` 只打老虎网关,网关不可达、HTTP 非 2xx 或业务码非 0
176
+ 时直接抛错(`src/rest.ts:114-141`、`143-177`),不返回任何来源不明或伪造的行情——旧实现在
177
+ 业务码非 0 时返回 `price: 0`、在网络失败时返回腾讯公开源数据,这两条路径都已删除。
178
+ 本包行情面的可用性因此完全等于老虎网关的可用性。
179
+ - **符号形态原样交给上游**:非港股形态(如 `AAPL`)按 `toTigerSymbol` 的结果原样进
180
+ `biz_content.symbols`(`src/rest.ts:26-37`),是否被网关接受由上游决定——**该后果未实证**。
181
+ - **非日线周期无本地兜底**:`interval` 只进 `period` 字段(`src/rest.ts:149`),上游不支持即
182
+ 失败上报;请求 `1m` 不会再拿到写死日线的本地回落 bar。
183
+ - **`env` 不改变地址**:`env: 'demo'` 只进意图台账作用域(`src/index.ts:118-122`),
184
+ 基地址恒为 `openapi.itiger.com/gateway`(`src/rest.ts:64`)。即实盘放行后,
185
+ `env` 仍为 `demo` 的部署也会把订单发往**生产**网关。
186
+ - **`tigerId` 缺省静默占位**:缺 `tigerId` 时 `requestJson` 用字面量 `'demo'`
187
+ 当 `tiger_id`(`src/rest.ts:72`),不报错也不提示——配置缺口会以「上游拒绝」的形态出现。
188
+ - **凭证缺失不发 sign**:`privateKey` 缺失时 `requestJson` 只是不加 `sign` 参数
189
+ (`src/rest.ts:86-92`),请求照发;只有资产与交易面方法做前置检查并抛
190
+ `TRADING_AUTH_FAILED`(`src/rest.ts:180-182`、`202-204`、`221-223`、`272-274`)。
191
+ - **`getBalance` / `getPositions` 返回形状与契约不符**:`getBalance` 返回
192
+ `{currency, available, total}`(`src/rest.ts:194-198`),而 `AccountBalance` 是
193
+ `{asset, free, locked}`(`packages/api/src/index.ts:578-582`);`getPositions` 每行返回
194
+ `{symbol, quantity, entryPrice, unrealizedPnl}`(`src/rest.ts:212-217`),而 `Position` 要求
195
+ `{symbol, side, size, entryPrice, timestamp}`(`packages/api/src/index.ts:499-520`)。
196
+ 消费方按契约字段读会读到 `undefined`(**运行期表现未实证**)。
197
+ - **`getOrder` 显式拒绝**:`src/index.ts:191-196` 抛 `TRADING_NOT_IMPLEMENTED`,
198
+ 不返回编造订单(与 futu 同位置返回常量占位对象的做法相反)。
199
+ - **无本地超时**(`src/rest.ts:71-112`)。
200
+ - **`getOrders` 恒空**(`src/index.ts:202-204`):空数组在消费方读作「无挂单」,而不是「本包不支持」。
201
+ - **持仓面有服务、无工具**:`getPositions` 只经 `tradingHkTrade` 服务可达(`src/index.ts:198-200`),
202
+ 工具面没有对应的 `hk_get_positions`。
203
+ - **交易面不进交易注册表**:`src/dataplane.ts` 全文件没有 `tradingTradeRegistry`,注册表里没有
204
+ `(hk, tiger)` 交易条目;读该注册表的消费方看不到本包的交易服务。
205
+ - **设置页市场面靠手工对齐**:`PROVIDER_LABELS` 里 tiger 的 `markets` 是 `['hk']`
206
+ (`trading-settings-controller.ts:69`),与本包 `MARKET = 'hk'`(`src/dataplane.ts:25`)、
207
+ `routeAllows(ctx, config, 'hk')`(`src/index.ts:218`)一致;该字段是控制器里的字面量,
208
+ `trading-settings-controller.ts:56-57` 的注释要求它等于 dataplane 注册面。
209
+ - **4 位 `.HK` 不补零**:`toTigerSymbol` 对含 `.` 的输入只取点前段(`src/rest.ts:29-32`),
210
+ `0700.HK` 与 `00700.HK` 归一结果不同(前者保持 4 位)。
211
+
212
+ ## 合规记录
213
+
214
+ **老虎 OpenAPI 是券商官方接口,走 BYOK**:`tigerId` 与 RSA 私钥由用户自备,私钥只进
215
+ `privateKeyRef` 环境变量名(`src/index.ts:57`、`220`),仓库永不内置、不落日志(铁律 #3/#5);
216
+ 本仓不缓存、不再分发任何行情或账户数据。使用边界以老虎开放平台的服务条款与用户自身的
217
+ 行情/交易权限为准。
218
+
219
+ **行情面零跨源回落**:`getTicker`/`getKlines` 只打老虎网关(`src/rest.ts:120`、`149`),上游失败时
220
+ 抛错而不是改打第三方公开源;本包不内置、也不回落任何非老虎行情源。腾讯公开端点
221
+ (`qt.gtimg.cn` / `web.ifzq.gtimg.cn`)只能由用户显式选择 `connector-tencent` 得到,其非官方、
222
+ 无 key、无 SLA 的边界与合规口径见 [connector-tencent 的合规记录](../connector-tencent/README.md)。
223
+
224
+ **不发 venue client key**:官方 `user_mark` 的键名尚未与官方 biz-content 逐字对齐,按
225
+ 「不猜字段」纪律不加(`src/index.ts:99-107`);`remark` 类字段不构成 venue 幂等,
226
+ 「同 ID 只下一单」由本地意图台账保证(`src/index.ts:162-163`)。
227
+
228
+ **实盘需人工签署授权**:`liveTrading` 只是非权威镜像,权威是 `@dshtrading/authority` 平面;
229
+ 未签署时下单与撤单都在服务缝结构化拒绝(`src/index.ts:133-139`、`177-182`)。
230
+
231
+ `package.json` 的 license 是 `PolyForm-Noncommercial-1.0.0`(非商业免费,商用需书面授权)。
232
+
233
+ ## 出网验证
234
+
235
+ 本包暂无已提交的独立出网验证脚本:`spikes/` 下没有 `impl-tiger*` 目录,
236
+ `spikes/impl-service-seam-gate/net-verify.mjs` 只覆盖 OKX,`spikes/verify-connectors-suite/`
237
+ 只提交了 `EVIDENCE.md`(无脚本)。可复跑的最小入口(不触网):
238
+
239
+ ```sh
240
+ pnpm --filter @dshtrading/connector-tiger test # 离线契约测试(契约化假网关,无真实网络)
241
+ ```
30
242
 
31
- - 服务类用 TS 编译期 `private`(禁 ECMAScript `#`,realm 代理会炸)。
32
- - 工具名 = 市场前缀 + 语义词(不带交易所名)——base 闸门正则只认市场前缀。
33
- - 行 id = `dsh-trading-<market>-connector-<slug>`,全仓唯一(insert-only 铁律 #1)。
34
- - 凭证只进 ref,绝不内置/落日志(铁律 #3/#5)。
243
+ 真机复跑需要用户自备老虎凭证(`TIGER_ID` / `TIGER_PRIVATE_KEY` / `TIGER_ACCOUNT_ID`),
244
+ 自写探针直调 `TigerRestClient`;本仓未提交任何老虎网关的请求或响应记录。本包没有可核对的
245
+ 跨源回落链路:行情面失败即抛错,失败路径由 `test/market-data-fail-closed.test.ts` 覆盖
246
+ (假网关记录每次请求的 method 与 URL,断言没有请求落到老虎网关之外的地址)——那同样是离线
247
+ 用例,不构成真机证据。
@@ -2,6 +2,6 @@ import { Config } from "./index.js";
2
2
  import { Context } from "@deepseek-ai/cordis";
3
3
  //#region src/dataplane.d.ts
4
4
  declare const inject: string[];
5
- declare function apply(ctx: Context, config: Config): void;
5
+ declare function apply(ctx: Context, config?: Config): void;
6
6
  //#endregion
7
- export { apply, inject };
7
+ export { Config, apply, inject };
package/lib/dataplane.js CHANGED
@@ -1,28 +1,29 @@
1
- import { TRADING_HK_MARKET_DATA_KEY, TigerMarketDataService } from "./index.js";
1
+ import { Config, ROUTER_PROVIDER, TRADING_HK_MARKET_DATA_KEY, TigerMarketDataService, resolvePrivateKey } from "./index.js";
2
2
  //#region src/dataplane.ts
3
3
  const inject = [];
4
+ /** schema 的运行时可调用形态(校验即补默认值);手工构造 ctx 不传 config 时按 schema 默认值处理。 */
5
+ const withDefaults = (value) => Config(value);
4
6
  function resolveMarketDataRegistry(ctx) {
5
7
  const candidate = ctx.get?.("tradingMarketDataRegistry", false);
6
8
  return candidate !== void 0 ? candidate : void 0;
7
9
  }
8
- const ROUTER_PROVIDER = "tiger";
9
10
  const MARKET = "hk";
10
- function apply(ctx, config) {
11
+ function apply(ctx, config = withDefaults(void 0)) {
11
12
  if (!config.enabled) return;
13
+ const privateKey = resolvePrivateKey(ctx, config);
14
+ const credentials = {
15
+ tigerId: config.tigerId,
16
+ accountId: config.accountId,
17
+ ...privateKey === void 0 ? {} : { privateKey }
18
+ };
12
19
  const registry = resolveMarketDataRegistry(ctx);
13
20
  if (registry === void 0) {
14
- new TigerMarketDataService(ctx, {
15
- tigerId: config.tigerId,
16
- accountId: config.accountId
17
- });
21
+ new TigerMarketDataService(ctx, credentials);
18
22
  return;
19
23
  }
20
24
  const inner = ctx.isolate(TRADING_HK_MARKET_DATA_KEY);
21
- const service = new TigerMarketDataService(inner, {
22
- tigerId: config.tigerId,
23
- accountId: config.accountId
24
- });
25
+ const service = new TigerMarketDataService(inner, credentials);
25
26
  ctx.effect(() => registry.register(MARKET, ROUTER_PROVIDER, service));
26
27
  }
27
28
  //#endregion
28
- export { apply, inject };
29
+ export { Config, apply, inject };
package/lib/index.d.ts CHANGED
@@ -29,20 +29,40 @@ declare class TigerTradeService extends Service implements TradeService {
29
29
  private readonly client;
30
30
  /** 插件配置(服务缝闸门 P0:dryRun 强制模拟 / liveTrading 非权威镜像——实盘授权由 @dshtrading/authority 的人工签署平面裁定)。 */
31
31
  private readonly config;
32
+ /**
33
+ * 订单意图台账(实盘幂等前提;写 agent 自己的 trading home,不写授权平面)。
34
+ *
35
+ * **不发 venue client key**:Tiger 官方 OpenAPI 的 `trade_order` 业务参数里确实有
36
+ * `user_mark`(官方 python SDK 的 `PlaceModifyOrderParams.user_mark`),但本连接器现在
37
+ * 发的是 camelCase 键(`orderType`/`totalQuantity`/`limitPrice`),与官方模型的
38
+ * snake_case 业务参数**尚未逐字对齐**——按「不猜字段」纪律,实测对齐之前不新增字段;
39
+ * 幂等完全由本地台账保证(台账已保证未知不重发)。
40
+ */
41
+ private readonly intents;
32
42
  constructor(ctx: Context, options: TigerRestOptions & {
33
43
  config: Config;
34
44
  }, serviceName?: string);
35
45
  getBalance(): Promise<AccountBalance$1>;
36
46
  placeOrder(order: OrderRequest): Promise<Order$1>;
37
- cancelOrder(orderId: string): Promise<{
38
- orderId: string;
39
- status: 'canceled';
40
- }>;
47
+ cancelOrder(orderId: string, _symbol?: string): Promise<void>;
48
+ /**
49
+ * 单笔订单查询:Tiger 本切片的 REST 面只覆盖 trade_order / cancel_order,
50
+ * 没有已核对的订单状态契约。按仓库惯例**显式拒绝**(TRADING_NOT_IMPLEMENTED),
51
+ * 绝不返回编造的订单——base 的账户工具与 GUI 会把返回值当交易所真值消费。
52
+ */
53
+ getOrder(_symbol: string, _id: string): Promise<Order$1>;
41
54
  getPositions(): Promise<Position$1[]>;
42
55
  getOrders(): Promise<Order$1[]>;
43
56
  }
44
57
  declare const ROUTER_PROVIDER = "tiger";
58
+ /**
59
+ * RSA 私钥解析:设置中心 `dshtrading.credentials.tiger.privateKey`(router `getCredential`)
60
+ * 优先、`process.env[config.privateKeyRef]` 兜底。值进 rest 层的 RSA2 签名
61
+ * (src/rest.ts 的 generateTigerSignature),设置页写的键不是纯承诺。
62
+ * tigerId 不走这里——它是非密账户标识,家是连接器 Config(`tigerId` / `TIGER_ID` 回落)。
63
+ */
64
+ declare function resolvePrivateKey(ctx: Context, config: Config): string | undefined;
45
65
  declare function routeAllows(ctx: Context, config: Config, market: string): boolean;
46
66
  declare function apply(ctx: Context, config: Config): void;
47
67
  //#endregion
48
- export { type AccountBalance, Config, INTERVAL_VOCABULARY, type Interval, type Kline, type Order, type Position, ROUTER_PROVIDER, TRADING_HK_MARKET_DATA_KEY, TRADING_HK_TRADE_KEY, type Ticker, TigerMarketDataService, TigerRestClient, TigerRestOptions, TigerTradeService, TradingServiceError, apply, generateTigerSignature, name, routeAllows, toTigerSymbol };
68
+ export { type AccountBalance, Config, INTERVAL_VOCABULARY, type Interval, type Kline, type Order, type Position, ROUTER_PROVIDER, TRADING_HK_MARKET_DATA_KEY, TRADING_HK_TRADE_KEY, type Ticker, TigerMarketDataService, TigerRestClient, TigerRestOptions, TigerTradeService, TradingServiceError, apply, generateTigerSignature, name, resolvePrivateKey, routeAllows, toTigerSymbol };
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { INTERVAL_VOCABULARY, TigerRestClient, TradingServiceError, generateTigerSignature, toTigerSymbol as toTigerSymbol$1 } from "./rest.js";
1
+ import { INTERVAL_VOCABULARY, TigerRestClient, TradingServiceError, generateTigerSignature, toTigerSymbol } from "./rest.js";
2
2
  import { liveTradingEnabled } from "@dshtrading/authority";
3
+ import { OrderIntentLedger, orderFromReceipt, receiptFromOrder } from "@dshtrading/authority/order-intents";
3
4
  import { Service } from "@deepseek-ai/cordis";
4
5
  import { defineTool } from "@deepseek-ai/dsh-tools";
5
6
  import Schema from "@deepseek-ai/schemastery";
@@ -46,10 +47,25 @@ var TigerTradeService = class extends Service {
46
47
  client;
47
48
  /** 插件配置(服务缝闸门 P0:dryRun 强制模拟 / liveTrading 非权威镜像——实盘授权由 @dshtrading/authority 的人工签署平面裁定)。 */
48
49
  config;
50
+ /**
51
+ * 订单意图台账(实盘幂等前提;写 agent 自己的 trading home,不写授权平面)。
52
+ *
53
+ * **不发 venue client key**:Tiger 官方 OpenAPI 的 `trade_order` 业务参数里确实有
54
+ * `user_mark`(官方 python SDK 的 `PlaceModifyOrderParams.user_mark`),但本连接器现在
55
+ * 发的是 camelCase 键(`orderType`/`totalQuantity`/`limitPrice`),与官方模型的
56
+ * snake_case 业务参数**尚未逐字对齐**——按「不猜字段」纪律,实测对齐之前不新增字段;
57
+ * 幂等完全由本地台账保证(台账已保证未知不重发)。
58
+ */
59
+ intents;
49
60
  constructor(ctx, options, serviceName = TRADING_HK_TRADE_KEY) {
50
61
  super(ctx, serviceName);
51
62
  this.client = new TigerRestClient(options);
52
63
  this.config = options.config;
64
+ this.intents = new OrderIntentLedger({
65
+ provider: "tiger",
66
+ environment: options.config.env,
67
+ account: options.config.accountId ?? "default"
68
+ });
53
69
  }
54
70
  async getBalance() {
55
71
  return this.client.getBalance();
@@ -68,11 +84,40 @@ var TigerTradeService = class extends Service {
68
84
  dryRun: true,
69
85
  timestamp: Date.now()
70
86
  };
71
- return this.client.placeOrder(void 0, order);
87
+ const payload = {
88
+ symbol: order.symbol,
89
+ side: order.side,
90
+ type: order.type,
91
+ quantity: order.quantity,
92
+ ...order.price !== void 0 ? { price: order.price } : {}
93
+ };
94
+ const decision = await this.intents.begin({
95
+ intentId: order.intentId,
96
+ payload
97
+ });
98
+ if (decision.action === "replay") return {
99
+ ...orderFromReceipt(decision.receipt),
100
+ status: decision.receipt.status
101
+ };
102
+ const placed = await this.client.placeOrder(void 0, order);
103
+ await this.intents.confirm({
104
+ intentId: decision.intentId,
105
+ payload,
106
+ receipt: receiptFromOrder(placed)
107
+ });
108
+ return placed;
72
109
  }
73
- async cancelOrder(orderId) {
110
+ async cancelOrder(orderId, _symbol) {
74
111
  if (!liveTradingEnabled(this.config.liveTrading) || this.config.dryRun) throw new TradingServiceError("TRADING_LIVE_TRADING_DISABLED", "Tiger TradeService.cancelOrder rejected at the service seam: cancel is a live action and requires a signed live-trading grant (see @dshtrading/authority) with dryRun=false.");
75
- return this.client.cancelOrder(void 0, orderId);
112
+ await this.client.cancelOrder(void 0, orderId);
113
+ }
114
+ /**
115
+ * 单笔订单查询:Tiger 本切片的 REST 面只覆盖 trade_order / cancel_order,
116
+ * 没有已核对的订单状态契约。按仓库惯例**显式拒绝**(TRADING_NOT_IMPLEMENTED),
117
+ * 绝不返回编造的订单——base 的账户工具与 GUI 会把返回值当交易所真值消费。
118
+ */
119
+ async getOrder(_symbol, _id) {
120
+ throw new TradingServiceError("TRADING_NOT_IMPLEMENTED", "Tiger: single-order lookup is not implemented in this slice (no verified order-status contract); do not treat a missing result as \"no such order\".");
76
121
  }
77
122
  async getPositions() {
78
123
  return this.client.getPositions();
@@ -82,6 +127,15 @@ var TigerTradeService = class extends Service {
82
127
  }
83
128
  };
84
129
  const ROUTER_PROVIDER = "tiger";
130
+ /**
131
+ * RSA 私钥解析:设置中心 `dshtrading.credentials.tiger.privateKey`(router `getCredential`)
132
+ * 优先、`process.env[config.privateKeyRef]` 兜底。值进 rest 层的 RSA2 签名
133
+ * (src/rest.ts 的 generateTigerSignature),设置页写的键不是纯承诺。
134
+ * tigerId 不走这里——它是非密账户标识,家是连接器 Config(`tigerId` / `TIGER_ID` 回落)。
135
+ */
136
+ function resolvePrivateKey(ctx, config) {
137
+ return (ctx.get?.("tradingMarketRouter", false))?.getCredential?.("tiger")?.privateKey || (config.privateKeyRef ? process.env[config.privateKeyRef] : void 0);
138
+ }
85
139
  function routeAllows(ctx, config, market) {
86
140
  if (!config.enabled) return false;
87
141
  const router = ctx.get?.("tradingMarketRouter", false);
@@ -91,16 +145,15 @@ function routeAllows(ctx, config, market) {
91
145
  function apply(ctx, config) {
92
146
  if (!config.enabled) return;
93
147
  if (!routeAllows(ctx, config, "hk")) return;
94
- const privateKey = config.privateKeyRef ? process.env[config.privateKeyRef] : void 0;
95
- const marketData = new TigerMarketDataService(ctx, {
96
- tigerId: config.tigerId,
97
- accountId: config.accountId,
98
- privateKey
99
- });
148
+ const privateKey = resolvePrivateKey(ctx, config);
149
+ const credentials = {
150
+ ...config.tigerId === void 0 ? {} : { tigerId: config.tigerId },
151
+ ...config.accountId === void 0 ? {} : { accountId: config.accountId },
152
+ ...privateKey === void 0 ? {} : { privateKey }
153
+ };
154
+ const marketData = new TigerMarketDataService(ctx, credentials);
100
155
  const trade = new TigerTradeService(ctx, {
101
- tigerId: config.tigerId,
102
- accountId: config.accountId,
103
- privateKey,
156
+ ...credentials,
104
157
  config
105
158
  });
106
159
  ctx.inject(["tools"], (ctx) => {
@@ -194,6 +247,10 @@ function apply(ctx, config) {
194
247
  type: "boolean",
195
248
  default: true,
196
249
  description: "Dry run"
250
+ },
251
+ intentId: {
252
+ type: "string",
253
+ description: "Stable idempotency id, required when dryRun=false. Same id + same request returns the recorded receipt and never places a second order; a different payload/provider/environment/account under the same id is rejected (TRADING_INTENT_CONFLICT); an unresolved id stays blocked as unknown until reconciled."
197
254
  }
198
255
  },
199
256
  output: {
@@ -233,7 +290,8 @@ function apply(ctx, config) {
233
290
  side: args.side,
234
291
  type: args.type,
235
292
  quantity: args.quantity,
236
- price: args.price,
293
+ ...args.price === void 0 ? {} : { price: args.price },
294
+ ...typeof args.intentId === "string" ? { intentId: args.intentId } : {},
237
295
  dryRun: false
238
296
  });
239
297
  return JSON.stringify(order);
@@ -278,4 +336,4 @@ function apply(ctx, config) {
278
336
  });
279
337
  }
280
338
  //#endregion
281
- export { Config, INTERVAL_VOCABULARY, ROUTER_PROVIDER, TRADING_HK_MARKET_DATA_KEY, TRADING_HK_TRADE_KEY, TigerMarketDataService, TigerRestClient, TigerTradeService, TradingServiceError, apply, generateTigerSignature, name, routeAllows, toTigerSymbol$1 as toTigerSymbol };
339
+ export { Config, INTERVAL_VOCABULARY, ROUTER_PROVIDER, TRADING_HK_MARKET_DATA_KEY, TRADING_HK_TRADE_KEY, TigerMarketDataService, TigerRestClient, TigerTradeService, TradingServiceError, apply, generateTigerSignature, name, resolvePrivateKey, routeAllows, toTigerSymbol };
package/lib/rest.js CHANGED
@@ -88,66 +88,38 @@ var TigerRestClient = class {
88
88
  }
89
89
  async getTicker(symbol) {
90
90
  const { symbol: sym, canonical } = toTigerSymbol(symbol);
91
- try {
92
- const data = await this.requestJson({ symbols: [sym] }, "quote_real_time");
93
- if (data.code === 0 && Array.isArray(data.data) && data.data.length > 0) {
94
- const row = data.data[0];
95
- return {
96
- symbol: canonical,
97
- price: row.latestPrice,
98
- volume: row.volume,
99
- timestamp: row.timestamp
100
- };
101
- }
102
- } catch {
103
- const hkCode = `hk${sym.padStart(5, "0")}`;
104
- const parts = (await (await this.fetchImpl(`https://qt.gtimg.cn/q=${hkCode}`)).text()).split("~");
105
- return {
106
- symbol: canonical,
107
- price: parseFloat(parts[3] || "0"),
108
- timestamp: Date.now()
109
- };
110
- }
91
+ const data = await this.requestJson({ symbols: [sym] }, "quote_real_time");
92
+ if (data.code !== 0) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger getTicker failed: ${data.message ?? `code ${data.code}`}`);
93
+ const row = Array.isArray(data.data) ? data.data[0] : void 0;
94
+ if (!row || !Number.isFinite(row.latestPrice) || row.latestPrice <= 0) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger getTicker: response carries no usable quote for ${canonical} (${JSON.stringify(data)})`);
111
95
  return {
112
96
  symbol: canonical,
113
- price: 0,
114
- timestamp: Date.now()
97
+ price: row.latestPrice,
98
+ volume: row.volume,
99
+ timestamp: row.timestamp
115
100
  };
116
101
  }
117
102
  async getKlines(symbol, interval = "1d", limit = 100) {
118
- const { symbol: sym, canonical } = toTigerSymbol(symbol);
119
- try {
120
- const items = (await this.requestJson({
121
- symbols: [sym],
122
- period: interval,
123
- limit
124
- }, "kline_quote")).data?.[0]?.items;
125
- if (Array.isArray(items)) return items.map((r) => ({
126
- openTime: r.time,
127
- open: r.open,
128
- high: r.high,
129
- low: r.low,
130
- close: r.close,
131
- volume: r.volume,
132
- closeTime: r.time + 864e5 - 1
133
- }));
134
- } catch {
135
- const hkCode = `hk${sym.padStart(5, "0")}`;
136
- const d = await (await this.fetchImpl(`https://web.ifzq.gtimg.cn/appstock/app/hkfqkline/get?param=${hkCode},day,,,${limit},qfq`)).json();
137
- return (d.data?.[hkCode]?.qfqday ?? d.data?.[hkCode]?.day ?? []).map((item) => {
138
- const openTime = new Date(item[0].replace(/-/g, "/")).getTime();
139
- return {
140
- openTime,
141
- open: parseFloat(item[1]),
142
- close: parseFloat(item[2]),
143
- high: parseFloat(item[3]),
144
- low: parseFloat(item[4]),
145
- volume: parseFloat(item[5]),
146
- closeTime: openTime + 864e5 - 1
147
- };
148
- });
149
- }
150
- return [];
103
+ const { symbol: sym } = toTigerSymbol(symbol);
104
+ const data = await this.requestJson({
105
+ symbols: [sym],
106
+ period: interval,
107
+ limit
108
+ }, "kline_quote");
109
+ if (data.code !== 0) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger getKlines failed: ${data.message ?? `code ${data.code}`}`);
110
+ if (!Array.isArray(data.data)) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger getKlines: unrecognizable payload (${JSON.stringify(data)})`);
111
+ if (data.data.length === 0) return [];
112
+ const items = data.data[0]?.items;
113
+ if (!Array.isArray(items)) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger getKlines: response carries no items array (${JSON.stringify(data)})`);
114
+ return items.map((r) => ({
115
+ openTime: r.time,
116
+ open: r.open,
117
+ high: r.high,
118
+ low: r.low,
119
+ close: r.close,
120
+ volume: r.volume,
121
+ closeTime: r.time + 864e5 - 1
122
+ }));
151
123
  }
152
124
  async getBalance() {
153
125
  if (!this.privateKey) throw new TradingServiceError("TRADING_AUTH_FAILED", "Tiger: privateKey (RSA PEM) is required for getBalance");
@@ -182,14 +154,17 @@ var TigerRestClient = class {
182
154
  totalQuantity: req.quantity,
183
155
  limitPrice: req.price
184
156
  }, "trade_order");
185
- if (res.code !== 0 || !res.data?.id && !res.data?.orderId) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger placeOrder failed: ${res.message ?? `code ${res.code}`}`);
186
- const id = res.data.id ?? res.data.orderId ?? `tiger-${Date.now()}`;
157
+ if (res.code !== 0) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger placeOrder failed: ${res.message ?? `code ${res.code}`}`);
158
+ const payload = res.data;
159
+ if (payload === void 0) throw new TradingServiceError("TRADING_EXCHANGE_ERROR", "Tiger placeOrder: response carries no data payload — the order is not confirmed");
160
+ const id = payload.id ?? payload.orderId;
161
+ if (typeof id !== "string" || id.trim() === "") throw new TradingServiceError("TRADING_EXCHANGE_ERROR", `Tiger placeOrder: response carries no real order id (${JSON.stringify(res.data)}) — the order is not confirmed`);
187
162
  return {
188
- id: String(id),
163
+ id,
189
164
  symbol: canonical,
190
165
  side: req.side,
191
166
  type: req.type,
192
- status: res.data.status?.toLowerCase() ?? "new",
167
+ status: payload.status?.toLowerCase() ?? "new",
193
168
  quantity: req.quantity,
194
169
  price: req.price ?? 0,
195
170
  dryRun: false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dshtrading/connector-tiger",
3
- "description": "【模板】交易所连接器插件脚手架:Tiger(tiger)v2 插件骨架 —— 市场数据服务 + 交易服务 + 三态闸门 + BYOK 凭证 ref。生成器:node scripts/new-connector.mjs --slug <exchange>",
4
- "version": "0.6.0",
3
+ "description": "老虎证券(Tiger Trade)港股连接器:直连券商 OpenAPI 网关(openapi.itiger.com/gateway,RSA2 签名 + BYOK 私钥 ref),行情面 fail-closed(上游失败即抛结构化错误,零跨源回落);实现 MarketDataService/TradeService 与 5 个 hk_* 工具;dry-run 缺省、liveTrading 只是非权威镜像(放行归 @dshtrading/authority 人工签署)。",
4
+ "version": "0.6.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -21,12 +21,12 @@
21
21
  ],
22
22
  "license": "PolyForm-Noncommercial-1.0.0",
23
23
  "dependencies": {
24
- "@dshtrading/authority": "^0.6.0",
25
- "@dshtrading/api": "^0.6.0"
24
+ "@dshtrading/authority": "^0.6.2",
25
+ "@dshtrading/api": "^0.6.2"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@deepseek-ai/cordis": ">=4.0.0",
29
- "@deepseek-ai/dsh-tools": ">=0.2.0-rc.2",
29
+ "@deepseek-ai/dsh-tools": ">=0.2.1-rc.2",
30
30
  "@deepseek-ai/schemastery": ">=3.18.0"
31
31
  },
32
32
  "devDependencies": {