Last updated on
DuckDB教程A股数据库Python量化数据pytdx本地数据库数据工程量化数据库

DuckDB搭建A股量化数据库教程:4589只股票本地数据库从零实战


为什么需要本地数据库?

做A股量化回测,最头疼的问题就是数据。

免费数据源(akshare、tushare)有调用次数限制,付费数据源(Wind、聚宽)年费几千到几万。而且每次回测都要重新拉取数据,速度慢、依赖网络、不可复现。

方案:用pytdx(通达信数据接口)下载原始数据,存入DuckDB本地数据库。一次下载,永久使用,查询速度毫秒级。

结果:294MB的DuckDB文件,管理了4589只股票 + 988只可转债 + 30只ETF的全部日线行情。


技术架构

pytdx(通达信接口)→ CSV文件(增量存储)→ DuckDB(OLAP查询引擎)→ 策略回测

三层架构,每层各司其职:

技术作用数据量
采集层pytdx从通达信服务器获取原始K线数据4500+只股票,每秒~6只
存储层CSV (ZSTD压缩)增量持久化,意外恢复每只股票一个文件
分析层DuckDBSQL查询、JOIN、聚合、回测294MB单文件,内存映射

第一步:数据采集(pytdx)

pytdx是Python连接通达信行情服务器的库,不需要账号,直接TCP连接。

from pytdx.hq import TDHQ
import pandas as pd

api = TDHQ()
# 连接通达信主站
api.connect('119.147.212.81', 7709)

# 获取个股日线数据
# market: 0=深圳, 1=上海
# code: 股票代码
# start: 0表示从第一天开始
# count: 每次最多800条
df = api.get_security_bars(9, 0, '000001', 0, 800)

但有个大坑要注意——pytdx返回的是不复权数据。纯分红除权的收益差异会被忽略。

⚠️ 详见后续文章《pytdx不复权数据的坑:纯分红收益去哪了?》

采集优化要点:

# 批量采集,每500只重连一次
def batch_download(stock_list, batch_size=500):
    results = []
    for i in range(0, len(stock_list), batch_size):
        batch = stock_list[i:i+batch_size]
        for code in batch:
            try:
                df = api.get_security_bars(9, market_of(code), code, 0, 800)
                results.append(df)
            except:
                continue
        # 每500只重连,防止连接断开
        api.disconnect()
        api.connect('119.147.212.81', 7709)
    return pd.concat(results)

性能:单线程采集,约0.16秒/只,4589只股票约12分钟采集完毕。


第二步:增量更新

全量采集一次后,每天只需要增量下载最新数据:

def incremental_update(code, existing_dates):
    """只下载缺失的最新数据"""
    # 先获取基本信息,确定最后日期
    bars = api.get_security_bars(9, market_of(code), code, 0, 1)
    latest_date = bars[0]['datetime']
    
    if latest_date in existing_dates:
        return  # 已是最新
    
    # 增量下载
    new_data = api.get_security_bars(
        9, market_of(code), code, 
        0, 800  # 最多800条,实际上只补最近几天
    )
    return pd.DataFrame(new_data)

增量下载单日全部股票约2-3分钟,适合每天定时运行。


第二步半:前复权处理

pytdx提供get_xdxr_info()接口获取除权除息信息:

# 获取除权除息数据
xdxr = api.get_xdxr_info(market, code)
# xdxr字段:bd(x)date(除权日), fh(分红/每10股), 
#           peigong(配股/每10股), songzhuangu(送转股/每10股), ...

纯分红(fh>0, szg=0, pg=0)的前复权处理:

def apply_fh_adjust(df, fh_per_10):
    """纯分红前复权:往前调整价格"""
    fh_per_share = fh_per_10 / 10
    df['adj_close'] = df['close'] - fh_per_share
    df['adj_open'] = df['open'] - fh_per_share
    df['adj_high'] = df['high'] - fh_per_share
    df['adj_low'] = df['low'] - fh_per_share
    return df

⚠️ 注意:pytdx的xdxr字段单位是每10股,需要除以10才能得到每股分红。


第三步:DuckDB建库

DuckDB最大的优势——不需要安装服务器,一个文件就是数据库:

import duckdb

# 创建/连接数据库
conn = duckdb.connect('quant_v2.duckdb')

# 创建股票日线表
conn.execute("""
    CREATE TABLE IF NOT EXISTS stock_daily (
        code VARCHAR,
        date DATE,
        open DOUBLE,
        high DOUBLE,
        low DOUBLE,
        close DOUBLE,
        volume BIGINT,
        amount DOUBLE,
        PRIMARY KEY (code, date)
    )
""")

# 批量导入CSV
conn.execute("""
    INSERT OR REPLACE INTO stock_daily
    SELECT * FROM read_csv_auto('data/stock/*.csv')
""")

DuckDB配置优化:

-- 使用多线程
SET threads = 8;
-- 内存限制
SET memory_limit = '4GB';

数据量实测

数据维度行数存储大小
股票日线15,200,000~180MB
可转债日线736,000~60MB
ETF日线69,000~12MB
其他(指数/财务等)-~42MB
总计~1600万行294MB

数据库大小294MB,普通笔记本电脑完全无压力。查询性能:

-- 获取某只股票全部历史数据:<10ms
SELECT * FROM stock_daily WHERE code = '601318' ORDER BY date;

-- 跨股票查询(某日所有股票收盘价):~50ms
SELECT code, close FROM stock_daily WHERE date = '2025-01-02';

-- 复杂聚合(月均收益率):~200ms
SELECT code, 
       AVG((close - open) / open) as daily_return,
       COUNT(*) as trading_days
FROM stock_daily 
WHERE date >= '2024-01-01'
GROUP BY code
HAVING COUNT(*) > 200;

踩坑总结

🕳️ 坑1:pytdx连接不稳定

通达信主站有连接数限制,频繁请求会被断开。

解决:每500只股票重连一次,用try/except包裹每个请求。

🕳️ 坑2:不复权数据丢失分红收益

pytdx返回的是原始不复权数据。纯分红(不送转股)的股票,如果不做前复权处理,回测会低估历史收益。

解决:从xdxr接口获取除权信息,手动计算前复权价格。

🕳️ 坑3:增量更新要去重

第一次全量采集后,增量更新时可能遇到日期重叠。

解决:用INSERT OR REPLACE(DuckDB的UPSERT),或者先查已有日期再下载。

🕳️ 坑4:DuckDB线程数

默认单线程查询,大数据量聚合会很慢。

解决SET threads = 8,利用多核CPU。


完整的数据管线

这套数据系统已经自动化运行了几个月:

每日06:00 → 早间检查(数据完整性)
每日18:00 → 增量数据下载(pytdx CSV → DuckDB同步)
每周一19:00 → 全市场估值周报

全部用Python脚本+Hermes Agent的cron调度,无人值守。


下一步

拥有了本地量化数据库后,就可以在上面跑各种策略回测了:

代码已开源,你可以在自己的笔记本上复现这套系统,只需要安装DuckDB和pytdx。


❓ 常见问题

DuckDB比SQLite好在哪?

DuckDB专为分析型查询设计,列式存储+向量化执行引擎使其在聚合查询上比SQLite快10-100倍。支持SQL窗口函数、复杂JOIN和JSON操作,非常适合量化数据的时间序列分析。

数据多久更新一次?

建议每日收盘后增量更新。本文提供的Python脚本支持增量模式,只下载当日新增数据,约5分钟完成全部更新。可用cron或Hermes Agent自动调度。

支持美股或期货数据吗?

当前实现基于pytdx(通达信数据接口),主要覆盖A股、可转债和ETF。如需美股数据,可扩展yfinance或Alpha Vantage接口;期货数据可用天勤(TQSDK)对接。

数据库会占用多大空间?

294MB(包含4589只股票+988只可转债+30只ETF),加上回测结果和因子计算表后约500MB。DuckDB压缩效率高,全量历史数据仅需数百MB。


风险提示:本文所有内容均为技术分享,不构成投资建议。历史回测不代表未来收益。

📖 相关文章推荐

💬 评论