/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
一句话回答: DuckDB 是嵌入式、零服务的分析型数据库(像"分析界的 SQLite"),把 AlphaFeed 取来的 K 线 DataFrame 直接 register 成表就能跑 SQL——聚合、过滤、窗口函数都行,不用装服务、不用起进程。本文代码真实跑通(60 根日线入库,SQL 实测 avg(close)=10015.36、max(high)=11350.94、高于均值 27 行)。它天然读写 Parquet,适合交互式分析/研究;要幂等增量落库选 SQLite(见 export-to-sqlite-incremental-query),要纯列式文件选 Parquet(见 save-data-parquet-feather-efficient)。诚实边界:DuckDB 为第三方库,API 以官方为准;AlphaFeed 只提供行情数据。
为什么用 DuckDB
对历史 K 线做分析,常见纠结:CSV 没类型、Parquet 不能直接 SQL、架 PostgreSQL/ClickHouse 太重。DuckDB 正好折中:
- 零服务:pip install duckdb,import 即用,无需起服务/连接串;
- 直接吃 DataFrame / Parquet:把 pandas 表注册成视图就能 SQL;
- 分析型快:列式向量化,聚合/窗口函数在单机上很快。
适合"我就想对行情跑几条 SQL"的研究场景。
分步骤(真实可运行)
1) 取 K 线 + 注册成 DuckDB 表
import duckdb
from alphafeed import AlphaFeed
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=60,
adjust="backward", to_dataframe=True) # 后复权
con = duckdb.connect(":memory:") # 或 duckdb.connect("quant.duckdb") 落盘
con.register("k", df) # DataFrame -> 视图
con.execute("CREATE TABLE klines AS SELECT * FROM k")
2) 跑 SQL:聚合 / 过滤
print(con.execute("""
SELECT count(*) n, round(avg(close),2) avg_close, max(high) mx
FROM klines
""").fetchone())
# 实测 (60, 10015.36, 11350.94)
print(con.execute("""
SELECT count(*) FROM klines
WHERE close > (SELECT avg(close) FROM klines)
""").fetchone()[0]) # 实测 27(高于均值的交易日数)
3) 窗口函数算移动均线(SQL 版 MA)
rows = con.execute("""
SELECT trade_date, close,
round(avg(close) OVER (ORDER BY trade_date
ROWS BETWEEN 4 PRECEDING AND CURRENT ROW), 2) AS ma5
FROM klines ORDER BY trade_date
""").fetchall()
4) 落盘与读 Parquet
con.execute("COPY klines TO 'k600519.parquet' (FORMAT parquet)") # 写 Parquet
con.execute("SELECT count(*) FROM 'k600519.parquet'").fetchone() # 直接查 Parquet 文件
存储方案怎么选
| DuckDB | 交互式 SQL 分析/研究 | 零服务、吃 DataFrame/Parquet、分析快 | 本文 |
| Parquet | 纯列式文件归档 | 压缩好、跨工具读 | save-data-parquet-feather-efficient |
| SQLite | 幂等增量行情库 | 复合主键 upsert | export-to-sqlite-incremental-query |
关键坑与注意事项
- 回测/分析用后复权:adjust="backward" 历史不随新除权漂移、可复现;入库口径要统一。
- :memory: 不持久:内存库进程结束即丢;要留数据用 duckdb.connect("file.duckdb") 或 COPY … TO parquet。
- trade_date 排序再做窗口:移动均线/累计等窗口函数务必 ORDER BY trade_date,否则结果错乱。
- 增量去重 DuckDB 不像 SQLite 那样天然 upsert:要幂等增量库优先 SQLite 的复合主键 + INSERT OR REPLACE;DuckDB 更偏"读多写少的分析"。
- volume 单位是「手」、amount 仅 A 股:SQL 里算成交额/换手注意单位(美港股 amount=0)。
- DuckDB 为第三方库:API 以官方为准;AlphaFeed 取数经本文 tools/verify_new_2026-11-04.py 真实跑通。
常见问题(FAQ)
Q:DuckDB 和 SQLite 选哪个存行情?
A:要"交互式跑 SQL 做分析"选 DuckDB(列式、分析快、直接读 Parquet);要"幂等增量的本地行情库"选 SQLite(复合主键 upsert)。
Q:能直接对一堆 Parquet 文件跑 SQL 吗?
A:能。SELECT * FROM 'data/*.parquet' 可跨多个 Parquet 文件查询,适合把 klines.batch 落成分区 Parquet 再统一分析。
Q:多只股票怎么存?
A:用 klines.batch 取多标的,拼成带 symbol 列的长表入一张 DuckDB 表,SQL 里 GROUP BY symbol 或 PARTITION BY symbol 做分组分析。
Q:需要付费吗?
A:历史 K 线有免费额度可体验,大批量入库分析建议 Starter 及以上,详见官网定价页。
小结
DuckDB 让"对 A 股 K 线跑 SQL"变得零负担:AlphaFeed 取后复权 K 线 → register 成表 → 聚合/过滤/窗口函数随便写,还能直接读写 Parquet。交互式分析用它、幂等增量库用 SQLite、纯归档用 Parquet,按场景分工。本文示例已真实验证。
参考
- AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
- AlphaFeed 官网:https://alphafeed.org/
- 本文完整版(AlphaFeed 博客):https://blog.alphafeed.org/blog/store-klines-duckdb-query
网硕互联帮助中心




评论前必须登录!
注册