没有运维的交易系统,是怎么自己活下去的
一个人维护的无人值守模拟盘集群,几个月横向运维复盘。
本文是「量化实操」系列第 3 篇,聚焦集群层面的系统性风险 :单个 bug 好修,但怎么让系统在出问题的第一时间自己喊出来?三个真实事故 + 三套集群级修法。
一、那个周一早晨
2026 年某个周一早上 9 点,我端着咖啡打开监控看板,以为又是平常的一天。
10 个模拟盘的净值曲线安静地躺在屏幕上,看起来岁月静好。
然后我开始挨个点开详情页,笑容一点点凝固:
- 转债盘:上周五"暴跌" 15.7%------但转债市场根本没这么大动静
- 美元指数:最新数据停在 7 天前------我整整一周在用上周的汇率做决策
- 某商品盘:一整个周六空跑了 5 个小时------而那天根本不是交易日
那一刻我才意识到一件事:
这些事故不是偶发,是集群在报警。
假崩盘、数据断更、空跑 5 小时------表面看是三个独立的 bug,实际上是同一类问题的三次暴露:这个集群没有任何一层在主动告诉我"哪里不对劲",所有问题都是等我自己撞上去才发现的。
上一篇我讲了单个事故怎么修(改一行估值代码就完事)。但修完一个 bug,下一个类似的 bug 还会在另一个角落冒出来------除非在集群层面换一种思路:让系统在出问题的第一时间自己喊出来,而不是等我去巡检。
这篇文章想聊的是:一个没人帮忙、没团队、没 SRE 的散户量化工程师,是怎么把这套"出问题才会被发现"的集群,改造成"自己会长嘴报警"的集群的------以及这一年我交了多少学费。
二、集群长什么样
先交代一下战场。我的量化系统大致长这样:
几个关键决策:
| 决策 | 为什么 |
|---|---|
| 唯一入口 | 所有任务都挂在 cron 调度器下,没有任何脚本允许自己起 cron------散乱 cron 是运维灾难的源头 |
| 统一日报引擎 | 不管哪个盘,日报都从同一个模板渲染,字段口径强一致 |
| 生产 vs 研究物理隔离 | research/ 目录随便造,生产 scripts/ 一个文件都不许动 |
| Dashboard 只读 | 看板就是看的,不允许反向操作账户 |
这套架构看起来挺美。但架不住细节处处是坑。
三、三个最有反差感的事故
一年下来踩的坑能写满一张 A4 纸。今天只讲 3 个------它们分别代表估值、数据、业务逻辑三类最典型的陷阱,反差感最强。
事故 ①:转债盘一夜"崩盘" 15.7%,我差点清仓
这个故事的完整版在第 1 篇《那只"暴跌15.7%"的可转债,根本没跌》。这里只讲集群视角的教训。
那个画面:周五晚上 11 点,手机告警------转债盘单日 -15.7%。我脑子里瞬间过了一遍"系统性风险?赎回挤兑?信用事件?"
打开详情页:某只持仓券单日 -99%,把整个盘拖下水。
真相 :那只券停牌 了。但我的熔断估值逻辑里写的是"取不到交易价就按 mark_tradeable 字段过滤",而 mark_tradeable=0 的券被我直接按 0 元估值。一只停牌券被我记成了"清零"。
集群视角的教训:
这个 bug 之所以吓人,不是因为它难修(改一行 get_last_valid_price 就完事),而是因为它暴露了整个集群的盲区 ------单券单日 ±50% 的波动,居然没有任何一层拦得住。
修复完估值逻辑之后,我紧接着做的是:在日报渲染层加一条哨兵------任何券单日波动超过 ±50%,日报强制标红 + 阻断推送,等人工复核。
这才是"集群级别"的修法:不只修当前 bug,而是在链路的关键节点插一根针,下次同类问题第一时间响。
事故 ②:美元指数静默断更一周
那个画面:某天下午例行看盘,顺手点开美元指数------报价定格在某个数,一动不动。
等等,昨天好像也是这个数?前天呢?
往前一拉,DXY 已经整整一周没更新了。而我好几个策略(商品的、汇率的),每天都在拿这个"上周的价格"做决策。
根因:
DXY 数据源是 FRED(美联储经济数据库)。API key 失效,抓取脚本返回空,但下游任务看到"表里有数据"就继续跑。没有任何一个环节告警"这数据一周没变了"。
这类问题有多普遍?
后来我专门排查了一遍,发现库里有好几个指标都有类似风险------宏观数据(社融/M2/房价/CPI)的部分免费接口(akshare 东财/新浪)滞后数周到数月不等,而策略里这些指标都参与计算。你以为在跟踪宏观,其实在看去年。
集群视角的修法:
python
# 新鲜度哨兵:超过 N 天没更新就告警
def check_freshness(table: str, date_col: str, max_lag_days: int):
latest = query(f"SELECT MAX({date_col}) FROM {table}")
lag = (today() - latest).days
if lag > max_lag_days:
alert(f"🔴 {table} 已 {lag} 天未更新(阈值 {max_lag_days} 天)")
- 给所有外部数据源加"新鲜度哨兵":超过 N 天没更新就告警(N 按指标特性设,日频 1 天、月频 35 天)
- "数据是否新鲜"和"数据是否存在"分开监控------前者看日期,后者看行数
- 宏观三指标(M2-CPI/社融/房价)主源切到付费的 dataPro,akshare 作备------多源全跑选日期最新者,任何单源停更都不会让数据变旧
一句话:
数据库里有数据 ≠ 数据是新的。静默断更是宏观策略的隐形杀手。
事故 ③:止损字段名错配,失效了一个月才发现
那个画面 :9 月底月度复盘,我随手翻止损触发记录------整个 9 月,一次止损都没触发过。
但 9 月明明有两波像样的回调。我赶紧去查止损代码:
python
if current_price < position.cost_price * (1 - stop_loss_pct):
trigger_stop_loss()
看起来没问题。再翻账本表结构------持仓表里没有 cost_price 字段,只有 avg_cost。
代码读 cost_price,永远拿到 None,条件永远不成立。止损静默失效了一个月。
根因:
getattr(obj, 'cost_price', None) 不会报错,只返回 None。None < number 在 Python3 里会抛 TypeError------但我的 try/except 把它吞了。
这类问题有多阴险?
字段错配不会报错,只会让功能静默失效。和崩溃完全两回事------崩溃你马上知道,静默失效你可能一年都发现不了。后来我又排查出几处类似隐患:
- 某类有"强制退出"机制的品种,其状态快照只取
MAX(snapshot_date),已退出标的没最新价就挂在账上永远退不出 - 兜底数据源挂了,默认值 PE/PB=100 静默填进因子库,30% 的股票(主要是小盘)在用着假数据
集群视角的修法:
sql
-- L4 层哨兵:cb holdings must have positive cost_price
SELECT COUNT(*) FROM cb_holdings WHERE cost_price IS NULL OR cost_price <= 0;
-- 结果 > 0 立刻告警
python
# 关键 try/except 不吞 TypeError、KeyError------该崩就让它崩,崩了才知道
try:
trigger_stop_loss(price, cost)
except (TypeError, KeyError) as e:
alert(f"🔴 止损逻辑异常: {e}")
raise # 不吞,继续抛
sql
-- 多实体场景禁用 MAX(date),改成 ROW_NUMBER() PARTITION BY entity
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY bond_code ORDER BY snapshot_date DESC) AS rn
FROM cb_redeem_snapshot
) WHERE rn = 1;
一句话:
字段错配是量化系统最阴险的 bug------它不报错,只是静默失效。
四、时间铁律 · 一句话带过
还有一类坑和"时间"有关,不展开,但值得你记下来:
| 铁律 | 教训 |
|---|---|
| 开盘前必查交易日历 | 不凭"周几+几点"推断------我曾经周一冷启动把所有数据更新跳过,也曾经让商品策略在周六空跑 5 小时 |
| "是不是交易日"和"数据是不是新的"是两个独立判断 | 千万别合并------否则数据滞后会被误判成"今天不开盘" |
| guard clause 永远前移 | 把"今天是不是交易日"判断放在数据拉取前,5 小时能省到 0.9 秒 |
五、这一年我学到的三件事
① 先盖监控,再上策略。
策略可以慢慢调,监控必须第一天就上。没有监控的策略,等于闭着眼睛开车。
② 默认值和静默兜底是最危险的敌人。
getattr(obj,'x',None)、MAX(date)、fillna(0)------看似无害,都是估值的隐形炸弹。永远显式,永远不要依赖默认。
③ 每修一个 bug,都问一句"下次怎么第一时间抓到"。
修完不要走,把答案写成代码插进链路------这样每个 bug 都会变成一层哨兵,系统越踩越稳。
六、收尾 · 下一篇见
这篇文章讲的 3 个事故,每一个都逼着我在集群里加了一根哨兵。一年下来,这些哨兵凑在一起,变成了一套九层验证体系------从数据采集、落库、口径、业务规则、估值、信号、执行、组合到全局,每一层独立监控,任何一层挂掉都会立刻响。
下一篇我会专门写这套九层体系是怎么搭起来的------不是大厂专利,散户用 cron + duckdb + 一个 QQ 机器人就够。
如果你也在自己搭量化系统,希望这篇文章能帮你少踩一两个坑。
作者:长坡夜行 一个在市场里慢慢爬的散户量化工程师 持续分享量化工程实战、模拟盘运维、策略研究方法论