拓冰建站拓冰建站
首页 / 资讯中心 / 正文

OpenStock搭建全攻略:从数据采集到量化策略回测实战

1. 先说清楚OpenStock解决什么问题不是又一个看盘软件如果你搜OpenStock这个词能看到的信息其实很杂。有拿它当股票数据接口用的有拿它做个人持仓管理的还有直接把它部署在服务器上当量化策略回测平台的。我第一次准备搭建OpenStock的时候也迷糊过但实际动手跑起来之后才明白这玩意儿本质上是一套开源的个人股票数据采集与分析系统核心价值不是看盘而是让你把行情数据、自选股监控、策略信号这三件事全部收拢到自己的服务器里数据是自己的、逻辑是自己写的、规则是自己定的。为什么这点很重要因为市面上的免费看盘软件数据接口基本是黑盒你想把历史K线拉下来做回测或者想每天收盘后自动跑一遍自己的选股条件几乎都要付费开会员或者忍受各种权限限制。而自己搭一套OpenStock等于把数据权拿回手里。你不需要懂太深的金融知识也不需要先成为量化大佬只要会基本的Linux操作、看得懂简单的Python配置就能把它跑起来。这篇博文就是一份完整的搭建记录。我会从架构选型讲起到环境准备、初始化配置、启动服务、接入行情数据、跑通一个最简单的策略最后把我实际踩过的那些坑一并列出来。整个流程我都用最朴素的命令行操作不绕弯子。适合谁看想自建行情数据服务的人、想拿Python练手做量化入门的开发者、以及受够了商业软件数据限制的散户投资者。看完你就能照着一步步搭出一套属于自己的股票数据与信号分析系统。2. 搭建OpenStock前的架构拆解数据源、存储、调度三件事很多人一上来就急着装依赖、跑启动命令结果后面天天在改配置。我建议你先花半小时把OpenStock的模块关系理清楚后面会省很多事。2.1 数据源选型行情接口决定了你系统的天花板OpenStock本身不是一个数据生产方它更像一个数据搬运工加工厂。所以第一步要确定的是行情数据从哪来。目前常用的开源/免费数据接口主要有几类一类是社区维护的数据库接口库比如akshare、tushare它们把新浪、东财、交易所官网这些公开页面上的数据做了封装按函数调用就能拿到实时报价和日线历史数据另一类是券商或数据服务商提供的API通常需要申请token有调用次数限制。我的建议是本地开发和功能验证优先用akshare这类纯开源库零门槛、不需要token、接口文档全。等系统稳定运行、你需要更高频的分钟级数据或者财务数据时再去申请tushare的pro接口把token填进OpenStock的配置文件里。这样的好处是前期不花钱也能把整条链路跑通后期更换数据源时不需要动业务代码只需要改配置文件里的数据源适配器。2.2 存储设计别一上来就整大数据架构OpenStock的数据持久化绝大多数场景下都用不到Hadoop、ClickHouse这类重型组件。你要存的无非是三张核心表表名作用关键字段stock_basic股票基础信息代码、名称、交易所、行业daily_price日线行情代码、日期、开高低收、成交量、成交额strategy_signal策略信号代码、日期、策略名、信号方向、净值这个量级的表用MySQL 8.0或者PostgreSQL 14完全扛得住。如果行情数据量特别大比如你拉了全市场十年日线大约几百万行给daily_price表加一个(code, date)的联合索引就够了没必要上分布式存储。我见过有人把OpenStock搭在云服务器上结果数据量才几百万行就配了一套Elasticsearch集群纯属杀鸡用牛刀维护成本翻了十倍。Redis在这里的角色是缓存层主要是存实时行情快照和策略运行的中间结果。因为实时行情是高频读取、低频落库的每次都查MySQL太慢用一个hash结构按股票代码存最新价、涨跌幅读的时候走Redis写的时候定时批量同步到MySQL性能会比直连数据库好一个数量级。2.3 任务调度定时采集是系统的脉搏股票行情采集有一个天然特征强周期性。每天9:30到15:00之间要高频采集收盘后要拉一次日线定稿数据每天晚上还要做一次全市场数据完整性检查。所以OpenStock必须有一个靠谱的定时任务框架。在开源生态里Celery Redis是经典组合分布式、支持定时任务和任务队列看起来很正统但说实话对单机小规模部署来说有点重。我更推荐用APScheduler它轻量、进程内调度、不需要额外启动worker进程写几个cron触发器就能覆盖盘中盘后的全部场景。OpenStock默认的调度配置文件里也内置了APScheduler的示例照格式填就行。这一块的配置逻辑是先定义采集函数入参是股票代码列表再定义调度触发器几点执行、间隔多久最后把两者绑在一起注册进调度器。中间任何一个环节出问题最典型的症状就是日志里什么都没有。所以后面我在调试章节会专门讲怎么用一条命令行手动触发一次采集先验证采集函数本身能跑通再查调度问题。3. 环境准备与配置文件详解最容易翻车但最少被说清楚的环节3.1 服务器与系统要求先说结论OpenStock对硬件的要求低到离谱。单机部署的话2核4G的云服务器就能带起来磁盘给40G以上用来堆历史数据。我自己第一次部署用的是1核2G的轻量服务器跑得动就是启动时编译依赖会慢一点。操作系统建议选Ubuntu 20.04或22.04 LTSDebian系在Python版本管理和依赖编译上比CentOS省心。如果你非要用CentOS那尽量别碰系统自带的Python用pyenv或者直接Docker跑否则光处理openssl-devel和mysql-devel的依赖关系就能耗掉你一下午。3.2 Python环境与基础服务安装OpenStock的核心代码是Python建议Python 3.10以上版本。环境隔离一定要做用virtualenv或conda都行我习惯用这组命令# 更新系统包索引 sudo apt update sudo apt upgrade -y # 安装基础编译工具某些Python包需要编译原生扩展 sudo apt install -y build-essential libssl-dev libffi-dev \ libmysqlclient-dev pkg-config # 安装MySQL和Redis sudo apt install -y mysql-server redis-server # 启动服务 sudo systemctl enable mysql sudo systemctl start mysql sudo systemctl enable redis-server sudo systemctl start redis-server这里有一个细节值得多说一句libmysqlclient-dev这个包如果不装后面pip install mysqlclient的时候百分之百会编译报错报错信息还很唬人什么mysql_config not found之类的。其实压根不是代码问题就是缺了这个系统级依赖。提前装上后面能少折腾一整轮。Python虚拟环境这块# 安装Python 3.10如果Ubuntu 20.04默认不是3.10 sudo apt install -y python3.10 python3.10-venv python3.10-dev # 创建虚拟环境 python3.10 -m venv openstock_env source openstock_env/bin/activate # 安装项目依赖 cd openstock pip install -r requirements.txt安装依赖的时候容易卡在两个地方一是国内网络访问PyPI慢建议加镜像源pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple二是pandas、numpy这类科学计算库在低配机器上编译慢如果等不了直接装它们的预编译wheel包或者用conda来管理环境。3.3 配置文件里到底该填什么OpenStock的配置主体是一个config.yaml文件结构大致如下database: host: 127.0.0.1 port: 3306 user: openstock password: your_password dbname: openstock redis: host: 127.0.0.1 port: 6379 db: 0 datasource: provider: akshare # provider: tushare tushare_token: scheduler: timezone: Asia/Shanghai jobs: - name: intraday_quote trigger: interval minutes: 5 - name: daily_kline trigger: cron hour: 15,16 minute: 30 strategy: ma_window_short: 5 ma_window_long: 20 log: level: INFO file: logs/openstock.log数据库和Redis的连接信息没什么好说的照着填。真正值得琢磨的是scheduler和strategy两个块。scheduler块决定系统什么时候干活。interval类型适合盘中采集我设的是每5分钟拉一次实时行情cron类型适合盘后定稿数据每天15:30和16:30各拉一次为什么拉两次因为有些数据源在收盘后半小时数据才会完全更新第一次拉可能缺最后一笔第二次拉就是为了补漏。时区这块我专门提一句一定显式配置Asia/Shanghai不要依赖服务器系统时区。我踩过这个坑服务器时区默认UTC调度任务每天都晚8小时执行晚上8点才跑收盘任务拉了一堆残缺数据进库。strategy块是给后面策略回测用的简单理解就是均线策略的短窗口和长窗口参数。5日和20日是一个很经典的均线组合短期均线上穿长期均线是买入信号下穿是卖出信号。这个配置现在先放着后面验证策略的时候会用到。4. 从零启动OpenStock初始化、采集、验证一条龙4.1 数据库初始化OpenStock启动前要把数据库表结构建好。项目里一般会带一个schema.sql或者提供init_db.py脚本。建议直接执行脚本python scripts/init_db.py这个脚本会做三件事创建数据库如果不存在、创建三张核心表、写入初始化的股票基础信息。如果你是用MySQL执行完可以用这条命令快速验证表是否建成功mysql -u openstock -p -e USE openstock; SHOW TABLES;正常情况下能看到stock_basic、daily_price、strategy_signal这三张表。这里有一个新手容易忽略的点数据库字符集。创建数据库时一定要用utf8mb4因为股票名称里可能出现生僻字如果用默认的latin1入库直接变乱码。如果你在执行init_db.py之前就建好了数据库那就要确认一下库的字符集ALTER DATABASE openstock CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;4.2 手动执行一次数据采集初始化完成后别急着启动整个系统。先手动执行一次采集验证数据源配置是通的。OpenStock一般会提供命令行工具比如python -m openstock.cli fetch --code 600519这条命令的含义是把贵州茅台600519的日线数据拉下来写入daily_price表。执行完以后查一下mysql -u openstock -p -e USE openstock; SELECT * FROM daily_price WHERE code600519 ORDER BY date DESC LIMIT 5;如果能查到最近几天的开高低收、成交量数据说明数据链路已经通了。这一步是整套系统里最关键的验证点因为后面所有定时任务、策略信号都建立在数据能正确入库这个前提上。如果你执行fetch命令报错最常见的两类原因一是数据源库比如akshare没装或者版本不对重新pip install akshare --upgrade即可二是网络问题很多数据源要访问外网如果你的服务器在国内且访问不稳定可以考虑换一个数据源适配器或者在配置里加上代理设置。4.3 启动调度器和Web服务数据链路通了就可以把整个系统拉起来了。OpenStock通常会有两个启动入口调度服务和Web服务。为了开发调试方便可以先在一个终端里跑调度服务另一个终端跑Web服务# 终端1启动调度服务 python -m openstock.scheduler # 终端2启动Web服务 python -m openstock.web --port 8000调度服务启动后日志里会打印APScheduler加载了多少个job、下一次执行时间是什么。如果只是Started字样但没有任何任务加载说明scheduler.jobs配置没读进去回config.yaml里检查缩进——YAML就是个魔鬼一个空格错位整个配置就废了。Web服务启动后用浏览器访问http://服务器IP:8000能看到OpenStock的仪表盘页面。首页一般会展示已收录股票总数、最近一次数据更新时间、今日采集任务执行状态。如果页面能正常显示且数据更新时间是刚才说明大功告成系统已经跑起来了。4.4 验证定时任务真的在跑很多人启动完Web服务就以为结束了实际上定时任务有没有真正跑起来是必须单独验证的。一个最朴素的验证方法记录daily_price表的最大日期等调度器预设的下一次执行时间过后再查一次最大日期看有没有往后走。mysql -u openstock -p -e USE openstock; SELECT MAX(date) FROM daily_price WHERE code600519;或者更直接一点看日志文件tail -f logs/openstock.log正常情况每5分钟会有一条intraday_quote completed之类的记录盘后会有daily_kline completed。日志是排查一切问题的第一入口我会把日志级别调成DEBUG跑一天确认稳定后再调回INFO这样能最直观地看到每个任务的实际执行情况定位问题也高效。5. 让OpenStock真正替你干活自选股监控和一个能跑通的均线策略系统搭好只是开始OpenStock的真正价值在于帮你盯盘和验证策略。这一节我讲两个高频需求的落地方式自选股异动监控和均线交叉策略的完整回测。5.1 自选股监控成交量异常提醒先说场景。你有一批自选股不想每分钟盯着行情软件看但希望某只股票出现成交量突然放大这种异动时能被提醒。OpenStock的做法是写一个监控任务每次采集完实时行情后对比当日成交量与过去5日均量超过N倍就生成一条提醒记录。具体实现上OpenStock的监控任务配置大概长这样monitor: watchlist: - 600519 - 000858 - 300750 rules: - name: volume_spike type: volume_ratio threshold: 3.0 window: 5这个配置的含义是盯住这三只票如果某只票当日成交量超过过去5日平均成交量的3倍就把这个信号写入strategy_signal表。提醒方式可以是写入Web页面的消息中心也可以接入企业微信机器人和钉钉机器人webhook。这里有一个经验之谈阈值不要设得太低。3倍这个数字我试过在震荡行情里能过滤掉大部分噪音设成2倍的话一天能给你弹几十条提醒全是无效信息很快你就会把通知功能关掉。先跑两周观察实际触发频率再微调阈值这是最靠谱的方式。5.2 写一个最简单的双均线策略接下来是很多人搭建OpenStock的核心目的策略回测。我们不用复杂的机器学习用一个最经典的双均线策略来验证整条链路。策略逻辑很简单当5日均线上穿20日均线时下一个交易日开盘买入当5日均线下穿20日均线时下一个交易日开盘卖出。代码逻辑用Python写OpenStock统一封装了策略接口你只需要实现一个函数def ma_cross_signal(df): 输入单只股票的历史日线DataFrame按日期升序 输出每天的持仓信号1持有/买入0空仓/卖出 df[ma_short] df[close].rolling(window5).mean() df[ma_long] df[close].rolling(window20).mean() df[signal] 0 df.loc[df[ma_short] df[ma_long], signal] 1 # 计算信号变化点只有发生金叉/死叉时才触发交易 df[position] df[signal].diff().fillna(0) return df写完这个函数后在策略配置里注册它然后运行回测python -m openstock.backtest --strategy ma_cross --code 600519 --start 2022-01-01 --end 2024-12-315.3 回测结果怎么读别只看总收益率回测跑完OpenStock会输出一张结果表包含几个核心指标总收益率、年化收益率、最大回撤、胜率、交易次数。我见过很多人第一次跑回测看到某只票三年总收益率80%就兴奋得不行觉得发现了财富密码。这里我要泼一盆冷水单只股票、单个时间段的回测结果没有统计意义。一个策略真正要看的是在全市场几千只股票上跑出来的整体表现具体看三个指标指标看什么及格线参考策略胜率多次交易的盈亏比例不低于50%盈亏比平均盈利/平均亏损大于1.5最大回撤账户从峰值往下掉的幅度越低越好超过25%要警惕可以用这个命令跑全市场回测python -m openstock.backtest --strategy ma_cross --market all --start 2022-01-01 --end 2024-12-31等它跑完把结果按年化收益率排序你会发现双均线策略在震荡市里大量亏损、在单边上涨市里表现很好。这不是策略错了而是任何趋势策略都有的天然特点。理解了这一点你才算是真正会用这套系统做策略验证而不是拿它当印钞机。6. 上线后我踩过的坑数据源限流、任务堆积、进程守护系统上线跑了一周各种问题开始浮出水面。这里我把踩过的坑按排查链路完整写出来省得你走弯路。6.1 数据源限流为什么采集中途突然没数据了现象跑了三天之后某天下午打开日志发现从14:20开始所有采集任务都在报错错误信息是请求频率超限或者直接被拒绝连接。排查过程我先检查了数据源服务商的文档确认了免费接口有每分钟访问次数的限制。然后回去看自己的调度配置——盘中每5分钟采集一次但每次采集会遍历所有自选股行情接口是一个股票代码一个HTTP请求自选股多了以后5分钟内累计的请求数很快就撞上了限流阈值。解决方案两个方向同时做。第一把盘中采集从循环请求每一只股票改成调用数据源的批量接口。以akshare为例它提供的全市场实时行情接口一次可以拿到几千只股票的当前报价代码里做一遍本地过滤就行这样每5分钟只发一个请求限流压力骤减。第二如果真的还是频发就在采集函数里加一个简单的请求间隔控制用time.sleep(0.5)这种最朴素的方式做一次人工限频代价是单次采集时间变长但至少不会触发封禁。6.2 定时任务堆积为什么凌晨的任务下午才跑完现象某天早上起来发现昨晚的日线采集任务还在跑而今天盘中的实时采集任务已经叠加在后面排队了。日志里全是任务警告。排查过程这个问题其实很好诊断。我查了任务执行时长发现日线采集要对全市场几千只股票逐只拉数据单次采集要2个多小时而APScheduler的默认行为是上一个任务没跑完下一个到点的任务不会新起线程只会记一条任务被跳过的警告。日线采集作为cron任务在15:30触发正常应该17:30跑完但一旦中间网络变慢导致单只股票请求超时重试整个链路就会被拉长好几倍。解决方案治本的办法是让日线采集支持断点续传。具体做法是在daily_price表里加一个last_update字段每次采集只处理距离上次更新超过一天的股票代码这样即使任务跑到一半中断下次启动也能跳过已经更新过的股票只补剩下的。治标的办法更简单把任务拆分按股票代码的哈希值分成4个分片分别用4个cron任务在15:30、15:40、15:50、16:00触发每个分片处理1/4的股票。分片法改造成本低、见效快我最后是两者都做了。6.3 Python进程说死就死systemd守护比nohup靠谱现象服务器重启或者SSH会话断开之后OpenStock的调度进程就消失了Web服务也跟着挂必须手动重新登录去启动。排查过程这个问题的根因倒不是OpenStock本身而是我之前一直用nohup python -m openstock.scheduler 这种方式跑后台任务。nohup只能抵御终端挂断信号一旦服务器重启所有进程都会被干掉也不会自动拉起。解决方案改用systemd把OpenStock的东西守护起来。在/etc/systemd/system/目录下新建一个服务文件[Unit] DescriptionOpenStock Scheduler Afternetwork.target mysql.service redis-server.service [Service] Typesimple Useryour_user WorkingDirectory/path/to/openstock ExecStart/path/to/openstock_env/bin/python -m openstock.scheduler Restartalways RestartSec10 [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable openstock-scheduler sudo systemctl start openstock-schedulerRestartalways这个参数是核心只要进程非正常退出systemd会在10秒后自动拉起来。Web服务用同样的方式再写一个service文件就行从这之后我再没被进程消失这种问题困住过。6.4 数据质量的最后一公里缺数据和脏数据要比没有数据更头疼这是我个人最想强调的一个经验。OpenStock运行两周后我做了一次数据完整性检查发现一个问题某些股票的日线数据在最开始初始化的时候漏了几十天。排查之后发现根因在数据源有些股票有过停牌停牌期间没有行情记录这是正常的但有些股票复牌后第一天的数据部分数据源偶尔会延迟半天才更新导致当日采集时拿到的数据不完整而我的采集逻辑是只插入不更新就把这条残缺数据永久留在了库里。解决方案是两条一是写一个每周执行的数据修补任务把最近30天内daily_price里volume0或者closeNaN的脏数据找出来强制重新拉取覆盖二是在采集函数里加一个数据完整性校验拿到一条记录先检查开盘价、收盘价、成交量这些关键字段是否合法异常就直接丢弃并告警而不是入库。宁可不入库也不让脏数据污染后续的策略信号。这个原则做数据出身的同学应该秒懂。最后再说两句大实话OpenStock搭起来不难真正难的是后续的持续运营。我在跑这套系统的过程中最大的体会是股票系统最核心的话题永远是数据质量行情数据晚10秒可能没关系但数据残缺、数字错位你的所有策略结论就全都建立在沙滩上。所以如果你准备自己搭一套我建议从第一天就养成两个习惯一是每天扫一眼日志确认采集任务都正常完成二是每周做一次数据完整性检查把异常记录修掉。这两个习惯花不了十分钟却能让你对系统状态心里有底。还有一个很实用的扩展方向OpenStock采集的历史数据完全可以导出来喂给其他工具比如用pandas直接读取MySQL再画K线图或者作为机器学习模型的训练数据集。我目前就在做第二件事——把两年多的日线数据清洗后用于一些基础的涨跌分类实验。系统最值钱的部分不是代码而是沉淀下来的干净数据这个认知等你跑上三个月之后一定会认同。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门