本地 A 股数据湖与 AI 智能体查询
ashare-lake 将 A 股数据存储在本地磁盘,提供 39 个数据集、退市记录、时点查询和面向 AI 智能体的 MCP 服务器。
本地 A 股数据湖,是将中国内地股票市场的历史数据以列式 Parquet 文件存放在本地磁盘上,可通过 DuckDB 或 Polars 读取,而不是逐页调用供应商端点。ashare-lake 是一个开源项目,负责构建这一数据湖,并提供保持数据更新的每日任务,以及一个允许 AI agent 查询数据的 Model Context Protocol 服务器。该项目有两项设计选择值得单独介绍:保留已退市股票,并支持按过去某一日期读取基本面数据。
为什么AI智能体需要本地A股数据湖
没有本地副本的中国股票研究智能体只有两条路。它可以抓取财经网页,把上下文窗口耗在HTML上,并生成下个月无人能够复现的数字。或者调用需要注册的供应商接口。此类接口会限制行数,并将每个结果绑定到一个账户。
规模是最容易被低估的部分。我们的数据仓库存储着分钟级的美国市场行情。下面是一周普通数据的规模:
每个数字背后的完整 SQL
SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS session,
formatDateTime(toDate(toTimeZone(window_start, 'America/New_York')), '%b %e') AS session_label,
uniqExact(ticker) AS tickers_count,
round(count() / 1000000, 2) AS minute_bars_millions
FROM global_markets.delayed_stocks_minute_aggs
WHERE toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-07-20')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-24')
GROUP BY session, session_label
ORDER BY session在Jul 20,行情数据产生了1.79百万条分钟K线,涵盖11737只证券;面板中的另外四个交易时段也有类似规模。一个市场、一个星期、一个分辨率。另一个市场十年的日K线、基本面、指数成分和资金流记录也具有相同的规模。通过HTTP逐页读取这些数据,会耗尽智能体的一次运行。
本地数据湖会同时改变两件事。读取数据变成文件扫描,而不是消耗配额。今天编写的查询在六个月后仍会返回相同的行,这正是可核验回测所需要的条件。我们关于面向AI智能体的市场数据技能以及基于市场数据的SQL API的笔记,也对美国数据提出了相同观点。
安装并固定到一个版本
Python 3.10 或更高版本。请固定版本:2026 年 7 月 27 日至 8 月 2 日期间发布了六个版本,而在代理的设置脚本中执行未固定版本的安装,结果会随时间变化。代码位于 rootSunc/ashare-lake,采用 Apache 2.0 许可证。
pip install ashare-lake==0.5.0安装截至 2026 年 8 月初的当前版本。asl --version显示已安装的构建版本。asl config init --data-root /path/to/ashare-lake写入已填入数据根目录的打包示例 TOML,无需检出代码仓库。--config设置输出路径,--force覆盖现有文件。asl doctor在任何数据移动前离线运行检查。asl servers test探测上游行情主机。asl sources探测每个数据源,并使用--vantage、cn、overseas或local。
先停在这里。下一条命令用于回填数据,不要在阅读时直接启动它。
回填功能的作用
asl init创建目录结构并填充历史数据。项目文档显示,完整运行需要数小时的实际时间,并会占用数GB磁盘空间;同时还需要连接上游通达信行情主机,asl servers test会验证这一连接。asl init --profile quick改为覆盖最近三年,几分钟内即可完成,并保留这段时间内曾经交易过的所有名称,包括后来已经退出市场的名称。之后,asl run daily负责增量更新,asl status --datasets报告各数据集的覆盖范围和时效性,asl serve则在127.0.0.1:8787上提供只读仪表盘。
代码仓库不附带任何数据。每个Parquet文件都在您的机器上生成,并且每一行都记录数据血缘,包括数据来源及抓取时间。
这39个数据集是什么?
它们按层级组织,从参考数据向外延展:36个整理后的表,以及3个派生表。
- 参考数据:证券、覆盖2016年至2027年的交易日历、交易状态。
- 市场数据:日线行情、指数行情、1分钟和5分钟行情、成交明细、商品行情、复权因子、退市事件。
- 公司事件:公司行动、公告索引、业绩披露日程。
- 基本面与估值:财务报表项目、估值指标、分析师一致预期。
- 资金流向:资金流、融资融券、北向资金流向及持仓、大宗交易披露龙虎榜、协议大宗交易、机构持仓。
- 结构与行业:板块成分股、指数成分股、行业成分股、行业指数。
- 宏观数据:宏观指标、市场宽度、经济日历。
- 情绪与轮动:情绪评分、热度排名、板块行情、板块资金流向、新闻标题、快讯电报。
- 风险与合规:股份解禁日程、监管事件。
数据集所处的层级,反映了作者关注的重点。复权因子和退市事件与日线行情并列于市场数据层,而不是放在附录中。对于任何基于历史数据测试交易想法的人而言,这种安排是整个项目中最有价值的一半。
本地湖泊如何处理幸存者偏差
幸存者偏差,是指研究样本只包含当前仍在挂牌交易的标的。所有已合并、私有化或退市的公司都会被默默排除,而消失的标的很少是赢家。
我们的数据库可以量化这一缺口,适用于美国市场;计算方法相同,只是代码体系不同。对每一年,先选出在3月第二周有分钟K线的所有代码,再检查其中哪些代码在2026年7月最后两周仍有交易记录:
每个数字背后的完整 SQL
WITH on_tape_now AS (
SELECT ticker
FROM global_markets.delayed_stocks_minute_aggs
WHERE toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-07-20')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
GROUP BY ticker
),
cohort AS (
SELECT toYear(toTimeZone(window_start, 'America/New_York')) AS cohort_year,
ticker
FROM global_markets.delayed_stocks_minute_aggs
WHERE toYear(toTimeZone(window_start, 'America/New_York')) BETWEEN 2016 AND 2025
AND toMonth(toTimeZone(window_start, 'America/New_York')) = 3
AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) BETWEEN 10 AND 14
GROUP BY cohort_year, ticker
)
SELECT c.cohort_year AS year,
count() AS names_on_tape_count,
countIf(n.ticker != '') AS still_trading_count,
count() - countIf(n.ticker != '') AS gone_count,
round(100 * countIf(n.ticker != '') / count(), 1) AS still_trading_pct
FROM cohort AS c
LEFT JOIN on_tape_now AS n ON c.ticker = n.ticker
GROUP BY year
ORDER BY year在2016当周交易的8101个代码中,50.1%个在2026年7月下旬仍有交易记录,4040个已经没有。2025组的结果为86.7%。如果从当前挂牌标的出发,向前回溯10年进行筛选,随着回溯时间拉长,被排除的市场份额会不断扩大。
一种看似合理的假设是,消失的标的全都是低价股。但将2021年3月的样本按当月平均日成交额分层后,结果并非如此:
每个数字背后的完整 SQL
WITH on_tape_now AS (
SELECT ticker
FROM global_markets.delayed_stocks_minute_aggs
WHERE toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2026-07-20')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
GROUP BY ticker
),
march_2021 AS (
SELECT ticker,
sum(toFloat64(close) * toFloat64(volume))
/ uniqExact(toDate(toTimeZone(window_start, 'America/New_York'))) AS avg_daily_dollar_volume
FROM global_markets.delayed_stocks_minute_aggs
WHERE toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2021-03-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2021-03-31')
GROUP BY ticker
)
SELECT multiIf(d.avg_daily_dollar_volume >= 1000000000, '$1B or more',
d.avg_daily_dollar_volume >= 100000000, '$100M to $1B',
d.avg_daily_dollar_volume >= 10000000, '$10M to $100M',
d.avg_daily_dollar_volume >= 1000000, '$1M to $10M',
'under $1M') AS liquidity_bucket,
count() AS names_count,
countIf(n.ticker = '') AS gone_count,
round(100 * countIf(n.ticker = '') / count(), 1) AS gone_pct
FROM march_2021 AS d
LEFT JOIN on_tape_now AS n ON d.ticker = n.ticker
GROUP BY liquidity_bucket
ORDER BY min(d.avg_daily_dollar_volume)under $1M层级的流失比例最高,3968个标的中有57.5%个消失。交易最活跃的层级也未能幸免:2021年3月日成交额为$1B or more的89个代码中,有3.4%个在2026年7月下旬前退出了市场。无论是合并、私有化、破产还是被指数剔除,在价格表中的结果都相同:相应行停止更新。
ashare-lake将其视为核心问题。 instruments 数据集保留退市代码,而不是只筛选仍在交易的标的;delisting_events 表记录每个标的离开市场的原因;Python API 中的universe="all_a"则会返回包含这些代码的历史截面。该项目文档显示,在2016年至2021年期间,仅包含幸存者的回测与纳入退市标的的回测之间,结果差距大致达到两倍。这与我们关于回测中前视偏差的笔记正好相反:问题在于样本空间悄悄掌握了未来信息。
时点数据读取
load("financial_statement_items", as_of="2018-04-30")返回截至该日期或更早公布的每个科目最新版本,而不是之后修订的数值。K线数据带有adjust参数:hfq表示向后复权,qfq表示在查询窗口内进行标准化,或使用原始价格。若因子基于市场当时尚未公布的数据拟合,它就没有实际测量意义;这是检查任何LLM生成的alpha因子流程时首先要确认的事项。
将其注册为 MCP 服务器
Model Context Protocol 是智能体获取工具的方式。asl mcp通过标准输入输出(stdio)通信,客户端负责启动该进程:
claude mcp add ashare-lake -- asl mcp --config /abs/path/to/ashare-lake.toml- 其他 MCP 客户端也需要相同的两部分:将
asl作为命令,并将mcp --config加上绝对路径作为参数。路径必须是绝对路径,因为客户端可能从任意目录启动该进程。
服务器会提供六个工具。它们按问题类型组织,而不是按数据集组织:describe_lake 用于说明已有数据及其读取方式;resolve_symbol 用于将名称解析为代码,包括已退市的名称;query_bars 用于查询行情柱;query_fundamentals 支持 as_of 参数;query_dataset 用于查询其他数据;run_sql 用于跨数据集执行单条只读 DuckDB SELECT 查询。启用 --live 标志后,服务器可以从上游获取证券代码查询结果和未经复权的日线行情,而无需写入数据湖。
首先应了解的限制
- 仅支持A股。不支持香港、美国上市股票,也不支持中国大陆以外的任何市场。
- 这是一个个人项目。Issue和Pull Request仅会得到尽力处理。文档明确说明,项目不保证可用性:上游网站可能发生变化,IP地址也可能被封禁,从而导致数据采集暂停,直到有人修复问题。
- Apache 2.0许可证适用于代码,不适用于数据。每个上游数据源仍受其自身条款约束,维护者不授予您重新分发或转售所生成Parquet文件的权利。用于任何商业用途前,请先阅读数据源条款。
- 读取这些数据源无需账户或令牌。这既是项目的便利之处,也是其脆弱性所在。
- 0.3.0版本开始支持Windows,0.3.1版本将Python最低版本提升至3.10。
:::details这些项目事实的来源
0.5.0版本、六个发布日期以及Python最低版本,来自项目在2026年8月4日查阅的PyPI发布历史和CHANGELOG。数据集目录、退市处理和时点行为、CLI参数、MCP工具列表,以及许可和支持说明,来自仓库文档:docs/datasets/catalog.md、docs/reference/cli.md、docs/reference/mcp.md和docs/legal-and-data-sources.md。软件的变化速度快于文章更新速度,因此请以您所安装版本对应的文档为准。两幅生存偏差图展示的是我们自有数据仓库中的美国股票代码,而非中国上市股票;它们用于说明该机制,不代表对A股市场的测量。
:::
常见问题
构建本地 A 股数据湖需要 API 密钥吗?
不需要。它读取的上游数据源无需注册或令牌,数据湖本身也运行在您的本地设备上。每个数据源都有自己的使用条款,开展商业用途前须先确认相关条款。
ashare-lake 回补数据需要多长时间?
文档显示,完整历史数据回补需要数小时的实际运行时间,并占用数 GB 磁盘空间。整个过程都需要保持与上游行情主机的有效连接。asl init --profile quick 可在数分钟内覆盖最近三年的数据,并且仍包含后来退市的股票。
本地 A 股数据湖包含退市股票吗?
包含。退市股票代码会保留在 instruments 数据集中,delisting_events 表会记录每只股票的退出情况,universe="all_a" 则会生成包含退市股票的历史快照。上文对美国市场的测算显示,只保留存续股票的股票池会遗漏多少数据。
AI 智能体可以直接查询 ashare-lake 吗?
可以。asl mcp 通过 Model Context Protocol 提供六个工具,其中一个是只读 SQL 工具。因此,智能体可以查询本地 Parquet 文件,而不必抓取网页数据。请使用 claude mcp add ashare-lake -- asl mcp --config /abs/path/to/ashare-lake.toml 完成注册。
ashare-lake 能取代 AkShare 或 Baostock 吗?
不能。它位于这两者之上。相关库负责从上游获取数据;本项目则将获取的数据存储、版本化并进行核对,整理为经过筛选的 Parquet 文件,并提供行级数据溯源和每个数据集一套数据契约。
上文每个数字都来自基于真实市场数据、已存储并版本化的查询。展开任意面板即可查看 SQL,也可以在 Strasmore 终端上对您自己的股票池运行相同的幸存者偏差检查。