@dshtrading/strategies 0.6.1 → 0.6.3

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.
@@ -1,49 +1,50 @@
1
- import { SKIP_WRITE, transactStore } from "@dshtrading/dsh-home";
2
- import { readFile } from "node:fs/promises";
1
+ import { SKIP_WRITE, createRecordStoreAdapter, readStoreText } from "@dshtrading/dsh-home";
3
2
  //#region src/builtin-tombstones-fs.ts
4
3
  /**
5
4
  * 文件持久化版内置策略/选股器墓碑存储(Node.js 宿主侧使用,
6
5
  * 落 ~/.dsh/strategies/builtin-tombstones.json,形状 { deleted: string[] })。
7
6
  *
8
- * 整表持久化经 `@dshtrading/dsh-home` 的 {@link transactStore}——跨进程排他锁 +
9
- * 锁内新鲜读盘 + 原子写(2026-10-09 多进程互相覆盖事故修复):两进程各自删除的
10
- * 内置策略都留下来,后到者不会用陈旧墓碑集合覆盖。旧宿主从未写过该文件,
11
- * 无迁移问题(ENOENT 视为空表)。
7
+ * 读写骨架经 `@dshtrading/dsh-home` 的 {@link createRecordStoreAdapter}——跨进程排他锁 +
8
+ * 锁内新鲜读盘 + 原子写 + 落盘成功后才发布缓存(2026-10-09 多进程互相覆盖事故修复):
9
+ * 两进程各自删除的内置策略都留下来,后到者不会用陈旧墓碑集合覆盖。旧宿主从未写过该
10
+ * 文件,无迁移问题(ENOENT 视为空表)。
12
11
  */
13
12
  const LOG_PREFIX = "[dsh-trading/strategies] failed to atomic flush builtin tombstones to";
14
13
  function createFileBuiltinTombstonesStore(filePath) {
15
- let cache = null;
16
- /** 新鲜读盘(坏 JSON 降级空表);供 load 与锁内重读共用。 */
17
- async function readFromDisk() {
14
+ /** 读盘/解析失败的日志前缀(坏 JSON 与非 ENOENT 读失败同一条,与既有 load 口径逐字一致)。 */
15
+ const READ_FAILED = `[dsh-trading/strategies] failed to read builtin tombstones from ${filePath}:`;
16
+ /**
17
+ * 文本 → 墓碑集合:只收容器里的 `deleted` 字符串数组,去空串、去重;坏 JSON 降级空集
18
+ * 并打日志,坏形(容器不是 `{ deleted: [] }`)静默降级空集。Set 形态是本 store 的差异。
19
+ */
20
+ function decode(text) {
21
+ if (text === void 0) return /* @__PURE__ */ new Set();
18
22
  try {
19
- const content = await readFile(filePath, "utf8");
20
- const parsed = JSON.parse(content);
23
+ const parsed = JSON.parse(text);
21
24
  const set = /* @__PURE__ */ new Set();
22
25
  if (Array.isArray(parsed?.deleted)) {
23
26
  for (const id of parsed.deleted) if (typeof id === "string" && id) set.add(id);
24
27
  }
25
28
  return set;
26
29
  } catch (err) {
27
- if (err?.code !== "ENOENT") console.error(`[dsh-trading/strategies] failed to read builtin tombstones from ${filePath}:`, err);
30
+ console.error(READ_FAILED, err);
28
31
  return /* @__PURE__ */ new Set();
29
32
  }
30
33
  }
31
- async function load() {
32
- if (cache !== null) return cache;
33
- cache = await readFromDisk();
34
- return cache;
35
- }
36
- async function commit(mutate) {
37
- cache = await transactStore(filePath, readFromDisk, mutate, (set) => ({ deleted: [...set] }), LOG_PREFIX);
38
- return cache;
39
- }
34
+ const store = createRecordStoreAdapter({
35
+ filePath,
36
+ logPrefix: LOG_PREFIX,
37
+ readFromDisk: () => readStoreText(filePath, READ_FAILED),
38
+ decode,
39
+ encode: (set) => ({ deleted: [...set] })
40
+ });
40
41
  return {
41
42
  async list() {
42
- return [...await load()];
43
+ return [...await store.load()];
43
44
  },
44
45
  async add(id) {
45
46
  let fresh = false;
46
- await commit((onDisk) => {
47
+ await store.commit((onDisk) => {
47
48
  fresh = !onDisk.has(id);
48
49
  if (fresh) onDisk.add(id);
49
50
  return fresh ? onDisk : SKIP_WRITE;
@@ -52,7 +53,7 @@ function createFileBuiltinTombstonesStore(filePath) {
52
53
  },
53
54
  async remove(id) {
54
55
  let existed = false;
55
- await commit((onDisk) => {
56
+ await store.commit((onDisk) => {
56
57
  existed = onDisk.delete(id);
57
58
  return existed ? onDisk : SKIP_WRITE;
58
59
  });
package/lib/custom-fs.js CHANGED
@@ -1,51 +1,51 @@
1
1
  import { dirname } from "node:path";
2
- import { SKIP_WRITE, transactStore } from "@dshtrading/dsh-home";
3
- import { appendFile, mkdir, readFile } from "node:fs/promises";
2
+ import { SKIP_WRITE, createRecordStoreAdapter, readStoreText } from "@dshtrading/dsh-home";
3
+ import { appendFile, mkdir } from "node:fs/promises";
4
4
  //#region src/custom-fs.ts
5
5
  /**
6
6
  * 文件持久化版自定义策略存储(Node.js 宿主侧使用,落 ~/.dsh/strategies/custom.json)。
7
7
  *
8
- * 整表持久化经 `@dshtrading/dsh-home` 的 {@link transactStore}——跨进程排他锁 +
9
- * 锁内新鲜读盘 + 原子写(2026-10-09 多进程互相覆盖事故修复;issue #31 落盘形状不变)。
8
+ * 读写骨架经 `@dshtrading/dsh-home` 的 {@link createRecordStoreAdapter}——跨进程排他锁 +
9
+ * 锁内新鲜读盘 + 原子写 + 落盘成功后才发布缓存(2026-10-09 多进程互相覆盖事故修复;
10
+ * issue #31 落盘形状不变)。
10
11
  * remove(archive=true) 会先把被删记录归档到 sidecar 文件(<path>.archive.jsonl,
11
12
  * JSONL 追加)——策略管理(覆盖 + 墓碑)丢弃用户 override 时可找回(2026-09-07)。
12
13
  */
13
14
  const LOG_PREFIX = "[dsh-trading/strategies] failed to atomic flush custom strategies to";
14
15
  function createFileCustomStrategyStore(filePath) {
15
- let cache = null;
16
- /** 新鲜读盘(坏 JSON/坏形同既有 load 口径降级空表);供 load 与锁内重读共用。 */
17
- async function readFromDisk() {
16
+ /** 读盘/解析失败的日志前缀(坏 JSON 与非 ENOENT 读失败同一条,与既有 load 口径逐字一致)。 */
17
+ const READ_FAILED = `[dsh-trading/strategies] failed to read custom strategies from ${filePath}:`;
18
+ /** 文本 → 表:坏 JSON 降级空表并打日志,坏形状(非数组 / 缺 id)静默丢弃(不外包)。 */
19
+ function decode(text) {
20
+ if (text === void 0) return /* @__PURE__ */ new Map();
18
21
  try {
19
- const content = await readFile(filePath, "utf8");
20
- const parsed = JSON.parse(content);
22
+ const parsed = JSON.parse(text);
21
23
  const map = /* @__PURE__ */ new Map();
22
24
  if (Array.isArray(parsed)) {
23
25
  for (const item of parsed) if (item && typeof item.id === "string") map.set(item.id, item);
24
26
  }
25
27
  return map;
26
28
  } catch (err) {
27
- if (err?.code !== "ENOENT") console.error(`[dsh-trading/strategies] failed to read custom strategies from ${filePath}:`, err);
29
+ console.error(READ_FAILED, err);
28
30
  return /* @__PURE__ */ new Map();
29
31
  }
30
32
  }
31
- async function load() {
32
- if (cache !== null) return cache;
33
- cache = await readFromDisk();
34
- return cache;
35
- }
36
- async function commit(mutate) {
37
- cache = await transactStore(filePath, readFromDisk, mutate, (table) => [...table.values()], LOG_PREFIX);
38
- return cache;
39
- }
33
+ const store = createRecordStoreAdapter({
34
+ filePath,
35
+ logPrefix: LOG_PREFIX,
36
+ readFromDisk: () => readStoreText(filePath, READ_FAILED),
37
+ decode,
38
+ encode: (table) => [...table.values()]
39
+ });
40
40
  return {
41
41
  async list() {
42
- return [...(await load()).values()];
42
+ return [...(await store.load()).values()];
43
43
  },
44
44
  async get(id) {
45
- return (await load()).get(id);
45
+ return (await store.load()).get(id);
46
46
  },
47
47
  async save(record) {
48
- await commit((onDisk) => {
48
+ await store.commit((onDisk) => {
49
49
  onDisk.set(record.id, { ...record });
50
50
  return onDisk;
51
51
  });
@@ -53,7 +53,7 @@ function createFileCustomStrategyStore(filePath) {
53
53
  async remove(id, archive = false) {
54
54
  let existed = false;
55
55
  let removedRecord;
56
- await commit((onDisk) => {
56
+ await store.commit((onDisk) => {
57
57
  removedRecord = onDisk.get(id);
58
58
  existed = onDisk.delete(id);
59
59
  return existed ? onDisk : SKIP_WRITE;
@@ -1,52 +1,51 @@
1
1
  import { dirname } from "node:path";
2
- import { SKIP_WRITE, transactStore } from "@dshtrading/dsh-home";
3
- import { appendFile, mkdir, readFile } from "node:fs/promises";
2
+ import { SKIP_WRITE, createRecordStoreAdapter, readStoreText } from "@dshtrading/dsh-home";
3
+ import { appendFile, mkdir } from "node:fs/promises";
4
4
  //#region src/custom-screener-fs.ts
5
5
  /**
6
6
  * 文件持久化版自定义选股器存储(Node.js 宿主侧使用,
7
7
  * 落 ~/.dsh/strategies/custom-screeners.json)。
8
8
  *
9
- * 整表持久化经 `@dshtrading/dsh-home` 的 {@link transactStore}——跨进程排他锁 +
10
- * 锁内新鲜读盘 + 原子写(2026-10-09 多进程互相覆盖事故修复)。
9
+ * 读写骨架经 `@dshtrading/dsh-home` 的 {@link createRecordStoreAdapter}——跨进程排他锁 +
10
+ * 锁内新鲜读盘 + 原子写 + 落盘成功后才发布缓存(2026-10-09 多进程互相覆盖事故修复)。
11
11
  * remove(archive=true) 先把被删记录归档到 sidecar(<path>.archive.jsonl,JSONL
12
12
  * 追加)——选股器管理丢弃用户 override 时可找回(2026-09-07 审查补强)。
13
13
  */
14
14
  const LOG_PREFIX = "[dsh-trading/strategies] failed to atomic flush custom screeners to";
15
15
  function createFileCustomScreenerStore(filePath) {
16
- let cache = null;
17
- /** 新鲜读盘(坏 JSON/坏形同既有 load 口径降级空表);供 load 与锁内重读共用。 */
18
- async function readFromDisk() {
16
+ /** 读盘/解析失败的日志前缀(坏 JSON 与非 ENOENT 读失败同一条,与既有 load 口径逐字一致)。 */
17
+ const READ_FAILED = `[dsh-trading/strategies] failed to read custom screeners from ${filePath}:`;
18
+ /** 文本 → 表:坏 JSON 降级空表并打日志,坏形状(非数组 / 缺 id)静默丢弃(不外包)。 */
19
+ function decode(text) {
20
+ if (text === void 0) return /* @__PURE__ */ new Map();
19
21
  try {
20
- const content = await readFile(filePath, "utf8");
21
- const parsed = JSON.parse(content);
22
+ const parsed = JSON.parse(text);
22
23
  const map = /* @__PURE__ */ new Map();
23
24
  if (Array.isArray(parsed)) {
24
25
  for (const item of parsed) if (item && typeof item.id === "string") map.set(item.id, item);
25
26
  }
26
27
  return map;
27
28
  } catch (err) {
28
- if (err?.code !== "ENOENT") console.error(`[dsh-trading/strategies] failed to read custom screeners from ${filePath}:`, err);
29
+ console.error(READ_FAILED, err);
29
30
  return /* @__PURE__ */ new Map();
30
31
  }
31
32
  }
32
- async function load() {
33
- if (cache !== null) return cache;
34
- cache = await readFromDisk();
35
- return cache;
36
- }
37
- async function commit(mutate) {
38
- cache = await transactStore(filePath, readFromDisk, mutate, (table) => [...table.values()], LOG_PREFIX);
39
- return cache;
40
- }
33
+ const store = createRecordStoreAdapter({
34
+ filePath,
35
+ logPrefix: LOG_PREFIX,
36
+ readFromDisk: () => readStoreText(filePath, READ_FAILED),
37
+ decode,
38
+ encode: (table) => [...table.values()]
39
+ });
41
40
  return {
42
41
  async list() {
43
- return [...(await load()).values()];
42
+ return [...(await store.load()).values()];
44
43
  },
45
44
  async get(id) {
46
- return (await load()).get(id);
45
+ return (await store.load()).get(id);
47
46
  },
48
47
  async save(record) {
49
- await commit((onDisk) => {
48
+ await store.commit((onDisk) => {
50
49
  onDisk.set(record.id, { ...record });
51
50
  return onDisk;
52
51
  });
@@ -54,7 +53,7 @@ function createFileCustomScreenerStore(filePath) {
54
53
  async remove(id, archive = false) {
55
54
  let existed = false;
56
55
  let removedRecord;
57
- await commit((onDisk) => {
56
+ await store.commit((onDisk) => {
58
57
  removedRecord = onDisk.get(id);
59
58
  existed = onDisk.delete(id);
60
59
  return existed ? onDisk : SKIP_WRITE;
package/lib/plugin.d.ts CHANGED
@@ -4,12 +4,12 @@ import { ScreenerColumnSpec, ScreenerDefinition } from "./screeners/types.js";
4
4
  import { CustomScreenerRecord, CustomScreenerStore } from "./custom-screener.js";
5
5
  import { isBuiltinScreenerId, isBuiltinStrategyId } from "./management.js";
6
6
  import { BuiltinTombstonesStore } from "./builtin-tombstones.js";
7
- import { MarketDataService } from "./api/lib/index.js";
8
7
  import { createFileCustomStrategyStore } from "./custom-fs.js";
9
8
  import { createFileBuiltinTombstonesStore } from "./builtin-tombstones-fs.js";
10
9
  import { createFileCustomScreenerStore } from "./custom-screener-fs.js";
11
10
  import { validateCustomScreenerNode, validateCustomStrategyNode } from "./validate-node.js";
12
11
  import { Context, Service } from "@deepseek-ai/cordis";
12
+ import { MarketDataService } from "@dshtrading/api";
13
13
  //#region src/plugin.d.ts
14
14
  /** Cordis 插件名 = patch 行 id(TEMPLATES §8),市场无关共享行命名空间。 */
15
15
  declare const name = "dsh-trading-strategies";
package/lib/plugin.js CHANGED
@@ -172,22 +172,9 @@ function createStrategyListTool(options) {
172
172
  }
173
173
  });
174
174
  }
175
- /** 选股器记录 → 名册条目(paramsJson/columnsJson 解析在读取边界,坏数据降级为空数组)。 */
175
+ /** 选股器记录 → 名册条目(paramsJson/columnsJson 解析复用 parseScreenerSpecs,坏数据降级为空数组)。 */
176
176
  function screenerRecordEntry(record) {
177
- let params = [];
178
- let columns = [];
179
- try {
180
- const parsed = JSON.parse(record.paramsJson);
181
- if (Array.isArray(parsed)) params = parsed;
182
- } catch {
183
- params = [];
184
- }
185
- try {
186
- const parsed = JSON.parse(record.columnsJson);
187
- if (Array.isArray(parsed)) columns = parsed;
188
- } catch {
189
- columns = [];
190
- }
177
+ const { params, columns } = parseScreenerSpecs(record);
191
178
  return {
192
179
  id: record.id,
193
180
  title: record.title,
@@ -349,28 +336,7 @@ function createStrategyBacktestTool(deps) {
349
336
  if (service === void 0) throw new Error(`strategy_backtest: no market data service for market "${market}" — install/activate a market connector first`);
350
337
  const bars = await service.getKlines(symbol, interval, limit);
351
338
  if (!Array.isArray(bars) || bars.length === 0) throw new Error(`strategy_backtest: no klines returned for ${symbol} (${market}, ${interval}) — check the symbol/interval vocabulary`);
352
- const requested = {};
353
- const paramsOverride = {};
354
- const paramsJson = typeof args.paramsJson === "string" && args.paramsJson.trim() ? args.paramsJson.trim() : void 0;
355
- if (paramsJson !== void 0) {
356
- let parsed;
357
- try {
358
- parsed = JSON.parse(paramsJson);
359
- } catch {
360
- throw new Error("strategy_backtest: paramsJson must be a JSON object string like {\"fast\":10,\"slow\":30}");
361
- }
362
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("strategy_backtest: paramsJson must be a JSON object string like {\"fast\":10,\"slow\":30}");
363
- const specs = new Map(definition.params.map((spec) => [spec.key, spec]));
364
- for (const [key, value] of Object.entries(parsed)) {
365
- const spec = specs.get(key);
366
- if (spec === void 0) throw new Error(`strategy_backtest: unknown param ${JSON.stringify(key)} for strategy "${definition.id}" — valid keys: ` + (definition.params.length > 0 ? definition.params.map((item) => item.key).join(", ") : "(this strategy declares no params)"));
367
- if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`strategy_backtest: param ${JSON.stringify(key)} must be a finite number (got ${JSON.stringify(value)})`);
368
- requested[key] = value;
369
- paramsOverride[key] = Math.min(spec.max, Math.max(spec.min, value));
370
- }
371
- }
372
- const effectiveParams = {};
373
- for (const spec of definition.params) effectiveParams[spec.key] = paramsOverride[spec.key] ?? spec.default;
339
+ const { requested, effective: effectiveParams, override: paramsOverride } = parseParamsOverride(typeof args.paramsJson === "string" && args.paramsJson.trim() ? args.paramsJson.trim() : void 0, definition.params, "strategy_backtest", definition.id);
374
340
  const result = run(bars, definition, paramsOverride);
375
341
  return JSON.stringify({
376
342
  ok: true,
@@ -797,7 +763,7 @@ async function withTimeout(promise, ms, label) {
797
763
  if (timer !== void 0) clearTimeout(timer);
798
764
  }
799
765
  }
800
- /** 解析 paramsJson(与 strategy_backtest 同口径:声明键校验 + clamp)。 */
766
+ /** 解析 paramsJson(strategy_backtest / screener_run 共用:声明键校验 + clamp + 默认值补齐)。 */
801
767
  function parseParamsOverride(paramsJson, specs, tool, ownerId) {
802
768
  const requested = {};
803
769
  const override = {};
@@ -822,7 +788,8 @@ function parseParamsOverride(paramsJson, specs, tool, ownerId) {
822
788
  for (const spec of specs) effective[spec.key] = override[spec.key] ?? spec.default;
823
789
  return {
824
790
  requested,
825
- effective
791
+ effective,
792
+ override
826
793
  };
827
794
  }
828
795
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dshtrading/strategies",
3
3
  "description": "Trading strategies kernel for dsh-trading: pure math backtesting engine, strategy contracts, and 6 reference paradigms (short, swing, long). Pure library (zero runtime dependencies, browser-packable).",
4
- "version": "0.6.1",
4
+ "version": "0.6.3",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -21,18 +21,19 @@
21
21
  ],
22
22
  "license": "PolyForm-Noncommercial-1.0.0",
23
23
  "dependencies": {
24
- "@dshtrading/dsh-home": "^0.6.1",
25
- "@dshtrading/indicators": "0.6.1"
24
+ "@dshtrading/dsh-home": "^0.6.3",
25
+ "@dshtrading/indicators": "0.6.3"
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
  },
31
31
  "devDependencies": {
32
32
  "tsdown": "^0.22.0",
33
33
  "vitest": "^3.0.0",
34
34
  "typescript": "^5.5.0",
35
- "@dshtrading/api": "0.6.1"
35
+ "@dshtrading/api": "0.6.3",
36
+ "@dshtrading/test-support": "0.6.3"
36
37
  },
37
38
  "scripts": {
38
39
  "build": "tsdown",
@@ -1,636 +0,0 @@
1
- import "./instrument-form.js";
2
- import "@deepseek-ai/cordis";
3
- //#region ../api/lib/index.d.ts
4
- //#region src/index.d.ts
5
- /** K线周期(Binance 现货/合约 interval 词汇)。 */
6
- type Interval = '1m' | '3m' | '5m' | '15m' | '30m' | '1h' | '2h' | '4h' | '6h' | '8h' | '12h' | '1d' | '3d' | '1w' | '1M';
7
- /** 最新行情快照(公共数据,无需凭证)。 */
8
- interface Ticker {
9
- /** 交易对符号,**市场规范词汇**(docs/symbol-vocabulary.md:crypto=BTCUSDT,us=AAPL,cn=600519.SH,hk=00700.HK)。 */
10
- readonly symbol: string;
11
- /** 标的/公司名称(如“紫光股份”、“苹果”、“腾讯控股”;部分数据源可缺省)。 */
12
- readonly name?: string;
13
- /** 最新成交价。 */
14
- readonly price: number;
15
- /** 最优买价(部分数据源可缺省)。 */
16
- readonly bid?: number;
17
- /** 最优卖价(部分数据源可缺省)。 */
18
- readonly ask?: number;
19
- /** 24h 成交量(base 资产计)。 */
20
- readonly volume?: number;
21
- /** 快照时间(epoch ms)。 */
22
- readonly timestamp: number;
23
- /** 官方昨收(涨跌基准锚点;部分数据源可缺省,缺省时消费方退回日 K 自算)。 */
24
- readonly prevClose?: number;
25
- /** 相对昨收的涨跌幅(百分比,如 -0.89 表示 -0.89%;部分数据源可缺省)。 */
26
- readonly changePercent?: number;
27
- }
28
- /** 单根 K 线。 */
29
- interface Kline {
30
- readonly openTime: number;
31
- readonly open: number;
32
- readonly high: number;
33
- readonly low: number;
34
- readonly close: number;
35
- readonly volume: number;
36
- readonly closeTime: number;
37
- }
38
- /** 衍生品市场指标快照(持仓量/多空比/资金费等)。 */
39
- interface DerivativesData {
40
- /** 交易对符号,市场规范词汇(如 BTCUSDT 或 BTCUSDT-SWAP)。 */
41
- readonly symbol: string;
42
- /** 数据来源(如 binance / okx)。 */
43
- readonly source: string;
44
- /** 未平仓合约量(Open Interest,base 币数或张数)。 */
45
- readonly openInterest?: number;
46
- /** 未平仓合约总价值(USD 或 quote 计价)。 */
47
- readonly openInterestValue?: number;
48
- /** 多空人数比(Long/Short Account Ratio)。 */
49
- readonly longShortRatio?: number;
50
- /** 大户持仓多空比(Top Trader Long/Short Position Ratio)。 */
51
- readonly topTraderLongShortRatio?: number;
52
- /** 主动买入/卖出量比(Taker Buy/Sell Volume Ratio)。 */
53
- readonly takerBuySellRatio?: number;
54
- /** 最新资金费率(小数,如 0.0001 表示 0.01%)。 */
55
- readonly fundingRate?: number;
56
- /** 预测(下一期)资金费率(小数;交易所未发布预测时缺省,issue #54)。 */
57
- readonly nextFundingRate?: number;
58
- /** 下一期资金费率结算时间(epoch ms,GUI 倒计时用,issue #54)。 */
59
- readonly nextFundingTime?: number;
60
- /** 标记价格(Mark Price,永续合约;issue #54 基差卡用)。 */
61
- readonly markPrice?: number;
62
- /** 指数价格(Index Price,现货指数;与 markPrice 配对计算基差)。 */
63
- readonly indexPrice?: number;
64
- /** 快照时间(epoch ms)。 */
65
- readonly timestamp: number;
66
- }
67
- /** 衍生品时序数据点(资金费率历史 / OI 历史共用词汇,issue #54)。 */
68
- interface DerivativesPoint {
69
- /** 采样时间(epoch ms)。 */
70
- readonly time: number;
71
- /** 采样值(费率序列为小数费率;OI 序列单位与快照 openInterest 同语义)。 */
72
- readonly value: number;
73
- }
74
- /**
75
- * 衍生品历史序列(GUI「衍生品」页签趋势卡用,issue #54):
76
- * 资金费率历史 + 未平仓量历史,时间一律升序(旧→新,与 K 线序列同向)。
77
- * 可选方法 getDerivativesHistory 的返回面;任一序列不可得时该字段缺省,
78
- * 消费方按卡降级(隐藏趋势图、保留快照读数)。
79
- */
80
- interface DerivativesHistory {
81
- /** 交易对符号,市场规范词汇(如 BTCUSDT-SWAP)。 */
82
- readonly symbol: string;
83
- /** 数据来源(如 binance / okx)。 */
84
- readonly source: string;
85
- /** 资金费率历史(时间升序,value 为小数费率)。 */
86
- readonly fundingRates?: DerivativesPoint[];
87
- /** OI 历史(时间升序,value 单位与 DerivativesData.openInterest 同语义)。 */
88
- readonly openInterest?: DerivativesPoint[];
89
- }
90
- /** 股票市场标的基本面与财务估值快照(US/CN/HK)。 */
91
- interface StockFundamentals {
92
- /** 标的规范符号(如 AAPL, 600519.SH, 00700.HK)。 */
93
- readonly symbol: string;
94
- /** 公司/标的名称。 */
95
- readonly name?: string;
96
- /** 总市值(本位币计价)。 */
97
- readonly marketCap?: number;
98
- /** 流通市值(A 股/港股适用)。 */
99
- readonly floatMarketCap?: number;
100
- /** 滚动市盈率 PE (TTM)。 */
101
- readonly peTtm?: number;
102
- /** 静态市盈率 PE (静)。 */
103
- readonly peStatic?: number;
104
- /** 动态/预测市盈率 Forward / Dynamic PE。 */
105
- readonly peDynamic?: number;
106
- /** 市净率 PB。 */
107
- readonly pb?: number;
108
- /** 市销率 PS。 */
109
- readonly ps?: number;
110
- /** 每股收益 EPS。 */
111
- readonly eps?: number;
112
- /** 每股净资产 BPS。 */
113
- readonly bps?: number;
114
- /** 股息率(小数,如 0.015 表示 1.5%)。 */
115
- readonly dividendYield?: number;
116
- /** 换手率(小数或百分比)。 */
117
- readonly turnoverRate?: number;
118
- /** 振幅(百分比)。 */
119
- readonly amplitudePercent?: number;
120
- /** 涨停价(A 股适用)。 */
121
- readonly limitUpPrice?: number;
122
- /** 跌停价(A 股适用)。 */
123
- readonly limitDownPrice?: number;
124
- /** 52 周最高价。 */
125
- readonly fiftyTwoWeekHigh?: number;
126
- /** 52 周最低价。 */
127
- readonly fiftyTwoWeekLow?: number;
128
- /** 快照时间(epoch ms)。 */
129
- readonly timestamp: number;
130
- }
131
- /** 盘口档位(价格 + 挂单量,量单位=标的基准单位:股票股、crypto 币)。 */
132
- interface OrderbookLevel {
133
- readonly price: number;
134
- readonly amount: number;
135
- }
136
- /**
137
- * 盘口快照(GUI「盘口」竖栏用,issue #39)。实现保证:bids 按价格降序(买一在前)、
138
- * asks 按价格升序(卖一在前),档位数由数据源决定(沪深五档、crypto 常见 5~20 档)。
139
- */
140
- interface Orderbook {
141
- readonly symbol: string;
142
- readonly bids: readonly OrderbookLevel[];
143
- readonly asks: readonly OrderbookLevel[];
144
- /** 快照时间(epoch ms)。 */
145
- readonly timestamp: number;
146
- }
147
- /** 逐笔成交(taker 视角:side 为主动方;流水单条)。 */
148
- interface TradeTick {
149
- /** 交易所成交 id(字符串透传,排序稳定性由时间戳保证)。 */
150
- readonly id: string;
151
- readonly symbol: string;
152
- readonly price: number;
153
- /** 成交量(基准单位,同 OrderbookLevel.amount)。 */
154
- readonly amount: number;
155
- /** 主动方向:buy=主动买(外盘)、sell=主动卖(内盘);数据源缺方向时 unknown。 */
156
- readonly side: 'buy' | 'sell' | 'unknown';
157
- readonly timestamp: number;
158
- }
159
- type PositionSide = 'long' | 'short';
160
- /**
161
- * 保证金模式(合约交易语义,P7):`cross` = 全仓、`isolated` = 逐仓。
162
- * 现货(cash)不使用该字段——缺席即「非保证金/未设置」。
163
- */
164
- type MarginMode = 'cross' | 'isolated';
165
- /** 持仓快照。 */
166
- interface Position {
167
- readonly symbol: string;
168
- readonly side: PositionSide;
169
- /** 仓位数量(正数;方向由 side 表达)。合约仓位是 **base 币数**(连接器已按合约乘数 ctVal 由张换算)。 */
170
- readonly size: number;
171
- readonly entryPrice: number;
172
- readonly markPrice?: number;
173
- readonly unrealizedPnl?: number;
174
- readonly leverage?: number;
175
- /**
176
- * 强平价(交易所回传原样透传;交易所未提供或不适用时缺席,绝不本地推算强平价——
177
- * 强平口径与保证金模式、账户级保证金、维持保证金率有关,本地公式会给出错误的安全感)。
178
- */
179
- readonly liquidationPrice?: number;
180
- /** 维持保证金率(OKX `mgnRatio`;口径由交易所定义,原样透传,不在本地换算或反推)。 */
181
- readonly marginRatio?: number;
182
- /** 该仓位的保证金模式;现货持仓与交易所未提供时缺席。 */
183
- readonly marginMode?: MarginMode;
184
- /** 名义价值(USD,交易所口径;用于敞口核对,不参与本地风控公式)。 */
185
- readonly notionalUsd?: number;
186
- readonly timestamp: number;
187
- }
188
- type OrderSide = 'buy' | 'sell';
189
- type OrderType = 'limit' | 'market';
190
- type OrderStatus = 'new' | 'partially_filled' | 'filled' | 'canceled' | 'rejected' | 'expired';
191
- /** 下单请求(安全闸门:dryRun 缺省视为 true)。 */
192
- interface OrderRequest {
193
- readonly symbol: string;
194
- readonly side: OrderSide;
195
- readonly type: OrderType;
196
- /** **base 币数量**:合约(永续)由连接器按合约乘数换算成张,消费方恒用币数语义。 */
197
- readonly quantity: number;
198
- /** limit 单必填。 */
199
- readonly price?: number;
200
- /**
201
- * 合约订单的保证金模式(缺省 `cross`)。仅对永续(`-SWAP`)有效:
202
- * 现货订单只允许 cash 语义,传该字段由连接器结构化拒绝
203
- * (现货逐仓/杠杆是另一个产品面,本仓未接线)。
204
- * `isolated` 需先在交易所侧设好该标的的逐仓杠杆(OKX `setLeverage`)。
205
- */
206
- readonly marginMode?: MarginMode;
207
- /** 缺省/true 时仅模拟,不触碰交易所。实盘还受 @dshtrading/authority 的人工签署授权与 approval 约束 [S4]。 */
208
- readonly dryRun?: boolean;
209
- }
210
- /** 订单回执/状态。 */
211
- interface Order {
212
- readonly id: string;
213
- readonly symbol: string;
214
- readonly side: OrderSide;
215
- readonly type: OrderType;
216
- readonly status: OrderStatus;
217
- readonly price?: number;
218
- readonly quantity: number;
219
- readonly filledQuantity?: number;
220
- /** 本次订单是否为模拟单(回执必须显式回带,防 dry-run 语义丢失)。 */
221
- readonly dryRun: boolean;
222
- readonly timestamp: number;
223
- }
224
- interface AccountBalance {
225
- readonly asset: string;
226
- readonly free: number;
227
- readonly locked: number;
228
- }
229
- /** 成交流水单条(GUI 交易台「成交历史」用,issue #40)。 */
230
- interface TradeFill {
231
- /** 交易所成交 id。 */
232
- readonly id: string;
233
- readonly symbol: string;
234
- readonly side: OrderSide;
235
- readonly price: number;
236
- /** 成交量(base 币数)。 */
237
- readonly amount: number;
238
- /** 手续费(绝对值,币种由 feeAsset 表达)。 */
239
- readonly fee?: number;
240
- readonly feeAsset?: string;
241
- readonly timestamp: number;
242
- }
243
- /** 订阅句柄:dispose 即退订(连接器用 ctx.effect/cordis 生命周期托管)。 */
244
- interface Disposable {
245
- dispose(): void;
246
- }
247
- /**
248
- * 标的形态轴(2026-10-08 加密永续与 TradFi 永续落地):与 `market` 正交,**不新增市场键**。
249
- * 缺省 = `spot`——已存自选、localStorage 与 iOS 冻结契约因此零迁移。
250
- *
251
- * 形态由规范符号后缀承载(`-SWAP` = 永续),见 docs/guides/symbol-vocabulary.md;
252
- * `form` 只出现在名册/检索/wire 元数据里,行情方法不新增 form 形参。
253
- */
254
- type InstrumentForm = 'spot' | 'perp';
255
- /**
256
- * 标的资产类别标签(仅展示与检索排序用,不承载交易语义)。
257
- * **只信交易所元数据**(OKX `instCategory`、Binance `underlyingType`+`underlyingSubType`);
258
- * 取不到时留空,禁止按符号字面猜(`SPX` 在 OKX 是迷因币 SPX6900,标普 500 是 `US500`)。
259
- */
260
- type InstrumentAssetClass = 'crypto' | 'equity' | 'commodity' | 'index';
261
- /** 合约专有元数据(仅 `form=perp` 有值):交易所元数据原样透传,不本地推断。 */
262
- interface InstrumentContract {
263
- /** 合约乘数(OKX ctVal / Binance contractSize)。 */
264
- readonly multiplier?: number;
265
- /** 最小价格变动。 */
266
- readonly tickSize?: number;
267
- /** 最小下单量/张数步长。 */
268
- readonly lotSize?: number;
269
- /** 交易所允许的最大杠杆。 */
270
- readonly maxLeverage?: number;
271
- /** 结算币种(如 USDT)。 */
272
- readonly settleCcy?: string;
273
- }
274
- /**
275
- * 标的检索结果的最小元数据(名册/搜索/wire 共用)。
276
- * 元素形状对既有 `{ symbol, name? }` 实现向后兼容:`form`/`assetClass`/`contract` 全可选。
277
- */
278
- interface InstrumentRef {
279
- /** 市场规范词汇符号(docs/guides/symbol-vocabulary.md)。 */
280
- readonly symbol: string;
281
- readonly name?: string;
282
- /** 缺省 = spot(老数据零迁移)。 */
283
- readonly form?: InstrumentForm;
284
- readonly assetClass?: InstrumentAssetClass;
285
- readonly contract?: InstrumentContract;
286
- }
287
- /**
288
- * 行情服务契约:由市场连接器实现,注册到按市场命名空间的 ctx 键(如 ctx.tradingCrypto)。
289
- * 符号词汇(2026-08-31 规范,docs/guides/symbol-vocabulary.md):入参接受市场规范形与连接器原生形,
290
- * 输出 `symbol` 一律市场规范形——消费方(GUI/Agent/工作流)与数据源方言解耦。
291
- */
292
- interface MarketDataService {
293
- getTicker(symbol: string): Promise<Ticker>;
294
- getKlines(symbol: string, interval: Interval, limit?: number): Promise<Kline[]>;
295
- subscribeTicker(symbol: string, cb: (ticker: Ticker) => void): Disposable;
296
- /**
297
- * 查询本市场/交易所支持的全部标的名册(动态全集,Issue #15)。
298
- * 输出 `symbol` 一律市场规范词汇(docs/guides/symbol-vocabulary.md),形态由符号后缀承载
299
- * (`-SWAP` = 永续,`instrumentFormOf` 是唯一裁决实现);每行可带
300
- * `form`/`assetClass`/`contract` 元数据(InstrumentRef),缺省 = 现货。
301
- * 可选方法:无公开全集端点的数据源(如 tencent/yahoo/stooq)可缺省或由桥/前端回退。
302
- */
303
- listInstruments?(): Promise<InstrumentRef[]>;
304
- /**
305
- * 标的基本面与估值快照(GUI「基本面」页签用,2026-09-02)。
306
- * 可选方法:仅当数据源在同一公共端点里携带基本面字段时实现
307
- * (腾讯行情行 cn/hk 已实现);未实现的市场由消费方降级为派生数据(日K 52 周高低)。
308
- * 输出 `symbol` 一律市场规范词汇(docs/symbol-vocabulary.md)。
309
- */
310
- getFundamentals?(symbol: string): Promise<StockFundamentals>;
311
- /**
312
- * 衍生品市场指标快照(GUI「衍生品」面板用,issue #38,2026-09-02):持仓量/
313
- * 多空比/资金费等微观结构数据,crypto 永续合约市场专属。
314
- * 可选方法:现货/股票数据源不实现;未实现时消费方直接隐藏面板(不降级不报错)。
315
- * 入参接受规范形与连接器原生形,输出 `symbol` 一律规范词汇 SWAP 形(BTCUSDT-SWAP)。
316
- */
317
- getDerivatives?(symbol: string): Promise<DerivativesData>;
318
- /**
319
- * 衍生品历史序列(GUI「衍生品」页签趋势卡用,issue #54):资金费率历史 +
320
- * OI 历史,时间升序。可选方法:无公开历史端点的数据源不实现;未实现或失败
321
- * 时消费方隐藏趋势图、保留快照读数(与快照同族的降级纪律)。
322
- * 入参接受规范形与连接器原生形,输出 symbol 一律规范词汇 SWAP 形。
323
- */
324
- getDerivativesHistory?(symbol: string): Promise<DerivativesHistory>;
325
- /**
326
- * 盘口快照(GUI「盘口」竖栏用,issue #39):档位词汇见 Orderbook。可选方法:
327
- * 数据源无盘口能力时不实现(如 stooq/yahoo、腾讯 r_hk 港股行档位全 0)。
328
- * 入参接受规范形与连接器原生形,输出 `symbol` 一律市场规范词汇。
329
- */
330
- getOrderbook?(symbol: string): Promise<Orderbook>;
331
- /**
332
- * 最近逐笔成交流水(GUI「分笔」用,issue #39):取最近 limit 笔(缺省 ≤50),
333
- * **时间升序(旧→新)**,与 K 线序列同向;方向缺省的数据源 side='unknown'。
334
- * 可选方法:无公共逐笔端点的数据源(腾讯沪深行情行)不实现。
335
- */
336
- getRecentTrades?(symbol: string, limit?: number): Promise<TradeTick[]>;
337
- }
338
- /**
339
- * 杠杆设置请求(合约交易语义,P7;仅 `form=perp` 的标的可传)。
340
- * dryRun 缺省视为 true:只回本地模拟回执,不触碰交易所(与 placeOrder 同语义)。
341
- */
342
- interface SetLeverageRequest {
343
- readonly symbol: string;
344
- /** 目标杠杆倍数(> 0;上限以交易所规格 `InstrumentContract.maxLeverage` 为准,超出即结构化拒绝)。 */
345
- readonly leverage: number;
346
- readonly marginMode: MarginMode;
347
- /** 双向持仓模式下的方向(OKX `posSide`);净持仓模式下缺席。 */
348
- readonly posSide?: 'long' | 'short';
349
- /** 全仓模式下按币种设置杠杆时的保证金币(OKX `ccy`);逐仓与净持仓模式缺席。 */
350
- readonly ccy?: string;
351
- /** 缺省/true 时仅模拟。实盘还受 @dshtrading/authority 的人工签署授权与 approval 约束 [S4]。 */
352
- readonly dryRun?: boolean;
353
- }
354
- /** 杠杆设置回执(dryRun=true 时是本地模拟回执,不触网)。 */
355
- interface LeverageSetting {
356
- readonly symbol: string;
357
- readonly leverage: number;
358
- readonly marginMode: MarginMode;
359
- readonly posSide?: 'long' | 'short';
360
- readonly ccy?: string;
361
- /** 本次是否为模拟回执(回执必须显式回带,防 dry-run 语义丢失)。 */
362
- readonly dryRun: boolean;
363
- readonly timestamp: number;
364
- }
365
- /**
366
- * 交易服务契约:placeOrder 默认 dry-run;实盘前必须过 @dshtrading/authority 的人工签署授权与
367
- * ctx.approval.request(交互形态;headless 下 ask=deny,fail-closed [S4])。
368
- *
369
- * R3(okx 切片 2026-08-29)修订:cancelOrder 增加可选 symbol、新增 getOrder——
370
- * OKX 按 (instId, ordId) 双键定位订单,单参 id 形态不够;无其他实现方,扩展向后兼容。
371
- *
372
- * P7(合约交易 Tier 2,2026-10-08)修订:新增可选 `setLeverage`——杠杆/保证金模式变更
373
- * 是实盘动作,必须与下单同门槛(服务缝三态闸门 + base 审批闸门 + 人工签署授权)。
374
- */
375
- interface TradeService {
376
- placeOrder(req: OrderRequest): Promise<Order>;
377
- /** 撤单。symbol 可选:按 (symbol, id) 双键定位订单的交易所(如 OKX)必须提供。 */
378
- cancelOrder(id: string, symbol?: string): Promise<void>;
379
- /** 查询单笔订单状态(按 (symbol, id) 双键)。 */
380
- getOrder(symbol: string, id: string): Promise<Order>;
381
- getPositions(): Promise<Position[]>;
382
- /**
383
- * 账户余额快照(issue #40 GUI 交易台;只读,需凭证)。
384
- * 可选方法:实现方已有只读余额面时实现(connector-okx 的 crypto_get_balance 同源)。
385
- */
386
- getBalances?(): Promise<AccountBalance[]>;
387
- /**
388
- * 当前挂单列表(issue #40 GUI 交易台;只读,需凭证)。
389
- * 可选方法:无批量挂单端点的实现方可缺省(GUI 隐藏挂单区)。
390
- * 输出 `symbol` 一律市场规范词汇;status 只含 new / partially_filled。
391
- */
392
- listOpenOrders?(symbol?: string): Promise<Order[]>;
393
- /**
394
- * 最近成交流水(issue #40 GUI 交易台;只读,需凭证)。
395
- * 可选方法:时间升序(旧→新),最多 limit 条(缺省 ≤50)。
396
- */
397
- listTradeFills?(symbol?: string, limit?: number): Promise<TradeFill[]>;
398
- /**
399
- * 设置合约杠杆与保证金模式(P7 合约交易 Tier 2;仅支持合约的交易所实现)。
400
- * 可选方法:不实现的连接器缺席即可,消费方按 TRADING_NOT_IMPLEMENTED 语义处理。
401
- * 这是会改变账户真实风险参数的实盘动作:dryRun 缺省 true(只回本地模拟回执),
402
- * dryRun=false 必须同时具备人工签署的实盘授权,并经 base 审批闸门。
403
- */
404
- setLeverage?(req: SetLeverageRequest): Promise<LeverageSetting>;
405
- }
406
- /**
407
- * Cordis Context 服务键(能力三角色之「声明」):市场命名空间键由契约包统一声明,
408
- * 连接器 provide、消费方 inject 时获得完整类型。此处仅类型增强——本包保持
409
- * 零运行时依赖,不产生任何 JS 输出。
410
- */
411
- declare module '@deepseek-ai/cordis' {
412
- interface Context {
413
- /** crypto 市场行情服务(由 Binance 连接器以公共 REST 提供,无需凭证)。 */
414
- tradingCryptoMarketData: MarketDataService;
415
- /** us 市场行情服务(由 Stooq 连接器以公共 CSV 端点提供,无需凭证;us 复制 2026-08-31 补齐)。 */
416
- tradingUsMarketData: MarketDataService;
417
- /** cn 市场行情服务(由腾讯连接器以公共端点提供,无需凭证;cn+hk 切片 2026-08-31 补齐)。 */
418
- tradingCnMarketData: MarketDataService;
419
- /** hk 市场行情服务(由腾讯连接器同包双实例提供,config.market 分流;cn+hk 切片 2026-08-31 补齐)。 */
420
- tradingHkMarketData: MarketDataService;
421
- /** futures 市场行情服务(由同花顺期货连接器提供,issue #97;日K + 当日分时聚合)。 */
422
- tradingFuturesMarketData: MarketDataService;
423
- /** 全球品种行情(global 市场:大宗/外汇/海外指数;@dshtrading/connector-jin10 dataplane 行提供)。 */
424
- tradingGlobalMarketData: MarketDataService;
425
- /**
426
- * crypto 市场交易服务(R3 2026-08-29 补齐,crypto 市场第一个真实 TradeService):
427
- * 由 connector-okx 实现(签名 demo/live 下单),与 connector-binance 经
428
- * Config.enabled 互斥激活同一 tradingCryptoMarketData 键时一并 provide。
429
- */
430
- tradingCryptoTrade: TradeService;
431
- /**
432
- * 交易服务注册表(issue #40 GUI 交易台,@dshtrading/api 类型声明):
433
- * 与 tradingMarketDataRegistry 同构的宿主平面注册面——交易连接器 host 面数据行
434
- * 注册,GUI 桥按路由当前值惰性解析。**注册不改变安全语义**:placeOrder 的
435
- * 服务缝闸门(dryRun 缺省 true + @dshtrading/authority 人工签署授权)随服务实例生效;
436
- * GUI 桥只放行 dry-run 下单与只读查询,实盘路径仍走 Agent 工具的 base 审批闸门。
437
- */
438
- tradingTradeRegistry: TradeRegistry;
439
- /**
440
- * 市场路由服务(R5 2026-08-29 补齐,@dshtrading/router 提供):
441
- * 连接器 apply 时 consult activeProvider(market) 决定是否激活——用户设置
442
- * dshtrading.markets.<market>.provider 选谁谁激活(docs/exchange-routing.md)。
443
- */
444
- tradingMarketRouter: MarketRouterService;
445
- /**
446
- * 行情服务注册表(2026-08-30 注册表模式定稿,@dshtrading/router 同插件提供):
447
- * 连接器 host 面数据行注册,GUI 行情桥按路由当前值惰性解析(热切换)。
448
- */
449
- tradingMarketDataRegistry: MarketDataRegistry;
450
- /**
451
- * 新闻聚合器注册表(Issue #37,@dshtrading/router 同插件提供):
452
- * 各市场 Kit apply 时注册 aggregateNews 纯函数,GUI 行情桥按市场获取。
453
- */
454
- tradingNewsRegistry: TradingNewsRegistry;
455
- /** 跨市场快讯源(@dshtrading/connector-jin10 提供;缺席 = 未安装快讯连接器)。 */
456
- tradingFlashFeed: FlashFeedService;
457
- /** 宏观/利率源(@dshtrading/connector-jin10 提供;缺席 = 未安装金十连接器)。 */
458
- tradingMacroFeed: MacroFeedService;
459
- }
460
- }
461
- /**
462
- * 行情服务注册表契约(router 插件提供,2026-08-30 注册表模式定稿):
463
- * 连接器 host 面数据行不再互斥式 provide 市场键,而是全部注册进本注册表;
464
- * 消费方(GUI 行情桥)经 active() 按路由当前值惰性解析——settings 变更即刻生效
465
- * (GUI 热切换),无 watch、无进程重启。preset 平面不走注册表:会话内数据源
466
- * 一致性是有意语义,切交易所对会话 = 新建会话生效(docs/exchange-routing.md §2.2)。
467
- *
468
- * 与 tradeProvider 预留的衔接:本注册表只承载 MarketDataService;数据/交易分离
469
- * 落地时 TradeService 走独立注册面(不复用本键),铁律 #4 到时再抽象。
470
- */
471
- interface MarketDataRegistration {
472
- /** 市场 slug(crypto/us/cn/hk;开放词汇,新市场 = 新键)。 */
473
- readonly market: string;
474
- /** 提供者 slug(binance/okx/…;开放词汇,第三方连接器可注册新 slug)。 */
475
- readonly provider: string;
476
- readonly service: MarketDataService;
477
- }
478
- interface MarketDataRegistry {
479
- /**
480
- * 注册一个市场的某 provider 行情服务;同 (market, provider) 重复注册抛错
481
- * (配置错误必须响亮)。返回注销函数(调用方包进 ctx.effect 随 fiber 注销)。
482
- */
483
- register(market: string, provider: string, service: MarketDataService): () => void;
484
- /**
485
- * 路由裁决后的当前激活注册项:router 选中的 provider 已注册 → 返回之;
486
- * 选中了但未注册(包未装/enabled=false)→ undefined(调用方面向用户报错,
487
- * 不静默降级到别家——用户设置是权威);router 无该市场路由(未知市场键)
488
- * 且恰好一个注册项 → 返回之(新市场零配置可用);否则 undefined。
489
- */
490
- active(market: string): MarketDataRegistration | undefined;
491
- /** 某市场全部注册项(诊断/设置 UI 展示用)。 */
492
- list(market: string): readonly MarketDataRegistration[];
493
- }
494
- /** 交易服务注册项(tradingTradeRegistry 的条目,issue #40)。 */
495
- interface TradeRegistration {
496
- readonly market: string;
497
- readonly provider: string;
498
- readonly service: TradeService;
499
- }
500
- /**
501
- * 交易服务注册表(router 插件同款注册模式,issue #40):交易连接器在 host 面
502
- * 数据行注册(凭证经 ctx.credentials / 环境变量解析,缺失时只读方法 fail-closed
503
- * 报 TRADING_CREDENTIALS_MISSING);注册面本身不做安全裁决——闸门在服务缝
504
- * (placeOrder 三态)与桥层(GUI 只放行 dry-run)。
505
- */
506
- interface TradeRegistry {
507
- register(market: string, provider: string, service: TradeService): () => void;
508
- active(market: string): TradeRegistration | undefined;
509
- list(market: string): readonly TradeRegistration[];
510
- }
511
- /**
512
- * 市场路由服务契约(router 插件提供):按市场查询当前激活的数据/交易所提供方。
513
- * 用户设置(dshtrading namespace,settings.yaml)是权威;无设置时 = 组合默认值
514
- * (现状零变化)。供应商取值与连接器的 provider slug 比对,相符者激活。
515
- */
516
- interface MarketRouterService {
517
- /** 某市场当前激活的 provider slug(settings resolved:用户层赢,缺省 base 默认)。 */
518
- activeProvider(market: string): string | undefined;
519
- /** 订阅激活变化(settings commit 驱动)。 */
520
- watch(cb: (next: string | undefined, prev: string | undefined) => void): () => void;
521
- }
522
- /** 新闻/快讯条目(各市场 Kit 统一输出形状)。 */
523
- interface NewsItem {
524
- /** 来源标识(如 'eastmoney'、'yahoo'、'googlenews'、'binance'、'coindesk' 等)。 */
525
- readonly source: string;
526
- /** 新闻/快讯标题。 */
527
- readonly title: string;
528
- /** 详情页 URL。 */
529
- readonly url: string;
530
- /** ISO 8601 发布时间。 */
531
- readonly publishedAt: string;
532
- /** 关联标的代码(可选,格式随源不同)。 */
533
- readonly relatedCodes?: readonly string[];
534
- /** 热度等级(可选;金十快讯网页版 火/热/沸/爆,其他源不填)。 */
535
- readonly hot?: string;
536
- }
537
- /** 新闻聚合请求选项。 */
538
- interface AggregateNewsOptions {
539
- /** 输出条数上限(1~50,默认 20)。 */
540
- limit?: number | undefined;
541
- /** 时间窗口(小时,1~168,默认 24)。 */
542
- windowHours?: number | undefined;
543
- /** 按标的代码过滤(可选)。 */
544
- symbol?: string | undefined;
545
- /** CryptoPanic API token(仅 crypto 市场使用,可选)。 */
546
- cryptoPanicKey?: string | undefined;
547
- /**
548
- * 启用的新闻/公告源 id 列表(issue #96 源配置化;各 kit 的 NewsSource 词汇)。
549
- * 缺省 = 该市场全部默认源;空数组 = 显式关闭(返回空结果)。
550
- */
551
- sources?: readonly string[] | undefined;
552
- }
553
- /** 新闻聚合结果。 */
554
- interface AggregateNewsResult {
555
- /** 按发布时间倒序的新闻条目。 */
556
- readonly items: readonly NewsItem[];
557
- /** 失败的数据源名称(fail-soft 容错:可用源正常返回,不可用源记录在此)。 */
558
- readonly unavailable: readonly string[];
559
- }
560
- /** 新闻聚合器函数签名(各 Kit 导出的 aggregateNews 符合此形状)。 */
561
- type NewsAggregator = (options?: AggregateNewsOptions) => Promise<AggregateNewsResult>;
562
- /**
563
- * 跨市场快讯源契约(2026-09-13 金十接入):host 平面由快讯连接器 provide
564
- * tradingFlashFeed,GUI 快讯面板(桥 /dshtrading/api/flash)与 agent 快讯工具
565
- * 共用同一取数实现;服务缺席 = 未安装/未启用该数据源 → 消费方报
566
- * TRADING_NOT_IMPLEMENTED,绝不把「没有数据源」伪装成「没有快讯」。
567
- */
568
- interface FlashFeedService {
569
- /**
570
- * 最新快讯流(cursor 翻页);条目只含元数据(标题/时间/链接)。
571
- * `hot` 非空时按热度等级服务端过滤(金十网页版 火/热/沸/爆)。
572
- */
573
- listFlash(options?: {
574
- cursor?: string | undefined;
575
- limit?: number | undefined;
576
- hot?: readonly string[] | undefined;
577
- }): Promise<{
578
- readonly items: readonly NewsItem[];
579
- readonly nextCursor?: string | undefined;
580
- readonly hasMore: boolean;
581
- }>;
582
- /** 关键词搜快讯(上游一次性返回、不支持翻页)。 */
583
- searchFlash(keyword: string, limit?: number | undefined): Promise<readonly NewsItem[]>;
584
- }
585
- /**
586
- * 宏观经济日历条目(金十当周经济数据,2026-09-15):数值均为上游快照原样字符串,
587
- * `actual` 缺省 = 未公布。
588
- */
589
- interface MacroCalendarEntry {
590
- /** ISO 8601 公布时间(东八区解释,同 NewsItem 口径)。 */
591
- readonly publishedAt: string;
592
- /** 重要度(上游 star,0–5)。 */
593
- readonly star: number;
594
- /** 地区(标题前缀推断,如 美国/日本/中国;未识别 = 空串)。 */
595
- readonly region: string;
596
- readonly title: string;
597
- readonly previous?: string;
598
- readonly consensus?: string;
599
- readonly actual?: string;
600
- readonly revised?: string;
601
- /** 上游公布影响词(利多/利空/影响较小…;原样透传)。 */
602
- readonly affect?: string;
603
- }
604
- /** 央行最新利率条目(金十网页版,2026-09-15)。 */
605
- interface MacroRateEntry {
606
- /** 地区(flag 名推断,如 美国/日本/中国;未识别 = 空串)。 */
607
- readonly region: string;
608
- readonly bankName: string;
609
- /** 最新利率(上游原样字符串,如 "3.75"/"0")。 */
610
- readonly rate: string;
611
- /** 公布日(上游原样 YYYY-MM-DD)。 */
612
- readonly publishedAt: string;
613
- /** 所属指标名(如「美联储利率决定(上限)」;部分央行缺省)。 */
614
- readonly indicatorName?: string;
615
- }
616
- /**
617
- * 宏观/利率源契约(2026-09-15 金十接入):host 平面由金十连接器 provide
618
- * tradingMacroFeed,GUI 宏观面板(桥 /dshtrading/api/macro/*)消费;服务缺席 =
619
- * 未安装/未启用该数据源 → 消费方报 TRADING_NOT_IMPLEMENTED,绝不把「没有数据源」
620
- * 伪装成「没有数据」。日历走官方 MCP(list_calendar),利率走网页版公开接口
621
- * (spikes/impl-jin10-macro-rates/EVIDENCE.md)。
622
- */
623
- interface MacroFeedService {
624
- /** 当周经济日历(周一~周日,北京时间;limit 截尾,缺省全周)。 */
625
- listCalendar(limit?: number | undefined): Promise<readonly MacroCalendarEntry[]>;
626
- /** 央行最新利率(全量,客户端按地区过滤)。 */
627
- listRates(): Promise<readonly MacroRateEntry[]>;
628
- }
629
- /** 新闻聚合器注册表契约(Issue #37,router 插件提供)。 */
630
- interface TradingNewsRegistry {
631
- register(market: string, aggregator: NewsAggregator): () => void;
632
- get(market: string): NewsAggregator | undefined;
633
- markets(): string[];
634
- }
635
- //#endregion
636
- export { AccountBalance, AggregateNewsOptions, AggregateNewsResult, DerivativesData, DerivativesHistory, DerivativesPoint, Disposable, FlashFeedService, InstrumentAssetClass, InstrumentContract, InstrumentForm, InstrumentRef, Interval, Kline, LeverageSetting, MacroCalendarEntry, MacroFeedService, MacroRateEntry, MarginMode, MarketDataRegistration, MarketDataRegistry, MarketDataService, MarketRouterService, NewsAggregator, NewsItem, Order, OrderRequest, OrderSide, OrderStatus, OrderType, Orderbook, OrderbookLevel, Position, PositionSide, SetLeverageRequest, StockFundamentals, Ticker, TradeFill, TradeRegistration, TradeRegistry, TradeService, TradeTick, TradingNewsRegistry };
@@ -1 +0,0 @@
1
- import "./index.js";