没有运维的交易系统,是怎么自己活下去的

没有运维的交易系统,是怎么自己活下去的

一个人维护的无人值守模拟盘集群,几个月横向运维复盘。

本文是「量化实操」系列第 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 机器人就够。

如果你也在自己搭量化系统,希望这篇文章能帮你少踩一两个坑。


作者:长坡夜行 一个在市场里慢慢爬的散户量化工程师 持续分享量化工程实战、模拟盘运维、策略研究方法论

相关推荐
云和数据.ChenGuang1 小时前
python大小整数范围解析
java·开发语言·python
cpolar技术支持1 小时前
端口被谁占了、这台机上开了哪些监听?本机起一个端口占用只读清单,cpolar 给异地同事远程看谁在监听
运维·python·内网穿透·cpolar·端口占用
WeiXin_DZbishe2 小时前
基于springboot装修风格选择系统的设计与开发-计算机毕设【课程设计】77964
java·vue.js·spring boot·后端·python·php·课程设计
XZ-0700012 小时前
week9-三元表达式
java·前端·python
峰哥AI手记2 小时前
金融合规问答助手-RAG查询路由落地
python·aigc
benchmark_cc2 小时前
量化项目启动时,应该先设计数据接口还是先编写策略?从系统架构角度做出正确选择
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
CaptainT2 小时前
第 4 卷:面向对象、数据建模与类型系统
python
2601_962885722 小时前
如何用 Python 判断一只股票“追涨”还是“抄底”更有效?
android·开发语言·python
用户7783366132112 小时前
选 SERP API 别靠感觉:用 SerpBase 实测的评估脚本与判定标准
python·api