matrix-api-mcp 1.1.0__py3-none-any.whl

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.
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: matrix-api-mcp
3
+ Version: 1.1.0
4
+ Summary: MatriX MCP 工具层(AI 助手接入金融数据查询)
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: mcp>=2.3
8
+ Dynamic: description
9
+ Dynamic: description-content-type
10
+ Dynamic: requires-dist
11
+ Dynamic: requires-python
12
+ Dynamic: summary
13
+
14
+ # MatriX
15
+ V1.1.0-Beta Build 20261105
16
+
17
+ © 2026-2026 Xu Rendong. All Rights Reserved.
18
+
19
+ ### Project Summary
20
+ Quantitative Financial Data Framework.
21
+
22
+ ### Contact Information
23
+ WeChat:xrd_ustc,~~QQ:277195007~~,~~E-mail:xrd@ustc.edu~~
@@ -0,0 +1,11 @@
1
+ matrix_mcp/__init__.py,sha256=hX6p77_JSjgdttxrVVe9xMn_Q4_QvwAM1jL3aFSXsEc,796
2
+ matrix_mcp/__main__.py,sha256=L8ZS9V7TgfRqw-EymCNtQ0WkQ2N9GzTgofbHRLjqIPw,1111
3
+ matrix_mcp/estimate.py,sha256=U9jWihRRqTojCoxRxvZzqis-PO_Xe50sz4wDjGgFzeY,3250
4
+ matrix_mcp/license.txt,sha256=NXw_PcSZFOoVDB3BuDrTXVanE0XIxFtQD6LzGZV77lI,1482
5
+ matrix_mcp/readme.md,sha256=k-t_dPTo_YnmD-v9XqPglFMBCcFwPwPpR3FvjRU2e1M,245
6
+ matrix_mcp/server.py,sha256=U5vwpKqeBUJLQsJ1AOMrnAiafg159cuGJ9jnPfhg84g,2353
7
+ matrix_mcp/tools.py,sha256=22HpfxlQzEtLDofK_BokOUJU860tR9lJqP6FisQygdQ,14926
8
+ matrix_api_mcp-1.1.0.dist-info/METADATA,sha256=Z8inc267Xbli8dwQWoydTfO75umPNVWLVCyOF4INX3E,594
9
+ matrix_api_mcp-1.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
10
+ matrix_api_mcp-1.1.0.dist-info/top_level.txt,sha256=iXDu1bAB-6kjRV_0qT-XcOYLFTRKLx4VBWqcZpYjWog,11
11
+ matrix_api_mcp-1.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ matrix_mcp
matrix_mcp/__init__.py ADDED
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2026 the MatriX authors
4
+ # All rights reserved.
5
+ #
6
+ # The project sponsor and lead author is Xu Rendong.
7
+ # E-mail: xrd@ustc.edu, QQ: 277195007, WeChat: ustc_xrd
8
+ # See the contributors file for names of other contributors.
9
+ #
10
+ # Commercial use of this code in source and binary forms is
11
+ # governed by a LGPL v3 license. You may get a copy from the
12
+ # root directory. Or else you should get a specific written
13
+ # permission from the project author.
14
+ #
15
+ # Individual and educational use of this code in source and
16
+ # binary forms is governed by a 3-clause BSD license. You may
17
+ # get a copy from the root directory. Certainly welcome you
18
+ # contribute code of all sorts.
19
+ #
20
+ # Be sure to retain the above copyright notice and conditions.
21
+
22
+ # matrix_mcp:MCP Server 封装
matrix_mcp/__main__.py ADDED
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2026 the MatriX authors
4
+ # All rights reserved.
5
+ #
6
+ # The project sponsor and lead author is Xu Rendong.
7
+ # E-mail: xrd@ustc.edu, QQ: 277195007, WeChat: ustc_xrd
8
+ # See the contributors file for names of other contributors.
9
+ #
10
+ # Commercial use of this code in source and binary forms is
11
+ # governed by a LGPL v3 license. You may get a copy from the
12
+ # root directory. Or else you should get a specific written
13
+ # permission from the project author.
14
+ #
15
+ # Individual and educational use of this code in source and
16
+ # binary forms is governed by a 3-clause BSD license. You may
17
+ # get a copy from the root directory. Certainly welcome you
18
+ # contribute code of all sorts.
19
+ #
20
+ # Be sure to retain the above copyright notice and conditions.
21
+
22
+ # MCP Server 命令行入口:python -m matrix_mcp(设计 5.4.4)。
23
+ # 实际装配在 server.py(MCPServer stdio + 启动清理),此处只做转发,
24
+ # 保持入口单一(whl 分发后各 IDE 以 args ["-m","matrix_mcp"] 拉起)。
25
+
26
+ from matrix_mcp import server
27
+
28
+ def Main():
29
+ server.Main()
30
+
31
+ if __name__ == "__main__":
32
+ Main()
matrix_mcp/estimate.py ADDED
@@ -0,0 +1,71 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2026 the MatriX authors
4
+ # All rights reserved.
5
+ #
6
+ # The project sponsor and lead author is Xu Rendong.
7
+ # E-mail: xrd@ustc.edu, QQ: 277195007, WeChat: ustc_xrd
8
+ # See the contributors file for names of other contributors.
9
+ #
10
+ # Commercial use of this code in source and binary forms is
11
+ # governed by a LGPL v3 license. You may get a copy from the
12
+ # root directory. Or else you should get a specific written
13
+ # permission from the project author.
14
+ #
15
+ # Individual and educational use of this code in source and
16
+ # binary forms is governed by a 3-clause BSD license. You may
17
+ # get a copy from the root directory. Certainly welcome you
18
+ # contribute code of all sorts.
19
+ #
20
+ # Be sure to retain the above copyright notice and conditions.
21
+
22
+ # MCP 行数预算三层机制(设计 5.4.2):客户端识别(默认自动)→ env 覆盖(唯一配置项)
23
+ # → 动态估算(硬保证)。预算只需量级正确:偏大有硬顶兜底、偏小多一跳截断,
24
+ # 模型看到截断提示即自适应(缩小范围/limit 分页/to_file),故不做模型级配置。
25
+
26
+ import os
27
+
28
+ # ── 预算常量(5.4.2)──────────────────────────────────────────────────
29
+ ENV_CONTEXT_TOKENS = "MATRIX_MCP_CONTEXT_TOKENS" # 唯一配置项:客户端级上下文预算覆盖
30
+
31
+ SAFETY_RATIO = 0.2 # 预算安全系数:数据最多占上下文的 20%
32
+ TOKENS_PER_ROW = 20 # markdown 表格行实测平均 15~25 token,取中值作估算基线
33
+ MAX_ROWS = 1000 # 绝对硬顶:无论预算多大,单次回传行数不超此值
34
+ MAX_CHARS = 32000 # 绝对硬顶:渲染后总字符数上限(双保险)
35
+ UNKNOWN_BUDGET = 32000 # 未知客户端的保守默认预算
36
+
37
+ # 已知客户端 → 预算映射(按 initialize 握手 clientInfo.name 子串匹配,小写)。
38
+ # 只列主流大上下文客户端;新客户端默认走 UNKNOWN_BUDGET,无需维护本表也安全。
39
+ CLIENT_BUDGETS = {
40
+ "claude": 200000,
41
+ "codex": 200000,
42
+ "trae": 200000,
43
+ "workbuddy": 200000,
44
+ "cursor": 128000,
45
+ "windsurf": 128000,
46
+ "vscode": 128000,
47
+ }
48
+
49
+ def ResolveBudget(client_name = None):
50
+ # 三层解析:env 覆盖 > 客户端识别表 > 保守默认。
51
+ # 返回 (预算 token 数, 来源标签);来源标签进输出头部元信息,模型可见
52
+ env_val = os.environ.get(ENV_CONTEXT_TOKENS, "").strip()
53
+ if env_val.isdigit() and int(env_val) > 0:
54
+ return int(env_val), "env"
55
+ name = (client_name or "").lower()
56
+ for key, tokens in CLIENT_BUDGETS.items():
57
+ if key in name:
58
+ return tokens, "客户端表"
59
+ return UNKNOWN_BUDGET, "保守默认"
60
+
61
+ def AllowedRows(budget, requested = None):
62
+ # 预算层行数上限:预算×安全系数/每行token,再压绝对硬顶 MAX_ROWS;
63
+ # requested(Agent 显式 limit)只会更小不会放大。下限保 1 行(预览语义)
64
+ rows = int(budget * SAFETY_RATIO / TOKENS_PER_ROW)
65
+ if rows < 1:
66
+ rows = 1
67
+ if rows > MAX_ROWS:
68
+ rows = MAX_ROWS
69
+ if requested is not None and requested > 0:
70
+ rows = min(rows, requested)
71
+ return rows
matrix_mcp/license.txt ADDED
@@ -0,0 +1,26 @@
1
+ 3-clause BSD License
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are met:
5
+
6
+ * Redistributions of source code must retain the above copyright notice,
7
+ this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of Redis nor the names of its contributors may be used
12
+ to endorse or promote products derived from this software without
13
+ specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
+ POSSIBILITY OF SUCH DAMAGE.
26
+
matrix_mcp/readme.md ADDED
@@ -0,0 +1,10 @@
1
+ # MatriX
2
+ V1.1.0-Beta Build 20261105
3
+
4
+ © 2026-2026 Xu Rendong. All Rights Reserved.
5
+
6
+ ### Project Summary
7
+ Quantitative Financial Data Framework.
8
+
9
+ ### Contact Information
10
+ WeChat:xrd_ustc,~~QQ:277195007~~,~~E-mail:xrd@ustc.edu~~
matrix_mcp/server.py ADDED
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2026 the MatriX authors
4
+ # All rights reserved.
5
+ #
6
+ # The project sponsor and lead author is Xu Rendong.
7
+ # E-mail: xrd@ustc.edu, QQ: 277195007, WeChat: ustc_xrd
8
+ # See the contributors file for names of other contributors.
9
+ #
10
+ # Commercial use of this code in source and binary forms is
11
+ # governed by a LGPL v3 license. You may get a copy from the
12
+ # root directory. Or else you should get a specific written
13
+ # permission from the project author.
14
+ #
15
+ # Individual and educational use of this code in source and
16
+ # binary forms is governed by a 3-clause BSD license. You may
17
+ # get a copy from the root directory. Certainly welcome you
18
+ # contribute code of all sorts.
19
+ #
20
+ # Be sure to retain the above copyright notice and conditions.
21
+
22
+ # MCP Server 实例与生命周期(设计 5.4):stdio 传输(所有目标 IDE 均支持),
23
+ # 复用 SDK 走 HTTP API 访问数据服务,不直连数据库。使用 mcp 2.x 官方库
24
+ # (FastMCP 已在其 2.x 更名为 MCPServer,能力等价:tool 装饰器 + stdio 运行)。
25
+
26
+ import asyncio
27
+ import os
28
+
29
+ from pathlib import Path
30
+
31
+ from mcp.server.mcpserver import MCPServer
32
+
33
+ from matrix_api import define
34
+ from matrix_api import fileio
35
+ from matrix_mcp import tools
36
+
37
+ def CleanupAtStartup():
38
+ # 启动静默清理 >TTL 的 to_file 历史产出(5.4.5):文件归调用方管理,
39
+ # 此策略仅防磁盘膨胀,任何失败不阻断启动
40
+ env_dir = os.environ.get("MATRIX_MCP_FILE_DIR", "").strip()
41
+ env_ttl = os.environ.get("MATRIX_MCP_FILE_TTL_DAYS", "").strip()
42
+ ttl = int(env_ttl) if env_ttl.isdigit() and int(env_ttl) > 0 else 7
43
+ try:
44
+ fileio.CleanupOldFiles(directory = Path(env_dir) if env_dir else None, ttl_days = ttl)
45
+ except Exception:
46
+ pass
47
+
48
+ def BuildServer():
49
+ server = MCPServer(
50
+ name = "matrix",
51
+ version = define.APP_VERSION,
52
+ instructions = "MatriX 金融数据库(A股/基金/期货/期权日频数据)。"
53
+ "先用 matrix_dataset_info 了解数据集与字段,再用 matrix_query_data "
54
+ "或 matrix_get_daily_quotes 取数;大结果用 to_file=True 落盘后代码读取。",
55
+ )
56
+ tools.RegisterTools(server)
57
+ return server
58
+
59
+ def Main():
60
+ CleanupAtStartup()
61
+ server = BuildServer()
62
+ asyncio.run(server.run_stdio_async())
matrix_mcp/tools.py ADDED
@@ -0,0 +1,306 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Copyright (c) 2026 the MatriX authors
4
+ # All rights reserved.
5
+ #
6
+ # The project sponsor and lead author is Xu Rendong.
7
+ # E-mail: xrd@ustc.edu, QQ: 277195007, WeChat: ustc_xrd
8
+ # See the contributors file for names of other contributors.
9
+ #
10
+ # Commercial use of this code in source and binary forms is
11
+ # governed by a LGPL v3 license. You may get a copy from the
12
+ # root directory. Or else you should get a specific written
13
+ # permission from the project author.
14
+ #
15
+ # Individual and educational use of this code in source and
16
+ # binary forms is governed by a 3-clause BSD license. You may
17
+ # get a copy from the root directory. Certainly welcome you
18
+ # contribute code of all sorts.
19
+ #
20
+ # Be sure to retain the above copyright notice and conditions.
21
+
22
+ # MCP 工具定义(设计 5.4.1):五个工具全部复用 SDK 门面(matrix_api.wind),
23
+ # 本模块只做三件事——上下文预算裁剪(estimate 三层)、markdown 渲染、
24
+ # to_file 落盘摘要。工具 docstring 即 Agent 看到的说明(自解释,5.4.2),
25
+ # 是编码风格中唯一的 docstring 例外。函数定义为模块级(便于单测直取),
26
+ # 由 RegisterTools 统一挂到 MCPServer;ctx 参数标注 Context 类型,
27
+ # MCP 框架据此注入会话上下文(并自动排除出 JSON schema)。
28
+
29
+ import os
30
+
31
+ from pathlib import Path
32
+
33
+ from mcp.server.mcpserver import Context
34
+
35
+ import matrix_api
36
+
37
+ from matrix_api import define
38
+ from matrix_api import fileio
39
+ from matrix_mcp import estimate
40
+
41
+ # 预算来源 → 人读标签(元信息行展示,5.4.2)
42
+ SOURCE_LABELS = {
43
+ "env": "环境变量 %s" % estimate.ENV_CONTEXT_TOKENS,
44
+ "客户端表": "客户端识别表",
45
+ "保守默认": "保守默认(32k)",
46
+ }
47
+
48
+ TRUNCATE_HINT = ("⚠ 结果已按上下文预算截断。建议:缩小日期范围 / 减少代码数量 / "
49
+ "用 limit 参数分页 / 改用 to_file=True 落盘后用代码读取全量数据。")
50
+
51
+ _wind = None
52
+
53
+ def GetWind():
54
+ # 进程级 SDK 会话:MCP 无状态,stdio 进程随客户端会话启停,单会话够用。
55
+ # 认证码经环境变量 MATRIX_TOKEN 透传(mcp.json 的 env 保存,SDK 侧读取);
56
+ # User-Agent 自报 matrix-mcp 渠道,服务端访问日志据此区分来源
57
+ global _wind
58
+ if _wind is None:
59
+ _wind = matrix_api.wind(user_agent = "matrix_mcp/%s" % define.APP_VERSION)
60
+ return _wind
61
+
62
+ def ClientName(ctx):
63
+ # initialize 握手的 clientInfo.name 是识别层唯一输入;mcp 2.x pydantic
64
+ # 字段为 snake_case(client_info),1.x 风格 camelCase 一并兼容;
65
+ # 测试环境/异常链路任何一环缺失都回退空串(走保守默认预算)
66
+ try:
67
+ params = ctx.request_context.session.client_params
68
+ info = getattr(params, "client_info", None) or getattr(params, "clientInfo", None)
69
+ return getattr(info, "name", "") or ""
70
+ except Exception:
71
+ return ""
72
+
73
+ def ErrorText(result):
74
+ # 门面永不抛异常,失败统一渲染为人读文本(模型可读错误码自助纠正)
75
+ return "查询失败(错误码 %d):%s" % (result.code, result.msg)
76
+
77
+ def RenderValue(value):
78
+ # 单元格 → markdown 安全字符串:空值/NaN 置空,竖线与换行转义
79
+ if value is None:
80
+ return ""
81
+ if isinstance(value, float) and value != value:
82
+ return ""
83
+ text = str(value)
84
+ return text.replace("|", "\\|").replace("\n", " ")
85
+
86
+ def RenderDataFrame(df, cap_rows, cap_chars):
87
+ # DataFrame → markdown 表格文本;双重上限(行数/字符数)取先到者。
88
+ # 返回 (行文本列表, 实际展示行数)——字符硬顶触发时展示行数 < cap_rows
89
+ header = "| " + " | ".join(str(c) for c in df.columns) + " |"
90
+ sep = "|" + "|".join(["---"] * len(df.columns)) + "|"
91
+ lines = [header, sep]
92
+ total = len(header) + len(sep)
93
+ shown = 0
94
+ for i in range(min(len(df), cap_rows)):
95
+ row = "| " + " | ".join(RenderValue(v) for v in df.iloc[i].tolist()) + " |"
96
+ if shown >= 1 and total + len(row) > cap_chars:
97
+ break
98
+ lines.append(row)
99
+ total += len(row)
100
+ shown += 1
101
+ return lines, shown
102
+
103
+ def MetaLine(title, total_text, shown, cap, source):
104
+ # 头部元信息行(5.4.2):总行数/返回数/上限/预算来源,模型据此自主决策
105
+ return "【MatriX】%s | 总行数 %s | 本次返回 %d 行 | 回传上限 %d 行 | 预算来源:%s" % (
106
+ title, total_text, shown, cap, SOURCE_LABELS.get(source, source))
107
+
108
+ def TotalText(df):
109
+ # 服务端 LIMIT+1 探测只告知是否截断、不告知截断后总数,
110
+ # 诚实展示 "≥N" 而非假总数
111
+ truncated = bool(df.attrs.get("truncated", False))
112
+ if truncated:
113
+ return "≥%d(已截断)" % len(df)
114
+ return str(len(df))
115
+
116
+ def RenderQuery(dataset, r, cap, source, preview_only = False, title = None):
117
+ # 查询类工具公共渲染:元信息行 + markdown 表 + 截断提示
118
+ df = r.data
119
+ if df is None or len(df) == 0:
120
+ return "查询成功但无数据(数据集 %s,可能该区间无记录,可放宽条件重试)" % dataset
121
+ if preview_only:
122
+ cap = min(cap, 5) # 预览模式:只给元信息 + 前 5 行样本
123
+ lines, shown = RenderDataFrame(df, cap, estimate.MAX_CHARS)
124
+ out = [MetaLine(title or ("数据集 %s(版本 %s)" % (dataset, r.version or "?")),
125
+ TotalText(df), shown, cap, source)]
126
+ out.extend(lines)
127
+ if shown < len(df) or bool(df.attrs.get("truncated", False)):
128
+ out.append(TRUNCATE_HINT)
129
+ return "\n".join(out)
130
+
131
+ def ResolveFileDir(output_dir):
132
+ # to_file 目录:显式参数 > env MATRIX_MCP_FILE_DIR > SDK 缺省路径
133
+ if output_dir:
134
+ return Path(output_dir)
135
+ env_dir = os.environ.get("MATRIX_MCP_FILE_DIR", "").strip()
136
+ if env_dir:
137
+ return Path(env_dir)
138
+ return None
139
+
140
+ def matrix_dataset_info(dataset: str = None) -> str:
141
+ """查询 MatriX 金融数据库的数据集元信息。
142
+
143
+ 不传 dataset:返回全部数据集清单(名称/中文说明/类别/频率/可用版本)。
144
+ 传 dataset(如 stock_daily_quotes):返回该数据集详情——字段清单
145
+ (英文名/中文说明/类型/别名)、时间列与代码列、支持的复权方式、
146
+ 可用交易所与版本,并附查询示例。
147
+
148
+ 典型用法:先不传参浏览全部数据集,再传名称看字段明细,
149
+ 然后用 matrix_query_data 取数。"""
150
+ r = GetWind().dataset_info(dataset)
151
+ if not r.ok:
152
+ return ErrorText(r)
153
+ data = r.data
154
+ if dataset is None:
155
+ rows = ["| 数据集 | 说明 | 类别 | 频率 | 字段数 | 版本 |",
156
+ "|---|---|---|---|---|---|"]
157
+ for item in data or []:
158
+ rows.append("| %s | %s | %s | %s | %s | %s |" % (
159
+ item.get("name", ""), item.get("title", ""), item.get("category", ""),
160
+ item.get("freq", ""), item.get("field_count", ""),
161
+ ",".join(item.get("versions", []))))
162
+ head = "【MatriX】全部数据集清单 | 共 %d 个(详情传 dataset 参数)" % len(data or [])
163
+ return "\n".join([head] + rows)
164
+ rows = ["| 字段 | 说明 | 类型 | 别名 |", "|---|---|---|---|"]
165
+ for f in data.get("fields", []):
166
+ rows.append("| %s | %s | %s | %s |" % (
167
+ f.get("name", ""), f.get("desc", ""), f.get("type", ""),
168
+ ",".join(f.get("aliases", []))))
169
+ head = "【MatriX】数据集 %s(%s)| 类别 %s | 频率 %s | 时间列 %s | 代码列 %s | 复权 %s | 版本 %s" % (
170
+ data.get("name", ""), data.get("title", ""), data.get("category", ""),
171
+ data.get("freq", ""), data.get("time_field", "") or "无",
172
+ data.get("symbol_field", "") or "无",
173
+ "/".join(data.get("adjust", [])) or "不支持",
174
+ ",".join(data.get("versions", [])))
175
+ example = ("查询示例:matrix_query_data(dataset=\"%s\", symbols=\"600519.SH\", "
176
+ "start_date=\"20261008\", end_date=\"20261027\", fields=\"date_t,symbol,close\")"
177
+ % data.get("name", ""))
178
+ return "\n".join([head] + rows + ["", example])
179
+
180
+ def matrix_query_data(dataset: str, symbols: str = None, start_date: str = None,
181
+ end_date: str = None, fields: str = None, limit: int = None,
182
+ version: str = None, preview_only: bool = False,
183
+ to_file: bool = False, output_dir: str = None,
184
+ ctx: Context = None) -> str:
185
+ """通用数据查询(MatriX 金融数据库,PostgreSQL 日频数据)。
186
+
187
+ 参数:
188
+ - dataset(必填):数据集名,如 stock_daily_quotes(股票日行情)、
189
+ index_daily_quotes(指数日行情)、fund_daily_quotes(基金日行情)、
190
+ future_daily_quotes(期货日行情)。全部清单用 matrix_dataset_info 查看。
191
+ - symbols:逗号分隔的证券代码,如 "600519.SH,000001.SZ";不传=全市场。
192
+ - start_date / end_date:日期 YYYYMMDD(如 20261008)。
193
+ - fields:逗号分隔的字段名,如 "date_t,symbol,close";不传=全部字段。
194
+ - limit:本次最多返回行数(会再受上下文预算约束)。
195
+ - version:数据版本(一般不传,用当前版本)。
196
+ - preview_only=True:只返回元信息与前 5 行样本(探查数据形态用)。
197
+ - to_file=True:大数据模式——全量数据不受预算裁剪,直接落盘为本地
198
+ Parquet 文件,只返回文件摘要包(路径/行数/列/样本/读取示例),
199
+ 之后用代码 pd.read_parquet(路径) 读取分析,数据不进对话上下文。
200
+
201
+ 返回:markdown 表格,头部含总行数/返回行数/回传上限/预算来源;
202
+ 被截断时会提示缩小范围、分页或改用 to_file。
203
+
204
+ 示例:matrix_query_data(dataset="stock_daily_quotes", symbols="600519.SH",
205
+ start_date="20261008", end_date="20261027", fields="date_t,close")"""
206
+ budget, source = estimate.ResolveBudget(ClientName(ctx))
207
+ if to_file:
208
+ # to_file 不受上下文预算裁剪(数据不进上下文,5.4.5),
209
+ # 受服务端 max_rows 硬顶与 statement_timeout 约束
210
+ r = GetWind().get_data(dataset, symbols = symbols, fields = fields,
211
+ start_date = start_date, end_date = end_date,
212
+ version = version)
213
+ if not r.ok or r.data is None or len(r.data) == 0:
214
+ return ErrorText(r) if not r.ok else "查询成功但无数据,未落盘"
215
+ try:
216
+ path = fileio.SaveParquet(r, path = ResolveFileDir(output_dir))
217
+ except Exception as e:
218
+ return "落盘失败:%s" % e
219
+ return fileio.BuildSummary(r.data, path)
220
+ cap = estimate.AllowedRows(budget, limit)
221
+ r = GetWind().get_data(dataset, symbols = symbols, fields = fields,
222
+ start_date = start_date, end_date = end_date,
223
+ version = version, limit = cap)
224
+ if not r.ok:
225
+ return ErrorText(r)
226
+ return RenderQuery(dataset, r, cap, source, preview_only = preview_only)
227
+
228
+ def matrix_get_daily_quotes(symbol: str, start_date: str, end_date: str,
229
+ fields: str = None, adjust: str = None,
230
+ ctx: Context = None) -> str:
231
+ """取单个证券的日行情时间序列(最常用快捷方式)。
232
+
233
+ 参数:
234
+ - symbol(必填):证券代码,如 "600519.SH"(贵州茅台)、"IM_S"(股指期货主力连续)。
235
+ - start_date / end_date(必填):日期 YYYYMMDD,闭区间。
236
+ - fields:逗号分隔字段,如 "date_t,close,volume";不传=全部字段。
237
+ - adjust:复权方式——None 不复权;"forward" 前复权;"backward" 后复权(仅股票/基金)。
238
+
239
+ 返回:markdown 表格,头部含总行数/返回行数/回传上限;区间太长被截断时,
240
+ 请缩小日期范围或改用 matrix_query_data(..., to_file=True) 落盘全量。
241
+
242
+ 示例:matrix_get_daily_quotes(symbol="600519.SH", start_date="20261008",
243
+ end_date="20261027", fields="date_t,close", adjust="forward")"""
244
+ budget, source = estimate.ResolveBudget(ClientName(ctx))
245
+ cap = estimate.AllowedRows(budget, None)
246
+ r = GetWind().wsd(symbol, fields, start = start_date, end = end_date, adjust = adjust)
247
+ if not r.ok:
248
+ return ErrorText(r)
249
+ return RenderQuery("日行情 %s" % symbol, r, cap, source,
250
+ title = "%s 日行情(版本 %s)" % (symbol, r.version or "?"))
251
+
252
+ def matrix_trading_dates(start_date: str, end_date: str, ctx: Context = None) -> str:
253
+ """查询交易日历(闭区间,升序)。
254
+
255
+ 参数:
256
+ - start_date / end_date(必填):日期 YYYYMMDD,如 20260101~20261231。
257
+
258
+ 返回:交易日列表(同样受上下文预算截断保护,年份范围很大时会被截断,
259
+ 可分段查询)。
260
+
261
+ 示例:matrix_trading_dates(start_date="20261001", end_date="20261031")"""
262
+ budget, source = estimate.ResolveBudget(ClientName(ctx))
263
+ cap = estimate.AllowedRows(budget, None)
264
+ r = GetWind().get_trading_dates(start = start_date, end = end_date)
265
+ if not r.ok:
266
+ return ErrorText(r)
267
+ dates = r.data or []
268
+ if not dates:
269
+ return "查询成功但该区间无交易日"
270
+ shown = dates[:cap]
271
+ head = MetaLine("交易日历 %s~%s" % (start_date, end_date),
272
+ str(len(dates)), len(shown), cap, source)
273
+ lines = [head, "| trading_date |", "|---|"]
274
+ lines.extend("| %s |" % d for d in shown)
275
+ if len(shown) < len(dates):
276
+ lines.append("⚠ 已截断至 %d 行,请分段查询。" % cap)
277
+ return "\n".join(lines)
278
+
279
+ def matrix_is_trading_date(date: str) -> str:
280
+ """判断指定日期是否为A股交易日,并给出最近的已过去交易日。
281
+
282
+ 参数:
283
+ - date(必填):日期 YYYYMMDD,如 20261003。
284
+
285
+ 返回:是/否 + 最近交易日(≤该日期的最后一个交易日)。
286
+
287
+ 示例:matrix_is_trading_date(date="20261003")"""
288
+ r = GetWind().get_trading_dates(end = date)
289
+ if not r.ok:
290
+ return ErrorText(r)
291
+ dates = r.data or []
292
+ if not dates:
293
+ return "查询失败:交易日历为空"
294
+ iso = "%s-%s-%s" % (date[:4], date[4:6], date[6:8]) if len(date) == 8 else date
295
+ if iso in dates:
296
+ return "%s 是交易日(即最近交易日)" % iso
297
+ return "%s 不是交易日,最近交易日为 %s" % (iso, dates[-1])
298
+
299
+ # 注册顺序即 Agent 工具列表展示顺序(5.4.1 清单顺序)
300
+ ALL_TOOLS = (matrix_dataset_info, matrix_query_data, matrix_get_daily_quotes,
301
+ matrix_trading_dates, matrix_is_trading_date)
302
+
303
+ def RegisterTools(server):
304
+ # 由 server.py 调用:模块级工具统一挂载(名称取函数名,说明取 docstring)
305
+ for fn in ALL_TOOLS:
306
+ server.tool()(fn)