← All tools · Guides

AKShare: China A-share market data into SQLite, updated every day

Hands-on, part 2. Download, clean, and append China A-share bars so a backtest has a fresh table every weekday.

Short answer: install AKShare, pull one A-share with stock_zh_a_hist and adjust="qfq", rename the Chinese columns, and append only the missing dates to SQLite. Run that script at 18:30 on weekdays. Sleep half a second between symbols.

How to choose: To store China A-share daily bars on your own computer, use AKShare and append them to SQLite. After pip install, call stock_zh_a_hist with adjust="qfq" for one symbol, rename the Chinese columns to lowercase English, and each run fetch only the dates after MAX(date). The full-market snapshot is stock_zh_a_spot_em. The index series is stock_zh_index_daily. Run cron at 18:30 on weekdays, after the session, and sleep 0.5 seconds between symbols. Fees and the free tier for Tushare versus AKShare are a different article. A US-stock pipeline with yfinance or EODHD is a different article. This page only walks AKShare market data into a local database.

Who it is for: Someone who writes a little Python and wants China A-share daily bars, a full-market snapshot, and an index series in a local SQLite file, updated every weekday, with no registration key. The path is the three calls, the column rename, the incremental write, and a job after 18:30.

Who it is not for: Someone who needs a commercial data licence, a data contract that will not change, or US prices. AKShare aggregates public sites such as Eastmoney and Sina. An interface can change. The stored history is for personal research. This page is not the tool-order guide, not the site FAQ, and not the Tushare fee comparison. If you do not yet have a machine that stays on, start with the cloud quant desk, the same article in English and Chinese.

The same walkthrough in Chinese is AKShare: market data into a database. It is part 2 of that series. The previous part, a machine that stays on, is the cloud quant desk. The same article in Chinese is the Chinese cloud-setup guide. Fees, the free tier, and why an interface can fail are how to choose between Tushare and AKShare. That comparison is not this article. Which of yfinance, AKShare, OpenBB, and EODHD to use is how to choose a data source. That guide is not this article. Giving those bars to a research agent is market data and an LLM API. That note is not this article. A US-stock pipeline that was actually run is the daily price pipeline. The next part of this series, a first strategy on these bars, is the first backtrader strategy.

Facts below follow the Chinese walkthrough. The commands were not re-run for this English page. This site has no commercial relationship with AKShare. This is not investment advice. The interfaces are public aggregations. Cross-check anything you will rely on.

01 · What AKShare is, and who it fits

AKShare is an open-source Python library of finance interfaces. It aggregates public sources such as Eastmoney and Sina. It is free and needs no registration key. For a person doing their own research, it is the short path from an idea to a table. The cost is that those sources are not an official contract. An interface can change. The last section is the list of ways that shows up.

pip install akshare pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

The -i flag is the Tsinghua PyPI mirror from the Chinese page. pip install akshare pandas is the same install without that mirror.

02 · The three calls you will actually use

import akshare as ak

# 1) Daily bars for one symbol (forward-adjusted, the backtest convention). Eastmoney source.
df = ak.stock_zh_a_hist(symbol="000001", period="daily",
                        start_date="20230101", end_date="20250801", adjust="qfq")

# 2) Full-market snapshot (last price, change, market cap). Works during the session and after.
spot = ak.stock_zh_a_spot_em()

# 3) Index daily bars
idx = ak.stock_zh_index_daily(symbol="sh000001")

print(df.head())   # the columns come back with Chinese names

Chinese column names are awkward downstream. Rename them to lowercase English. That is step one of every AKShare pipeline. The strings below are the Chinese names the frame returns:

import pandas as pd

RENAME = {"日期": "date", "开盘": "open", "最高": "high",
          "最低": "low", "收盘": "close", "成交量": "volume"}

df["date"] = pd.to_datetime(df["日期"])
df = df.rename(columns=RENAME).set_index("date")[["open", "high", "low", "close", "volume"]]

03 · Incremental writes: SQLite is the personal default

One file, nothing to operate, and pandas reads and writes it in two lines. The Chinese page says that is enough into the tens of millions of rows. The rule is to fetch only the stretch the database does not have yet:

import sqlite3, time
import akshare as ak

DB = "a_daily.db"

def update_one(code: str, conn):
    start = "20230101"
    # If the code is already stored, start the day after the last date.
    exist = conn.execute(
        "SELECT MAX(date) FROM daily WHERE code=?", (code,)).fetchone()[0]
    if exist:
        start = (exist + pd.Timedelta(days=1)).strftime("%Y%m%d")

    df = ak.stock_zh_a_hist(symbol=code, period="daily",
                            start_date=start, end_date="20991231", adjust="qfq")
    if df.empty:
        return
    df["date"] = pd.to_datetime(df["日期"])
    df = df.rename(columns={"开盘": "open", "最高": "high", "最低": "low",
                            "收盘": "close", "成交量": "volume"})
    out = df[["date", "open", "high", "low", "close", "volume"]]
    out["code"] = code
    out.to_sql("daily", conn, if_exists="append", index=False)

conn = sqlite3.connect(DB)
conn.execute("""CREATE TABLE IF NOT EXISTS daily
    (code TEXT, date TIMESTAMP, open REAL, high REAL,
     low REAL, close REAL, volume REAL)""")
update_one("000001", conn)   # in a real project, loop the universe
conn.close()

pd is the pandas import from the rename step. Two details the loop still needs: time.sleep(0.5) between symbols, so the source is not hammered, and the universe itself from ak.stock_info_a_code_name().

04 · Update it every weekday

Save the script as update_daily.py and hang it on cron. The machine has to stay on:

crontab -e
# Weekdays at 18:30, after the session, when the daily bar has settled
30 18 * * 1-5 cd /home/quant/jobs && python update_daily.py >> logs/data_$(date +\%F).log 2>&1

05 · Pitfalls, each one already paid for

This page is enough for China A-shares. For US stocks, the sequel EODHD on the same SQLite table puts that source on the same daily table. The same walkthrough in Chinese is EODHD for overseas data. The English pipeline that was run, with yfinance or EODHD into its own script, is the daily price pipeline. That pipeline is not the EODHD sequel.

Once the table updates itself, the next job is one strategy on the real bars:

Next: a first backtrader strategy →

This tutorial is not investment advice. The sources are public aggregated interfaces. Cross-check important research.

Frequently asked questions

How to choose AKShare for a daily database?

To store China A-share daily bars on your own computer, use AKShare and append them to SQLite. After pip install, call stock_zh_a_hist with adjust="qfq" for one symbol, rename the Chinese columns to lowercase English, and each run fetch only the dates after MAX(date). The full-market snapshot is stock_zh_a_spot_em. The index series is stock_zh_index_daily. Run cron at 18:30 on weekdays, after the session, and sleep 0.5 seconds between symbols. Fees and the free tier for Tushare versus AKShare are a different article. A US-stock pipeline with yfinance or EODHD is a different article. This page only walks AKShare market data into a local database.

Who it is for?

Someone who writes a little Python and wants China A-share daily bars, a full-market snapshot, and an index series in a local SQLite file, updated every weekday, with no registration key. The path is the three calls, the column rename, the incremental write, and a job after 18:30.

Who it is not for?

Someone who needs a commercial data licence, a data contract that will not change, or US prices. AKShare aggregates public sites such as Eastmoney and Sina. An interface can change. The stored history is for personal research. This page is not the tool-order guide, not the site FAQ, and not the Tushare fee comparison. If you do not yet have a machine that stays on, start with the cloud quant desk, the same article in English and Chinese.

Which three AKShare calls does this walkthrough use?

Daily bars for one symbol use ak.stock_zh_a_hist. For a backtest, set adjust to qfq. The example is symbol 000001, from 20230101 through 20250801, and the source named on the Chinese page is Eastmoney. The full-market snapshot is ak.stock_zh_a_spot_em. It includes the latest price, the change, and market cap, and it can be called during the session or after the close. Index daily bars use ak.stock_zh_index_daily. The example symbol is sh000001. All three return Chinese column names.

Why rename the Chinese columns?

Chinese column names are awkward for everything that reads the table later. The first step of every AKShare pipeline renames date, open, high, low, close, and volume to those English names, parses the date, and sets it as the index. The code block uses the Chinese names AKShare returns.

How does the incremental SQLite update work?

SQLite is the default for personal research: one file, no server, and pandas can read and write it. The Chinese page says that still holds into the tens of millions of rows. The file is a_daily.db and the table is daily. Each call reads MAX(date) for that code. If a row exists, the next request starts the day after it and uses end_date 20991231. An empty frame is skipped. Otherwise the rows are appended. The universe can come from ak.stock_info_a_code_name(). Put time.sleep(0.5) in the loop so the source is not hammered.

When should the daily job run?

Save the script as update_daily.py and run it from cron on weekdays at 18:30: 30 18 * * 1-5. A daily bar pulled during the session is incomplete, so the job stays after the close.

What pitfalls does this walkthrough call out?

Five. Intraday bars are unfinished, so the job runs after 18:30. Rate limits and bans: sleep between symbols, and retry with backoff instead of immediately. Backtests use qfq. Forward-adjusted prices drift after dividends and splits, so refresh the whole history every few months. Interfaces change: pin the version in requirements.txt and rerun a check before you upgrade. Some calls return only names that are still listed, so a full-market backtest has survivor bias. Discount the conclusion.