# ashare-data A股数据抓取工具,股票列表、交易日历使用 AKShare,日线行情使用 BaoStock,其余部分按功能分别使用通达信本地缓存或 AKShare,保存到 MySQL 数据库。 ## 功能概览 | 数据类型 | 说明 | 数据来源 | |---------|------|---------| | 股票列表 | 沪深A股代码、名称、上市日期 | AKShare | | 交易日历 | 1990年至今的交易日列表 | AKShare | | 日线行情 | 开高低收、成交量/额、振幅、涨跌幅、换手率(前复权,限沪深 A 股) | BaoStock 日线接口 | | 新浪1分钟数据 | 最近 1 分钟K线(开高低收、成交量/额) | AKShare 新浪接口 | | 指数日线 | 上证/沪深300/中证500/中证1000/科创50/深证成指/创业板指/中小板指 | 通达信本地缓存 | | 涨跌停统计 | 每日主板(10%)/ 科创创业板(20%)涨跌停数量 | stock_daily 汇总 | | 概念板块 | 通达信本地概念板块缓存 | 通达信本地缓存 | **初始化说明**:首次运行 `--stock-info` / `--trading-day` 时会自动从 AKShare 拉取并写入数据库;`--daily` 现在改为 BaoStock 日线接口,只写 `stock_daily`;`--daily-no-data-only` 仅更新 `stock_no_data`;`--sector` / `--index` / `--intraday` 仍按通达信本地缓存工作,不再依赖 `hsjday.zip` 这类旧的日线初始化包。 ## 快速开始 ### 1. 环境要求 - Python >= 3.10 - MySQL >= 8.0 ### 2. 安装依赖 ```bash pip install pymysql sqlalchemy pyyaml pandas requests baostock akshare ``` > 说明:当前只需要 `pymysql`、`sqlalchemy`、`pyyaml`、`pandas`、`requests`、`baostock` 等基础依赖。 > 如需使用 `--sina-min1` 命令,还需要 `akshare`。 > 通达信分钟缓存刷新已自动按最多 100 只股票一批拆分。注意通达信客户端原生只支持刷新 `1m/5m` 本地缓存。 > 仅 `--sector` / `--index` / `--intraday` 等命令需要通达信本地目录,默认读取 `config.yaml` 里的 `tdx.dir`。 ### 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 # 并发线程数 tdx: dir: "C:\\new_tdx64" # 通达信安装目录 ``` 确保 MySQL 中已创建对应数据库: ```sql CREATE DATABASE ashare CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; ``` ### 4. 运行 ```bash # 1. 先抓取股票列表(数据来源: AKShare) python -m src.main --stock-info # 2. 抓取交易日历(数据来源: AKShare) python -m src.main --trading-day --start-date 20100101 --end-date 20261231 # 3. 抓取全历史日线行情(数据来源: BaoStock 日线接口,限沪深 A 股) python -m src.main --daily --start-date 20260521 --end-date 20260521 # 4. 日常增量更新日线(数据来源: BaoStock 日线接口,限沪深 A 股;只写 stock_daily) python -m src.main --daily # 4.1 仅更新停牌/无数据记录,不写 stock_daily(数据来源: BaoStock 日线接口,限沪深 A 股) python -m src.main --daily-no-data-only --start-date 20100101 --end-date 20260517 # 5. 下载新浪 1 分钟数据并写入 stock_min1(数据来源: AKShare 新浪接口,默认 2010 至今) python -m src.main --sina-min1 # 6. 下载新浪 1 分钟数据并指定区间(数据来源: AKShare 新浪接口) python -m src.main --sina-min1 --start-date 20260501 --end-date 20260517 --symbol 600519 # 7. 抓取概念板块及成分股(数据来源: 通达信本地缓存) python -m src.main --sector # 8. 汇总每日涨跌停统计(数据来源: stock_daily 汇总) python -m src.main --market-daily ``` ### 5. 命令行参数说明 ``` 数据抓取选项: --stock-info 抓取A股股票列表(数据来源: AKShare) --trading-day 抓取交易日历(数据来源: AKShare) --daily 抓取日线行情(数据来源: BaoStock 日线接口;限沪深 A 股;增量,自动分析数据缺口,已完整自动跳过;只写入 stock_daily,不更新 stock_no_data) --daily-no-data-only 仅更新 stock_no_data(数据来源: BaoStock 日线接口;限沪深 A 股;不写入 stock_daily) --sina-min1 抓取新浪 1 分钟数据并写入 stock_min1(数据来源: AKShare 新浪接口;默认全市场、默认 2010 至今,可配合 --symbol / --start-date / --end-date) --sector 抓取概念板块及成分股(数据来源: 通达信本地缓存;增量,本地板块已齐全时跳过) --market-daily 汇总每日涨跌停统计(数据来源: stock_daily 聚合) --symbol 指定单只或多个股票代码,逗号分隔;不传则抓取全市场,供 --sina-min1 使用 日期过滤(对日线行情、交易日历、新浪1分钟生效;新浪1分钟默认 2010 至今): --start-date 开始日期,格式 YYYYMMDD --end-date 结束日期,格式 YYYYMMDD ``` ## 数据库表结构 ### stock_info — 股票基本信息 | 字段 | 类型 | 说明 | |------|------|------| | code | VARCHAR(10) PK | 股票代码 | | name | VARCHAR(50) | 股票名称 | | ipo_date | DATE | 上市日期 | ### stock_daily — 日线行情(前复权) | 字段 | 类型 | 说明 | |------|------|------| | code | VARCHAR(10) | 股票代码 | | date | DATE | 交易日期 | | open | FLOAT NOT NULL | 开盘价 | | close | FLOAT NOT NULL | 收盘价 | | high | FLOAT NOT NULL | 最高价 | | low | FLOAT NOT NULL | 最低价 | | volume | FLOAT NOT NULL | 成交量 | | turnover | FLOAT NOT NULL | 成交额 | | amplitude | FLOAT NOT NULL | 振幅% | | pct_change | FLOAT NOT NULL | 涨跌幅% | | change | FLOAT NOT NULL | 涨跌额 | | turnover_rate | FLOAT NOT NULL | 换手率% | 联合主键:`(code, date)` ### trading_day — 交易日历(AKShare) | 字段 | 类型 | 说明 | |------|------|------| | 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_min1 — 1分钟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) | 概念板块代码 | | 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% 板块);跌停反向。当前可通过 `python -m src.main --market-daily` 运行 `src/fetchers/market_daily.py` 提供的 `fetch_market_daily()`。 ## 项目结构 ``` 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 按日滚动) │ ├── db.py # SQLAlchemy 模型 + 批量 upsert │ ├── main.py # 命令行入口 │ └── fetchers/ │ ├── __init__.py │ ├── stock_list.py # 股票列表(AKShare) │ ├── trading_day.py # 交易日历(AKShare) │ ├── daily.py # 日线行情(BaoStock) │ ├── index.py # 主要指数日线 │ ├── market_daily.py # 每日涨跌停统计(从 stock_daily 聚合) │ ├── sina_minute.py # 新浪 1 分钟数据(AKShare) │ └── sector.py # 概念板块及成分股(通达信本地) ├── tests/ # pytest 测试(66 个用例,纯函数 + mock) ├── benchmarks/ # 并发度压测脚本(不在 CI 跑,需真实 MySQL+外网) └── gzl/ # 选股脚本(独立子项目,可选) ├── Selector.py └── select_stock.py ``` ## 设计说明 - **数据源分工**:股票列表、交易日历使用 AKShare;日线行情使用 BaoStock;概念板块、指数日线、分钟线等仍按功能读取通达信本地缓存;新浪 1 分钟数据使用 AKShare 新浪接口。 - **线程安全**:通达信本地客户端调用集中在 `src/fetchers/tdx_client.py`。 - **去重写入**:所有表通过 `db.batch_upsert()` 走 MySQL `INSERT ON DUPLICATE KEY UPDATE`,重复执行不会产生重复数据。 - **全量增量**:所有模块均支持增量更新——日线按交易日对比找缺口,概念板块在本地已齐全时跳过;新浪 1 分钟数据支持按日期过滤后落库。 - **停牌识别**:日线行情若两端已覆盖、内部仍有缺口,则视为停牌,不再重抓。 - **表结构初始化**:`init_db()` 只负责建表。 - **进程内缓存**:`get_stock_codes()` / `get_ipo_dates()` 缓存全量股票代码与上市日期,避免重复扫库。 - **日志**:所有代码通过 `from src.log import get_logger` 输出;控制台 + `logs/ashare.log`(按日滚动,保留 7 天)双输出,通过 `ASHARE_LOG_LEVEL=DEBUG` 切换级别。 ## 运行测试 ```bash pip install pytest # 或 pip install -e ".[dev]" pytest -v ``` - 当前 66 个用例全部通过,覆盖: - `daily._ak_hist_to_rows` / `daily._fill_derived_fields` - `daily._fetch_one_stock` BaoStock 日线读取 - `daily.fetch_daily` BaoStock 日线抓取与落库 - `intraday._tdx_market_data_to_rows` / `intraday.fetch_intraday` 通达信分钟抓取 - `market_daily._is_20pct` 主板/创业板/科创板/北交所判定 - `tdx_blocks.load_infoharbor_blocks` 通达信本地板块缓存解析 - `sector._fetch_concept_list` / `sector._fetch_concept_stocks` 通达信本地概念板块解析 - `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)。