stock-sdk 2.3.0 → 2.4.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.
- package/README.md +3 -1
- package/dist/{adapters-BiH4keVd.d.cts → adapters-CZUNRbxi.d.cts} +29 -1
- package/dist/{adapters-BiH4keVd.d.ts → adapters-CZUNRbxi.d.ts} +29 -1
- package/dist/cache.cjs +1 -1
- package/dist/cache.d.cts +35 -3
- package/dist/cache.d.ts +35 -3
- package/dist/cache.js +1 -1
- package/dist/chunk-46ECBWOQ.cjs +1 -0
- package/dist/chunk-4RN7AVFQ.js +1 -0
- package/dist/chunk-6FZNYDNN.cjs +1 -0
- package/dist/chunk-6MCPZDKN.cjs +1 -0
- package/dist/chunk-H5FDXQRK.js +1 -0
- package/dist/chunk-HKREKO2F.js +1 -0
- package/dist/chunk-LDYRNH4X.cjs +1 -0
- package/dist/chunk-OPCBE3NR.js +1 -0
- package/dist/chunk-R7M3BHS4.cjs +1 -0
- package/dist/chunk-SMCYQ6NL.js +1 -0
- package/dist/chunk-USZUOBM2.cjs +1 -0
- package/dist/chunk-VKXPLWPL.js +1 -0
- package/dist/chunk-W625VPPB.js +1 -0
- package/dist/chunk-WJOFTWZL.cjs +1 -0
- package/dist/cli.cjs +16 -16
- package/dist/cli.js +16 -16
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +23 -7
- package/dist/index.d.ts +23 -7
- package/dist/index.js +1 -1
- package/dist/indicators.cjs +1 -1
- package/dist/indicators.js +1 -1
- package/dist/mcp.cjs +83 -5
- package/dist/mcp.d.cts +28 -2
- package/dist/mcp.d.ts +28 -2
- package/dist/mcp.js +83 -5
- package/dist/screener.cjs +1 -1
- package/dist/screener.d.cts +51 -16
- package/dist/screener.d.ts +51 -16
- package/dist/screener.js +1 -1
- package/dist/{sdk-CtqVdrof.d.ts → sdk-DGgSDZRx.d.ts} +67 -16
- package/dist/{sdk-6Qozazec.d.cts → sdk-RzQ-drS4.d.cts} +67 -16
- package/dist/signals.d.cts +3 -42
- package/dist/signals.d.ts +3 -42
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.d.cts +2 -2
- package/dist/symbols.d.ts +2 -2
- package/dist/symbols.js +1 -1
- package/dist/types-7n5rSHbd.d.cts +42 -0
- package/dist/types-7n5rSHbd.d.ts +42 -0
- package/package.json +2 -1
- package/dist/chunk-3ASJZ3YA.js +0 -1
- package/dist/chunk-6IFA2HGD.cjs +0 -1
- package/dist/chunk-C4CA7QBB.cjs +0 -1
- package/dist/chunk-DU7MCVLJ.js +0 -1
- package/dist/chunk-F3KZXCVO.js +0 -1
- package/dist/chunk-IWHFCLJM.js +0 -1
- package/dist/chunk-LCX5Q36O.js +0 -1
- package/dist/chunk-LMUFSVKQ.cjs +0 -1
- package/dist/chunk-QGYIBFXP.js +0 -1
- package/dist/chunk-T5CH7H3X.cjs +0 -1
- package/dist/chunk-TVIEKKDQ.cjs +0 -1
- package/dist/chunk-WCBK5KYA.cjs +0 -1
- package/dist/chunk-ZESMWMHP.cjs +0 -1
- package/dist/chunk-ZZ2IBUMV.js +0 -1
package/dist/mcp.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { S as StockSDK, R as RequestClientOptions } from './sdk-
|
|
1
|
+
import { S as StockSDK, R as RequestClientOptions } from './sdk-RzQ-drS4.cjs';
|
|
2
2
|
import './index-mlzPfoON.cjs';
|
|
3
3
|
import './chip-BbA23rr1.cjs';
|
|
4
4
|
import './addIndicators-_V3HEi0q.cjs';
|
|
5
|
+
import './types-7n5rSHbd.cjs';
|
|
5
6
|
|
|
6
7
|
type JsonRpcId = string | number | null;
|
|
7
8
|
interface JsonRpcRequest {
|
|
@@ -22,6 +23,25 @@ interface JsonRpcResponse {
|
|
|
22
23
|
error?: JsonRpcError;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/** MCP `prompts/list` 里单个 argument 的形状。 */
|
|
27
|
+
interface PromptArgumentDef {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
required: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** 一个派生后的 MCP prompt 定义。 */
|
|
33
|
+
interface PromptDef {
|
|
34
|
+
name: string;
|
|
35
|
+
title: string;
|
|
36
|
+
description: string;
|
|
37
|
+
tier: 'core' | 'full';
|
|
38
|
+
arguments: PromptArgumentDef[];
|
|
39
|
+
/** contract 测试用(不进 prompts/list 输出)。 */
|
|
40
|
+
usesTools: string[];
|
|
41
|
+
/** 把用户传入的 arguments 插值成任务说明书文本;必填缺失抛 InvalidArgumentError。 */
|
|
42
|
+
render: (args: Record<string, string>) => string;
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
/**
|
|
26
46
|
* MCP 工具描述符与 JSON Schema 类型(零依赖手写)。
|
|
27
47
|
*
|
|
@@ -69,10 +89,14 @@ interface ToolDef {
|
|
|
69
89
|
* 分发逻辑抽成纯函数 `dispatchMessage`(可单测);`startMcpServer` 负责 transport 绑定。
|
|
70
90
|
*/
|
|
71
91
|
|
|
92
|
+
/** 技能集范围:'core'(默认) / 'full' / 指定 name 列表(与工具集独立过滤)。 */
|
|
93
|
+
type PromptTier = 'core' | 'full';
|
|
72
94
|
interface DispatchContext {
|
|
73
95
|
sdk: StockSDK;
|
|
74
96
|
tools: ToolDef[];
|
|
75
97
|
toolMap: Map<string, ToolDef>;
|
|
98
|
+
prompts: PromptDef[];
|
|
99
|
+
promptMap: Map<string, PromptDef>;
|
|
76
100
|
}
|
|
77
101
|
/**
|
|
78
102
|
* 处理单条 JSON-RPC 请求,返回响应;通知类(无需回应)返回 `null`。
|
|
@@ -82,6 +106,8 @@ declare function dispatchMessage(msg: JsonRpcRequest, ctx: DispatchContext): Pro
|
|
|
82
106
|
interface McpServerOptions {
|
|
83
107
|
/** 工具集范围:'core'(默认) / 'full' / 指定 name 列表 */
|
|
84
108
|
tools?: ToolTier | string[];
|
|
109
|
+
/** 技能集范围:'core'(默认) / 'full' / 指定 name 列表(与工具集独立) */
|
|
110
|
+
prompts?: PromptTier | string[];
|
|
85
111
|
/**
|
|
86
112
|
* 透传给 StockSDK 的请求治理配置(timeout / retry / rateLimit / circuitBreaker / providerPolicies 等)。
|
|
87
113
|
* 也可通过环境变量 STOCK_SDK_MCP_TIMEOUT 单独设置超时(毫秒)。
|
|
@@ -91,4 +117,4 @@ interface McpServerOptions {
|
|
|
91
117
|
/** 启动 MCP server(监听 stdin,直到 stdin 关闭后 event loop 自然退出) */
|
|
92
118
|
declare function startMcpServer(options?: McpServerOptions): void;
|
|
93
119
|
|
|
94
|
-
export { type DispatchContext, type McpServerOptions, dispatchMessage, startMcpServer };
|
|
120
|
+
export { type DispatchContext, type McpServerOptions, type PromptTier, dispatchMessage, startMcpServer };
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { S as StockSDK, R as RequestClientOptions } from './sdk-
|
|
1
|
+
import { S as StockSDK, R as RequestClientOptions } from './sdk-DGgSDZRx.js';
|
|
2
2
|
import './index-mlzPfoON.js';
|
|
3
3
|
import './chip-BbA23rr1.js';
|
|
4
4
|
import './addIndicators-_V3HEi0q.js';
|
|
5
|
+
import './types-7n5rSHbd.js';
|
|
5
6
|
|
|
6
7
|
type JsonRpcId = string | number | null;
|
|
7
8
|
interface JsonRpcRequest {
|
|
@@ -22,6 +23,25 @@ interface JsonRpcResponse {
|
|
|
22
23
|
error?: JsonRpcError;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/** MCP `prompts/list` 里单个 argument 的形状。 */
|
|
27
|
+
interface PromptArgumentDef {
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
required: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** 一个派生后的 MCP prompt 定义。 */
|
|
33
|
+
interface PromptDef {
|
|
34
|
+
name: string;
|
|
35
|
+
title: string;
|
|
36
|
+
description: string;
|
|
37
|
+
tier: 'core' | 'full';
|
|
38
|
+
arguments: PromptArgumentDef[];
|
|
39
|
+
/** contract 测试用(不进 prompts/list 输出)。 */
|
|
40
|
+
usesTools: string[];
|
|
41
|
+
/** 把用户传入的 arguments 插值成任务说明书文本;必填缺失抛 InvalidArgumentError。 */
|
|
42
|
+
render: (args: Record<string, string>) => string;
|
|
43
|
+
}
|
|
44
|
+
|
|
25
45
|
/**
|
|
26
46
|
* MCP 工具描述符与 JSON Schema 类型(零依赖手写)。
|
|
27
47
|
*
|
|
@@ -69,10 +89,14 @@ interface ToolDef {
|
|
|
69
89
|
* 分发逻辑抽成纯函数 `dispatchMessage`(可单测);`startMcpServer` 负责 transport 绑定。
|
|
70
90
|
*/
|
|
71
91
|
|
|
92
|
+
/** 技能集范围:'core'(默认) / 'full' / 指定 name 列表(与工具集独立过滤)。 */
|
|
93
|
+
type PromptTier = 'core' | 'full';
|
|
72
94
|
interface DispatchContext {
|
|
73
95
|
sdk: StockSDK;
|
|
74
96
|
tools: ToolDef[];
|
|
75
97
|
toolMap: Map<string, ToolDef>;
|
|
98
|
+
prompts: PromptDef[];
|
|
99
|
+
promptMap: Map<string, PromptDef>;
|
|
76
100
|
}
|
|
77
101
|
/**
|
|
78
102
|
* 处理单条 JSON-RPC 请求,返回响应;通知类(无需回应)返回 `null`。
|
|
@@ -82,6 +106,8 @@ declare function dispatchMessage(msg: JsonRpcRequest, ctx: DispatchContext): Pro
|
|
|
82
106
|
interface McpServerOptions {
|
|
83
107
|
/** 工具集范围:'core'(默认) / 'full' / 指定 name 列表 */
|
|
84
108
|
tools?: ToolTier | string[];
|
|
109
|
+
/** 技能集范围:'core'(默认) / 'full' / 指定 name 列表(与工具集独立) */
|
|
110
|
+
prompts?: PromptTier | string[];
|
|
85
111
|
/**
|
|
86
112
|
* 透传给 StockSDK 的请求治理配置(timeout / retry / rateLimit / circuitBreaker / providerPolicies 等)。
|
|
87
113
|
* 也可通过环境变量 STOCK_SDK_MCP_TIMEOUT 单独设置超时(毫秒)。
|
|
@@ -91,4 +117,4 @@ interface McpServerOptions {
|
|
|
91
117
|
/** 启动 MCP server(监听 stdin,直到 stdin 关闭后 event loop 自然退出) */
|
|
92
118
|
declare function startMcpServer(options?: McpServerOptions): void;
|
|
93
119
|
|
|
94
|
-
export { type DispatchContext, type McpServerOptions, dispatchMessage, startMcpServer };
|
|
120
|
+
export { type DispatchContext, type McpServerOptions, type PromptTier, dispatchMessage, startMcpServer };
|
package/dist/mcp.js
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
|
-
import{a as
|
|
2
|
-
`)}function
|
|
3
|
-
`)}var
|
|
4
|
-
`);for(;n>=0;){let
|
|
5
|
-
`)}!t
|
|
1
|
+
import{a as C,d as I,e as N,g as J,h as $,j,m as q,n as K}from"./chunk-HKREKO2F.js";import{t as x}from"./chunk-H5FDXQRK.js";import"./chunk-W625VPPB.js";import"./chunk-4RN7AVFQ.js";import"./chunk-LAT6L2TO.js";import"./chunk-SMCYQ6NL.js";import{e as k,i as v}from"./chunk-UBIQBXQ7.js";import"./chunk-WOT6VMZA.js";var L=["2025-11-25","2025-06-18"],ie=L[0],w={name:"stock-sdk",version:"2.4.0"},Y=-32700,S=-32600,V=-32601,m=-32602,F=-32603;function z(e){return typeof e=="string"&&L.includes(e)?e:ie}function f(...e){process.stderr.write(e.map(t=>typeof t=="string"?t:JSON.stringify(t)).join(" ")+`
|
|
2
|
+
`)}function y(e){process.stdout.write(JSON.stringify(e)+`
|
|
3
|
+
`)}var D=1e6;function U(e){let t="",r=!1;process.stdin.setEncoding("utf8"),process.stdin.on("data",o=>{t+=o;let n=t.indexOf(`
|
|
4
|
+
`);for(;n>=0;){let s=t.slice(0,n);t=t.slice(n+1),r?(r=!1,y({jsonrpc:"2.0",id:null,error:{code:S,message:`Message too large (limit ${D} bytes)`}})):s.trim()&&e(s),n=t.indexOf(`
|
|
5
|
+
`)}!r&&t.length>D?(f(`[stock-sdk mcp] \u8F93\u5165\u8D85\u957F\uFF08${t.length} bytes \u65E0\u6362\u884C\uFF0C\u8D85 ${D} \u4E0A\u9650\uFF09\uFF0C\u4E22\u5F03\u81F3\u4E0B\u4E00\u6362\u884C`),r=!0,t=""):r&&(t="")}),process.stdin.on("error",o=>f("[stock-sdk mcp] stdin error:",o.message)),process.stdout.on("error",o=>{o.code==="EPIPE"?(f("[stock-sdk mcp] stdout EPIPE\uFF08client \u65AD\u5F00\uFF09\uFF0C\u9000\u51FA"),process.exit(0)):f("[stock-sdk mcp] stdout error:",o.message)})}function O(e,t){if(e)return Array.isArray(e)&&e.length===0?"core":e;let r=process.env[t];if(!r)return"core";let o=r.trim().toLowerCase();if(o==="core"||o==="full")return o;let n=r.split(",").map(s=>s.trim()).filter(Boolean);return n.length>0?n:"core"}function b(e,t){if(Array.isArray(t)){let r=new Set(t);return e.filter(o=>r.has(o.name))}return t==="full"?[...e]:e.filter(r=>r.tier==="core")}var se={string:"string",enum:"string",number:"integer",boolean:"boolean"};function A(e){return e.jsonKey??e.field??e.flag}function ae(e){return e.enum?.map(t=>e.map?.[t]??t)}function _(e){let t=se[e.type];if(!t)throw new Error(`\u53C2\u6570 --${e.flag} \u7684\u7C7B\u578B ${e.type} \u4E0D\u80FD\u6D3E\u751F MCP schema\uFF08\u8BF7\u6807\u8BB0 mcp:false\uFF09`);let r={type:t},o=ae(e);o&&(r.enum=o),e.default!==void 0&&(r.default=e.default);let n=e.mcpDesc??e.desc;return n&&(r.description=n),r}function ce(e){let t={type:"string"};return e.enum&&(t.enum=e.enum),e.default!==void 0&&(t.default=e.default),e.desc&&(t.description=e.desc),t}var B={type:"array",items:{type:"string"},description:"\u4EE3\u7801\u6570\u7EC4\uFF0C\u5E26\u4E0D\u5E26\u4EA4\u6613\u6240\u524D\u7F00\u5747\u53EF\uFF08A \u80A1 '600036'/'sh600519'\uFF1B\u6E2F\u80A1 '00700'/'hk00700'\uFF1B\u7F8E\u80A1 'AAPL'/'usAAPL'\uFF09\u3002A \u80A1\u6307\u6570\u9700\u5E26\u524D\u7F00\uFF08\u5982 'sh000001'\uFF09\uFF1B\u65E0\u6CD5\u8BC6\u522B\u6216\u8BE5\u6570\u636E\u6E90\u4E0D\u8986\u76D6\u7684\u4EE3\u7801\uFF08\u5982\u4E2D\u8BC1\u7279\u6B8A\u6307\u6570\uFF09\u4F1A\u88AB\u8DF3\u8FC7\u800C\u975E\u62A5\u9519"};function W(e){return(e.params??[]).filter(t=>t.mcp!==!1)}function le(e){let t={},r=[];(e.argShape==="codes[]"||e.argShape==="codes+options")&&(t.codes=e.codesDesc?{...B,description:e.codesDesc}:B,r.push("codes"));for(let o of e.positional??[])t[o.name]=ce(o),o.required&&r.push(o.name);for(let o of W(e))t[A(o)]=_(o),o.required&&r.push(A(o));return{type:"object",properties:t,required:r.length>0?r:void 0,additionalProperties:!1}}function de(e,t){let r=C(e,t);if(!r)throw new Error(`SDK \u4E0A\u4E0D\u5B58\u5728\u65B9\u6CD5: ${t.join(".")}`);return o=>r.fn.apply(r.parent,o)}function ue(e){let t=e.positional??[],r=W(e);return(o,n)=>{let s=de(o,e.path),u=t.map(c=>n[c.name]),i={};for(let c of r)i[c.field??c.flag]=n[A(c)];switch(e.argShape){case"none":return s([]);case"codes[]":return s([n.codes]);case"codes+options":return s([n.codes,i]);case"symbol+options":return s([...u,i]);case"options":return s([i]);case"positional":return s(u);default:throw new Error(`\u4E0D\u652F\u6301\u7684 argShape: ${e.argShape}`)}}}function X(e){if(e.mcp===!1||!e.toolName)throw new Error(`\u65B9\u6CD5 ${e.path.join(".")} \u672A\u58F0\u660E MCP \u5DE5\u5177\uFF0C\u4E0D\u80FD\u6D3E\u751F ToolDef`);if(e.mcpCustom)throw new Error(`\u65B9\u6CD5 ${e.path.join(".")} \u7684 MCP \u5DE5\u5177\u4E3A\u624B\u5199\u5B9E\u73B0\uFF0C\u4E0D\u80FD\u81EA\u52A8\u6D3E\u751F`);return{name:e.toolName,description:e.mcpDesc??e.summary,inputSchema:le(e),tier:e.tier??"full",invoke:ue(e)}}var pe=["ma","macd","boll","kdj","rsi","wr","bias","cci","atr","obv","roc","dmi","sar","kc"],R=K("kline.withIndicators"),H={name:R.toolName,tier:R.tier??"full",description:R.mcpDesc??R.summary,inputSchema:{type:"object",properties:{symbol:{type:"string",description:"\u80A1\u7968\u4EE3\u7801\u3002A \u80A1 / \u6E2F\u80A1\u88F8\u7801\u5373\u53EF\uFF08600519 / 00700\uFF09\uFF1B\u7F8E\u80A1\u652F\u6301\u88F8 ticker\uFF08AAPL\uFF09\u6216 secid\uFF08105.AAPL\uFF09"},market:_(j),period:_(N),adjust:_(I),startDate:{..._(J),description:"\u8D77\u59CB\u65E5\u671F YYYYMMDD \u6216 YYYY-MM-DD"},endDate:{..._($),description:"\u7ED3\u675F\u65E5\u671F YYYYMMDD \u6216 YYYY-MM-DD"},indicators:{type:"object",description:"\u6307\u6807\u914D\u7F6E\u5BF9\u8C61\uFF0C\u952E\u53D6\u81EA "+pe.join(" / ")+"\uFF1B\u503C\u4E3A true\uFF08\u9ED8\u8BA4\u53C2\u6570\uFF09\u6216\u914D\u7F6E\u5BF9\u8C61\u3002\u820D\u5165\u578B\u6307\u6807\u53EF\u4F20 decimals \u6307\u5B9A\u8F93\u51FA\u5C0F\u6570\u4F4D\uFF08\u9ED8\u8BA4 3\uFF09",properties:{ma:{description:"MA \u5747\u7EBF\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { periods: [5,10,20], type: sma|ema|wma, decimals }"},macd:{description:"MACD\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { short, long, signal, decimals }"},boll:{description:"\u5E03\u6797\u5E26\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, stdDev, decimals }"},kdj:{description:"KDJ\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, kPeriod, dPeriod, decimals }"},rsi:{description:"RSI\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { periods, decimals }"},wr:{description:"\u5A01\u5EC9\u6307\u6807 WR\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { periods, decimals }"},bias:{description:"BIAS \u4E56\u79BB\u7387\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { periods, decimals }"},cci:{description:"CCI\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, decimals }"},atr:{description:"ATR \u771F\u5B9E\u6CE2\u5E45\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, decimals }"},obv:{description:"OBV\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { maPeriod }"},roc:{description:"ROC\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, signalPeriod }"},dmi:{description:"DMI / ADX\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { period, adxPeriod }"},sar:{description:"SAR \u629B\u7269\u7EBF\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { afStart, afIncrement, afMax }"},kc:{description:"Keltner \u901A\u9053 KC\uFF0Ctrue \u6216\u914D\u7F6E\u5BF9\u8C61 { emaPeriod, atrPeriod, multiplier }"}}}},required:["symbol"],additionalProperties:!1},invoke:(e,t)=>e.kline.withIndicators(t.symbol,{market:t.market,period:t.period,adjust:t.adjust,startDate:t.startDate,endDate:t.endDate,indicators:t.indicators})};var me={"kline.withIndicators":H},Q=q.filter(e=>e.mcp!==!1).map(e=>{if(e.mcpCustom){let t=me[e.path.join(".")];if(!t)throw new Error(`\u65B9\u6CD5 ${e.path.join(".")} \u6807\u8BB0\u4E86 mcpCustom \u4F46\u672A\u6CE8\u518C\u624B\u5199\u5DE5\u5177`);return t}return X(e)}),Je=new Map(Q.map(e=>[e.name,e]));function G(e){return b(Q,e)}var Z=[{name:"analyze_stock",title:"Analyze stock (technical)",description:"Full technical analysis of one symbol: klines + indicators + signals, summarized.",tier:"core",args:[{name:"symbol",description:"Stock code, e.g. sh600519 / 600519 / 00700 / AAPL",required:!0},{name:"period",description:"Kline period: daily / weekly / monthly (default daily)",default:"daily"}],usesTools:["search","get_kline_with_indicators","get_kline_signals"],render:e=>`You are a rigorous equity technical-analysis assistant. Perform a complete technical analysis of ${e.symbol}.
|
|
6
|
+
|
|
7
|
+
[Data \u2014 call these read-only tools as needed; never fabricate data]
|
|
8
|
+
1. If ${e.symbol} might not be a canonical code, confirm it with \`search\` first.
|
|
9
|
+
2. Use \`get_kline_with_indicators\` to fetch ${e.period} klines with MA(5/10/20/60), MACD, BOLL, KDJ and RSI overlaid.
|
|
10
|
+
3. Use \`get_kline_signals\` to detect golden/death crosses, overbought/oversold, BOLL breakouts and SAR reversals.
|
|
11
|
+
|
|
12
|
+
[Analysis]
|
|
13
|
+
- Trend: MA alignment, price-vs-MA relationship, MACD direction.
|
|
14
|
+
- Momentum: KDJ / RSI overbought/oversold, any divergence.
|
|
15
|
+
- Key levels: BOLL upper/lower bands, recent support/resistance.
|
|
16
|
+
- Signals: list recent valid signals (type + date) and judge reliability \u2014 corroborate with price/volume, don't just name them.
|
|
17
|
+
|
|
18
|
+
[Output]
|
|
19
|
+
Give (1) a one-line verdict (bullish / bearish / range-bound); (2) per-dimension points (trend / momentum / signals / risk); (3) an explicit note that this is technical analysis of public market data and not investment advice.`},{name:"screen_stocks",title:"Screen stocks",description:"Build a candidate shortlist over a chosen universe by natural-language criteria, coarse-filtering before per-symbol indicator work.",tier:"core",args:[{name:"criteria",description:'Screening criteria in natural language, e.g. "MACD golden cross and turnover > 5%"',required:!0},{name:"scope",description:"Universe to screen: an industry/concept board name, or the whole market (default)",default:"the whole A-share market"}],usesTools:["get_industry_constituents","get_concept_constituents","get_fund_flow_rank","get_a_share_quotes","get_kline_with_indicators","get_kline_signals"],render:e=>`You are a disciplined equity screening assistant. Build a candidate shortlist from ${e.scope} that matches: ${e.criteria}.
|
|
20
|
+
|
|
21
|
+
[Workflow \u2014 read-only tools; mind the cost of scanning many symbols]
|
|
22
|
+
1. Resolve the universe:
|
|
23
|
+
- If ${e.scope} names an industry board, call \`get_industry_constituents\`; if a concept board, call \`get_concept_constituents\` to list its members.
|
|
24
|
+
- If it is the whole market, start from \`get_fund_flow_rank\` (whole-market main-force net-inflow ranking, top ~200) as a manageable candidate pool.
|
|
25
|
+
2. Coarse-filter FIRST on cheap fields: call \`get_a_share_quotes\` on the universe and screen by change% / turnover / volume-ratio / market cap \u2014 narrow to a small subset BEFORE any per-symbol indicator work.
|
|
26
|
+
3. Confirm on the subset only: for each surviving candidate call \`get_kline_with_indicators\` and \`get_kline_signals\` to check the technical part of the criteria (e.g. MACD golden cross, RSI not overbought).
|
|
27
|
+
|
|
28
|
+
[Output]
|
|
29
|
+
Return a ranked shortlist \u2014 code, name, why it matched (the specific criteria met), and any caveat. State how many symbols you scanned vs. shortlisted. Never run per-symbol klines across thousands of names; always coarse-filter first.`},{name:"market_overview",title:"Market overview",description:"Today's A-share snapshot: market status, limit-up breadth, northbound flow, main-force money direction.",tier:"core",args:[],usesTools:["get_market_status","get_zt_pool","get_northbound_flow_summary","get_fund_flow_rank"],render:()=>`You are a market briefing assistant. Give a concise snapshot of today's A-share market.
|
|
30
|
+
|
|
31
|
+
[Data \u2014 read-only]
|
|
32
|
+
1. \`get_market_status\` \u2014 is the market pre-open / open / lunch break / after-hours / closed (tells you how live the read is).
|
|
33
|
+
2. \`get_zt_pool\` \u2014 the limit-up pool (breadth and risk appetite; also prior-day limit-ups for follow-through).
|
|
34
|
+
3. \`get_northbound_flow_summary\` \u2014 northbound / southbound money direction.
|
|
35
|
+
4. \`get_fund_flow_rank\` \u2014 where main-force money is flowing (leaders by net inflow).
|
|
36
|
+
|
|
37
|
+
[Output]
|
|
38
|
+
A short brief: (1) market status line; (2) risk appetite read from limit-up breadth; (3) capital direction (northbound + main-force leaders); (4) a one-line takeaway. Note this is a single snapshot, not continuous monitoring.`},{name:"monitor_watchlist",title:"Monitor watchlist",description:"Single-shot check of a watchlist: batch quotes, then drill into names that crossed a threshold.",tier:"core",args:[{name:"symbols",description:'Comma-separated stock codes to monitor, e.g. "600519,000858,300750"',required:!0}],usesTools:["get_a_share_quotes","get_today_timeline","get_individual_fund_flow","get_kline_signals"],render:e=>`You are a watchlist monitoring assistant. Check the current state of these symbols: ${e.symbols}.
|
|
39
|
+
|
|
40
|
+
[Data \u2014 read-only]
|
|
41
|
+
1. \`get_a_share_quotes\` \u2014 batch real-time quotes for all of ${e.symbols} at once (price, change%, turnover, volume-ratio, bid/ask).
|
|
42
|
+
2. For any name that looks active (big move / volume spike), dig in:
|
|
43
|
+
- \`get_today_timeline\` \u2014 the intraday path, to tell a steady move from a one-off spike.
|
|
44
|
+
- \`get_individual_fund_flow\` \u2014 whether main-force money is flowing in or out.
|
|
45
|
+
- \`get_kline_signals\` \u2014 any fresh technical signal (cross / breakout) on the daily.
|
|
46
|
+
|
|
47
|
+
[Output]
|
|
48
|
+
One line per symbol (price, change%, notable flag). Then call out the 1\u20133 names that crossed a threshold (big move / volume / main-force inflow / new signal) with a short why. This is a single snapshot \u2014 for continuous watching, re-run this skill on a schedule from your client.`},{name:"analyze_capital_flow",title:"Analyze capital flow",description:"Smart-money / main-force read for one stock or the broad market: fund flow + dragon-tiger + block trades + margin + northbound.",tier:"full",args:[{name:"symbol",description:"Optional stock code to focus on; omit for a market-wide read",required:!1}],usesTools:["get_individual_fund_flow","get_market_fund_flow","get_dragon_tiger_detail","get_dragon_tiger_seat_detail","get_block_trade_detail","get_margin_account_info","get_northbound_individual"],render:e=>`You are a capital-flow / smart-money analyst. Focus: ${e.symbol?`stock ${e.symbol}`:"the broad market"}.
|
|
49
|
+
|
|
50
|
+
[Data \u2014 read-only; pick what fits the focus]
|
|
51
|
+
- \`get_individual_fund_flow\` \u2014 per-stock main / large / medium / small net inflow (history).
|
|
52
|
+
- \`get_market_fund_flow\` \u2014 market-wide main-force flow.
|
|
53
|
+
- \`get_dragon_tiger_detail\` \u2014 dragon-tiger board: which stocks made it and when.
|
|
54
|
+
- \`get_dragon_tiger_seat_detail\` \u2014 which seats (institutions / hot-money desks) traded.
|
|
55
|
+
- \`get_block_trade_detail\` \u2014 block trades (discount/premium, buyer/seller side).
|
|
56
|
+
- \`get_margin_account_info\` \u2014 margin balance trend (leverage appetite).
|
|
57
|
+
- \`get_northbound_individual\` \u2014 northbound per-stock holdings change.
|
|
58
|
+
|
|
59
|
+
For a specific stock, lean on get_individual_fund_flow + get_dragon_tiger_detail / get_dragon_tiger_seat_detail + get_block_trade_detail + get_northbound_individual + get_margin_account_info.
|
|
60
|
+
For a market-wide read, lean on get_market_fund_flow + get_margin_account_info + the northbound picture.
|
|
61
|
+
|
|
62
|
+
[Output]
|
|
63
|
+
Read main-force intent \u2014 accumulation vs. distribution \u2014 corroborated across at least two independent flow sources (don't over-read a single day). List the concrete evidence per source, then a net judgment and key caveats.`},{name:"analyze_fund",title:"Analyze fund",description:"Mutual-fund review: profile + NAV history + category ranking + intraday estimate.",tier:"full",args:[{name:"fundCode",description:"6-digit fund code, e.g. 110022",required:!0}],usesTools:["get_fund_profile","get_fund_nav_history","get_fund_rank_history","get_fund_estimate"],render:e=>`You are a mutual-fund analyst. Assess fund ${e.fundCode}.
|
|
64
|
+
|
|
65
|
+
[Data \u2014 read-only]
|
|
66
|
+
1. \`get_fund_profile\` \u2014 type, manager, size, strategy.
|
|
67
|
+
2. \`get_fund_nav_history\` \u2014 historical NAV trend: cumulative return, drawdown, volatility.
|
|
68
|
+
3. \`get_fund_rank_history\` \u2014 same-category ranking percentile over time (consistency vs. one-off).
|
|
69
|
+
4. \`get_fund_estimate\` \u2014 today's intraday NAV estimate (note: QDII / non-trading days / niche funds may return null or stale estimates \u2014 say so rather than guess).
|
|
70
|
+
|
|
71
|
+
[Output]
|
|
72
|
+
A structured review: (1) what it is (type / manager / size); (2) performance (returns across horizons, worst drawdown, volatility); (3) ranking percentile and whether it's consistent; (4) today's estimate with its caveat; (5) a balanced takeaway on strengths / risks. Public data only, not investment advice.`},{name:"diagnose_stock",title:"Diagnose stock (multi-factor)",description:"Cross-dimension diagnosis of one stock: technicals + capital + chips, scored and combined.",tier:"full",args:[{name:"symbol",description:"Stock code to diagnose, e.g. sh600519",required:!0}],usesTools:["get_kline_with_indicators","get_kline_signals","get_individual_fund_flow","get_dragon_tiger_stock_stats","get_chip_distribution"],render:e=>`You are a multi-factor stock diagnostician. Diagnose ${e.symbol} across technicals, capital and chips.
|
|
73
|
+
|
|
74
|
+
[Data \u2014 read-only]
|
|
75
|
+
- Technicals: \`get_kline_with_indicators\` (trend / momentum) + \`get_kline_signals\` (crosses / breakouts).
|
|
76
|
+
- Capital: \`get_individual_fund_flow\` (main-force in/out) + \`get_dragon_tiger_stock_stats\` (dragon-tiger frequency / stats).
|
|
77
|
+
- Chips: \`get_chip_distribution\` (profit ratio, 90/70 cost range, concentration).
|
|
78
|
+
|
|
79
|
+
[Output]
|
|
80
|
+
Score each dimension (technical / capital / chips) as bullish / neutral / bearish with the evidence, then a combined read and the key risks. Be explicit where dimensions disagree. Public-data technical analysis, not investment advice.`}];var fe=`
|
|
81
|
+
|
|
82
|
+
IMPORTANT: Respond in the same language the user is using.
|
|
83
|
+
Read and analyze only \u2014 never place orders or move funds. If data is missing or a source returns nothing, say so honestly; do not fabricate numbers.`;function ee(e){return{name:e.name,title:e.title,description:e.description,tier:e.tier,arguments:e.args.map(t=>({name:t.name,description:t.description,required:t.required===!0})),usesTools:e.usesTools,render:t=>{let r={};for(let o of e.args){let n=t[o.name];if(n==null||n===""){if(o.required)throw new k(`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570 "${o.name}"`,{argument:o.name});o.default!==void 0&&(r[o.name]=o.default)}else r[o.name]=typeof n=="string"?n:String(n)}return e.render(r)+fe}}}var te=Z.map(ee),Fe=new Map(te.map(e=>[e.name,e]));function re(e){return b(te,e)}var M=200,ge=8e4;function he(e){return Array.isArray(e)&&e.length>M?{total:e.length,truncated:!0,note:`\u7ED3\u679C\u8FC7\u5927\uFF0C\u4EC5\u8FD4\u56DE\u524D ${M} \u6761\uFF1B\u8BF7\u7528\u65E5\u671F / \u5206\u9875 / \u5E02\u573A\u7B49\u53C2\u6570\u7F29\u5C0F\u8303\u56F4`,sample:e.slice(0,M)}:e}function oe(e){let t=JSON.stringify(he(e))??"null";return t.length>ge?{content:[{type:"text",text:JSON.stringify({truncated:!0,reason:"oversized",approxKB:Math.round(t.length/1024),note:"\u7ED3\u679C\u4F53\u79EF\u8FC7\u5927\uFF0C\u5DF2\u7701\u7565\u6570\u636E\u4F53\uFF1B\u8BF7\u7528\u66F4\u5C0F\u7684\u65E5\u671F / \u5206\u9875 / \u8303\u56F4\u53C2\u6570\uFF0C\u6216\u76F4\u63A5\u7528 SDK \u83B7\u53D6\u5B8C\u6574\u6570\u636E"})}],_meta:{truncated:!0,approxChars:t.length}}:{content:[{type:"text",text:t}]}}function E(e){let t=v(e)?e.code:"UNKNOWN",r=e instanceof Error?e.message:String(e),o=v(e)?{code:e.code,provider:e.provider,url:e.url,status:e.status,details:e.details}:{code:t};return{content:[{type:"text",text:`Error[${t}]: ${r}`}],isError:!0,_meta:o}}function g(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function _e(e,t){switch(t){case"array":return Array.isArray(e);case"string":return typeof e=="string";case"integer":return Number.isInteger(e);case"number":return typeof e=="number";case"boolean":return typeof e=="boolean";case"object":return g(e);default:return!0}}function ye(e,t){if(e.additionalProperties===!1){let r=new Set(Object.keys(e.properties));for(let o of Object.keys(t))if(!r.has(o))return`\u672A\u77E5\u53C2\u6570 "${o}"`}for(let r of e.required??[])if(t[r]===void 0||t[r]===null)return`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570 "${r}"`;for(let[r,o]of Object.entries(e.properties)){let n=t[r];if(n!==void 0){if(o.type&&!_e(n,o.type))return`\u53C2\u6570 "${r}" \u7C7B\u578B\u5E94\u4E3A ${o.type}`;if(o.enum&&!o.enum.includes(n))return`\u53C2\u6570 "${r}" \u53D6\u503C\u975E\u6CD5\u300C${String(n)}\u300D\uFF0C\u53EF\u9009: ${o.enum.join(" / ")}`}}return null}async function ke(e,t){let r=e.id??null,o=e.id!==void 0&&e.id!==null,n=i=>({jsonrpc:"2.0",id:r,result:i}),s=(i,c)=>({jsonrpc:"2.0",id:r,error:{code:i,message:c}});if(e.jsonrpc!=="2.0"||typeof e.method!="string"||e.method.length===0)return o?s(S,"Invalid Request"):null;let u=e.params;switch(e.method){case"initialize":{let i=g(u)?u.protocolVersion:void 0;return n({protocolVersion:z(i),capabilities:{tools:{},prompts:{}},serverInfo:w})}case"notifications/initialized":case"notifications/cancelled":return null;case"ping":return n({});case"tools/list":return n({tools:t.tools.map(i=>({name:i.name,description:i.description,inputSchema:i.inputSchema}))});case"tools/call":{if(!g(u))return s(m,"params must be an object");let i=u.name;if(typeof i!="string")return s(m,"params.name must be a string");let c=t.toolMap.get(i);if(!c)return s(m,`Unknown tool: ${i}`);let d=u.arguments;if(d!==void 0&&!g(d))return s(m,"params.arguments must be an object");let h=g(d)?d:{},a=ye(c.inputSchema,h);if(a)return n(E(new k(a)));try{let l=await c.invoke(t.sdk,h);return n(oe(l))}catch(l){return n(E(l))}}case"prompts/list":return n({prompts:t.prompts.map(i=>({name:i.name,title:i.title,description:i.description,arguments:i.arguments}))});case"prompts/get":{if(!g(u))return s(m,"params must be an object");let i=u.name;if(typeof i!="string")return s(m,"params.name must be a string");let c=t.promptMap.get(i);if(!c)return s(m,`Unknown prompt: ${i}`);let d=u.arguments;if(d!==void 0&&!g(d))return s(m,"params.arguments must be an object");let h=g(d)?d:{};try{let a=c.render(h),l=c.arguments[0]?.name,p=l?h[l]:void 0;return n({description:p?`${c.title} \xB7 ${p}`:c.title,messages:[{role:"user",content:{type:"text",text:a}}]})}catch(a){return s(m,a?.message??"invalid prompt arguments")}}default:return o?s(V,`Unknown method: ${e.method}`):null}}function we(e){return O(e,"STOCK_SDK_MCP_TOOLS")}function Se(e){return O(e,"STOCK_SDK_MCP_PROMPTS")}function be(e){if(e)return e;let t=process.env.STOCK_SDK_MCP_TIMEOUT,r=t?Number(t):void 0;return r&&!Number.isNaN(r)&&r>0?{timeout:r}:{}}function rt(e={}){let t=new x(be(e.sdk)),r=we(e.tools),o=Se(e.prompts),n=G(r),s=re(o),u={sdk:t,tools:n,toolMap:new Map(n.map(a=>[a.name,a])),prompts:s,promptMap:new Map(s.map(a=>[a.name,a]))};f(`[stock-sdk mcp] ready \xB7 ${n.length} tools \xB7 ${s.length} prompts \xB7 ${w.name}@${w.version}`);let i=(a,l,p)=>{if(!Array.isArray(l))return;let ne=new Set(p.map(P=>P.name)),T=l.filter(P=>!ne.has(P));T.length>0&&f(`[stock-sdk mcp] \u26A0 ${a} \u540D\u5355\u4E2D\u6709 ${T.length} \u4E2A\u672A\u77E5\u540D\uFF08\u5DF2\u5FFD\u7565\uFF09\uFF1A${T.join(", ")}`)};i("\u5DE5\u5177",r,n),i("\u6280\u80FD",o,s);let c=new Set(n.map(a=>a.name)),d=new Set;for(let a of s)for(let l of a.usesTools)c.has(l)||d.add(l);d.size>0&&f(`[stock-sdk mcp] \u26A0 \u5DF2\u542F\u7528\u7684\u6280\u80FD\u5F15\u7528\u4E86 ${d.size} \u4E2A\u4E0D\u5728\u5F53\u524D\u5DE5\u5177\u96C6\u5185\u7684\u5DE5\u5177\uFF1A${[...d].join(", ")}\u3002\u8FD9\u4E9B\u6280\u80FD\u7F16\u6392\u5230\u8BE5\u6B65\u4F1A\u5931\u8D25 \u2014\u2014 \u8BF7\u8BBE STOCK_SDK_MCP_TOOLS=full\uFF08\u6216\u628A\u8FD9\u4E9B\u5DE5\u5177\u52A0\u8FDB\u540D\u5355\uFF09\u3002`),U(a=>{h(a)}),process.stdin.on("end",()=>f("[stock-sdk mcp] stdin closed, exiting"));async function h(a){let l;try{l=JSON.parse(a)}catch{y({jsonrpc:"2.0",id:null,error:{code:Y,message:"Parse error"}});return}try{let p=await ke(l,u);p&&y(p)}catch(p){f("[stock-sdk mcp] internal error:",p?.message??String(p)),l.id!==void 0&&l.id!==null&&y({jsonrpc:"2.0",id:l.id,error:{code:F,message:String(p?.message??p)}})}}}export{ke as dispatchMessage,rt as startMcpServer};
|
package/dist/screener.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _chunkLDYRNH4Xcjs = require('./chunk-LDYRNH4X.cjs');require('./chunk-4Q7UQ5Q3.cjs');require('./chunk-4VDLQK6F.cjs');exports.backtest = _chunkLDYRNH4Xcjs.b; exports.screen = _chunkLDYRNH4Xcjs.a;
|
package/dist/screener.d.cts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
interface ScreenerBuilder<T> {
|
|
2
2
|
/** 过滤:保留满足条件的项 */
|
|
3
3
|
where(predicate: (item: T) => boolean): ScreenerBuilder<T>;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 排序:按数值选择器,默认降序。
|
|
6
|
+
* 字符串数值(原始行情 JSON 常见,如 `'999999'`)会经 `Number()` 归一后参与排序;
|
|
7
|
+
* null / undefined / NaN / 空串 / 非数值字符串统一沉底。
|
|
8
|
+
* direction 仅接受 `'asc'` / `'desc'`,其余取值抛 InvalidArgumentError。
|
|
9
|
+
*/
|
|
10
|
+
sortBy(selector: (item: T) => number | string | null | undefined, direction?: 'asc' | 'desc'): ScreenerBuilder<T>;
|
|
6
11
|
/** 取前 n 项并返回 */
|
|
7
12
|
top(n: number): T[];
|
|
8
13
|
/** 返回当前全部结果 */
|
|
@@ -21,48 +26,78 @@ interface ScreenerBuilder<T> {
|
|
|
21
26
|
*/
|
|
22
27
|
declare function screen<T>(items: readonly T[]): ScreenerBuilder<T>;
|
|
23
28
|
|
|
29
|
+
type StrategySignal = 'buy' | 'sell' | 'hold';
|
|
24
30
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* 策略:对每根 K 线返回操作信号。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 第三个参数 `series` 是**完整 K 线数组(含当前 bar 之后的未来数据)**,仅供
|
|
34
|
+
* 读取历史窗口(如 `series.slice(0, index + 1)`)。读取 `series[index + 1]` 及之后
|
|
35
|
+
* 的元素即引入前视偏差(look-ahead bias),回测结果会严重虚高且不可复现于实盘。
|
|
27
36
|
*/
|
|
28
|
-
type
|
|
29
|
-
/** 策略:对每根 K 线返回操作信号 */
|
|
30
|
-
type Strategy<T> = (bar: T, index: number, history: readonly T[]) => StrategySignal;
|
|
37
|
+
type Strategy<T> = (bar: T, index: number, series: readonly T[]) => StrategySignal;
|
|
31
38
|
interface Trade {
|
|
32
39
|
entryIndex: number;
|
|
40
|
+
/** 出场 bar 下标;强制平仓时指向**最后一根有效价 bar**(与 exitPrice 必然对应同一根) */
|
|
33
41
|
exitIndex: number;
|
|
34
42
|
entryPrice: number;
|
|
35
43
|
exitPrice: number;
|
|
36
|
-
/**
|
|
44
|
+
/** 本笔收益率(百分数,含双边费,相对本笔投入资金) */
|
|
37
45
|
returnPercent: number;
|
|
46
|
+
/**
|
|
47
|
+
* true = 数据走完时强制平仓(非策略意图出场),统计时可据此过滤。
|
|
48
|
+
* 尾部无效 bar 上挂起的 sell 视为策略意图 → forced=false(按最后有效价成交)。
|
|
49
|
+
*/
|
|
50
|
+
forced: boolean;
|
|
51
|
+
/** 入场 bar 日期(传入 getDate 时才有) */
|
|
52
|
+
entryDate?: string | number;
|
|
53
|
+
/** 出场 bar 日期(传入 getDate 时才有) */
|
|
54
|
+
exitDate?: string | number;
|
|
38
55
|
}
|
|
39
56
|
interface BacktestOptions<T> {
|
|
40
57
|
klines: readonly T[];
|
|
41
58
|
strategy: Strategy<T>;
|
|
42
|
-
/** 初始资金,默认 100000 */
|
|
59
|
+
/** 初始资金,默认 100000(须为正有限数) */
|
|
43
60
|
initialCapital?: number;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
/**
|
|
62
|
+
* 手续费率,默认 0。数字 = 买卖同费率;对象形式可表达买卖不对称
|
|
63
|
+
* (如 A 股卖出侧印花税:`{ buy: 0.0003, sell: 0.0013 }`)。取值须在 [0, 1)。
|
|
64
|
+
*/
|
|
65
|
+
fee?: number | {
|
|
66
|
+
buy: number;
|
|
67
|
+
sell: number;
|
|
68
|
+
};
|
|
69
|
+
/** 每次买入动用现金的比例,(0, 1],默认 1(全仓) */
|
|
70
|
+
positionSize?: number;
|
|
71
|
+
/** 取收盘价;默认读取 `bar.close`(分时数据字段是 price 而非 close,需自传) */
|
|
47
72
|
getClose?: (bar: T) => number | null;
|
|
73
|
+
/** 取 bar 日期(如 `bar.date`);传入后成交记录带 entryDate/exitDate */
|
|
74
|
+
getDate?: (bar: T) => string | number | null | undefined;
|
|
48
75
|
}
|
|
49
76
|
interface BacktestReport {
|
|
50
77
|
initialCapital: number;
|
|
51
78
|
finalEquity: number;
|
|
52
79
|
/** 总收益率(百分数) */
|
|
53
80
|
totalReturn: number;
|
|
54
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* 胜率(百分数)。按 `returnPercent > 0` 计胜:恰好 0(纯费差平局)与强制平仓单
|
|
83
|
+
* 均计入分母 —— 过滤强平单请用 `trades.filter(t => !t.forced)` 自行统计。
|
|
84
|
+
*/
|
|
55
85
|
winRate: number;
|
|
56
|
-
/**
|
|
86
|
+
/** 最大回撤(百分数,正数;以初始资金为起始基线) */
|
|
57
87
|
maxDrawdown: number;
|
|
88
|
+
/** 买入持有基准收益率(百分数,首根有效收盘 → 末根有效收盘,不含费) */
|
|
89
|
+
buyHoldReturn: number;
|
|
90
|
+
/** 有效价 bar 数。为 0 说明所有 bar 都取不到有效收盘价(如字段名不是 close) */
|
|
91
|
+
validBars: number;
|
|
58
92
|
tradeCount: number;
|
|
59
93
|
trades: Trade[];
|
|
60
94
|
/** 每根 K 线收盘后的权益 */
|
|
61
95
|
equityCurve: number[];
|
|
62
96
|
}
|
|
63
97
|
/**
|
|
64
|
-
* 执行回测。规则:`buy` 且空仓 →
|
|
65
|
-
*
|
|
98
|
+
* 执行回测。规则:`buy` 且空仓 → 按 positionSize 买入;`sell` 且持仓 → 全部卖出;
|
|
99
|
+
* 结束仍持仓则按最后有效收盘价强制平仓(Trade.forced = true)。
|
|
100
|
+
* 成交时点 / 无效价挂起 / 手数简化等契约见文件头注释。
|
|
66
101
|
*/
|
|
67
102
|
declare function backtest<T>(options: BacktestOptions<T>): BacktestReport;
|
|
68
103
|
|
package/dist/screener.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
interface ScreenerBuilder<T> {
|
|
2
2
|
/** 过滤:保留满足条件的项 */
|
|
3
3
|
where(predicate: (item: T) => boolean): ScreenerBuilder<T>;
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* 排序:按数值选择器,默认降序。
|
|
6
|
+
* 字符串数值(原始行情 JSON 常见,如 `'999999'`)会经 `Number()` 归一后参与排序;
|
|
7
|
+
* null / undefined / NaN / 空串 / 非数值字符串统一沉底。
|
|
8
|
+
* direction 仅接受 `'asc'` / `'desc'`,其余取值抛 InvalidArgumentError。
|
|
9
|
+
*/
|
|
10
|
+
sortBy(selector: (item: T) => number | string | null | undefined, direction?: 'asc' | 'desc'): ScreenerBuilder<T>;
|
|
6
11
|
/** 取前 n 项并返回 */
|
|
7
12
|
top(n: number): T[];
|
|
8
13
|
/** 返回当前全部结果 */
|
|
@@ -21,48 +26,78 @@ interface ScreenerBuilder<T> {
|
|
|
21
26
|
*/
|
|
22
27
|
declare function screen<T>(items: readonly T[]): ScreenerBuilder<T>;
|
|
23
28
|
|
|
29
|
+
type StrategySignal = 'buy' | 'sell' | 'hold';
|
|
24
30
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
31
|
+
* 策略:对每根 K 线返回操作信号。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 第三个参数 `series` 是**完整 K 线数组(含当前 bar 之后的未来数据)**,仅供
|
|
34
|
+
* 读取历史窗口(如 `series.slice(0, index + 1)`)。读取 `series[index + 1]` 及之后
|
|
35
|
+
* 的元素即引入前视偏差(look-ahead bias),回测结果会严重虚高且不可复现于实盘。
|
|
27
36
|
*/
|
|
28
|
-
type
|
|
29
|
-
/** 策略:对每根 K 线返回操作信号 */
|
|
30
|
-
type Strategy<T> = (bar: T, index: number, history: readonly T[]) => StrategySignal;
|
|
37
|
+
type Strategy<T> = (bar: T, index: number, series: readonly T[]) => StrategySignal;
|
|
31
38
|
interface Trade {
|
|
32
39
|
entryIndex: number;
|
|
40
|
+
/** 出场 bar 下标;强制平仓时指向**最后一根有效价 bar**(与 exitPrice 必然对应同一根) */
|
|
33
41
|
exitIndex: number;
|
|
34
42
|
entryPrice: number;
|
|
35
43
|
exitPrice: number;
|
|
36
|
-
/**
|
|
44
|
+
/** 本笔收益率(百分数,含双边费,相对本笔投入资金) */
|
|
37
45
|
returnPercent: number;
|
|
46
|
+
/**
|
|
47
|
+
* true = 数据走完时强制平仓(非策略意图出场),统计时可据此过滤。
|
|
48
|
+
* 尾部无效 bar 上挂起的 sell 视为策略意图 → forced=false(按最后有效价成交)。
|
|
49
|
+
*/
|
|
50
|
+
forced: boolean;
|
|
51
|
+
/** 入场 bar 日期(传入 getDate 时才有) */
|
|
52
|
+
entryDate?: string | number;
|
|
53
|
+
/** 出场 bar 日期(传入 getDate 时才有) */
|
|
54
|
+
exitDate?: string | number;
|
|
38
55
|
}
|
|
39
56
|
interface BacktestOptions<T> {
|
|
40
57
|
klines: readonly T[];
|
|
41
58
|
strategy: Strategy<T>;
|
|
42
|
-
/** 初始资金,默认 100000 */
|
|
59
|
+
/** 初始资金,默认 100000(须为正有限数) */
|
|
43
60
|
initialCapital?: number;
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
/**
|
|
62
|
+
* 手续费率,默认 0。数字 = 买卖同费率;对象形式可表达买卖不对称
|
|
63
|
+
* (如 A 股卖出侧印花税:`{ buy: 0.0003, sell: 0.0013 }`)。取值须在 [0, 1)。
|
|
64
|
+
*/
|
|
65
|
+
fee?: number | {
|
|
66
|
+
buy: number;
|
|
67
|
+
sell: number;
|
|
68
|
+
};
|
|
69
|
+
/** 每次买入动用现金的比例,(0, 1],默认 1(全仓) */
|
|
70
|
+
positionSize?: number;
|
|
71
|
+
/** 取收盘价;默认读取 `bar.close`(分时数据字段是 price 而非 close,需自传) */
|
|
47
72
|
getClose?: (bar: T) => number | null;
|
|
73
|
+
/** 取 bar 日期(如 `bar.date`);传入后成交记录带 entryDate/exitDate */
|
|
74
|
+
getDate?: (bar: T) => string | number | null | undefined;
|
|
48
75
|
}
|
|
49
76
|
interface BacktestReport {
|
|
50
77
|
initialCapital: number;
|
|
51
78
|
finalEquity: number;
|
|
52
79
|
/** 总收益率(百分数) */
|
|
53
80
|
totalReturn: number;
|
|
54
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* 胜率(百分数)。按 `returnPercent > 0` 计胜:恰好 0(纯费差平局)与强制平仓单
|
|
83
|
+
* 均计入分母 —— 过滤强平单请用 `trades.filter(t => !t.forced)` 自行统计。
|
|
84
|
+
*/
|
|
55
85
|
winRate: number;
|
|
56
|
-
/**
|
|
86
|
+
/** 最大回撤(百分数,正数;以初始资金为起始基线) */
|
|
57
87
|
maxDrawdown: number;
|
|
88
|
+
/** 买入持有基准收益率(百分数,首根有效收盘 → 末根有效收盘,不含费) */
|
|
89
|
+
buyHoldReturn: number;
|
|
90
|
+
/** 有效价 bar 数。为 0 说明所有 bar 都取不到有效收盘价(如字段名不是 close) */
|
|
91
|
+
validBars: number;
|
|
58
92
|
tradeCount: number;
|
|
59
93
|
trades: Trade[];
|
|
60
94
|
/** 每根 K 线收盘后的权益 */
|
|
61
95
|
equityCurve: number[];
|
|
62
96
|
}
|
|
63
97
|
/**
|
|
64
|
-
* 执行回测。规则:`buy` 且空仓 →
|
|
65
|
-
*
|
|
98
|
+
* 执行回测。规则:`buy` 且空仓 → 按 positionSize 买入;`sell` 且持仓 → 全部卖出;
|
|
99
|
+
* 结束仍持仓则按最后有效收盘价强制平仓(Trade.forced = true)。
|
|
100
|
+
* 成交时点 / 无效价挂起 / 手数简化等契约见文件头注释。
|
|
66
101
|
*/
|
|
67
102
|
declare function backtest<T>(options: BacktestOptions<T>): BacktestReport;
|
|
68
103
|
|
package/dist/screener.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a,b}from"./chunk-
|
|
1
|
+
import{a,b}from"./chunk-OPCBE3NR.js";import"./chunk-UBIQBXQ7.js";import"./chunk-WOT6VMZA.js";export{b as backtest,a as screen};
|