ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenStock开源项目实战:从零搭建本地A股行情库与量化选股系统

OpenStock开源项目实战:从零搭建本地A股行情库与量化选股系统 OpenStock 这个开源项目我第一次在 GitHub 上看到的时候其实没当回事以为又是一堆爬虫代码拼起来的玩具。直到自己动手在本地搭建起来跑了一周我才发现这类工具最值钱的地方不是代码本身而是它把“数据采集、指标计算、筛选分析”整个链路串起来了。今天就把我这套从零到一的搭建过程完整记录下来环境怎么配、依赖怎么装、数据源怎么选、指标怎么算、有哪些坑一次说清楚。如果你也是一直苦于“行情软件不让你导出数据策略想法没法快速验证”那这套 OpenStock 方案基本就是为你准备的。它能让你在本地拥有一份属于自己的 A 股日线行情库每天定时自动更新自己算指标、自己写筛选条件想怎么折腾都行。下面直接进入正题。1. OpenStock 到底是个什么东西1.1 用一句话定位OpenStock 是一个开源的股票行情数据采集与分析工具核心能力是从公开数据源拉取 A 股日线行情落库到本地 SQLite自动计算常用技术指标然后跑出一个可以交互查看的 Web 页面。它解决的痛点很具体你没法从市面上任何一款行情软件里批量导出十年历史数据但你的量化策略、选股模型、技术指标回测全都需要一份完整、连续、可追溯的日线数据。自己写爬虫倒也能做但反爬、增量更新、字段清洗、存储设计、指标对齐这些杂事会消耗掉你大量时间。OpenStock 把这层脏活累活封装好了你只需要把系统拉起来把数据喂进去剩下的就是研究策略本身。1.2 核心模块拆解从工程角度看OpenStock 并不是一个单文件的脚本而是一个结构清晰的小型系统整体拆成四块。第一块是数据源适配层。它统一封装了 AkShare 和 Tushare 两类接口外部调用时不用关心底层是哪个数据源、接口参数是什么格式。这意味着如果你今天用 AkShare明天想切到 Tushare只需要改配置不用改业务代码。第二块是存储层。默认用 SQLite理由很实在个人使用场景下开一个 MySQL 服务可能比软件本身还要重而 SQLite 一个文件搞定全部数据备份就是复制文件特别省心。核心表就三张股票列表、日线行情、指标结果。第三块是计算层。这里我比较喜欢它的设计思路指标全部用纯 pandas 实现没有依赖 TA-Lib 这类需要编译 C 扩展的库。做过技术方案选型的人都知道TA-Lib 在 Windows 上编译报错能劝退一半新手纯 Python 实现虽然性能差一点但对日线级别的数据量来说完全够用。第四块是展示层。后端用 FastAPI 提供查询接口前端是原生 HTML 加 ECharts 渲染 K 线图。本地启动后浏览器打开一个地址就能看到股票列表、K 线详情和筛选结果做演示或者自己日常看都很顺手。1.3 适合谁来搭我给身边朋友推荐的时候会把人群分成三类。第一类是刚接触数据分析和量化的小白。OpenStock 的代码量不大架构清晰是很好的学习样本。你能从中看到怎么设计数据库表结构、怎么做定时任务、怎么用 pandas 做金融数据计算这些经验是可以迁移到其他项目里的。第二类是已经有一定基础、想快速拥有自己的行情数据仓库的人。这类人通常手头有现成的选股思路但缺数据。搭一套 OpenStock把历史数据先全量拉下来之后每天增量更新数据基础就打牢了。第三类是喜欢倒腾本地工具的技术爱好者。对他们来说OpenStock 就像是一套可以随时按自己需求魔改的“自建行情终端”想加个板块统计、想跑个指标排序改几行代码就能实现。2. 动手之前的准备工作2.1 环境要求与版本选择在开始搭建之前先把基础环境理清楚不然中途报错再来补环境会非常被动。OpenStock 基于 Python 3 开发我实测在 Python 3.9 到 3.11 的各个版本下表现稳定建议直接用 3.10 或 3.11社区兼容性最好。操作系统方面Windows、macOS、Linux 都能跑但如果你用 Windows后面有几个依赖的安装方式会略有不同我会在对应位置单独提醒。Git 是必须的因为要从 GitHub 拉取项目代码。如果你之前没装过 Git去官网下载安装包一路默认安装就行装完在终端里执行git --version能看到版本号就说明成功了。另外强烈建议准备好 Python 虚拟环境。虚拟环境的作用是给项目单独隔离一份依赖避免不同项目之间的包版本互相打架。我见过太多人在全局环境里装包结果某个库升级把另一个项目搞挂了这种事情只要用虚拟环境就永远不会发生。2.2 数据源选型为什么优先用 AkShareOpenStock 支持多个数据源但我个人强烈建议第一次搭建时选择 AkShare。AkShare 是一个开源的财经数据接口库最大的优势是不需要注册、不需要 token安装后直接就能用尤其适合新手和环境测试阶段。Tushare 虽然数据质量稳定、字段规范但你需要先注册账户、申请 token而且部分高频数据接口有积分门槛对刚起步的个人用户来说不太友好。不过要注意AkShare 的接口本质上是爬取公开网页数据再解析所以接口返回的字段名偶尔会变动而且高频调用可能会被对方限流。因此在实际使用中我的做法是设置合理的请求间隔比如每只股票之间间隔 0.1 到 0.3 秒并且把单次拉取数量控制在合理范围内。OpenStock 的适配层已经处理了一部分容错逻辑但数据源本身的特性决定我们不能完全不设防。2.3 目录结构与配置设计把项目代码拉下来之后先别急着运行把目录结构看明白。以我常用的这个版本为例核心文件如下OpenStock/ ├── app/ │ ├── main.py # FastAPI 入口 │ ├── config.py # 全局配置 │ ├── database.py # SQLite 连接与建表 │ ├── datasource.py # 数据源适配层 │ ├── indicators.py # 技术指标计算 │ ├── scheduler.py # 定时任务 │ └── web/ │ ├── static/ # 前端资源 │ └── templates/ # HTML 页面 ├── requirements.txt └── config.iniconfig.ini 是重点里面通常包含数据库路径、数据源类型、请求间隔、定时任务开关这些关键参数。第一次配置时我建议只动两个地方数据源类型设为 akshare数据库路径保持默认的相对路径。之所以强调把配置和代码分离是因为你后续很可能需要调整数据源或者数据库位置。如果配置硬编码在代码里每次升级项目代码都会把你的配置覆盖掉设计良好的配置文件可以让你在升级后原样复用之前的设置。3. 完整部署实操3.1 拉取项目代码第一步很直接就是克隆代码到本地。我习惯把这类工具统一放在一个目录下比如~/workspace/或者D:\projects\方便管理。git clone https://github.com/yourname/OpenStock.git cd OpenStock如果你访问 GitHub 速度不稳定也可以用国内镜像地址克隆效果一样。克隆完成后先看一眼README.md确认当前项目的版本和依赖要求再继续下一步。3.2 创建虚拟环境并安装依赖在项目根目录下执行python -m venv venvWindows 下激活命令是venv\Scripts\activatemacOS 和 Linux 下是source venv/bin/activate看到终端提示符前面出现(venv)就说明虚拟环境已经激活了。接下来安装依赖pip install -r requirements.txt这里我要专门提醒一下依赖安装的坑。requirements.txt 里核心依赖是 pandas、numpy、FastAPI、uvicorn、AkShare、APScheduler 这些。如果你网络环境一般强烈建议先配置国内 pip 镜像源否则装 pandas 这种重包可能要等很久。pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple装完之后可以用一条命令验证关键依赖是否就绪python -c import pandas, akshare, fastapi; print(deps ok)如果正常输出deps ok说明依赖全部就位。3.3 配置数据源与数据库初始化打开config.ini把数据源配置改成 AkShare[datasource] type akshare request_interval 0.2修改完配置后执行数据库初始化脚本python scripts/init_db.py这一步会创建data/stock.db文件并建好stock_basic、stock_daily、indicator_daily三张表。建议初始化完成后用 SQLite 工具打开看一眼表结构确认主键、索引是否都建上了。日线表的主键通常是(ts_code, trade_date)的联合唯一索引这个设计保证同一个股票同一天的数据最多只有一条为后面的幂等写入打基础。3.4 首次全量数据拉取数据库初始化只是搭好了骨架真正耗时的是全量历史数据拉取。OpenStock 提供了脚本从 AkShare 拉取沪深京 A 股的日线数据。执行python scripts/fetch_all.py这个脚本会先获取股票列表然后遍历每一只股票拉取历史日线数据。默认拉取从 2015 年到现在单只股票数据量不大但全市场五千多只股票循环下来确实需要一些时间。我在实机上的数据供参考使用 0.2 秒请求间隔大约每分钟能处理 30 只左右全量跑完大约需要两到三个小时。拉取过程中建议不要断开网络也不要中途关掉终端。如果中途意外中断也不用慌脚本一般会记录已完成的进度重新运行会自动跳过已拉取过的部分这就是幂等写入的好处。3.5 启动 Web 服务并验证数据拉取完成后启动 Web 服务uvicorn app.main:app --host 0.0.0.0 --port 8000看到类似Uvicorn running on http://0.0.0.0:8000的日志说明服务已经起来了。浏览器打开http://127.0.0.1:8000正常情况下应该能看到股票列表页面。第一次打开先做两件事一是随便点进一只股票看看 K 线图能不能正常渲染二是在筛选页面跑一个简单的条件比如“MA5 大于 MA10”如果结果能正常返回说明从数据、计算到展示的整个链路都是通的。4. 核心玩法指标计算与选股筛选4.1 技术指标是怎么算的数据仓库建好后最有意思的部分就是指标计算了。OpenStock 里的指标计算模块不依赖 TA-Lib全部用 pandas 的滚动窗口实现逻辑透明方便自己改参数。拿最常用的均线举例。MA5 就是过去 5 个交易日收盘价的平均值pandas 里一行代码搞定import pandas as pd df[ma5] df[close].rolling(5).mean() df[ma10] df[close].rolling(10).mean() df[ma20] df[close].rolling(20).mean() df[ma60] df[close].rolling(60).mean()MACD 稍微复杂一点核心是计算 EMA12 和 EMA26 的差离值 DIF再对 DIF 计算 DEA 信号线ema12 df[close].ewm(span12, adjustFalse).mean() ema26 df[close].ewm(span26, adjustFalse).mean() df[dif] ema12 - ema26 df[dea] df[dif].ewm(span9, adjustFalse).mean() df[macd] (df[dif] - df[dea]) * 2这段代码里的adjustFalse很关键它表示 EMA 计算沿用传统的前瞻调整方式否则结果会和主流行情软件对不上。这是我在核对指标时踩过的坑很多自写的 MACD 和炒股软件显示不一致问题就出在这个参数上。4.2 写一个简单的均线多头筛选指标算出来后选股就是一段普通的 pandas 筛选逻辑。以经典的“均线多头排列”为例条件是 MA5 大于 MA10、MA10 大于 MA20、MA20 大于 MA60同时排除掉 ST 股票和上市不足一年的次新股。def filter_bull_stocks(dfs): picks [] for ts_code, df in dfs.items(): if ST in df[name].values[-1]: continue last df.iloc[-1] if last[ma5] last[ma10] last[ma20] last[ma60]: picks.append((ts_code, last[name], last[close])) return picks这段逻辑看起来简单但实际操作中有两个细节值得注意。第一一定要使用最新一条数据以外的前值做确认也就是等收盘后再筛选盘中数据不稳定会出现信号反复跳变。第二均线计算需要至少 60 个交易日的数据新股或停牌久的股票如果数据不足 60 条计算出来的均线值没有任何意义直接跳过即可。4.3 定时任务与增量更新手动跑数据不是常态只有接入自动更新才能真正省心。OpenStock 用 APScheduler 做定时任务配置在app/scheduler.py里。A 股交易时间是周一到周五收盘后数据才会稳定下来所以增量更新最合理的时间点是每个交易日的 15:30。对应的 cron 表达式是scheduler.add_job( fetch_daily_increment, CronTrigger(day_of_weekmon-fri, hour15, minute30), iddaily_update )增量更新只拉取最近两个交易日的行情执行完直接INSERT OR REPLACE写入数据库几分钟内就能完成全市场更新。定时任务跑起来之后基本可以做到“打开浏览器就是最新数据”真正实现了本地行情仓库的自动化。5. 常见问题与排查实录5.1 问题速查表我搭建和日常使用 OpenStock 的过程中把踩过的典型问题整理成了一张速查表先给各位做个索引问题现象可能原因解决办法全量拉取中途断掉网络中断或 AkShare 限流重新执行脚本已拉取的数据会自动跳过K 线页显示空白前端请求接口超时或数据未关联上检查数据库stock_daily是否有数据确认股票代码格式一致MACD 数值与软件对不上EMA 参数 adjust 设置不对改用adjustFalse重新计算指标定时任务不执行时区或 cron 表达式配置错误检查 scheduler 时区设置为 Asia/Shanghaipip 安装依赖超时网络问题配置清华等国内镜像源后重新安装这些属于高频问题大部分都能在五分钟内定位。如果你的问题不在表里优先看日志文件OpenStock 在logs/目录下会输出采集和调度日志定位问题效率高很多。5.2 两个印象最深的坑第一个坑是 AkShare 字段名变动。某一个周五下午我跑完增量更新发现当天的成交量全部为零排查了大半天才发现是数据源接口把“成交量”字段改名了旧字段取不到值程序又没做为空校验就直接把空值写进了库里。这个教训让我在后续代码里对关键字段增加了缺省校验一旦发现字段缺失就拉警报避免脏数据悄悄入库。第二个坑是 Windows 下 TA-Lib 编译报错。虽然 OpenStock 计算层没用 TA-Lib但我一开始想额外跑一个基于布林带的策略自作主张装了 TA-Lib 依赖包结果在 Windows 上编译 C 扩展直接报错花了一晚上装编译工具才搞定最后发现 OpenStock 自带的纯 pandas 实现完全够用白白折腾了。现在我的原则是新库引入前先看平台兼容性能用纯 Python 方案解决的绝不碰需要编译的扩展包。5.3 数据准确性验证方法搭建完成后最重要的一步是验证数据质量不能光看“能跑”就完事。我自己的验证方法是拿三只不同板块、不同市值的股票跟行情软件的日线数据做对比。分别核对收盘价、成交量、复权因子的连续性以及除权除息日前后数据的衔接是否合理。重点关注两点一是连续 K 线是否有缺失日期这关系到后续指标计算的正确性二是复权价的计算逻辑是否一致前复权和后复权数据不能混用。具体验证时从数据库里随机取一只股票的年线数据算一下当年的最高价、最低价、年涨跌幅再跟行情软件里的年度统计对比。偏差在可接受范围内说明这套系统的数据链路是可靠的可以放心用来做后续的策略研究。搭建 OpenStock 这套系统对我来说最大的收获其实是建立了一种“数据自主”的底气。以前做任何分析都要依赖各种平台现在本地有一份完整可控的行情数据库指标算法自己想怎么改就怎么改筛选条件想写多复杂都行。如果你也想走这条路建议第一次搭建别急着改代码先按默认配置完整跑通一遍理解了整个数据流之后再按自己的策略需求动手改造。最后再说一个小技巧数据库文件记得定期备份我都是每天晚上用一条简单的文件复制命令把 stock.db 同步到另一块硬盘上整套系统跑半年来数据一次都没丢过这个习惯建议你从第一天就养成。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进