在Windows上自己写一套股市盯盘软件,这个念头最早是被弹窗广告逼出来的。我用过的行情软件不少,可它们总喜欢在盯盘界面里塞“热门股票”“大师战法”,提醒稍微自定义一点就要开会员。我想要的只是“某只股票跌破20日均线”“某只股票涨跌幅超过3%”这种朴素提示,到头来发现还是自己动手最靠谱。前后折腾了两周,整套工具终于稳定跑起来了,行情采集、条件判断、弹窗提醒、开机自启、异常恢复都摸了一遍。这篇文章就把完整的思路和踩坑记录写出来,不是什么高深的大工程,就是一台普通Windows机器加上一点Python代码的实用组合。如果你也用Windows办公,也想按自己的规则盯盘,不想被现成软件绑架,这篇应该对你有用。
1. 现成软件不是不好,只是和我的“盯盘方式”不匹配
1.1 我为什么决定自建
先说清楚,我并不是否定同花顺、东方财富这类专业软件,日常翻翻公告、看看F10资料,我还是会打开它们。真正让我不舒服的,是它们作为“盯盘工具”时的几个硬伤。
弹窗和内容干扰是最明显的一个。你打开自选股页面,眼神正要扫价格,角落里一个“猜涨停”弹窗就冒出来了,首页还挂着一堆资讯流和广告位。对于只想专注盯几只票的人来说,这些全是视觉噪音。更难受的是提醒规则被产品设计锁死了,你可以设置涨跌幅提醒,但很难设置“价格跌破5日均线且当日成交量大于昨日1.5倍”这种组合条件。就算有高级条件单,也往往需要更高的权限或者收费。
延迟和稳定性也是个问题。免费用户拿到的基本是轮询推送,遇到极端行情,软件自身卡顿、数据延迟是常有的事。最让我崩溃的一次是,某只股票已经跌到我的止损位,软件弹窗在我手动刷新之后才慢悠悠出现。那一刻我就想通了:与其依赖别人封装好的黑盒,不如自己写一个“规则引擎”。
自建盯盘软件本质上解决的是三件事:把行情数据按自己的规则过滤一遍;把“该看盘了”这件事用你认为最有效的方式推给你;把行情数据留在本地,方便事后复盘。尤其是第三点,现成软件很少让你把分时数据、分钟K线完整导出来,但自己做,数据就是自己的,想怎么折腾都行。
1.2 什么情况不建议自己写
聊完自建的好处,也要泼盆冷水。如果你的需求只是“打开软件看看自选股涨跌”,那完全没必要自建,现成软件装一个就行。如果依赖Level-2逐笔成交、十档行情这类专业数据,个人开发者很难拿到稳定且低价的授权,自建没有意义。还有一点,如果你平时完全不写代码,那这篇文章读起来可能会吃力,建议要么找个懂行的朋友带,要么直接用现成的条件预警功能。
我给自己的判断标准很简单:有没有“用规则替代人肉盯盘”的明确需求?有,就值得折腾;没有,别浪费时间。我的需求就是盯两三个板块、七八只票,要能在价格突破或跌破某条均线的时候第一时间提醒我,不弹广告,不推荐股票,不收集我的数据。这个需求听着简单,现成软件还真没几个能干净利落地做到。
2. 先把“盯盘”这件事拆成三个环节
刚开始动手的时候,我满脑子都是“写个脚本抓数据”,结果写着写着发现,盯盘软件和普通数据抓取脚本之间隔着一整个工程思维的距离。真正想清楚之后,我把这套软件拆成了三个核心环节:数据接入、条件判断、提醒输出。
2.1 数据接入层
数据接入层的任务很简单:定期拿到每只自选股的最新行情,包括最新价、开盘价、昨收、最高最低、成交量等。这个环节看起来简单,实际上要考虑的问题不少。
第一个是数据频率。专业量化系统可以走行情网关做毫秒级推送,但个人免费方案里,主流数据源都是HTTP轮询接口。把轮询频率设成1秒1次,一是容易触发数据源限流,二是对个人盯盘没有意义——普通人的决策周期是分钟级的,5秒到10秒拉一次已经非常充裕。我最后把默认频率设在5秒,行情激烈时最多不会超过3秒,这个强度挨个请求腾讯和新浪的免费接口完全没问题。只有做高频交易的才会有更低延迟需求,但那种场景本来就不适合用免费接口。
第二个是接口可靠性。免费接口没有任何可用性承诺,可能今天能访问明天就挂,也可能盘中突然返回一堆乱码。所以数据接入层必须内置超时、重试、备用源切换机制。我的方案是主力源和备用源两个通道,哪个通用哪个。
第三个是数据格式。不同数据源返回的字段名不一样,有的价格是字符串,有的是整数乘以1000,有的股票代码带交易所前缀,有的不带。这些细节如果不在一开始统一抽象,后面写条件判断的时候会满地是坑。
2.2 条件判断层
数据拉回来之后,要判断“要不要提醒你”。最简单的规则是阈值判断:价格大于某个数、涨跌幅超过某个百分比、成交量突破某个量。但真正好用一点的规则,往往需要配合历史数据。比如“跌破20日均线”这种经典规则,你得先有最近20个交易日的收盘价,才能算出均线数值,再和当前价比较。
所以条件判断层不只是写个if语句那么简单,它背后需要一个小型的历史数据存储。最简单的是本地CSV,稍微规范一点可以用SQLite,再重度一点才轮到MySQL或者Elasticsearch。我在初期用的是SQLite,一张表存日线收盘价,一张表存实时快照,表结构简单,查询速度也快,完全够用。如果你一上来就想着我是不是应该装个ClickHouse或者ES,我劝你先忍一忍,把最简单的版本跑通了再考虑扩展。
2.3 提醒输出层
条件触发了,怎么让你知道?这里的选择很多。最基础的是声音,Windows下一条winsound.Beep就能发出明显的提示音,哪怕你不坐在电脑前也能听到动静。其次是系统通知,可以通过win10toast或plyer库做Toast弹窗,不比正常应用的推送通知差。再进一步是手机端,通过企业微信机器人或者Server酱这类工具,把消息推到微信里,出门在外也能收到盯盘结果。
我把提醒做成了一层层可插拔的组件,默认全开:声音+弹窗,可选手机推送。每个提醒渠道都写在单独的模块里,互不干扰。因为我知道,提醒渠道这种东西今天喜欢弹窗,明天可能就想用钉钉机器人,做成插件化最省事。
这三个环节想清楚之后,后面写代码就是按部就班的事。
3. 免费行情源的选取与容灾设计
行情数据源是整个盯盘软件的地基,这块选不好,其他都是空中楼阁。市面上个人能用的免费方案主要有四类,我这里直接给结论。
3.1 我实际对比过的数据源
| 数据源 | 类型 | 个人免费额度 | 优点 | 缺点 |
|---|---|---|---|---|
| akshare | Python开源库 | 免费 | 接口丰富,股票/基金/期货都能拿,文档完善 | 部分接口依赖第三方网站,可用性波动 |
| tushare | Python库/API | 积分制,基础分有限 | 数据质量好,稳定性高,有社区 | 高积分才能解锁更多接口 |
| 腾讯行情接口 | HTTP接口 | 免费无显式限制 | 速度快,返回字段丰富,代码前缀明确 | 非正式API,随时可能调整 |
| 新浪行情接口 | HTTP接口 | 免费 | 经典老牌,覆盖广 | 偶发返回空值,需要Referer头,限制较多 |
我实际的主力源是akshare,它封装了很多现成接口,比如股票实时行情、历史K线,一个函数就能拿到数据,开发效率很高。但akshare毕竟是个聚合库,底层对接的也是公开网页或接口,盘中偶尔会不稳定。所以我把腾讯行情接口作为热备源,平时不用,一旦akshare拉取异常,自动切换到腾讯接口补位。腾讯接口返回的是GBK编码的文本字符串,速度很快,对个人盯盘来说稳定性反而比很多聚合库更高。
3.2 主备切换和限频策略
主备切换的逻辑不复杂,核心就一句话:每个数据源模块都有统一的get_quote(symbol)入口,主源抛异常或超时就自动降级到备用源。注意这里要做容错,不能因为一次失败就连续切换,否则备用源也会被打爆。我加了一个“熔断”机制:连续3次主源失败才切换备用源,切过去之后每30秒探活一次主源,恢复后自动切回。
# data_source.py 简化思路 import requests import time TIMEOUT = 5 main_fail_count = 0 use_backup = False def get_quote(symbol: str) -> dict: global main_fail_count, use_backup try: if not use_backup: quote = akshare_main(symbol) main_fail_count = 0 return quote else: return tencent_backup(symbol) except Exception: main_fail_count += 1 if main_fail_count >= 3: use_backup = True return tencent_backup(symbol)限频方面,我串行轮询股票池,每只股票之间加time.sleep(0.5)左右,一整轮下来节奏稳定。股票池超过20只的时候,开始考虑用简单的线程池并发拉取,但并发量控制在5个以内,避免瞬间请求过多。
3.3 数据解析的隐藏坑
免费接口最恶心的不是不稳定,而是你不知道它什么时候换个字段名或者改个返回格式。腾讯接口的返回是一串用~分隔的字符串,股票的某个价格可能在第4位,也可能在第6位,不同字段在不同版本里位置还会漂移。我在代码里做了字段长度校验,解析失败就抛异常,让上层走备用源,而不是拿一堆脏数据去算均线。
新浪接口的坑是偶尔会返回空字符串,特别是在集合竞价阶段。所以我在解析层加了兜底:拿不到有效价格就跳过本轮,不报警、不写库,等下一轮再说,避免把空值推进历史表里。
另外单位问题必须统一。有的接口返回“元”,有的返回“分”,有的价格是字符串“10.23”,有的直接给你整数1023。我的建议是,在所有数据源入口处就统一转换成float价格,单位统一为元,后面所有判断都基于这个标准化之后的字典,否则光单位换算就能让你头皮发麻。
4. Windows环境准备:别一上来就装全家桶
很多人在Windows上写这类小工具,容易走入一个误区:一听到要缓存、要存历史,就想着上Docker、装Redis、配Elasticsearch,结果环境搭了两天,核心功能一行没写。我的建议是先搭一个最小可运行环境,跑通全链路,再按需加组件。
4.1 最小环境:Python + venv + Git
Python直接去官网下载安装包,安装时务必勾选“Add Python to PATH”,这样后面在命令行里敲python才有反应。装完之后,在项目根目录执行python -m venv venv创建虚拟环境,然后激活环境:venv\Scripts\activate。为什么要用虚拟环境?因为做Python开发的人总有一天会体会到依赖地狱的痛苦。这个项目要装requests、akshare,那个项目要装flask、pandas,版本稍微冲突一下,系统就乱了。venv就是给每个项目单独隔离一个房间,互不干扰,这是所有Python项目的起点习惯。
Git for Windows装一个,意义不只是代码版本管理,更重要的是能让你随时回退。盯盘脚本改崩了、数据解析写错了,git revert一下就能回到上一个能跑的状态,这种安全感是无价的。
4.2 缓存组件:Redis到底要不要上
很多朋友看到“缓存”两个字第一反应就是Redis。但在做Windows盯盘软件时,我一开始压根没上Redis,用的是Python内存字典加本地SQLite。原因很简单:盯盘软件的单机状态是轻量的,股票池几十只,每5秒快照一次,内存里放最近几轮数据完全没压力,根本不需要单独起一个缓存服务。Redis的好处是多个脚本共享状态、数据持久化更规范,但这不是初期刚需。
如果你的项目确实需要Redis,比如多个进程读写行情、或者要和另一个程序共享实时快照,那我建议用WSL2或Docker Desktop跑Linux版Redis,而不是装那个Windows民间移植版。Windows原生的Redis版本维护不太积极,功能也滞后,用起来容易踩坑。WSL2是Windows官方支持的功能,装好之后在Ubuntu子系统里执行apt install redis-server,一行命令搞定,服务由Windows侧随开机自动启动,体验比原生版顺畅不少。
4.3 历史存储:SQLite足够,ES是非必需的
盯盘软件最需要存的数据有两类:日线收盘价(用来算均线)和分钟级快照(用来复盘)。这两类数据用SQLite一张表就能存得很好。SQLite是文件型数据库,不需要单独的服务进程,Python自带标准库支持,最适合单机小规模项目。
Elasticsearch好不好?好,全文检索、聚合分析都很强,但你要考虑它背后挂着Java环境,ES 8.x至少需要JDK17,安装配置又是一坨事。我见过不少个人项目上来就上ES,最后光内存就吃掉几个G,风扇呼呼转,实际查询性能不比SQLite快多少。所以我的选型原则特别简单:当前功能复杂度用SQLite能解决,就绝不引入外部服务。等哪天数据量到几千万行、需要按时间做复杂聚合检索了,再迁移也不迟。到那时候你也已经知道自己的明确需求是什么了。
5. 核心代码实现:从行情拉到提醒触发
环境准备好,接下来进入正戏。我把整个项目分成几个模块,每个模块功能单一,替换起来也方便。下面这几段代码都是简化后的核心逻辑,但骨架是完整的,你照着能把整个系统搭起来。
5.1 项目结构
win-stock-monitor/ ├── config.json # 股票池和规则配置 ├── requirements.txt # 依赖:requests, akshare等 ├── data_source.py # 行情拉取与主备切换 ├── monitor.py # 主循环与条件判断 ├── notifier.py # 声音/弹窗/推送 └── logs/5.2 配置文件示例
{ "stock_pool": ["sh600000", "sz000001", "sz300750"], "check_interval": 5, "rules": [ {"code": "sh600000", "type": "price_above", "value": 12.5}, {"code": "sz300750", "type": "chg_pct_above", "value": 3.0}, {"code": "sz000001", "type": "price_below", "value": 10.0} ], "notify": { "sound": true, "toast": true, "wecom_webhook": "" } }我习惯把股票池和规则单独放到配置文件里,而不是写死在代码里。这样改股票、改提醒阈值,只需要编辑config.json,不用动代码,也不用重启服务时还要想“上次改到哪了”。代码和配置分离,这也是从正经工程里学来的好习惯。
5.3 行情拉取示例
# data_source.py import requests import re import time TIMEOUT = 5 def akshare_main(symbol: str) -> dict: import akshare as ak # akshare的实时行情接口,symbol需转换 code = symbol[2:] df = ak.stock_zh_a_spot_em() row = df[df["代码"] == code] if row.empty: raise ValueError(f"{symbol} not found") return { "symbol": symbol, "price": float(row.iloc[0]["最新价"]), "yclose": float(row.iloc[0]["昨收"]), "open": float(row.iloc[0]["开盘价"]), "change_pct": float(row.iloc[0]["涨跌幅"]), "volume": float(row.iloc[0]["成交量"]), } def tencent_backup(symbol: str) -> dict: url = f"https://qt.gtimg.cn/q={symbol}" resp = requests.get(url, timeout=TIMEOUT) resp.encoding = "gbk" text = resp.text.strip() m = re.search(r'="(.*)"', text) if not m: raise ValueError(f"unexpected response: {text[:50]}") parts = m.group(1).split("~") if len(parts) < 40: raise ValueError(f"parse error: {text[:50]}") return { "symbol": symbol, "name": parts[1], "price": float(parts[3]), "yclose": float(parts[4]), "open": float(parts[5]), "change_pct": float(parts[32]) if parts[32] else 0.0, "volume": float(parts[6]), }腾讯接口返回的是GBK编码,必须设置resp.encoding = "gbk",不然中文会乱码。字段位置也容易漂移,建议先用你手里的真实返回打出来核对一遍再写死索引。
5.4 主循环与条件判断
# monitor.py 核心片段 import json import logging import time from datetime import datetime from zoneinfo import ZoneInfo from data_source import get_quote from notifier import notify TZ = ZoneInfo("Asia/Shanghai") def is_trading_time() -> bool: now = datetime.now(TZ) if now.weekday() >= 5: return False hm = now.hour * 60 + now.minute return (9*60+15 <= hm <= 11*60+30) or (13*60 <= hm <= 15*60) def main_loop(): config = json.load(open("config.json", encoding="utf-8")) last_notify_map = {} # 防止同一个条件反复通知 while True: try: if not is_trading_time(): time.sleep(60) continue for item in config["stock_pool"]: quote = get_quote(item) for rule in config["rules"]: if rule["code"] != item: continue hit = False if rule["type"] == "price_above" and quote["price"] > rule["value"]: hit = True if rule["type"] == "price_below" and quote["price"] < rule["value"]: hit = True if rule["type"] == "chg_pct_above" and quote["change_pct"] > rule["value"]: hit = True if hit and last_notify_map.get(f"{item}-{rule['type']}") is not True: notify(quote, f"{item} 触发 {rule['type']} {rule['value']}") last_notify_map[f"{item}-{rule['type']}"] = True if not hit: last_notify_map[f"{item}-{rule['type']}"] = False time.sleep(0.5) except Exception as exc: logging.exception(f"main loop error: {exc}") time.sleep(config.get("check_interval", 5)) if __name__ == "__main__": logging.basicConfig(level=logging.INFO) main_loop()这一段里最容易被忽略的是“防重复通知”。如果不加last_notify_map,价格一直在阈值上方,脚本每5秒就会提醒一次,几分钟就能把人逼疯。我的策略是做边沿触发:只有从“未触发”状态跳变到“触发”状态的瞬间才提醒一次;想要再次提醒,必须先回落到阈值以下,再重新触发。
5.5 提醒模块
# notifier.py import winsound import requests def notify(quote, message): if config.get("sound", True): winsound.Beep(880, 400) # 拉高音,400毫秒 if config.get("toast", True): try: from plyer import notification notification.notify( title=f"{quote['symbol']} 盯盘提醒", message=message, timeout=10 ) except Exception: pass webhook = config.get("wecom_webhook") if webhook: requests.post( webhook, json={"msgtype": "text", "text": {"content": message}}, timeout=5 )winsound只在Windows上有,这正好扣住主题。plyer是跨平台的系统通知库,Windows下能弹Toast,非常合适。如果想推手机,企业微信机器人是最省事的渠道,新建一个群,加一个机器人,把Webhook地址填进配置里,手机端就能收到提醒,不需要单独开发App。
6. 把脚本变成“不会消失”的后台任务
脚本写好了,总不能每次都手动打开命令行盯着它跑。而且命令行窗口一旦被误关,或者系统重启,盯盘就中断了。把Python脚本常驻后台,是这整套方案里工程化味道最浓的一块。
6.1 为什么要用服务或计划任务
第一种想法是“我不关窗口就行”,这我试过,不可靠。Windows自动更新会在半夜重启电脑,第二天开盘时脚本根本没起来。就算不重启,一个终端窗口坐在那里也很容易随手关掉。所以必须让系统来管理进程的生命周期,方案有两个:NSSM服务封装,或者计划任务。
6.2 NSSM把Python脚本变成Windows服务
NSSM是一个把任意可执行文件封装成Windows服务的小工具。它负责进程的启动、守护、崩溃重启,比单纯的计划任务更稳。我的做法是先装Python的pythonw.exe,它会静默运行Python脚本,不弹黑窗口。
nssm install StockMonitor "C:\Python311\pythonw.exe" "C:\win-stock-monitor\monitor.py" nssm set StockMonitor AppDirectory "C:\win-stock-monitor" nssm set StockMonitor AppStdout "C:\win-stock-monitor\logs\service.log" nssm set StockMonitor AppStderr "C:\win-stock-monitor\logs\service.err.log" nssm start StockMonitorpythonw.exe运行时不带控制台窗口,非常适合做后台服务。注意AppStdout和AppStderr一定要设置,否则脚本里的print和异常输出会被吞掉,出问题完全无从排查。
6.3 计划任务的轻量替代方案
如果你不想引入NSSM,用Windows自带的任务计划程序也能实现开机自启。创建一个任务,触发器选“计算机启动时”,操作选“启动程序”,程序填pythonw.exe,参数填脚本路径。再补一个触发器,每30分钟重复一次,这样即使进程异常退出,任务计划也能在下次触发时尝试拉起。
用命令行创建计划任务也很方便:
schtasks /Create /TN "StockMonitor" /TR "C:\Python311\pythonw.exe C:\win-stock-monitor\monitor.py" /SC ONSTART /RU SYSTEM /RL HIGHEST需要注意,/RU SYSTEM表示以系统账户运行,脚本里写日志文件时要保证该路径对系统账户可写,不要放在需要登录才能访问的加密盘上。
6.4 日志与闪退排查思路
后台运行的脚本,最怕的就是它无声无息地死掉。所以日志必须从一开始就规范化。我在monitor.py里直接用logging模块,同时还加了RotatingFileHandler,让日志按大小自动轮转,不会几个月下来撑爆磁盘。
import logging from logging.handlers import RotatingFileHandler handler = RotatingFileHandler("logs/monitor.log", maxBytes=5*1024*1024, backupCount=5) logging.basicConfig(level=logging.INFO, handlers=[handler])如果你发现脚本“闪退”,第一反应不要瞎猜,去查日志。常见的闪退原因包括:代码里直接print但环境没有控制台导致编码报错、依赖库没装进当前虚拟环境、路径写死了不存在的目录。把AppStdout和AppStderr指到日志文件,异常信息就会原原本本记录下来。我曾经被一个“脚本一运行就消失”的问题折腾了一下午,最后看日志才发现是zoneinfo在旧版Python里没有时区数据,直接把启动校验换成pytz才解决。这提醒我了:后台服务环境下,任何“看起来正常”的异常都不能放过。
7. 我踩过的Windows特有坑
如果说前面是搭房子的过程,那这一章就是装修时踩到钉子后的处理经验。Windows下跑这类脚本,有一些非常典型的坑,几乎每次都会遇到。
7.1 控制台中文乱码
Windows控制台默认编码是GBK,而Python 3里字符串是Unicode,print中文时一旦遇到特殊字符就报UnicodeEncodeError。我刚开始跑的时候,行情名称一打印就乱码,日志文件里全是问号。
解决办法是在脚本入口设置环境变量或者重配标准输出:
import sys sys.stdout.reconfigure(encoding="utf-8")或者在运行时设置系统环境变量PYTHONIOENCODING=utf-8。如果你把脚本封装成NSSM服务,就格外需要这个,因为pythonw.exe模式下控制台不存在,编码问题更隐蔽。
7.2 交易时段判断的时区坑
这是最容易出问题的逻辑。最初我用的是datetime.now(),等Windows系统时区被改到其他区域之后,脚本的“交易时段”判断整个乱了套。更隐蔽的是,有些机器虽然时区是东八区,但启用了“自动调整夏令时”之类的设置,结果导致时间偏差一小时。
正确做法是用zoneinfo强制指定亚洲上海时区:
from datetime import datetime from zoneinfo import ZoneInfo TZ = ZoneInfo("Asia/Shanghai") now = datetime.now(TZ)这样不管本机时区设成什么,脚本判断的都一定是北京时间。胜过我之前用“本地系统时间手动偏移”的笨办法。
7.3 数据源返回格式变动
股票数据解析的坑在前面已经提过,但Windows环境下还有一个特有现象:requests库受系统代理设置影响很大。很多Windows机器上装着安全软件或者代理工具,它们会在系统级别设置代理环境变量,requests默认会读取这些代理,然后访问行情接口时要么超时,要么被拦截。
解决方法是显式关闭代理信任:
session = requests.Session() session.trust_env = False这样requests就不会理会系统代理环境变量,直接走本机网络。如果你的网络环境本身需要代理才能出去,那就改成显式设置proxies参数,哪个IP能通就用哪个,别让它自动瞎猜。
7.4 Windows更新重启导致的中断
Windows半夜自动重启更新,是后台常驻任务最怕的事。有人选择干脆关闭自动更新,我其实不太赞成长期关闭,安全补丁还是很重要的。更好用的方式是把计划任务做成开机触发加周期触发双保险,或者用NSSM的服务模式——服务默认就带恢复选项,进程挂了系统会自动拉起。
还有一个容易被忽略的点:如果装了杀毒软件,它可能把pythonw.exe的后台运行当作可疑行为拦截。遇到这种情况,最稳妥的方式是把脚本目录加入杀毒软件的白名单,不然你排查半天以为是代码问题,其实进程根本没被允许运行。
8. 进阶玩法:把盯盘数据变成自己的历史资产
脚本稳定跑起来之后,我开始琢磨怎么把每天的行情数据沉淀下来。一开始只是想复盘当天的操作,结果做着做着,发现这套东西的价值远超“盯盘提醒”本身。
8.1 用SQLite留存分钟级快照
我在monitor.py里加了一个轻量写入逻辑:每轮拉取行情后,往SQLite里写一条快照记录,包含时间戳、股票代码、价格、涨跌幅、成交量。表结构就这么简单,一个月下来数据量也就几万行,SQLite毫无压力。
有了这张表,复盘就很方便了。我可以查“那天下午两点半,这只股票的价格到底是多少”,可以回放整个交易日的价格走势,甚至可以统计某个时间段内价格触及阈值但没触发提醒的情况,用来反推规则设置得是否合理。这个能力,现成软件很少会给你。
8.2 上Redis的场景是什么
当你的脚本从一个变成两个,或者想在Web面板里看实时状态时,Python进程内存里的数据就不好共享了。这时候Redis作为“共享状态层”的价值才显现出来:一个进程写快照,另一个进程读状态,Web页面也能直接拉取实时价格。用WSL2跑一个Redis,配置不在话下。但一定要记住:Redis是为解决“多进程状态共享”问题服务的,不是为了“显得专业”而上的。不要为了用而用。
8.3 加一个局域网Web面板
写一个简单的Flask面板,展示当前股票池里每只股票的实时状态,谁触发了提醒,谁刚回落到安全区间,手机上打开浏览器就能看。代码量不大,几十行就够。好处是盯盘提醒从“单机声音”变成了“可以随时瞄一眼的状态页”。如果还想更进一步,可以把面板部署到NAS或者云服务器,人在外面也能看。
做Web面板的时候我特别建议把轮询数据和判断逻辑完全分开。Web面板只负责展示数据库或Redis里的数据,不直接拉行情,否则会引入额外的请求频率,打破之前辛苦维持的限频平衡。
8.4 更复杂的组合规则怎么扩展
现在的规则还是“价格大于某值”“涨跌幅超过某值”这种单条件。要扩展成“涨跌幅超过3%且成交量大于10日均量的1.5倍”,核心思路差不多:多拉两个历史字段,判断条件写成一个rules列表,每个规则项是一个字典,把全部条件都放进去。这条路径走深了,其实就是在写一个微型规则引擎。
不过我给自己的忠告是,别一开始就追求大而全的规则系统。先用硬编码的五六条规则跑一段时间,观察哪些条件确实能帮你减少盯盘时间,再慢慢沉淀成配置化规则。盯盘软件的真正价值不是“自动化一切”,而是把重复劳动交给机器,把自己从屏幕前解放出来。
我实际跑下来大半年,最深的体会是:稳定性永远比功能多寡重要。一套能连续运行几个星期不出错的简单脚本,远胜一个功能丰富但三天两头崩的系统。所以我建议所有想尝试的朋友,先从最简链路跑通开始:一个数据源、三条规则、声音提醒、NSSM常驻。等它稳如老狗了,再去折腾数据库、Web面板、组合条件这些花活。
最后再分享一个我自己的使用习惯:整个项目用Git管理,config.json和代码分开提交,每次改规则都留个commit记录。换电脑或者重装系统之后,clone下来装好依赖,几分钟就能恢复整套盯盘环境。这个习惯帮我省过不少麻烦,也让我在这套“私人盯盘系统”上越用越顺手。