@dshtrading/strategies 0.1.5 → 0.2.0

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.
@@ -22,6 +22,12 @@ const donchianBreakoutStrategy = {
22
22
  compute(bars, params) {
23
23
  const n1 = Math.max(2, Math.round(params.lookbackEntry ?? 20));
24
24
  const n2 = Math.max(1, Math.round(params.lookbackExit ?? 10));
25
+ const fmtPrice = (value) => {
26
+ if (!Number.isFinite(value)) return "—";
27
+ const abs = Math.abs(value);
28
+ if (abs < 1) return value.toFixed(abs >= .01 ? 4 : 6);
29
+ return value.toFixed(Math.abs(Number(value.toFixed(2)) - value) < 1e-9 ? 2 : 3);
30
+ };
25
31
  const signals = [];
26
32
  let inPosition = false;
27
33
  for (let i = Math.max(n1, n2); i < bars.length; i++) {
@@ -37,12 +43,12 @@ const donchianBreakoutStrategy = {
37
43
  action: "entry",
38
44
  direction: "long",
39
45
  price: currentClose,
40
- reason: `收盘价 (${currentClose.toFixed(2)}) 突破前 ${n1} 根最高价 (${highestHigh.toFixed(2)})`,
46
+ reason: `收盘价 (${fmtPrice(currentClose)}) 突破前 ${n1} 根最高价 (${fmtPrice(highestHigh)})`,
41
47
  reasonKey: "strat.donchian-breakout.reason.entry",
42
48
  reasonParams: {
43
- close: currentClose.toFixed(2),
49
+ close: fmtPrice(currentClose),
44
50
  n: n1,
45
- high: highestHigh.toFixed(2)
51
+ high: fmtPrice(highestHigh)
46
52
  }
47
53
  });
48
54
  inPosition = true;
@@ -53,12 +59,12 @@ const donchianBreakoutStrategy = {
53
59
  action: "exit",
54
60
  direction: "flat",
55
61
  price: currentClose,
56
- reason: `收盘价 (${currentClose.toFixed(2)}) 跌破前 ${n2} 根最低价 (${lowestLow.toFixed(2)})`,
62
+ reason: `收盘价 (${fmtPrice(currentClose)}) 跌破前 ${n2} 根最低价 (${fmtPrice(lowestLow)})`,
57
63
  reasonKey: "strat.donchian-breakout.reason.exit",
58
64
  reasonParams: {
59
- close: currentClose.toFixed(2),
65
+ close: fmtPrice(currentClose),
60
66
  n: n2,
61
- low: lowestLow.toFixed(2)
67
+ low: fmtPrice(lowestLow)
62
68
  }
63
69
  });
64
70
  inPosition = false;
@@ -1,11 +1,4 @@
1
- import { ema } from "@dshtrading/indicators";
2
1
  //#region src/paradigms/ema-crossover.ts
3
- /**
4
- * 双均线趋势跟踪策略(波段)。
5
- *
6
- * 入场:EMA(fast) 上穿 EMA(slow) 金叉做多。
7
- * 出场:EMA(fast) 下穿 EMA(slow) 死叉离场。
8
- */
9
2
  const emaCrossoverStrategy = {
10
3
  id: "ema-crossover",
11
4
  horizon: "swing",
@@ -29,9 +22,29 @@ const emaCrossoverStrategy = {
29
22
  compute(bars, params) {
30
23
  const fastP = Math.max(2, Math.round(params.fastPeriod ?? 20));
31
24
  const slowP = Math.max(fastP + 1, Math.round(params.slowPeriod ?? 60));
25
+ const fmtPrice = (value) => {
26
+ if (!Number.isFinite(value)) return "—";
27
+ const abs = Math.abs(value);
28
+ if (abs < 1) return value.toFixed(abs >= .01 ? 4 : 6);
29
+ return value.toFixed(Math.abs(Number(value.toFixed(2)) - value) < 1e-9 ? 2 : 3);
30
+ };
31
+ const emaOf = (values, period) => {
32
+ const out = new Array(values.length).fill(void 0);
33
+ if (!Number.isFinite(period) || period < 1 || values.length < period) return out;
34
+ const k = 2 / (period + 1);
35
+ let seed = 0;
36
+ for (let index = 0; index < period; index++) seed += values[index];
37
+ let prev = seed / period;
38
+ out[period - 1] = prev;
39
+ for (let index = period; index < values.length; index++) {
40
+ prev = values[index] * k + prev * (1 - k);
41
+ out[index] = prev;
42
+ }
43
+ return out;
44
+ };
32
45
  const closes = bars.map((b) => b.close);
33
- const fastEma = ema(closes, fastP);
34
- const slowEma = ema(closes, slowP);
46
+ const fastEma = emaOf(closes, fastP);
47
+ const slowEma = emaOf(closes, slowP);
35
48
  const signals = [];
36
49
  let inPosition = false;
37
50
  for (let i = 1; i < bars.length; i++) {
@@ -47,13 +60,13 @@ const emaCrossoverStrategy = {
47
60
  action: "entry",
48
61
  direction: "long",
49
62
  price: bars[i].close,
50
- reason: `EMA(${fastP}) (${currFast.toFixed(2)}) 上穿 EMA(${slowP}) (${currSlow.toFixed(2)}) 形成金叉`,
63
+ reason: `EMA(${fastP}) (${fmtPrice(currFast)}) 上穿 EMA(${slowP}) (${fmtPrice(currSlow)}) 形成金叉`,
51
64
  reasonKey: "strat.ema-crossover.reason.entry",
52
65
  reasonParams: {
53
66
  fastP,
54
- fast: currFast.toFixed(2),
67
+ fast: fmtPrice(currFast),
55
68
  slowP,
56
- slow: currSlow.toFixed(2)
69
+ slow: fmtPrice(currSlow)
57
70
  }
58
71
  });
59
72
  inPosition = true;
@@ -64,13 +77,13 @@ const emaCrossoverStrategy = {
64
77
  action: "exit",
65
78
  direction: "flat",
66
79
  price: bars[i].close,
67
- reason: `EMA(${fastP}) (${currFast.toFixed(2)}) 下穿 EMA(${slowP}) (${currSlow.toFixed(2)}) 形成死叉`,
80
+ reason: `EMA(${fastP}) (${fmtPrice(currFast)}) 下穿 EMA(${slowP}) (${fmtPrice(currSlow)}) 形成死叉`,
68
81
  reasonKey: "strat.ema-crossover.reason.exit",
69
82
  reasonParams: {
70
83
  fastP,
71
- fast: currFast.toFixed(2),
84
+ fast: fmtPrice(currFast),
72
85
  slowP,
73
- slow: currSlow.toFixed(2)
86
+ slow: fmtPrice(currSlow)
74
87
  }
75
88
  });
76
89
  inPosition = false;
@@ -1,11 +1,4 @@
1
- import { sma } from "@dshtrading/indicators";
2
1
  //#region src/paradigms/momentum-12m.ts
3
- /**
4
- * 12 个月动量择时策略(长线,Fama-French 动量单标的经典化)。
5
- *
6
- * 入场:近 12 个月(约 250 根日 K)累计收益 > 0 且价格高于年线,强动量做多。
7
- * 出场:动量转负或价格跌破年线,动量衰竭离场。
8
- */
9
2
  const momentum12mStrategy = {
10
3
  id: "momentum-12m",
11
4
  horizon: "long",
@@ -21,8 +14,24 @@ const momentum12mStrategy = {
21
14
  }],
22
15
  compute(bars, params) {
23
16
  const lookback = Math.max(10, Math.round(params.lookbackBars ?? 250));
24
- const closes = bars.map((b) => b.close);
25
- const smaValues = sma(closes, lookback);
17
+ const fmtPrice = (value) => {
18
+ if (!Number.isFinite(value)) return "—";
19
+ const abs = Math.abs(value);
20
+ if (abs < 1) return value.toFixed(abs >= .01 ? 4 : 6);
21
+ return value.toFixed(Math.abs(Number(value.toFixed(2)) - value) < 1e-9 ? 2 : 3);
22
+ };
23
+ const smaOf = (values, period) => {
24
+ const out = new Array(values.length).fill(void 0);
25
+ if (!Number.isFinite(period) || period < 1 || values.length < period) return out;
26
+ let sum = 0;
27
+ for (let index = 0; index < values.length; index++) {
28
+ sum += values[index];
29
+ if (index >= period) sum -= values[index - period];
30
+ if (index >= period - 1) out[index] = sum / period;
31
+ }
32
+ return out;
33
+ };
34
+ const smaValues = smaOf(bars.map((b) => b.close), lookback);
26
35
  const signals = [];
27
36
  let inPosition = false;
28
37
  for (let i = lookback; i < bars.length; i++) {
@@ -38,12 +47,12 @@ const momentum12mStrategy = {
38
47
  action: "entry",
39
48
  direction: "long",
40
49
  price: currentClose,
41
- reason: `近 ${lookback} 周期动量为正 (+${(momentumReturn * 100).toFixed(1)}%) 且位于均线 (${currentSma.toFixed(2)}) 之上,确认强动量`,
50
+ reason: `近 ${lookback} 周期动量为正 (+${(momentumReturn * 100).toFixed(1)}%) 且位于均线 (${fmtPrice(currentSma)}) 之上,确认强动量`,
42
51
  reasonKey: "strat.momentum-12m.reason.entry",
43
52
  reasonParams: {
44
53
  n: lookback,
45
54
  pct: (momentumReturn * 100).toFixed(1),
46
- sma: currentSma.toFixed(2)
55
+ sma: fmtPrice(currentSma)
47
56
  }
48
57
  });
49
58
  inPosition = true;
@@ -55,12 +64,12 @@ const momentum12mStrategy = {
55
64
  action: "exit",
56
65
  direction: "flat",
57
66
  price: currentClose,
58
- reason: `${exitCause} (${(momentumReturn * 100).toFixed(1)}% / SMA ${currentSma.toFixed(2)}),动量衰减平仓`,
67
+ reason: `${exitCause} (${(momentumReturn * 100).toFixed(1)}% / SMA ${fmtPrice(currentSma)}),动量衰减平仓`,
59
68
  reasonKey: "strat.momentum-12m.reason.exit",
60
69
  reasonParams: {
61
70
  cause: momentumReturn <= 0 ? "momentumNegative" : "belowBaseline",
62
71
  pct: (momentumReturn * 100).toFixed(1),
63
- sma: currentSma.toFixed(2)
72
+ sma: fmtPrice(currentSma)
64
73
  }
65
74
  });
66
75
  inPosition = false;
@@ -1,11 +1,4 @@
1
- import { rsi } from "@dshtrading/indicators";
2
1
  //#region src/paradigms/rsi-reversion.ts
3
- /**
4
- * 短周期 RSI 极值均值回归策略(短线,Connors 式)。
5
- *
6
- * 入场:RSI(2) < 10 极端超卖入场。
7
- * 出场:RSI(2) > 60 快速反弹获利了结。
8
- */
9
2
  const rsiReversionStrategy = {
10
3
  id: "rsi-reversion",
11
4
  horizon: "short",
@@ -41,8 +34,28 @@ const rsiReversionStrategy = {
41
34
  const period = Math.max(2, Math.round(params.rsiPeriod ?? 2));
42
35
  const enterThresh = Number(params.entryThreshold ?? 10);
43
36
  const exitThresh = Number(params.exitThreshold ?? 60);
44
- const closes = bars.map((b) => b.close);
45
- const rsiValues = rsi(closes, period);
37
+ const rsiOf = (values, period) => {
38
+ const out = new Array(values.length).fill(void 0);
39
+ if (!Number.isFinite(period) || period < 1 || values.length < period) return out;
40
+ let gain = 0;
41
+ let loss = 0;
42
+ for (let index = 1; index <= period; index++) {
43
+ const delta = values[index] - values[index - 1];
44
+ if (delta >= 0) gain += delta;
45
+ else loss -= delta;
46
+ }
47
+ gain /= period;
48
+ loss /= period;
49
+ out[period] = loss === 0 ? 100 : 100 - 100 / (1 + gain / loss);
50
+ for (let index = period + 1; index < values.length; index++) {
51
+ const delta = values[index] - values[index - 1];
52
+ gain = (gain * (period - 1) + Math.max(delta, 0)) / period;
53
+ loss = (loss * (period - 1) + Math.max(-delta, 0)) / period;
54
+ out[index] = loss === 0 ? 100 : 100 - 100 / (1 + gain / loss);
55
+ }
56
+ return out;
57
+ };
58
+ const rsiValues = rsiOf(bars.map((b) => b.close), period);
46
59
  const signals = [];
47
60
  let inPosition = false;
48
61
  for (let i = 0; i < bars.length; i++) {
@@ -1,11 +1,4 @@
1
- import { sma } from "@dshtrading/indicators";
2
1
  //#region src/paradigms/sma-baseline.ts
3
- /**
4
- * 200 日均线择时基线策略(长线,Meb Faber GTAA 经典)。
5
- *
6
- * 入场:收盘价站上 SMA200 牛市生命线,全仓持有。
7
- * 出场:收盘价跌破 SMA200 熊市防线,空仓避险。
8
- */
9
2
  const smaBaselineStrategy = {
10
3
  id: "sma-baseline",
11
4
  horizon: "long",
@@ -21,8 +14,24 @@ const smaBaselineStrategy = {
21
14
  }],
22
15
  compute(bars, params) {
23
16
  const period = Math.max(10, Math.round(params.period ?? 200));
24
- const closes = bars.map((b) => b.close);
25
- const smaValues = sma(closes, period);
17
+ const fmtPrice = (value) => {
18
+ if (!Number.isFinite(value)) return "—";
19
+ const abs = Math.abs(value);
20
+ if (abs < 1) return value.toFixed(abs >= .01 ? 4 : 6);
21
+ return value.toFixed(Math.abs(Number(value.toFixed(2)) - value) < 1e-9 ? 2 : 3);
22
+ };
23
+ const smaOf = (values, period) => {
24
+ const out = new Array(values.length).fill(void 0);
25
+ if (!Number.isFinite(period) || period < 1 || values.length < period) return out;
26
+ let sum = 0;
27
+ for (let index = 0; index < values.length; index++) {
28
+ sum += values[index];
29
+ if (index >= period) sum -= values[index - period];
30
+ if (index >= period - 1) out[index] = sum / period;
31
+ }
32
+ return out;
33
+ };
34
+ const smaValues = smaOf(bars.map((b) => b.close), period);
26
35
  const signals = [];
27
36
  let inPosition = false;
28
37
  for (let i = period - 1; i < bars.length; i++) {
@@ -36,12 +45,12 @@ const smaBaselineStrategy = {
36
45
  action: "entry",
37
46
  direction: "long",
38
47
  price: currentClose,
39
- reason: `收盘价 (${currentClose.toFixed(2)}) 站上长期基线 SMA(${period}) (${currentSma.toFixed(2)}),确立多头趋势`,
48
+ reason: `收盘价 (${fmtPrice(currentClose)}) 站上长期基线 SMA(${period}) (${fmtPrice(currentSma)}),确立多头趋势`,
40
49
  reasonKey: "strat.sma-baseline.reason.entry",
41
50
  reasonParams: {
42
- close: currentClose.toFixed(2),
51
+ close: fmtPrice(currentClose),
43
52
  period,
44
- sma: currentSma.toFixed(2)
53
+ sma: fmtPrice(currentSma)
45
54
  }
46
55
  });
47
56
  inPosition = true;
@@ -52,12 +61,12 @@ const smaBaselineStrategy = {
52
61
  action: "exit",
53
62
  direction: "flat",
54
63
  price: currentClose,
55
- reason: `收盘价 (${currentClose.toFixed(2)}) 跌破长期基线 SMA(${period}) (${currentSma.toFixed(2)}),转入防御避险`,
64
+ reason: `收盘价 (${fmtPrice(currentClose)}) 跌破长期基线 SMA(${period}) (${fmtPrice(currentSma)}),转入防御避险`,
56
65
  reasonKey: "strat.sma-baseline.reason.exit",
57
66
  reasonParams: {
58
- close: currentClose.toFixed(2),
67
+ close: fmtPrice(currentClose),
59
68
  period,
60
- sma: currentSma.toFixed(2)
69
+ sma: fmtPrice(currentSma)
61
70
  }
62
71
  });
63
72
  inPosition = false;
package/lib/plugin.d.ts CHANGED
@@ -1,15 +1,28 @@
1
- import { StrategyDefinition } from "./types.js";
1
+ import { StrategyDefinition, StrategyParamSpec } from "./types.js";
2
2
  import { CustomStrategyRecord, CustomStrategyStore } from "./custom.js";
3
+ import { ScreenerColumnSpec, ScreenerDefinition } from "./screeners/types.js";
4
+ import { CustomScreenerRecord, CustomScreenerStore } from "./custom-screener.js";
5
+ import { isBuiltinScreenerId, isBuiltinStrategyId } from "./management.js";
6
+ import { BuiltinTombstonesStore } from "./builtin-tombstones.js";
3
7
  import { MarketDataService } from "./api/lib/index.js";
4
8
  import { createFileCustomStrategyStore } from "./custom-fs.js";
5
- import { Context } from "@deepseek-ai/cordis";
9
+ import { createFileBuiltinTombstonesStore } from "./builtin-tombstones-fs.js";
10
+ import { createFileCustomScreenerStore } from "./custom-screener-fs.js";
11
+ import { validateCustomScreenerNode, validateCustomStrategyNode } from "./validate-node.js";
12
+ import { Context, Service } from "@deepseek-ai/cordis";
6
13
  //#region src/plugin.d.ts
7
14
  /** Cordis 插件名 = patch 行 id(TEMPLATES §8),市场无关共享行命名空间。 */
8
15
  declare const name = "dsh-trading-strategies";
9
16
  /** 本插件不硬依赖任何服务(headless 宿主零要求);tools/注册表经 ctx.inject 声明。 */
10
17
  declare const inject: string[];
11
- /** 默认存储路径:~/.dsh/strategies/custom.json。 */
18
+ /** 默认存储路径:$DSH_HOME/strategies/custom.json(缺省 ~/.dsh)。 */
12
19
  declare function defaultStorePath(): string;
20
+ /** 默认墓碑路径:$DSH_HOME/strategies/builtin-tombstones.json(策略管理)。 */
21
+ declare function defaultTombstonesStorePath(): string;
22
+ /** 默认自定义选股器路径:$DSH_HOME/strategies/custom-screeners.json(选股器管理)。 */
23
+ declare function defaultScreenerStorePath(): string;
24
+ /** SDK 服务键:自定义策略 store 单实例(桥与工具共享同一缓存)。 */
25
+ declare const TRADING_STRATEGIES_KEY = "tradingStrategies";
13
26
  /** tradingEvents 的最小发布面(鸭式,不定死接口;总线缺席时静默降级)。 */
14
27
  interface TradingEventsPublisher {
15
28
  emit(store: 'strategies'): void;
@@ -21,24 +34,167 @@ interface StrategyMarketDataResolver {
21
34
  declare function createMarketDataResolver(ctx: Context): StrategyMarketDataResolver;
22
35
  interface StrategyAuthorToolOptions {
23
36
  store: CustomStrategyStore;
37
+ /** 可选:墓碑表(覆盖内置 id 时顺带恢复删除标记,策略管理语义)。 */
38
+ tombstones?: BuiltinTombstonesStore;
24
39
  /** 可选:策略成功落盘后的回调(issue #30:事件总线 emit('strategies') 接线点)。 */
25
40
  onWritten?: (record: CustomStrategyRecord) => void;
26
41
  }
27
42
  /** strategy_author 工厂(独立导出便于单测)。 */
28
43
  declare function createStrategyAuthorTool(options: StrategyAuthorToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
44
+ interface StrategyListToolOptions {
45
+ store: CustomStrategyStore;
46
+ /** 可选:墓碑表(内置删除状态回显)。 */
47
+ tombstones?: BuiltinTombstonesStore;
48
+ }
49
+ /**
50
+ * strategy_list 工厂(issue #86 / G3):名册 + 覆盖 + 墓碑三态一次读全。
51
+ * 此前 strategy_delete / strategy_backtest 都要 id 而 agent 拿不到 id,
52
+ * 只能让用户口述或去读 ~/.dsh-trading/strategies/custom.json。
53
+ */
54
+ declare function createStrategyListTool(options: StrategyListToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
55
+ interface ScreenerListToolOptions {
56
+ store: CustomScreenerStore;
57
+ /** 可选:墓碑表(内置选股器删除状态回显)。 */
58
+ tombstones?: BuiltinTombstonesStore;
59
+ }
60
+ /** screener_list 工厂(issue #86 / G3):选股器名册三态一次读全。 */
61
+ declare function createScreenerListTool(options: ScreenerListToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
29
62
  interface StrategyBacktestToolOptions {
30
63
  store: CustomStrategyStore;
64
+ /** 可选:墓碑表(内置删除后回测拒绝并引导恢复,策略管理语义)。 */
65
+ tombstones?: BuiltinTombstonesStore;
31
66
  marketData: StrategyMarketDataResolver;
32
67
  }
33
- /** 自定义或范式策略 → 回测用 StrategyDefinition(自定义 compute 经编译落定)。 */
34
- declare function resolveStrategyDefinition(store: CustomStrategyStore, strategyId: string): Promise<StrategyDefinition | undefined>;
68
+ /**
69
+ * 自定义或范式策略 → 回测用 StrategyDefinition(自定义 compute 经编译落定)。
70
+ * 覆盖 + 墓碑合成:store 同 id 记录优先(含内置覆盖);墓碑命中的 id 一律
71
+ * 视为不存在(含误留覆盖记录的边界——删除语义优先)。
72
+ */
73
+ declare function resolveStrategyDefinition(store: CustomStrategyStore, strategyId: string, options?: {
74
+ tombstones?: BuiltinTombstonesStore | undefined;
75
+ }): Promise<StrategyDefinition | undefined>;
35
76
  interface StrategyBacktestToolDeps {
36
77
  store: CustomStrategyStore;
78
+ tombstones?: BuiltinTombstonesStore;
37
79
  marketData: StrategyMarketDataResolver;
38
80
  }
39
81
  /** strategy_backtest 工厂(独立导出便于单测)。 */
40
82
  declare function createStrategyBacktestTool(deps: StrategyBacktestToolDeps): import("@deepseek-ai/dsh-tools").ToolDefinition;
41
- /** Host plugin body:注册 strategy_author / strategy_backtest(host 平面,全会话可见)。 */
83
+ interface StrategyDeleteToolOptions {
84
+ store: CustomStrategyStore;
85
+ tombstones?: BuiltinTombstonesStore;
86
+ /** 可选:删除成功后的回调(emit('strategies') 接线点;幂等未命中也通知,GUI 对账无害)。 */
87
+ onDeleted?: (id: string, scope: 'custom' | 'builtin', removed: boolean) => void;
88
+ }
89
+ /**
90
+ * strategy_delete 工厂(策略管理):自定义策略 = 移除记录;内置范式 = 落墓碑
91
+ * (出厂代码不动,strategy_reset 可恢复)。覆盖 + 墓碑模型见 management.ts。
92
+ */
93
+ declare function createStrategyDeleteTool(options: StrategyDeleteToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
94
+ interface StrategyResetToolOptions {
95
+ store: CustomStrategyStore;
96
+ tombstones?: BuiltinTombstonesStore;
97
+ /** 可选:恢复成功后的回调(emit('strategies') 接线点)。 */
98
+ onReset?: (id: string, changed: boolean) => void;
99
+ }
100
+ /**
101
+ * strategy_reset 工厂(策略管理):恢复内置策略出厂默认——清覆盖记录与墓碑。
102
+ * 仅对内置范式 id 有意义;自定义策略无出厂版本,明确报错引导 strategy_delete。
103
+ */
104
+ declare function createStrategyResetTool(options: StrategyResetToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
105
+ interface ScreenerAuthorToolOptions {
106
+ store: CustomScreenerStore;
107
+ /** 可选:墓碑表(覆盖内置选股器时顺带恢复删除标记)。 */
108
+ tombstones?: BuiltinTombstonesStore;
109
+ /** 可选:选股器成功落盘后的回调(emit('strategies') 接线点)。 */
110
+ onWritten?: (record: CustomScreenerRecord) => void;
111
+ }
112
+ /**
113
+ * screener_author 工厂(选股器管理):提交 → 结构/语法/多场景试算校验(vm 熔断)
114
+ * → 落盘。evaluate(bars, params) 返回 ScreenerMatch | null(单时点截面判断,
115
+ * 无信号序列语义)。id 为内置选股器('scr.*')→ 覆盖该内置(strategy_reset/
116
+ * screener_reset 可恢复出厂)。
117
+ */
118
+ declare function createScreenerAuthorTool(options: ScreenerAuthorToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
119
+ interface ScreenerDeleteToolOptions {
120
+ store: CustomScreenerStore;
121
+ tombstones?: BuiltinTombstonesStore;
122
+ /** 可选:删除成功后的回调(emit('strategies') 接线点)。 */
123
+ onDeleted?: (id: string, scope: 'custom' | 'builtin', removed: boolean) => void;
124
+ }
125
+ /**
126
+ * screener_delete 工厂(选股器管理):自定义选股器 = 移除记录;内置选股器 =
127
+ * 落墓碑(出厂代码不动,screener_reset 可恢复)。与 strategy_delete 共用墓碑表。
128
+ */
129
+ declare function createScreenerDeleteTool(options: ScreenerDeleteToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
130
+ interface ScreenerResetToolOptions {
131
+ store: CustomScreenerStore;
132
+ tombstones?: BuiltinTombstonesStore;
133
+ /** 可选:恢复成功后的回调(emit('strategies') 接线点)。 */
134
+ onReset?: (id: string, changed: boolean) => void;
135
+ }
136
+ /**
137
+ * screener_reset 工厂(选股器管理):恢复内置选股器出厂默认——清覆盖记录与墓碑。
138
+ * 仅对内置选股器 id 有意义;自定义选股器无出厂版本,明确报错引导 screener_delete。
139
+ */
140
+ declare function createScreenerResetTool(options: ScreenerResetToolOptions): import("@deepseek-ai/dsh-tools").ToolDefinition;
141
+ /** Host plugin body:provide store 服务 + 注册策略工具族(host 平面,全会话可见)。 */
42
142
  declare function apply(ctx: Context): void;
143
+ /** 解析后的选股器:内置(可信代码)或自定义(源码经 vm 熔断 runner 执行)。 */
144
+ type ResolvedScreener = {
145
+ kind: 'builtin';
146
+ definition: ScreenerDefinition;
147
+ } | {
148
+ kind: 'custom';
149
+ id: string;
150
+ name: string;
151
+ summary: string;
152
+ params: StrategyParamSpec[];
153
+ columns: ScreenerColumnSpec[];
154
+ evaluateSource: string;
155
+ };
156
+ /**
157
+ * 选股器解析(覆盖 + 墓碑合成,与 resolveStrategyDefinition 同模型):
158
+ * 墓碑命中 → undefined;自定义记录优先(覆盖内置同 id);损坏记录回落内置
159
+ * 出厂定义,非内置损坏记录视为不存在(与名册语义一致)。
160
+ */
161
+ declare function resolveScreener(store: CustomScreenerStore, screenerId: string, options?: {
162
+ tombstones?: BuiltinTombstonesStore | undefined;
163
+ }): Promise<ResolvedScreener | undefined>;
164
+ interface ScreenerRunToolDeps {
165
+ store: CustomScreenerStore;
166
+ tombstones?: BuiltinTombstonesStore;
167
+ /** registry-first 解析(provider 标签 + 行情服务)。 */
168
+ active: (market: string) => {
169
+ provider: string;
170
+ service: MarketDataService;
171
+ } | undefined;
172
+ /** 老部署回退:市场键直读行情服务(无 registry 时)。 */
173
+ fallback?: (market: string) => MarketDataService | undefined;
174
+ /** 单次扫描总预算 ms(缺省 90s;测试注入小值/假时钟)。 */
175
+ budgetMs?: number;
176
+ /** 注入时钟(测试用;缺省 Date.now)。 */
177
+ now?: () => number;
178
+ }
179
+ /**
180
+ * screener_run 工厂(issue #86 / G4):把选股器从「只对 UI 可用」提升为 agent 可跑。
181
+ * 扫描调度 = 名册 → 截断扫描池 → 受限并发拉 500 根日 K → 纯函数 evaluate;
182
+ * 数据不足(evaluate 返回 null)静默跳过,单标的失败只计数不中断。
183
+ * 只读行情 + 本地计算,无交易语义(铁律 #3 不涉及)。
184
+ */
185
+ declare function createScreenerRunTool(deps: ScreenerRunToolDeps): import("@deepseek-ai/dsh-tools").ToolDefinition;
186
+ /** 策略 store 服务(桥与工具的单实例共享点,issue #33 收口模式)。 */
187
+ declare class StrategiesStoreService extends Service {
188
+ readonly store: CustomStrategyStore;
189
+ /** 内置墓碑表(策略/选股器管理共用;桥 DELETE/POST /strategies/* 与工具共享)。 */
190
+ readonly tombstones: BuiltinTombstonesStore;
191
+ /** 自定义选股器 store(选股器管理)。 */
192
+ readonly screenerStore: CustomScreenerStore;
193
+ constructor(ctx: Context, deps: {
194
+ store: CustomStrategyStore;
195
+ tombstones: BuiltinTombstonesStore;
196
+ screenerStore: CustomScreenerStore;
197
+ }, serviceName?: string);
198
+ }
43
199
  //#endregion
44
- export { StrategyAuthorToolOptions, StrategyBacktestToolDeps, StrategyBacktestToolOptions, StrategyMarketDataResolver, TradingEventsPublisher, apply, createFileCustomStrategyStore, createMarketDataResolver, createStrategyAuthorTool, createStrategyBacktestTool, defaultStorePath, inject, name, resolveStrategyDefinition };
200
+ export { ResolvedScreener, ScreenerAuthorToolOptions, ScreenerDeleteToolOptions, ScreenerListToolOptions, ScreenerResetToolOptions, ScreenerRunToolDeps, StrategiesStoreService, StrategyAuthorToolOptions, StrategyBacktestToolDeps, StrategyBacktestToolOptions, StrategyDeleteToolOptions, StrategyListToolOptions, StrategyMarketDataResolver, StrategyResetToolOptions, TRADING_STRATEGIES_KEY, TradingEventsPublisher, apply, createFileBuiltinTombstonesStore, createFileCustomScreenerStore, createFileCustomStrategyStore, createMarketDataResolver, createScreenerAuthorTool, createScreenerDeleteTool, createScreenerListTool, createScreenerResetTool, createScreenerRunTool, createStrategyAuthorTool, createStrategyBacktestTool, createStrategyDeleteTool, createStrategyListTool, createStrategyResetTool, defaultScreenerStorePath, defaultStorePath, defaultTombstonesStorePath, inject, isBuiltinScreenerId, isBuiltinStrategyId, name, resolveScreener, resolveStrategyDefinition, validateCustomScreenerNode, validateCustomStrategyNode };