ashare-lake:本地 A 股資料湖與 AI agent 查詢
ashare-lake 將 A 股資料以 Parquet 儲存在本機,提供 39 個資料集、下市紀錄、時間點查詢,以及供 AI agent 使用的 MCP 伺服器。
本地 A 股資料湖,是將中國大陸股票市場的歷史資料以欄式 Parquet 檔案儲存在自己的磁碟上,可透過 DuckDB 或 Polars 讀取,而不是逐頁呼叫資料供應商的端點。ashare-lake 是一項開源專案,負責建立這套資料湖,並提供維持資料更新的每日工作,以及讓 AI agent 查詢資料的 Model Context Protocol 伺服器。這裡值得專文介紹的原因,在於它採用兩項設計:保留已下市的股票,且可依過去某個日期讀取基本面資料。
為什麼 AI agent 需要本地 A 股資料湖
研究中國股票的 agent 若沒有本地資料副本,只有兩條路可走。它可以擷取財經網頁,把 context window 浪費在 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 檔標的;面板中的另外四個交易時段也是如此。一個全國性市場、一週、單一解析度。另一個市場若累積十年的日線資料、基本面資料、指數成分資料與資金流向紀錄,也會呈現同樣的規模;若透過 HTTP 分頁讀取,將耗盡 agent 的執行資源。
本地資料湖能同時改變兩件事。讀取資料時,執行的是檔案掃描,而不是受配額限制的請求;此外,今天撰寫的查詢在六個月後仍會回傳相同資料列,這是讓 backtest 可供查核的必要條件。我們關於AI agent 的市場資料技能以及建構在市場資料上的 SQL API的筆記,也對美國市場資料提出相同論點。
安裝,並固定至單一版本
Python 3.10 或更新版本。請固定版本:2026年7月27日至8月2日期間發布了六個版本,因此在 agent 的設定腳本中未固定版本的安裝,會隨時變動。程式碼位於 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,並填入你的資料根目錄;不需要簽出 repository。--config設定輸出路徑,--force會覆寫現有內容。asl doctor離線執行檢查,在任何資料移動前先完成驗證。asl servers test探測上游報價主機。asl sources探測每個來源,並使用--vantage、cn、overseas或local。
到此為止。下一個指令會執行回補;不要一邊閱讀一邊啟動它。
回填作業的內容
asl init 建立目錄結構並填入歷史資料。專案文件指出,完整執行需要數小時的實際執行時間,並會占用數 GB 的磁碟空間;此外,還需要連線至上游 Tongdaxin 報價主機,這正是 asl servers test 所驗證的項目。asl init --profile quick 則改為處理最近三年的資料,幾分鐘內即可完成;在該期間內曾交易過的所有名稱都會保留,包括其後已退出市場者。之後由 asl run daily 執行增量作業,asl status --datasets 回報各資料集的涵蓋範圍與資料新鮮度,asl serve 則在 127.0.0.1:8787 提供唯讀儀表板。
此 repository 完全不附帶資料。每個 Parquet 檔案都會在你的機器上建立,且每一列都記錄來源及抓取時間,以保留列層級的資料 lineage。
39 個資料集有哪些?
這些資料集按層次排列,從參考資料向外延伸:包括 36 個整理後的資料表,以及 3 個衍生資料表。
- 參考資料:金融工具、涵蓋 2016年至2027年的交易日曆、交易狀態。
- 市場資料:日線行情、指數行情、1分鐘與5分鐘行情、逐筆成交資料、商品行情、調整因子、下市事件。
- 公司事件:公司行動、公告索引、財報披露時程。
- 基本面與估值:財務報表項目、估值指標、分析師共識。
- 資金流:資金流向、融資融券、北向資金流向與持股、大宗交易披露的龍虎榜、鉅額交易、機構持股。
- 市場結構與產業:板塊成分股、指數成分股、產業成員、產業指數。
- 總體經濟:總體經濟指標、市場廣度、經濟日曆。
- 市場情緒與輪動:情緒分數、熱門排名、板塊行情、板塊資金流向、新聞標題、即時新聞快訊。
- 風險與法遵:股份解禁時程、監管事件。
資料集所處的層次,反映作者重視的內容。調整因子與下市事件和日線行情一樣,均列在市場資料層,而不是放在附錄中。對任何以歷史資料測試交易想法的人而言,這項安排是整個專案中最有價值的部分。
本地資料如何處理存活者偏誤
存活者偏誤發生於研究樣本只取自目前仍掛牌的標的。所有曾合併、下市或轉為私人公司的企業,都會悄悄從樣本中消失;而消失的標的通常並非績效最佳者。
我們的資料倉庫可以量化美國市場中的這個缺口,計算方式相同,只是使用不同的代碼。對每一年,先取出在3月第2週出現過分鐘線資料的所有代號,再檢查其中哪些代號在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 server
Model Context Protocol 是 agent 取得工具的方式。asl mcp 透過 stdio 傳遞協定,而 client 會啟動該程序:
claude mcp add ashare-lake -- asl mcp --config /abs/path/to/ashare-lake.toml- 其他 MCP client 也會使用相同的兩個部分:以
asl作為命令,並將mcp --config加上絕對路徑作為引數。路徑必須是絕對路徑,因為 client 可能會從任何目錄啟動該程序。
系統會提供六項工具,依問題類型而非資料集分類:describe_lake 用於查詢現有內容及其讀取方式;resolve_symbol 用於將名稱轉換為代碼,也包括已下市名稱;query_bars;帶有 as_of 引數的 query_fundamentals;用於其他所有查詢的 query_dataset;以及可跨資料集執行單一唯讀 DuckDB SELECT 的 run_sql。加入 --live 旗標後,server 可直接從上游來源回覆代碼查詢及未調整的每日行情資料,而不必寫入資料湖。
先了解這些限制
- 僅支援 A 股。不涵蓋香港、美國上市標的,也不涵蓋中國大陸以外的市場。
- 這是個人專案。Issue 與 pull request 僅會獲得盡力處理,文件也明確說明不保證可用性:上游網站可能變更,IP 也可能遭封鎖,導致資料擷取中斷,直到有人修補為止。
- Apache 2.0 授權適用於程式碼,不適用於資料。每個上游來源均有自己的使用條款,維護者不授予重新散布或轉售所建立 Parquet 檔案的權利。任何商業用途前,請先閱讀來源條款。
- 讀取這些來源不需要帳戶或 token;這正是其吸引力所在,也使其更為脆弱。
- Windows 支援於 0.3.0 版本加入,Python 最低版本則在 0.3.1 版提升至 3.10。
這些專案資訊的來源
0.5.0 版本、六個發布日期及 Python 最低版本,取自專案的 PyPI 發布歷史與 CHANGELOG,資料查閱日期為2026年8月4日。資料集目錄、下架與 point-in-time 行為、CLI 旗標、MCP 工具清單,以及授權與支援說明,取自儲存庫文件:docs/datasets/catalog.md、docs/reference/cli.md、docs/reference/mcp.md 及 docs/legal-and-data-sources.md。軟體更新速度快於文章,因此請以所安裝版本的文件為準。兩個存活偏誤面板衡量的是我們自有資料倉儲中的美國 symbols,而非中國上市標的;其用途是說明運作機制,不代表對 A 股市場的測量。
常見問答
建立本機 A 股資料湖需要 API key 嗎?
不需要。它讀取的上游來源不要求註冊或 token,資料湖本身則存放在你的電腦上。每個來源都有各自的使用條款,在進行任何商業用途前都必須確認。
ashare-lake 回補完整歷史資料需要多久?
文件顯示,完整歷史資料回補需要數小時的實際執行時間,並占用數 GB 磁碟空間;整個過程都需要維持與上游報價主機的正常連線。asl init --profile quick 可在數分鐘內涵蓋最近三年資料,且仍包含後來下市的標的。
本機 A 股資料湖是否包含已下市股票?
包含。已下市的股票代號會保留在 instruments 資料集中,delisting_events 資料表則記錄每個標的的下市結果;universe="all_a" 會建立包含這些標的的歷史快照。我們上方對美國市場所做的測量,也顯示只保留存續標的的母體會遺漏多少資料。
AI agent 能直接查詢 ashare-lake 嗎?
可以。asl mcp 透過 Model Context Protocol 提供六項工具,其中一項是唯讀 SQL 工具,因此 agent 查詢的是本機 Parquet,而不是進行網頁抓取。請使用 claude mcp add ashare-lake -- asl mcp --config /abs/path/to/ashare-lake.toml 進行註冊。
ashare-lake 能取代 AkShare 或 Baostock 嗎?
不能。它位於這些工具之上。這些函式庫負責從上游取得資料;本專案則將取得的資料儲存、建立版本並進行調節,整理成具備逐列資料來源追蹤能力、且每個資料集各有一份契約的精選 Parquet。
上方每一項數據都是根據真實市場資料執行的已儲存、具版本控管的查詢。展開任一面板即可查看 SQL,或在 Strasmore terminal 上對你自己的標的母體執行相同的存活者偏誤檢查。