# ashare-data A股数据抓取工具,以 [BaoStock](http://baostock.com) 为主、新浪/腾讯/东方财富为辅,保存到 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/15/30/60 分钟K线(开高低收、成交量/额) | BaoStock | | 概念板块 | 东方财富全量概念板块及其成分股(已有≥300个概念时自动跳过) | 东方财富 | **已知限制**:BaoStock 不含北交所(920xxx)股票;新浪/腾讯/东方财富数据源对北交所同样不支持。 ## 快速开始 ### 1. 环境要求 - Python >= 3.10 - MySQL >= 8.0 ### 2. 安装依赖 ```bash pip install baostock akshare pymysql sqlalchemy pyyaml pandas requests ``` > 说明:`baostock` 为主要数据源;`akshare` 用于新浪日线兜底;`requests` 用于腾讯/东方财富/概念板块抓取。 ### 3. 配置数据库 > ⚠️ **安全提示**:`config.yaml` 包含数据库密码,**必须**保留在 `.gitignore` 中(本仓库已默认忽略)。请勿在提交前删除此规则。如曾不慎提交真实密码,参见 [TODO.md](./TODO.md) "清理 git 历史中的明文密码"。 复制配置文件并修改 MySQL 连接信息: ```bash cp config.example.yaml config.yaml ``` 编辑 `config.yaml`: ```yaml mysql: host: "localhost" port: 3306 user: "root" password: "your_password" database: "ashare" charset: "utf8mb4" fetch: delay: 0.1 # 请求间隔(秒) retry: 2 # 失败重试次数 workers: 1 # 并发线程数 ``` 确保 MySQL 中已创建对应数据库: ```sql CREATE DATABASE ashare CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` ### 4. 运行 ```bash # 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 --start-date 20260101 # 抓取全部频率分钟K线(5/15/30/60) python -m src.main --intraday --freq all --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](./TODO.md)。 ### 5. 命令行参数说明 ``` 数据抓取选项: --stock-info 抓取A股股票列表 --trading-day 抓取交易日历 --daily 抓取日线行情(增量;自动分析数据缺口,已完整自动跳过) --source 日线数据源: baostock/sina/tencent/eastmoney/all(默认 all,轮换+失败自动切换) --index 抓取主要指数日线 --financial 抓取季频财务指标(增量;只抓缺失季度) --dividend 抓取分红送转数据(增量;只抓缺失年份) --intraday 抓取分钟K线行情(增量;只抓缺失日期) --freq K线频率: 5/15/30/60/all(默认 5) --sector 抓取概念板块及成分股(增量;已有≥300个概念时跳过) --market-daily 汇总每日涨跌停统计(从 stock_daily 聚合) 日期过滤(对日线行情、交易日历、分钟K线、指数生效): --start-date 开始日期,格式 YYYYMMDD --end-date 结束日期,格式 YYYYMMDD 股票过滤(对财务指标、分红送转、分钟K线生效): --symbol 指定单只股票代码,如 000001,默认全部股票 ``` > 注:`--market-daily` 在历史版本中存在,当前已下线(功能代码仍保留,但未挂到 main.py)。如需重新启用,参见 [TODO.md](./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 / stock_min15 / stock_min30 / stock_min60 — 分钟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](./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 测试(42 个用例,纯函数 + mock) ├── benchmarks/ # 并发度压测脚本(不在 CI 跑,需真实 MySQL+外网) └── gzl/ # 选股脚本(独立子项目,可选) ├── Selector.py └── select_stock.py ``` ## 设计说明 - **多数据源容灾**:日线行情默认 `--source all` 在 BaoStock / 新浪 / 腾讯 / 东方财富之间轮换并自动切换,单源失败不影响整体进度;其他模块(财务、分红、指数、分钟K线)仍以 BaoStock 为主。 - **线程安全**:BaoStock 的 `query_xxx()` 非线程安全,所有调用通过 `src/baostock_conn.py` 的全局锁串行化;查询超时/连接断开时自动重连。 - **去重写入**:所有表通过 `db.batch_upsert()` 走 MySQL `INSERT ON DUPLICATE KEY UPDATE`,重复执行不会产生重复数据。 - **全量增量**:所有模块均支持增量更新——日线/指数/分钟K线按交易日对比找缺口,财务按缺失季度,分红按缺失年份,概念板块已有≥300个时跳过。 - **停牌识别**:日线行情若两端已覆盖、内部仍有缺口,则视为停牌,不再重抓。 - **表结构自动迁移**:`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,控制台默认仅看到进度/异常。 ## 运行测试 ```bash pip install pytest # 或 pip install -e ".[dev]" pytest -v ``` 当前 42 个用例全部通过,覆盖: - `baostock_conn.code_to_bs` / `daily._code_to_*` 各数据源代码前缀映射 - `daily._fill_derived_fields` 振幅/涨跌幅/涨跌额补算 - `_fetch_tencent` / `_fetch_eastmoney` HTTP JSON 解析(mock requests,含空字段、异常包装、北交所短路) - `financial._recent_quarters` 季度滚动跨年 - `market_daily._is_20pct` 主板/创业板/科创板/北交所判定 - `sector.fetch_sector` 概念板块增量跳过逻辑 - `src.log.get_logger` 命名空间、handler 幂等、env 控制 level ## 性能压测 ```bash python -m benchmarks.bench_daily --codes 200 --days 30 --workers 1,2,4,8 ``` 会依次以指定的并发档位跑一遍真实抓取并输出 speedup 表,用于确定 `fetch.workers` 的最优档位。详见 [benchmarks/README.md](./benchmarks/README.md)。 ## 选股子项目 `gzl/` `gzl/` 目录是独立的选股策略原型(依赖 `scipy` + 本地 CSV,不读 MySQL)。当前与主项目 `src/` 解耦,**不参与** `python -m src.main` 的任何流程。后续是否合并到 `src/strategies/` 见 [TODO.md](./TODO.md)。