ashare-data
A股数据抓取工具,以 BaoStock 为主、新浪/腾讯/东方财富为辅,保存到 MySQL 数据库。
功能概览
| 数据类型 | 说明 | 数据来源 |
|---|---|---|
| 股票列表 | 沪深A股代码、名称、上市日期 | BaoStock |
| 交易日历 | 1990年至今的交易日列表(自动用 stock_daily 校验节假日) | BaoStock |
| 日线行情 | 开高低收、成交量/额、振幅、涨跌幅、换手率(前复权) | BaoStock / 新浪 / 腾讯 / 东方财富(多源轮换) |
| 指数日线 | 上证/沪深300/中证500/中证1000/科创50/深证成指/创业板指/中小板指 | BaoStock |
| 涨跌停统计 | 每日主板(10%)/ 科创创业板(20%)涨跌停数量 | stock_daily 汇总 |
| 季频财务指标 | 盈利能力、偿债能力、现金流(最近8个季度,JSON 存储) | BaoStock |
| 分红送转 | 每10股送转、派息、除权除息日(最近10年) | BaoStock |
| 分钟K线 | 5分钟K线(开高低收、成交量/额) | 通达信本地客户端(默认) / BaoStock(可选) |
| 概念板块 | 通达信本地概念板块缓存,缺失时回退同花顺 | 通达信本地 / 同花顺 |
已知限制:BaoStock 不含北交所(920xxx)股票;新浪/腾讯/东方财富数据源对北交所同样不支持。
快速开始
1. 环境要求
- Python >= 3.10
- MySQL >= 8.0
2. 安装依赖
pip install baostock akshare pymysql sqlalchemy pyyaml pandas requests
说明:
baostock为主要数据源;akshare用于新浪日线兜底和同花顺概念板块列表;requests用于腾讯/同花顺概念板块抓取;通达信概念板块来自本地缓存文件。 通达信分钟缓存刷新已自动按最多 100 只股票一批拆分;--tdx-cache会先预热本地缓存,再全历史回填分钟表。--tdx-local-cache会在预热后自动校验是否真的可读;若只想单独确认缓存是否能被读到,请用--tdx-verify-cache。注意通达信客户端原生只支持刷新1m/5m本地缓存。
通达信本地目录默认读取
config.yaml里的tdx.dir。
3. 配置数据库
⚠️ 安全提示:
config.yaml包含数据库密码,必须保留在.gitignore中(本仓库已默认忽略)。请勿在提交前删除此规则。如曾不慎提交真实密码,参见 TODO.md "清理 git 历史中的明文密码"。
复制配置文件并修改 MySQL 连接信息:
cp config.example.yaml config.yaml
编辑 config.yaml:
mysql:
host: "localhost"
port: 3306
user: "root"
password: "your_password"
database: "ashare"
charset: "utf8mb4"
fetch:
delay: 0.1 # 请求间隔(秒)
retry: 2 # 失败重试次数
workers: 1 # 并发线程数
tdx:
dir: "C:\\new_tdx64" # 通达信安装目录
确保 MySQL 中已创建对应数据库:
CREATE DATABASE ashare CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
4. 运行
# 1. 先抓取股票列表(其他模块依赖此数据)
python -m src.main --stock-info
# 2. 抓取交易日历(覆盖全历史,只需执行一次;此后每年末执行一次延长至未来)
python -m src.main --trading-day --start-date 19901219 --end-date 20261231
# 3. 抓取全历史日线行情(首次,耗时较长)
python -m src.main --daily --start-date 19901201 --end-date 20260511
# 4. 日常增量更新日线(默认走多源轮换:baostock/sina/tencent/eastmoney)
python -m src.main --daily
# 指定单一数据源(默认 all 表示多源轮换+自动切换)
python -m src.main --daily --source baostock
python -m src.main --daily --source sina
# 抓取主要指数日线(上证/沪深300/中证500/中证1000/科创50/深证成指/创业板指/中小板指)
python -m src.main --index
# 抓取指数日线(指定日期范围)
python -m src.main --index --start-date 19901219 --end-date 20260511
# 抓取财务指标(全部股票,最近8个季度)
python -m src.main --financial
# 抓取单只股票的财务数据
python -m src.main --financial --symbol 000001
# 抓取分红送转(全部股票,最近10年)
python -m src.main --dividend
# 抓取单只股票的分红
python -m src.main --dividend --symbol 000001
# 抓取分钟K线(默认5分钟,近30天,默认走通达信本地客户端)
python -m src.main --intraday
# 使用通达信本地客户端抓取分钟K线(默认)
python -m src.main --intraday --intraday-source tdx
# 全历史回填通达信分钟数据(先预热本地缓存,再写入分钟表)
python -m src.main --tdx-cache
# 仅预热通达信本地分钟缓存,并自动校验,不写数据库(原生只刷 1m/5m)
python -m src.main --tdx-local-cache
# 只验证通达信本地分钟缓存是否可读
python -m src.main --tdx-verify-cache
# 抓取5分钟K线
python -m src.main --intraday --start-date 20260101
# 抓取概念板块及成分股(通达信本地,缺失时回退同花顺)
python -m src.main --sector
⚠️ 涨跌停统计 (
market_daily) 已实现于src/fetchers/market_daily.py,但当前main.py尚未挂载--market-daily参数,暂只能在 Python 内直接调用fetch_market_daily(...)。详见 TODO.md。
5. 命令行参数说明
数据抓取选项:
--stock-info 抓取A股股票列表
--trading-day 抓取交易日历
--daily 抓取日线行情(增量;自动分析数据缺口,已完整自动跳过)
--source 日线数据源: baostock/sina/tencent/eastmoney/all(默认 all,轮换+失败自动切换)
--index 抓取主要指数日线
--financial 抓取季频财务指标(增量;只抓缺失季度)
--dividend 抓取分红送转数据(增量;只抓缺失年份)
--intraday 抓取分钟K线行情(增量;只抓缺失日期)
--intraday-source 分钟K线数据源:tdx/baostock(默认 tdx)
--tdx-cache 全历史回填通达信5分钟数据(先预热本地缓存,再写入分钟表)
--tdx-local-cache 仅预热通达信本地分钟缓存,并自动校验,不写数据库(原生只刷 1m/5m)
--tdx-verify-cache 只验证通达信本地分钟缓存是否可读,不写数据库
--freq K线频率: 5(默认 5)
--sector 抓取概念板块及成分股(增量;本地板块已齐全时跳过)
--market-daily 汇总每日涨跌停统计(从 stock_daily 聚合)
日期过滤(对日线行情、交易日历、分钟K线、指数生效):
--start-date 开始日期,格式 YYYYMMDD
--end-date 结束日期,格式 YYYYMMDD
股票过滤(对财务指标、分红送转、分钟K线生效):
--symbol 指定单只股票代码,如 000001,默认全部股票
注:
--market-daily在历史版本中存在,当前已下线(功能代码仍保留,但未挂到 main.py)。如需重新启用,参见 TODO.md 中的 "重新挂载 market_daily 命令"。
数据库表结构
stock_info — 股票基本信息
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) PK | 股票代码 |
| name | VARCHAR(50) | 股票名称 |
| ipo_date | DATE | 上市日期 |
stock_daily — 日线行情(前复权)
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| date | DATE | 交易日期 |
| open | FLOAT | 开盘价 |
| close | FLOAT | 收盘价 |
| high | FLOAT | 最高价 |
| low | FLOAT | 最低价 |
| volume | FLOAT | 成交量 |
| turnover | FLOAT | 成交额 |
| amplitude | FLOAT | 振幅% |
| pct_change | FLOAT | 涨跌幅% |
| change | FLOAT | 涨跌额 |
| turnover_rate | FLOAT | 换手率% |
联合主键:(code, date)
stock_financial_income — 季频盈利能力
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| report_date | VARCHAR(20) | 报告期 |
| data | TEXT | JSON格式数据(ROE、净利率、毛利率等) |
联合主键:(code, report_date)
stock_financial_balance — 季频偿债能力
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| report_date | VARCHAR(20) | 报告期 |
| data | TEXT | JSON格式数据(流动比率、资产负债率等) |
联合主键:(code, report_date)
stock_financial_cashflow — 季频现金流
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| report_date | VARCHAR(20) | 报告期 |
| data | TEXT | JSON格式数据 |
联合主键:(code, report_date)
stock_dividend — 分红送转
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| name | VARCHAR(50) | 股票名称 |
| report_date | VARCHAR(20) | 报告期 |
| dividend_date | DATE | 除权除息日 |
| bonus_ratio | FLOAT | 每10股送转比例 |
| cash_div | FLOAT | 每10股派息 |
| convert_ratio | FLOAT | 每10股转增比例 |
| ex_right_date | DATE | 除权日 |
| dividend_yield | FLOAT | 股息率% |
联合主键:(code, report_date)
trading_day — 交易日历
| 字段 | 类型 | 说明 |
|---|---|---|
| date | DATE UQ | 交易日期 |
stock_no_data — 停牌/无数据记录
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| date | DATE | 停牌/无数据日期 |
联合主键:(code, date)
stock_min5 — 分钟K线
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| datetime | DATETIME | 时间 |
| open | FLOAT | 开盘价 |
| high | FLOAT | 最高价 |
| low | FLOAT | 最低价 |
| close | FLOAT | 收盘价 |
| volume | FLOAT | 成交量 |
| amount | FLOAT | 成交额 |
联合主键:(code, datetime)
stock_concept — 概念板块及成分股
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 股票代码 |
| concept_code | VARCHAR(20) | 概念板块代码(同花顺 BK 编码) |
| concept_name | VARCHAR(100) | 概念板块名称 |
联合主键:(code, concept_code)
index_daily — 指数日线行情
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) | 指数代码 |
| date | DATE | 交易日期 |
| open | FLOAT | 开盘价 |
| high | FLOAT | 最高价 |
| low | FLOAT | 最低价 |
| close | FLOAT | 收盘价 |
| volume | FLOAT | 成交量 |
| amount | FLOAT | 成交额 |
| pct_change | FLOAT | 涨跌幅% |
联合主键:(code, date)
market_daily — 每日涨跌停统计
| 字段 | 类型 | 说明 |
|---|---|---|
| date | DATE PK | 交易日期 |
| limit_up_10 | INT | 涨停数(10% 主板) |
| limit_up_20 | INT | 涨停数(20% 科创板/创业板) |
| limit_down_10 | INT | 跌停数(10% 主板) |
| limit_down_20 | INT | 跌停数(20% 科创板/创业板) |
阈值:涨停
pct_change >= 9.8(主板)或>= 19.5(20% 板块);跌停反向。当前由src/fetchers/market_daily.py提供fetch_market_daily(),main.py 暂未挂载,详见 TODO.md。
项目结构
ashare-data/
├── config.yaml # MySQL 连接配置(.gitignore)
├── config.example.yaml # 配置文件模板
├── pyproject.toml # 项目元数据
├── requirements.txt # 运行依赖
├── README.md # 项目说明(当前文档)
├── TODO.md # 已知问题与待办事项
├── src/
│ ├── __init__.py
│ ├── config.py # 配置读取模块(YAML + 环境变量 ASHARE_CONFIG)
│ ├── log.py # 统一 logging(控制台 + logs/ashare.log 按日滚动)
│ ├── baostock_conn.py # BaoStock 连接管理(login/logout/线程锁/超时重连)
│ ├── db.py # SQLAlchemy 模型 + 批量 upsert + 自动迁移
│ ├── main.py # 命令行入口
│ └── fetchers/
│ ├── __init__.py
│ ├── stock_list.py # 股票列表
│ ├── trading_day.py # 交易日历
│ ├── daily.py # 日线行情(多源轮换:baostock/sina/tencent/eastmoney)
│ ├── index.py # 主要指数日线
│ ├── market_daily.py # 每日涨跌停统计(从 stock_daily 聚合)
│ ├── financial.py # 季频财务指标(盈利/偿债/现金流,JSON 存储)
│ ├── dividend.py # 分红送转
│ ├── intraday.py # 分钟K线(5/15/30/60)
│ └── sector.py # 概念板块及成分股(通达信本地 + 同花顺)
├── tests/ # pytest 测试(48 个用例,纯函数 + mock)
├── benchmarks/ # 并发度压测脚本(不在 CI 跑,需真实 MySQL+外网)
└── gzl/ # 选股脚本(独立子项目,可选)
├── Selector.py
└── select_stock.py
设计说明
- 多数据源容灾:日线行情默认
--source all在 BaoStock / 新浪 / 腾讯 / 东方财富之间轮换并自动切换,单源失败不影响整体进度;其他模块(财务、分红、指数)仍以 BaoStock 为主,分钟K线可选本地通达信客户端。 - 线程安全:BaoStock 的
query_xxx()非线程安全,所有调用通过src/baostock_conn.py的全局锁串行化;查询超时/连接断开时自动重连。 - 去重写入:所有表通过
db.batch_upsert()走 MySQLINSERT ON DUPLICATE KEY UPDATE,重复执行不会产生重复数据。 - 全量增量:所有模块均支持增量更新——日线/指数/分钟K线按交易日对比找缺口,财务按缺失季度,分红按缺失年份,概念板块在本地已齐全时跳过。
- 停牌识别:日线行情若两端已覆盖、内部仍有缺口,则视为停牌,不再重抓。
- 表结构自动迁移:
init_db()会检查并升级旧版stock_no_data/stock_sector/stock_intraday的列定义,无需手动改库。 - 进程内缓存:
get_stock_codes()/get_ipo_dates()缓存全量股票代码与上市日期,避免重复扫库。 - 日志:所有代码通过
from src.log import get_logger输出;控制台 +logs/ashare.log(按日滚动,保留 7 天)双输出,通过ASHARE_LOG_LEVEL=DEBUG切换级别。BaoStock 查询签名走 DEBUG,控制台默认仅看到进度/异常。
运行测试
pip install pytest # 或 pip install -e ".[dev]"
pytest -v
当前 59 个用例全部通过,覆盖:
baostock_conn.code_to_bs/daily._code_to_*各数据源代码前缀映射daily._fill_derived_fields振幅/涨跌幅/涨跌额补算_fetch_tencent/_fetch_eastmoneyHTTP JSON 解析(mock requests,含空字段、异常包装、北交所短路)financial._recent_quarters季度滚动跨年market_daily._is_20pct主板/创业板/科创板/北交所判定tdx_blocks.load_infoharbor_blocks通达信本地板块缓存解析sector._fetch_concept_list_ths/sector._fetch_concept_stocks_ths同花顺概念板块解析sector.fetch_sector概念板块增量跳过逻辑src.log.get_logger命名空间、handler 幂等、env 控制 level
性能压测
python -m benchmarks.bench_daily --codes 200 --days 30 --workers 1,2,4,8
会依次以指定的并发档位跑一遍真实抓取并输出 speedup 表,用于确定 fetch.workers 的最优档位。详见 benchmarks/README.md。
选股子项目 gzl/
gzl/ 目录是独立的选股策略原型(依赖 scipy + 本地 CSV,不读 MySQL)。当前与主项目 src/ 解耦,不参与 python -m src.main 的任何流程。后续是否合并到 src/strategies/ 见 TODO.md。