P0: - 清理 src/fetchers/sector.py 第 225 行起的旧版残留代码 - 修复 src/fetchers/market_daily.py 中 fetch_history -> _fetch_history 笔误 - 在 src/main.py 挂载 --market-daily 子命令 P1: - 修正 src/db.py docstring(market_breadth -> market_daily 等) - requirements.txt 补 baostock;pyproject.toml 同步 + 新增 [dev] extras - README 增加 config.yaml 安全提示,将 git 历史清理升级为高风险 P0 由用户决策 P2: - 引入 src/log.py 统一 logging(控制台 + logs/ashare.log 按日滚动 7 天) - 建立 tests/ 框架,4 个测试文件 / 19 个 pytest 用例全部通过 - pyproject.toml 新增 [tool.pytest.ini_options] - config.example.yaml 补全 workers 字段与多源说明 - README 更新功能概览/参数说明/表结构/项目结构/设计说明全章节 - TODO.md 全面重写,按 P0/P2 整理剩余条目并附变更日志 - .gitignore 新增 .claude/
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/15/30/60 分钟K线(开高低收、成交量/额) | BaoStock |
| 行业+地域分类 | 证监会行业分类 + 省份 | BaoStock + 东方财富 |
| 概念板块 | 东方财富全量概念板块及其成分股 | 东方财富 |
已知限制:BaoStock 不含北交所(920xxx)股票;新浪/腾讯/东方财富数据源对北交所同样不支持。
快速开始
1. 环境要求
- Python >= 3.10
- MySQL >= 8.0
2. 安装依赖
pip install baostock akshare pymysql sqlalchemy pyyaml pandas requests
说明:
baostock为主要数据源;akshare用于新浪日线兜底;requests用于腾讯/东方财富/概念板块抓取。
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 # 并发线程数
确保 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 --start-date 20260101
# 抓取全部频率分钟K线(5/15/30/60)
python -m src.main --intraday --freq all --start-date 20260101
# 抓取行业+地域分类
python -m src.main --sector
# 仅抓取行业分类 / 仅抓取地域分类
python -m src.main --sector --industry-only
python -m src.main --sector --region-only
# 仅抓取概念板块及成分股(东方财富)
python -m src.main --sector --concept-only
⚠️ 涨跌停统计 (
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线行情
--freq K线频率: 5/15/30/60/all(默认 5)
--sector 抓取行业+地域分类(默认两者都抓)
--industry-only 仅抓取行业分类
--region-only 仅抓取地域分类
--concept-only 仅抓取概念板块及成分股
日期过滤(对日线行情、交易日历、分钟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 / 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_sector — 行业+地域分类
| 字段 | 类型 | 说明 |
|---|---|---|
| code | VARCHAR(10) PK | 股票代码 |
| industry | VARCHAR(100) | 证监会行业分类 |
| region | VARCHAR(20) | 省份/地域 |
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)
│ ├── 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 # 行业+地域分类 + 概念板块
└── gzl/ # 选股脚本(独立子项目,可选)
├── Selector.py
└── select_stock.py
设计说明
- 多数据源容灾:日线行情默认
--source all在 BaoStock / 新浪 / 腾讯 / 东方财富之间轮换并自动切换,单源失败不影响整体进度;其他模块(财务、分红、指数、分钟K线、行业)仍以 BaoStock 为主。 - 线程安全:BaoStock 的
query_xxx()非线程安全,所有调用通过src/baostock_conn.py的全局锁串行化;查询超时/连接断开时自动重连。 - 去重写入:所有表通过
db.batch_upsert()走 MySQLINSERT ON DUPLICATE KEY UPDATE,重复执行不会产生重复数据。 - 增量更新:日线行情按月统计已有数据并与交易日历对比,只抓取真正缺口段;其他数据按 (code, 周期) 粒度跳过已完成的股票。
- 停牌识别:日线行情若两端已覆盖、内部仍有缺口,则视为停牌,不再重抓。
- 表结构自动迁移:
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切换级别。历史print(..., flush=True)调用本期保留兼容。
运行测试
pip install pytest # 或 pip install -e ".[dev]"
pytest -v
当前覆盖:baostock_conn.code_to_bs、daily._fill_derived_fields、financial._recent_quarters、market_daily._is_20pct 等纯函数,共 19 个用例。
选股子项目 gzl/
gzl/ 目录是独立的选股策略原型(依赖 scipy + 本地 CSV,不读 MySQL)。当前与主项目 src/ 解耦,不参与 python -m src.main 的任何流程。后续是否合并到 src/strategies/ 见 TODO.md。