尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

自建开源行情监控系统:OpenStock搭建与部署全指南

发布时间:2026/9/24 11:46:06

资讯中心
01
ARTICLE

自建开源行情监控系统:OpenStock搭建与部署全指南

自建开源行情监控系统:OpenStock搭建与部署全指南
最近花了一个晚上把自己在用的开源行情监控系统OpenStock从头到尾重新搭了一遍。换了个服务器顺便把搭建过程整理成文。OpenStock这个名字容易让人觉得是个量化交易平台实际上它的定位非常朴素一个开源的股票行情采集与监控看板解决的是“自己掌握行情数据、随时看得见、跌破关键位置有提醒”这三件事。后端是Python Flask前端是标准HTML加上ECharts图表数据存SQLite都是资料多、上手快的常见技术。如果你有基本的Linux操作经验、稍微懂点Python照着这篇文章的步骤来一个晚上就能把整套系统跑起来。我们先把丑话说在前面搭建自建系统最耗时间的从来不是敲命令而是遇到问题不知道去哪里排查。所以这篇文章不只是把安装命令列出来我会把每一步背后的原理、目录结构的设计意图、实际运行中容易踩到的坑全部讲清楚。你照着敲完命令得到的不是一个“能跑但不明白为什么能跑”的黑盒子而是一套你可以自己维护、自己改代码、遇到问题能独立排查的完整系统。内容覆盖技术选型思路、环境准备、数据采集与存储模块原理、指标计算和预警逻辑、Docker容器化部署以及我实际运行几个月后攒下来的高频问题和应对方案。1. 为什么我最终选择了OpenStock这套方案先说说我自己的实际场景。每天开盘我需要同时盯住二十多只自选股的分时走势关注价格有没有跌破关键支撑位晚上复盘的时候还得翻出一段时间的历史数据做简单回看。市面上的行情软件不是做不到这些但用久了你会发现自己被裹挟得很紧开屏广告、消息弹窗、各种社区讨论和大V荐股内容全塞在同一个App里想专心看个盘面都费劲。更核心的问题在于平台的数据不归你——你想导出某只股票过去一年的分钟级数据做自己的分析几乎做不到平台巴不得你留在App里看它想让你看的东西。OpenStock的出现就是为了打破这个局面。它是一个开源的、可私有化部署的股票行情采集与监控看板核心职责就是四件事定时抓取公开行情数据、存储原始记录、计算常用技术指标、在Web端以表格和图表形式展示行情。它没有社区功能没有广告位不会给你推送资讯流打开页面就是干净的价格数据、走势曲线和预警提醒。说起来有点反直觉这个项目最让我满意的地方恰恰是它的“功能克制”。它没有把自己做成一个大而全的炒股软件而是把“盯盘、记录、提醒”这三个基础动作做到位。交易策略、回测、自动盯单这些更重的需求完全可以自己写Python脚本去对接库里那些干净的数据灵活性反而更高。如果你想找一个自带完整回测引擎、模拟盘、自动交易的开源系统那OpenStock确实不合适但如果你恰恰需要的是一个可靠的数据底座加轻量看板它就是我见过的最务实的选择。自己写一个行情抓取脚本其实并不难真正麻烦的是后面那一串事数据表结构怎么设计、定时任务怎么避免重复执行、历史数据怎么看、预警怎么通知、Web界面要不要做、做的话用什么技术栈。这些事情单个拎出来都不复杂但凑在一起工作量一下子就上来了。OpenStock的价值在于把这些周边工程全部替你做好了你只需要改配置文件关心里面的股票列表就能获得一个完整可用的服务。自己写脚本更适合作为练习路径如果只是想尽快有一个能用的系统直接用OpenStock会少走很多弯路。就适用人群来说下面这三类朋友最合适想自建行情数据记录系统不想把历史数据长期依赖在第三方平台上的个人投资者。需要多端访问电脑浏览器、手机浏览器监控自选股行情又不想要App里杂七杂八功能的技术型用户。计划做量化分析需要一个干净数据底座来跑Python策略脚本的研究型玩家。如果你属于其中之一跟着这篇教程从零搭一套投入产出比相当高。整个搭建过程按部就班走一遍大概需要一个小时而且后面遇到问题基本也能在文章里找到对应的解法。2. 技术栈选型为什么是Python Flask SQLite这种“不性感”的组合在正式开始动手之前先把OpenStock的设计思路讲清楚。理解架构之后再操作后面出问题排查起来会顺手很多。2.1 后端与前端的分工逻辑OpenStock的后端采用Python编写核心框架是Flask。为什么不是FastAPI或者Django选型的时候我仔细对比过三者的优劣框架优势劣势适配度Flask轻量、简单、生态成熟异步支持弱高FastAPI性能好、自动API文档生态相对年轻中Django大而全、自带后台重、学习成本高低OpenStock的核心业务是定时采集数据加提供查询接口并发压力并不大Flask这种短平快的框架反而最合适。它的路由设计和扩展机制都很直观改起来不费劲。前端部分则是标准的HTML JavaScript ECharts图表库没有引入重型的Vue/React框架。原因在于监控看板的交互逻辑其实很有限一个自选股列表、一个行情表格、几张图表用原生JavaScript配合ECharts就足够支撑加载速度还更快。依赖少了部署和后续维护的压力都小很多。2.2 数据层的存储策略存储层用了SQLite这可能是整篇文章里最容易引起争议的选择——都上服务器了为什么不装MySQL或PostgreSQL我的理解是对于个人自建监控系统SQLite是最务实的选择。OpenStock的典型场景是每分钟抓取一次几十只股票的数据一天下来大概一两万条记录一年也就几百万条。SQLite对这个量级完全能胜任而且有几个非常现实的优点零配置不需要单独维护数据库服务一个文件就是一个库备份直接拷贝文件即可读多写少的场景下SQLite的性能表现并不比客户端—服务器架构的数据库差。当然如果后面要接入更多数据源或开启多用户功能迁移到PostgreSQL也不难OpenStock把数据访问层单独封装了一层切换数据库时只需要改配置项和驱动。2.3 数据从哪来又是怎么流动的这里要说明一个关键点OpenStock本身不产生数据它依托的是公开的股票行情接口。整体数据流是这样的定时调度器触发采集任务。采集模块向公开行情接口发起HTTP请求。拿到JSON数据后做清洗和格式转换。写入SQLite数据库。Web后端应用直接从数据库读取数据处理后返回给前端页面。前端定时轮询后端接口刷新行情看板和图表。这个链路设计得很直白每一条都有明确目的。采集和展示分离即使行情接口临时挂了前端展示的还是本地数据库里的历史数据不会整个系统崩掉。这个“采集与展示解耦”的思路是我在实际使用中最欣赏的一点它意味着就算数据源出了问题你随时能打开看板查看历史记录而不是面对一片白屏。2.4 目录结构一览搭建前的最后一步准备是搞清楚代码放在哪。标准的OpenStock项目目录大概是这样的openstock/ ├── app.py # Flask应用入口 ├── config.py # 全局配置 ├── requirements.txt # Python依赖清单 ├── collector/ │ ├── __init__.py │ ├── fetcher.py # 数据采集模块 │ └── scheduler.py # 定时调度模块 ├── storage/ │ ├── __init__.py │ ├── db.py # SQLite初始化 │ └── models.py # 数据模型 ├── calculator/ │ ├── __init__.py │ └── indicators.py # 技术指标计算 ├── web/ │ ├── routes.py # 接口路由 │ └── views.py # 页面渲染 ├── static/ │ ├── css/ │ └── js/ │ ├── dashboard.js │ └── chart.js ├── templates/ │ ├── index.html │ └── stock_detail.html └── docker/ ├── Dockerfile └── docker-compose.yml不用急着背这个结构后面每一步实际操作的时候我会对应着目录说清楚。3. 先把系统跑起来环境准备与本地启动现在进入实际操作。我按照自己重新搭建一遍的顺序来写遇到什么坑也都会标注出来。3.1 环境要求与检查清单先确认服务器或本机环境满足要求依赖版本建议说明Python3.93.9以下是真跑不起来pip20.3安装依赖用git任意版本拉取代码Docker20.10如果走容器部署Docker Compose2.x容器编排我用的环境是Ubuntu 22.04服务器加Python 3.10这些都满足要求。如果你用的是Windows建议在WSL2里操作文件路径和命令行的兼容性问题会少很多。3.2 获取项目代码与Python依赖代码拉取和依赖安装是最简单的两步但也是最容易因为版本问题卡住的地方。git clone https://github.com/your-repo/openstock.git cd openstock python3 -m venv venv source venv/bin/activate pip install -r requirements.txt这里我强烈建议用虚拟环境不要直接往系统Python里装依赖。原因很简单OpenStock依赖的Python包版本如果和系统里其他项目的包版本冲突排查起来非常痛苦。虚拟环境把依赖隔离在一个独立目录里出问题直接删掉重建就行。requirements.txt里面的核心依赖大概是这几个Flask3.0.3 requests2.32.3 APScheduler3.10.4 pandas2.2.2 pytz2024.1APScheduler负责定时调度pandas负责数据处理和指标计算这两个是除了Flask之外最重要的依赖后面会单独讲。提示如果你所在网络环境访问Python官方源比较慢可以用国内镜像源安装把pip命令改成pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple速度会快很多。我实际装的时候不换源要等两分钟换源几秒就完成了。3.3 初始配置改这三个地方就够了安装完依赖后先别急着启动打开config.py把配置改一遍。默认配置长这样# config.py class Config: # 股票代码列表按交易所前缀区分sh代表上交所sz代表深交所 STOCK_LIST [ sh600519, # 贵州茅台 sz000001, # 平安银行 sz300750, # 宁德时代 sh601318, # 中国平安 ] # 采集频率秒60表示每60秒采集一次 FETCH_INTERVAL 60 # 数据库文件路径 DB_PATH data/openstock.db # 监听端口 PORT 8000 # 预警配置 ALERT_RULES { enabled: True, check_interval: 60, notification_url: , # 预留的Webhook通知地址 }上面列出的股票代码只是用来演示配置文件怎么填不构成任何投资建议。第一个要改的是STOCK_LIST把你关注的股票代码填进去。注意格式sh开头代表上交所股票sz开头代表深交所股票bj代表北交所这是公开行情接口统一使用的代码标记方式写错了会直接导致股票查不到数据。第二个要改的是FETCH_INTERVAL如果你只是看日线级别趋势60秒一次完全够用如果是日内短线盯盘可以改成10秒但采集频率越高被数据源限流的概率越大。第三个是PORT如果服务器上已经有别的服务占了8000端口建议改成8001或者8080。我个人的习惯是用一个不常见的端口比如8765避免和常见Web服务端口冲突。3.4 初始化数据库并跑一个冒烟测试配置改完后先手动初始化数据库python3 -m storage.init_db执行完之后你会看到data目录下出现一个openstock.db文件。如果这一步报错八成是之前没有创建data目录手动mkdir -p data一下就好。接下来先不启动完整的Web服务单独跑一下采集模块确认接口能通、数据能写进数据库python3 -m collector.fetcher --once这个命令会触发一次手动采集。执行完成后打开数据库看一眼sqlite3 data/openstock.db SELECT * FROM kline_data ORDER BY ts DESC LIMIT 5;能看到最新行情记录就说明数据链路已经打通。到这里最核心的管道已经验证通过接下来再启动Web服务看界面。3.5 启动Web服务OpenStock的启动命令很简单python3 app.py看到类似下面的日志说明启动成功* Running on http://127.0.0.1:8000 * Running on http://192.168.1.100:8000浏览器打开http://服务器IP:8000就能看到行情看板页面。不过这时候页面上可能还是空的因为定时采集任务虽然已经启动了但还没到第一个采集周期。别急等一分钟刷新一下数据就会显示出来。到这里一个最小可用的OpenStock已经跑起来了。但“跑起来”和“好用”之间还有一段距离接下来的几节我会把每个核心模块讲透再讲怎么用Docker把整个服务优雅地部署到服务器上。4. 核心模块拆开看采集调度、存储模型、指标计算与预警实现只把项目跑起来还不够真正有价值的是理解每个模块的原理遇到问题知道从哪里下手查。我按数据流动的顺序逐个讲。4.1 采集模块怎么对接公开行情接口先看collector/fetcher.py的核心逻辑。它做的事情说起来很简单循环遍历股票代码请求行情接口解析JSON写入数据库。# collector/fetcher.py核心逻辑示意 import requests from datetime import datetime, timezone def fetch_quote(stock_code): 获取单只股票的实时行情 返回结构化dict字段包含价格、成交量、时间戳等 url fhttps://api.example-stock-market.com/quote/{stock_code} resp requests.get(url, timeout10) resp.raise_for_status() data resp.json() return { code: stock_code, price: float(data[price]), volume: float(data[volume]), high: float(data[high]), low: float(data[low]), open: float(data[open]), ts: datetime.now(timezone.utc).isoformat(), }这里有几个细节值得注意。第一个是timeout10必须设置。如果不设超时一旦接口无响应requests会一直挂在那里导致整个采集任务卡死。我第一天部署就吃过这个亏——有个数据源接口那天下班后响应变慢采集任务被卡住调度器后续所有任务全部排队看板上的数据整整停了半小时。加上超时参数之后即使某个接口异常最多等10秒就跳过不影响下一轮采集。第二个是异常处理。写采集逻辑容易忽略的是“部分成功”的情况。比如你一次要采集50只股票其中3只接口返回错误如果整个任务直接抛异常退出那47只已经拿到的新数据也会被丢掉。更合理的做法是每只股票单独try-except失败就记一条错误日志继续处理下一只。OpenStock的代码里就是这么做的。第三个是数据的时间戳处理。采集到的行情数据一定要用统一时区标记推荐用UTC存储展示时再转换成本地时间。如果直接存本地时间服务器迁移到别的时区或者切换夏令时所有历史数据的时间线就全乱了。4.2 定时调度器别把采集逻辑和调度逻辑混在一起定时采集功能用的是APScheduler库。核心调度逻辑在collector/scheduler.py里# collector/scheduler.py核心逻辑示意 from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.triggers.interval import IntervalTrigger scheduler BackgroundScheduler(timezoneAsia/Shanghai) scheduler.add_job( fetch_all_stocks, # 采集函数 triggerIntervalTrigger(secondsConfig.FETCH_INTERVAL), idfetch_quotes, max_instances1, # 关键同一任务只允许一个实例 coalesceTrue, # 关键错过的任务合并执行 ) scheduler.start()这里两个参数特别关键。max_instances1是为了防止任务重入。正常情况下采集一轮只需要几秒但如果某轮数据源响应特别慢上一轮还没跑完调度器又触发了下一轮两个采集任务就会并发执行。并发本身不是大问题但如果同时写数据库可能造成锁等待或重复数据。设置max_instances1之后如果上一轮没结束这一轮的触发会被跳过保证同一时刻只有1个采集任务在运行。coalesceTrue的目的是合并错过的任务。服务器休眠、重启或网络中断之后恢复运行APScheduler会尝试把错过的多个周期任务一次补跑。如果不设置coalesce它会从预期运行时间开始逐次补跑所有错过的任务服务器停机一天就补一天的数据非常浪费。设置之后只补跑最近一次其他中间周期直接关掉。这两个参数看起来不起眼实际使用中能帮你省掉大量“为什么数据多了一段”“为什么采集中断后疯狂补数据”的麻烦。4.3 数据库表结构行情数据到底怎么存OpenStock的默认建表语句如下CREATE TABLE IF NOT EXISTS kline_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, code TEXT NOT NULL, price REAL NOT NULL, open REAL NOT NULL, high REAL NOT NULL, low REAL NOT NULL, volume REAL NOT NULL, ts TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_code_ts ON kline_data(code, ts);核心就是kline_data这一张表字段分别是股票代码、最新价、开盘价、最高价、最低价、成交量、时间戳。价格数据全部存成REAL浮点型。可能有朋友会问既然有pandas为什么不直接算好指标再存答案是原始数据是唯一不能被还原的资产衍生指标随时可以重算。所以存储层只保存原始行情等到你需要几日均线、历史回测的时候再从原始数据重新计算就好。4.4 技术指标计算均线是怎么从原始数据中算出来的OpenStock内置了最常用的均线指标MA这也是大多数看盘界面的基础功能。以MA5和MA20为例实现思路是取最近N个周期的收盘价算平均# calculator/indicators.py核心逻辑示意 def calculate_ma(prices, window): # prices: 按时间升序排列的收盘价列表 # window: 均线周期如5、10、20、60 import pandas as pd series pd.Series(prices) return series.rolling(windowwindow).mean() def get_ma_for_stock(code, window): # 从数据库取最近window*2 1条记录确保数据充足 rows get_recent_klines(code, limitwindow * 2 1) prices [row[price] for row in rows] ma calculate_ma(prices, window) return ma.iloc[-1] # 返回最新的均线值实际使用中有一个心得计算均线时要多取一些数据不能只取刚好等于window数量的记录。原因在于数据源在非交易时段也可能返回相同的快照价格而且可能存在个别缺失记录。多取一半数量的缓冲数据然后取最后N条来计算算出来的均线值才稳定。OpenStock默认在详情页展示MA5、MA20和MA60三条均线分别代表短期、中期、长期趋势够用了。想要加入MACD、RSI等更复杂的指标在这个模块的基础上加函数就行数据基础是一样的。4.5 预警模块价格跌破均线时怎么通知你预警是OpenStock里我觉得最实用的功能。它做的事情是每隔一段时间检查所有自选股看是否触发了你设置的规则触发就通过Webhook推送通知。规则配置在config.py里面目前内置了一种最常用的规则当最新价低于MA20均线时触发预警这个规则用在短线风控上非常有效。实现逻辑如下# collector/alert.py核心逻辑示意 from calculator.indicators import get_ma_for_stock def check_alerts(): for code in Config.STOCK_LIST: latest_price get_latest_price(code) ma20 get_ma_for_stock(code, 20) if latest_price ma20: send_alert(code, latest_price, ma20)预警频率不用设置得太高因为计算均线依赖的是分钟级数据趋势的形成需要时间一分钟检查和十分钟检查在结果上不会有本质区别。我实际使用中设置的是每5分钟检查一次收到的推送数量刚刚好不会把人弄烦。这个模块给扩展留下了很大空间比如加一条“涨幅超过5%”的异动预警加一条“成交量突然放大到前5日均量2倍”的放量预警或者对接钉钉、企业微信、Server酱等通知渠道都是一个模式检查条件触发通知。理解了alert.py的骨架扩展起来很容易。5. 正式部署用Docker把OpenStock跑成常驻服务前面几节的操作都是在终端前台运行本地开发这么折腾没问题但部署到服务器长期运行还是得用Docker来包一层。容器部署有几个直接的好处环境隔离Python版本和依赖不会和其他项目互相污染随开随停升级回滚都干净方便迁移数据目录一挂载换台服务器直接搬家。5.1 编写DockerfileOpenStock的Dockerfile基于python:3.10-slim镜像这是最小体积的Python运行环境比ubuntu加手动装Python的方式轻得多FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . # 数据目录挂载点 VOLUME /app/data EXPOSE 8000 CMD [python, app.py]这里有个细节pip install --no-cache-dir表示安装完依赖后不保留pip的缓存包这样构建出来的镜像体积能少几十兆。VOLUME /app/data的作用是把数据目录声明为挂载点后面用docker-compose把宿主机的目录映射进去容器删了数据还在。5.2 用docker-compose编排整个服务docker-compose.yml文件的配置如下version: 3.8 services: openstock: build: context: . dockerfile: docker/Dockerfile container_name: openstock restart: always ports: - 8765:8000 volumes: - ./data:/app/data environment: - TZAsia/Shanghai - PYTHONUNBUFFERED1 logging: driver: json-file options: max-size: 10m max-file: 3几个重要的配置项说一下。restart: always保证服务宕了或服务器重启之后docker会自动把它拉起来这是“常驻服务”的基本修养。- 8765:8000把宿主机的8765端口映射到容器内的8000端口这样浏览器访问服务器IP:8765就能打开看板。映射一个不常见的高端口而不是直接用80可以减少被各种扫描器盯上的概率。TZAsia/Shanghai设置容器时区保证日志时间戳和预警时间符合北京时间没有这个环境变量会出现8小时偏移。PYTHONUNBUFFERED1强制Python输出实时刷新到标准输出否则docker logs看到的日志会有延迟。日志限制也值得关注max-size和max-file不设置的话常驻服务跑几个月/var/lib/docker/containers目录可能膨胀到几十个GB。5.3 构建镜像并启动配置写好之后构建和启动就两条命令docker compose build docker compose up -d启动完成后用docker compose ps看状态应该是Up或healthy。然后查看日志确认采集任务正常运行docker compose logs -f openstock一切正常的话你会看到类似下面的日志输出2024-06-01 09:30:01 INFO 采集任务开始共4只股票 2024-06-01 09:30:02 INFO 股票 sh600519 采集成功 2024-06-01 09:30:02 INFO 股票 sz000001 采集成功 2024-06-01 09:30:03 INFO 全部股票采集完成耗时 2.1 秒到这一步OpenStock就已经作为一个常驻服务在服务器上稳定运行了。5.4 数据备份一个命令的事最后提一下SQLite的好处。因为数据全在一个.db文件里备份极其简单。写个cron任务每天凌晨把data目录下的数据库文件拷贝到备份盘就行# crontab -e 0 2 * * * cp /opt/openstock/data/openstock.db /backup/openstock_$(date \%Y\%m\%d).db如果追求更保险的备份策略也可以用sqlite3 openstock.db .backup /backup/openstock.db做在线备份避免直接拷贝文件造成的数据不一致。我实际使用中每天备份一次每周把备份文件同步到另一台机器用了半年多从没出过数据丢失问题。6. 实测中踩过的坑数据源、存储与调度的高频问题部署完成不代表万事大吉。我实际使用OpenStock一段时间之后整理了几个最有代表性的问题。这些问题不看实际运行日志光看代码很难预料到遇到类似情况可以直接对号入座。6.1 数据源限流为什么采集到一半突然全是401第一次连续运行到第二天上午我发现数据库里的记录数在某段时间突然一直不增加查看日志发现大量401错误。刚开始以为是接口Key过期了排查半天才发现是触发了数据源的频率限制。大部分公开行情接口都有每分钟请求次数上限免费额度通常是每分钟60次或每小时300次。如果STOCK_LIST里的股票太多采集间隔又设得太短很容易撞上这个限制。我的应对策略是把采集频率调低到每分钟1次同时在采集函数里加一个0.2秒的延迟让每次请求之间留出时间间隙import time def fetch_all_stocks(stock_list): for code in stock_list: try: fetch_quote(code) except Exception as e: logger.error(f股票 {code} 采集失败: {e}) time.sleep(0.2) # 限速避免触发数据源限制这个思路就是“求稳不求快”。对于个人监控场景30秒到60秒的数据延迟对决策来说完全够用了没必要为了快几秒去冒被封的风险。如果你确实需要高频率数据就得付费购买数据源或自建多个数据源做轮询这已经超出了这个项目的定位。6.2 为什么SQLite文件越来越大SQLite数据库文件膨胀是另一个比较常见的问题。数据累积到几个月后openstock.db可能膨胀到几个GB查询速度也会明显下降。这背后的原因是SQLite删除数据不会自动释放磁盘空间它只是把数据页标记为“可复用”。如果你不时常做VACUUM数据库文件只会不断变大。解决办法是加一个数据保留策略。一般只保留最近半年的分钟级数据更老的数据导出成CSV存档后删除。定期执行SQLDELETE FROM kline_data WHERE ts datetime(now, -180 days); VACUUM;这两条SQL配合cron定时任务每个月执行一次数据库文件的体积能控制在合理范围内。实际上删除大量老数据之后文件体积可能没有立刻变小执行一次VACUUM后空间才会显著释放。6.3 服务器重启后采集任务为什么不跑这个问题我遇到过两次。第一次是在给服务器升级内核重启之后发现数据停在了重启那一刻就不动了。当时docker有restart策略容器按理说已经自动起来了。排查之后发现容器确实拉起来了但容器里的APScheduler没有把定时任务重新注册因为app.py启动的时候先调scheduler.start()而scheduler在init_db阶段还没有初始化完全。根本原因是启动顺序的问题启动脚本在数据库初始化之前就尝试注册定时任务导致任务注册失败。解决办法是在采集任务注册之前先确保数据库已经初始化好加一个启动依赖校验# app.py核心逻辑示意 def ensure_database_ready(): if not os.path.exists(Config.DB_PATH): subprocess.run([python, -m, storage.init_db]) scheduler.start()这样即使容器重启只要启动流程从头走一遍数据库和定时任务都能可靠初始化。提示如果重启后还是发现数据不涨先看容器日志docker compose logs openstock | tail -50大多数情况下问题会直接暴露在日志里比猜要快得多。6.4 时间戳错乱容器时区引发的数据排序问题还有一个比较隐蔽的坑发生在容器时区没有设置的时候。容器默认使用UTC时区而行情数据的自然日期应该是北京时间。如果存储层存的是UTC时间展示层又按本地时间读取就会产生8小时偏移。具体表现是每天凌晨0点打开看板发现“今日”数据没有任何记录因为UTC时间的0点刚好是北京时间的早上8点前一天的数据在UTC视角下还没结束。解决办法就是在docker-compose里设置TZAsia/Shanghai同时采集模块存储时间戳统一用Asia/Shanghai时区。正确做法是数据库统一存带时区信息的ISO时间字符串展示端按用户时区解析。OpenStock默认配置里两个时区都已经处理好了只要别自己改动时区相关逻辑就不会踩到这个坑。6.5 浏览器缓存导致的页面不刷新最后说一个非技术因素的坑。前端看板有时看起来“不刷新”不是服务端数据没更新而是浏览器缓存了旧的JavaScript和CSS文件。OpenStock改动前端代码后浏览器会优先使用本地缓存的旧资源页面展示的还是旧逻辑。解决思路很简单给静态资源文件加上版本查询参数每次改完前端文件在模板引用时拼上版本号script src/static/js/dashboard.js?v20240601/script如果只是自己用也可以按F5强制刷新或CtrlShiftR清除缓存。但如果要分享给别人用还是老老实实加上版本号避免不必要的困惑。7. 从“能跑”到“好用”进阶扩展方向和最终心得一个系统真正好用靠的不是初始功能而是能不能平滑地长出你想要的新能力。OpenStock的架构简单、分层清楚这给扩展带来了很大便利。第一个值得做的扩展方向是用积累的行情数据做历史分析。跑上两三个礼拜SQLite里就有了几十万条分钟级数据这些数据就是你的私人分析资产。我自己的典型用法是写脚本统计分析# analysis/volatility_report.py实际使用示意 import sqlite3 import pandas as pd from datetime import datetime, timedelta conn sqlite3.connect(data/openstock.db) sql SELECT code, price, ts FROM kline_data WHERE ts ? df pd.read_sql_query(sql, conn, params[(datetime.now() - timedelta(days30)).isoformat()]) # 按股票、按小时分组计算价格波动 df[hour] pd.to_datetime(df[ts]).dt.hour report df.groupby([code, hour])[price].agg([std, mean]) print(report.head(10))这类脚本写起来非常快因为有完整历史数据在分析的重心全在你想分析什么而不是在找数据上。我用这个方式跑过每只股票在不同交易时段的波动特征整理完发现有些股票开盘半小时内的波动率是全天均值的两倍以上这个结论对日内短线择时很有参考价值。类似的统计分析用行情软件自带功能往往只能看单只股票的K线形态远没有自己从数据库里取数做聚合来得灵活。第二个扩展方向是接入更多数据维度。行情数据只是股市的一个侧面你还可以把每日公告、公司财报指标、龙虎榜数据都采集到同一个数据库里这样以后做选股分析时一个数据源就能拿到关联数据不用在Excel和平台之间反复导出粘贴。这类扩展在OpenStock里通常意味着新增一张表和一个采集器由于原始行情存储层是独立的新数据不会干扰原有功能升级风险很小。第三个方向是通知渠道的完善。预警模块的Webhook接口本身是中立的可以接到任意支持Webhook的服务上。实际用下来我的建议是先接一个最简单、免费的渠道花几天时间验证规则的有效性再决定要不要接更重、可能要付费的短信或语音电话服务。如果一上来就设置一堆高频规则同时又接了多个通知渠道你收到的只会是一堆没有人看的无效推送——过几天你就麻木了真正重要的预警也会被淹没。关于容灾和监控还有一个建议值得一提。如果服务器上还跑着别的服务建议给OpenStock单独加一个健康检查。docker-compose里可以增加healthcheck: test: [CMD, curl, -f, http://localhost:8000/api/health] interval: 60s timeout: 5s retries: 3配上这个之后docker会定期检查OpenStock是否还活着如果连续三次检查失败配合restart策略容器会自动重启。这里有个细节healthcheck需要一个能返回200的health接口。如果没有现成接口最简单的方式是给Flask加一个极简路由app.route(/api/health) def health(): return {status: ok}实际使用中这个healthcheck帮我自动恢复过一次无声故障。那次服务器磁盘长时间没有清理导致Python进程进入不可响应状态容器没有退出但服务实际已经死了。healthcheck检测不到健康响应自动重启了容器避免了数据采集断档一整天。最后说说围绕“数据自主权”的一点体会。这套系统跑了几个月之后我最深的感受不是省了多少会员费而是终于有一个东西是完全归我管的数据在自己服务器上代码逻辑清清楚楚写在项目里想怎么改就怎么改。这种对系统底层逻辑的掌控感很踏实是任何外部服务都给不了的。如果你想更进一步还可以给OpenStock加上数据导出接口一键导出CSV或者加一个简单的多用户登录功能给家里人分配只读账号。这些扩展在架构清晰的Flask项目里都算不上大工程。每完成一项扩展你对这套系统的理解和掌控就更深一层这个“越用越顺手”的良性循环才是自建系统最迷人的地方。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。