Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor · · Updated 2026-08-08

AI 에이전트를 위한 로컬 A주 데이터 레이크 구축

ashare-lake는 서른아홉 개의 데이터셋과 상장 폐지 기록을 포함한 A주 데이터를 로컬 디스크에 구축합니다. DuckDB와 Polars를 활용한 시점별 쿼리 및 MCP 서버를 지원하여 AI 에이전트의 효율적인 데이터 분석 환경을 제공합니다.

로컬 A주 데이터 레이크 구축

로컬 A주 데이터 레이크는 중국 본토 주식 시장의 과거 데이터를 개별 페이지 단위로 호출하는 벤더 엔드포인트 대신, 사용자의 디스크에 칼럼형 Parquet 파일 형태로 저장해 DuckDB나 Polars로 읽을 수 있게 만든 환경을 의미합니다. ashare-lake는 이러한 데이터 레이크를 구축하는 오픈소스 프로젝트로, 데이터를 최신 상태로 유지하는 일일 작업과 AI 에이전트가 이를 쿼리할 수 있도록 지원하는 Model Context Protocol 서버를 포함합니다. 이 프로젝트가 주목받는 이유는 두 가지 설계 선택 때문입니다. 첫째, 상장 폐지된 종목의 데이터를 유지하며, 둘째, 과거 특정 시점의 펀더멘털 데이터를 조회할 수 있다는 점입니다.

AI 에이전트에 로컬 A주 데이터 레이크가 필요한 이유

로컬 데이터 사본 없이 중국 주식을 조사하는 에이전트에게는 두 가지 경로가 있습니다. 금융 페이지를 스크래핑하여 컨텍스트 윈도우를 HTML 해석에 낭비하고 다음 달에는 아무도 재현할 수 없는 수치를 생성하는 것입니다. 아니면 등록이 필요한 벤더를 호출하는 것인데, 이는 행 수를 제한하고 모든 결과를 특정 계정에 귀속시킵니다.

규모는 과소평가되는 부분입니다. 당사의 데이터 웨어하우스는 분 단위 해상도로 미국 시장의 테이프(tape) 데이터를 보유하고 있으며, 평범한 한 주의 데이터는 다음과 같습니다.

조회미국 분 단위 거래 데이터 1주일: 2026년 7월 20일~24일 세션별 종목 및 바(bar) 수
모든 수치 뒤에 숨겨진 정확한 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
Run this yourself

Jul 20에 테이프는 11737개의 심볼에 걸쳐 1.79백만 개의 분 단위 바(bar)를 생성했으며, 패널의 나머지 네 세션도 이와 유사합니다. 하나의 국가 시장, 일주일, 하나의 해상도입니다. 다른 시장에 대한 십 년 치의 일간 바, 펀더멘털, 지수 구성 종목, 자금 흐름 기록도 같은 형태를 띠며, 이를 HTTP를 통해 페이징하는 것은 에이전트의 실행 자원을 소모시킵니다.

로컬 데이터 레이크는 두 가지를 동시에 변화시킵니다. 읽기 작업은 할당량 제한이 아닌 파일 스캔이 되며, 오늘 작성된 쿼리는 6개월 후에도 동일한 행을 반환하는데, 이는 백테스트가 검증 가능하기 위해 필수적인 요소입니다. 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은 각 소스를 탐색하며, --vantagecn, overseas 또는 local 중 하나를 반환합니다.

여기서 중단하십시오. 다음 명령어는 백필(backfill) 작업이며, 문서를 읽는 도중에 실행할 성격의 것이 아닙니다.

백필(backfill)의 역할

asl init는 디렉토리 구조를 생성하고 과거 데이터를 채워 넣습니다. 프로젝트 문서에 따르면 전체 실행 시 수 시간의 벽시계 시간(wall time)과 수 기가바이트(GB)의 디스크 용량이 소요되며, 상위 통다신(Tongdaxin) 시세 호스트와의 실시간 연결이 필요합니다. asl servers test은 이 연결 상태를 확인합니다. asl init --profile quick는 대신 최근 3년 치 데이터를 수 분 내에 처리하며, 해당 기간 내에 거래된 모든 종목을 포함합니다. 여기에는 현재 시장에서 상장 폐지된 종목도 포함됩니다. asl run daily는 이후 수행되는 증분 작업이며, asl status --datasets은 데이터셋별 커버리지와 최신성을 보고합니다. asl serve은 127.0.0.1:8787 주소에 읽기 전용 대시보드를 제공합니다.

이 저장소는 어떠한 데이터도 포함하고 있지 않습니다. 모든 Parquet 파일은 사용자의 기기에서 직접 생성되며, 각 행(row)에는 해당 데이터를 생성한 소스와 수집 시점을 기록하는 행 단위 계보(row-level lineage)가 포함됩니다.

39개의 데이터셋은 무엇인가?

이 데이터셋은 참조 데이터에서 외부로 확장되는 계층 구조를 가지며, 36개의 큐레이션된 테이블과 3개의 파생 테이블로 구성됩니다.

  • 참조(Reference): 종목 정보, 2016년부터 2027년까지의 거래 캘린더, 거래 상태.
  • 시장 데이터(Market data): 일봉(daily bars), 지수 봉(index bars), 1분 및 5분 봉, 거래 체결(trade ticks), 원자재 봉, 수정 주가 계수(adjustment factors), 상장폐지 이벤트.
  • 기업 이벤트(Corporate events): 기업 공시(corporate actions), 공시 인덱스, 실적 발표 일정.
  • 펀더멘털 및 밸류에이션(Fundamentals and valuation): 재무제표 항목, 밸류에이션 지표, 애널리스트 컨센서스.
  • 자본 흐름(Capital flow): 펀드 자금 흐름, 신용 거래(margin trading), 북향 자금(northbound flows) 및 보유 현황, 대량 주문 공시인 드래곤-타이거 보드(dragon-tiger board), 블록딜, 기관 보유 현황.
  • 구조 및 산업(Structure and industry): 섹터 구성 종목, 지수 구성 종목, 산업군 구성 종목, 산업 지수.
  • 거시 경제(Macro): 거시 경제 지표, 시장 폭(market breadth), 경제 캘린더.
  • 투자 심리 및 순환(Sentiment and rotation): 투자 심리 점수, 인기 순위(hot rank), 섹터 봉, 섹터 자금 흐름, 뉴스 헤드라인, 속보 와이어.
  • 리스크 및 규제 준수(Risk and compliance): 보호예수 해제 일정, 규제 관련 이벤트.

데이터셋의 위치는 작성자가 무엇을 중요하게 여기는지를 보여줍니다. 수정 주가 계수와 상장폐지 이벤트는 부록이 아닌 일봉과 같은 시장 데이터 계층에 배치되어 있습니다. 이러한 배치는 과거 데이터를 바탕으로 전략을 검증하는 투자자에게 이 프로젝트가 가진 가치의 절반을 차지합니다.

How a local lake handles survivorship bias

Survivorship bias is what you get when the universe of a study is drawn from the names that are still listed today. Every company that merged, went private, or was delisted is silently absent, and the names that vanish are rarely the winners.

Our warehouse can size that hole for the US market, the same arithmetic in a different alphabet. For each year, take every symbol that printed a minute bar in the second week of March, then check which of them were still printing in the last two weeks of July 2026:

조회거래 데이터 생존율 측정: 2026년 7월 말까지 거래 중인 미국 종목의 3월 코호트(cohort)
모든 수치 뒤에 숨겨진 정확한 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
Run this yourself

Of the 8101 symbols trading that week in 2016, 50.1% were still on the tape in late July 2026, and 4040 were not. The 2025 cohort reads 86.7%. Run a screen built from today's listings backwards across those 10 years and it drops a widening share of the market as it goes.

The comfortable assumption is that the missing names were all penny stocks. Sorting the March 2021 cohort into tiers by its average daily dollar volume that month says otherwise:

조회거래 데이터에서 사라진 종목: 2021년 3월 종목별 일일 달러 거래대금 기준, 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
),
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)
Run this yourself

The under $1M tier lost the largest share, 57.5% of 3968 names. The busiest tier was not exempt: 3.4% of the 89 symbols trading $1B or more a day in March 2021 had left by late July 2026. Mergers, take-privates, bankruptcies, and index exits all end the same way in a price table: the rows stop.

ashare-lake treats that as a first-class problem. The instruments dataset retains delisted symbols rather than filtering down to live ones, a delisting_events table records how each departed name ended, and universe="all_a" in the Python API resolves a historical snapshot that includes them. The project's own docs report roughly a two-fold gap between a survivor-only backtest and a delisting-inclusive one over 2016 to 2021. That is the mirror image of the trap in our look-ahead bias in backtesting notes: a universe that quietly knows the future.

Point-in-time reads

load("financial_statement_items", as_of="2018-04-30") returns the latest version of each line item announced on or before that date, not the number as later restated. Bars carry an adjust argument: hfq for backward adjustment, qfq normalized inside the query window, or raw prices. A factor fitted on numbers the market had not published yet measures nothing, which is the first thing to check in any LLM generated alpha factor pipeline.

MCP 서버로 등록하기

Model Context Protocol(MCP)은 에이전트가 도구를 선택하는 방식입니다. 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, as_of 인수를 포함하는 query_fundamentals, 그 외 모든 데이터를 위한 query_dataset, 그리고 데이터셋 전반에 걸쳐 단일 읽기 전용 DuckDB SELECT를 실행하는 run_sql이 있습니다. --live 플래그를 사용하면 서버는 레이크(lake)에 기록하지 않고도 업스트림에서 종목 조회 및 미조정 일봉 데이터를 가져와 응답할 수 있습니다.

먼저 알아두어야 할 제한 사항

  • A-shares(중국 본토 상장 주식)에 한정됩니다. 홍콩이나 미국 상장 종목, 그 외 중국 본토 이외의 종목은 포함되지 않습니다.
  • 개인 프로젝트입니다. 이슈와 풀 리퀘스트(pull request)는 최선을 다해 처리하지만, 문서에 명시된 대로 가용성에 대한 보장은 없습니다. 업스트림 사이트가 변경되거나 IP가 차단될 경우, 누군가 패치를 적용하기 전까지 데이터 수집이 중단될 수 있습니다.
  • Apache 2.0 라이선스는 코드에만 적용되며 데이터에는 적용되지 않습니다. 각 업스트림 소스는 자체적인 이용 약관을 따르며, 관리자는 사용자가 생성한 Parquet 파일의 재배포나 재판매 권리를 부여하지 않습니다. 상업적 용도로 사용하기 전에 원천 데이터의 이용 약관을 확인하십시오.
  • 데이터를 읽어오는 소스에 계정이나 토큰이 필요하지 않다는 점이 이 프로젝트의 장점인 동시에 취약점입니다.
  • Windows 지원은 0.3.0 버전부터 추가되었으며, Python 최소 요구 사양은 0.3.1 버전부터 3.10으로 상향되었습니다.
이 프로젝트 정보의 출처

0.5.0 버전, 6개의 릴리스 날짜, Python 최소 요구 사양은 2026년 8월 4일 기준 프로젝트의 PyPI 릴리스 기록 및 CHANGELOG에서 확인했습니다. 데이터셋 카탈로그, 상장 폐지 및 시점(point-in-time) 데이터 처리 방식, CLI 플래그, MCP 도구 목록, 라이선스 및 지원 관련 문구는 저장소 문서인 docs/datasets/catalog.md, docs/reference/cli.md, docs/reference/mcp.md, docs/legal-and-data-sources.md에서 가져왔습니다. 소프트웨어는 게시물보다 빠르게 업데이트되므로, 설치하는 버전에 맞는 문서를 확인하시기 바랍니다. 두 개의 생존 편향(survivorship) 패널은 중국 상장 종목이 아닌 당사 데이터 웨어하우스의 미국 종목을 측정한 것이며, A-share 시장의 측정치가 아닌 메커니즘을 설명하기 위한 예시입니다.

자주 묻는 질문(FAQ)

로컬 A주 데이터 레이크를 구축하려면 API 키가 필요합니까?

이 프로젝트에는 필요하지 않습니다. 이 레이크가 읽어오는 상위 소스들은 등록이나 토큰이 필요 없으며, 레이크 자체도 사용자의 기기 내에서 구동됩니다. 각 소스는 고유한 이용 약관을 유지하므로, 상업적 용도로 활용하기 전에 이를 확인해야 합니다.

ashare-lake의 백필(backfill, 과거 데이터 채우기)에는 시간이 얼마나 걸립니까?

문서에 따르면 전체 이력을 백필하는 데는 수 시간의 실제 시간과 수 기가바이트(GB)의 디스크 공간이 필요하며, 전체 과정 동안 상위 시세 호스트와의 연결이 유지되어야 합니다. asl init --profile quick은 최근 3년 치 데이터를 수 분 내에 처리하며, 상장 폐지된 종목들도 포함하고 있습니다.

로컬 A주 데이터 레이크에 상장 폐지된 종목도 포함됩니까?

포함됩니다. 상장 폐지된 심볼은 종목 데이터셋에 그대로 유지되며, delisting_events 테이블에 각 종목의 종료 사유가 기록됩니다. 또한 universe="all_a"는 이를 포함한 과거 스냅샷을 생성합니다. 위에서 제시한 미국 시장 측정 사례는 생존 종목만 포함한 유니버스가 무엇을 누락하는지를 보여줍니다.

AI 에이전트가 ashare-lake를 직접 쿼리할 수 있습니까?

가능합니다. asl mcp은 Model Context Protocol을 통해 6개의 도구를 제공하며, 그중 하나가 읽기 전용 SQL 도구입니다. 따라서 에이전트는 웹 스크래핑 대신 로컬 Parquet 파일을 직접 쿼리합니다. claude mcp add ashare-lake -- asl mcp --config /abs/path/to/ashare-lake.toml을 통해 등록하십시오.

ashare-lake가 AkShare나 Baostock을 대체합니까?

아닙니다. 이 프로젝트는 해당 라이브러리들 상위에 위치합니다. 해당 라이브러리들은 상위 소스에서 데이터를 가져오는 역할을 하며, 이 프로젝트는 가져온 데이터를 저장, 버전 관리 및 조정하여 행 단위 계보(lineage)와 데이터셋당 하나의 계약을 갖춘 정제된 Parquet 형태로 관리합니다.


위의 모든 수치는 실제 시장 데이터를 대상으로 저장 및 버전 관리된 쿼리 결과입니다. 패널을 확장하여 SQL을 확인하거나, Strasmore 터미널에서 동일한 생존 편향(survivorship) 검사를 직접 실행해 보십시오.

#market-data#mcp#a-shares#open-source#ai-agents