实盘运维是把策略「养活」的过程。研究和回测解决的是「能不能赚钱」,运维解决的是「能不能持续赚钱、出问题能不能及时发现」。很多团队在策略研究上投入大量精力,却在运维上几乎裸奔,结果是策略明明有效,却因为一次未监控的故障亏掉了几个月的收益。
真正难的地方在于区分「正常波动」和「真实故障」。策略回撤 5% 可能是正常波动,也可能是模型失效的前兆;行情延迟 100 毫秒可能是网络抖动,也可能是通道故障的早期信号。运维系统的价值就是在噪声中识别出真正的异常。
本文按「上线 → 监控 → 告警 → 处置 → 高可用 → 复盘 → 衰减 → 应急」的顺序展开。账务层面的核对在 清结算与对账体系 中讨论,本文聚焦运行期的运维。
目录
- 上线流程:从仿真盘到实盘
- 灰度与资金递增
- 策略健康度监控
- 资金曲线与回撤告警
- 盘中异常处置
- 故障切换与高可用
- 日终复盘与归因
- 策略衰减与下线决策
- 值班与应急预案
1. 上线流程:从仿真盘到实盘
策略上线的四道关卡,每道都有明确的通过标准:
| 关卡 | 时长 | 通过标准 |
|---|---|---|
| 回测 | 天级 | 样本外年化 > 15%,Sharpe > 1.5 |
| 仿真盘 | 20 交易日 | 与回测日收益相关性 > 0.8 |
| 小资金实盘 | 1 个月 | 滑点与仿真偏差 < 20% |
| 逐步加仓 | 2 周/档 | 每档观察期无异常 |
仿真盘(paper trading)不能省。它暴露的问题回测永远覆盖不到:
- 柜台拒单(资金不足、涨跌停、非交易时段)
- 部分成交与撤单竞态
- 行情丢包与重连
- 订单状态机未覆盖的状态
- 报单延迟的真实分布
仿真盘的实现方式是用真实行情驱动真实 OMS,但不下真单(或下模拟单)。它与回测的最大区别是实时性:回测可以加速,仿真盘必须按真实时间跑,因此能暴露所有时序相关的问题。
class PaperTradingOMS(RealOMS):
def send_order(self, order):
fill = self.simulator.match(order, self.current_tick) # 不下真单,本地撮合
self.on_exec_report(fill) # 走真实的回报处理路径
return order.id
2. 灰度与资金递增
策略上线不是「全量开启」,而是「小资金验证 + 逐步加仓」:
第 1 档:10% 目标资金,观察 2 周
第 2 档:25%,观察 2 周
第 3 档:50%,观察 2 周
第 4 档:100%
每一档的观察指标:
def can_promote(stats, target_vol):
checks = {
'sharpe': stats.sharpe > 1.0, # 风险调整收益达标
'max_dd': stats.max_drawdown < 0.10, # 回撤可控
'slippage': stats.slippage_bps < 15, # 滑点在预期内
'fill_rate': stats.fill_rate > 0.95, # 成交率正常
'reject_rate': stats.reject_rate < 0.02, # 拒单率低
}
return all(checks.values()), checks
降档的规则必须同样明确:如果实盘回撤超过回测最大回撤的 1.5 倍,自动降到上一档。这个机制比「人工判断」可靠得多,因为人在亏损时容易抱有侥幸心理。
3. 策略健康度监控
策略监控不能只看盈亏,要监控一整组指标:
| 指标 | 含义 | 异常阈值 |
|---|---|---|
| 信号数量 | 每周期产生的信号数 | 偏离历史均值 ±50% |
| 成交率 | 成交单/报单 | < 90% |
| 滑点 | 实际成交价 vs 决策价 | > 20 bp |
| 持仓偏离 | 实际 vs 目标持仓 | > 5% |
| 行情延迟 | tick 到达延迟 | P99 > 500 ms |
| 报单延迟 | 报单到确认 | P99 > 100 ms |
| 通道状态 | 连接是否正常 | 断连即告警 |
信号数量是最灵敏的预警指标。如果某天信号数量骤降 80%,可能是数据管道断了、也可能是因子计算出错。它比盈亏更早发现问题,因为盈亏有滞后性。
def check_signal_health(today_count, history):
mean = np.mean(history[-20:])
std = np.std(history[-20:])
z = (today_count - mean) / std if std > 0 else 0
if abs(z) > 3:
return f"信号数量异常:今日 {today_count},历史均值 {mean:.0f},z={z:.1f}"
return None
监控数据的采集与可视化需要一套覆盖指标、日志、链路三者的可观测性栈,且要保证交易时段的采集本身不成为性能瓶颈。
4. 资金曲线与回撤告警
资金曲线是最直观的监控对象,但要区分几个层次:
账户净值 = 总资产(含浮盈浮亏)
已实现净值 = 只含已平仓盈亏(更平滑)
超额净值 = 相对基准的表现(策略真实能力)
回撤告警要分级,并且要基于回测的统计特征而不是拍脑袋:
def drawdown_alert(current_dd, backtest_max_dd): # 以回测最大回撤为基准
if current_dd > backtest_max_dd * 2.0:
return 'CRITICAL' # 严重偏离,可能模型失效
if current_dd > backtest_max_dd * 1.5:
return 'WARNING' # 显著偏离,降档观察
if current_dd > backtest_max_dd * 1.2:
return 'NOTICE' # 轻微偏离,留意
return None
回撤告警必须带上下文,否则会淹没在噪声里:
当前回撤:6.2%
回测最大回撤:8.0%(P95 分位 5.1%)
持续天数:8 个交易日
历史相似回撤的平均恢复天数:12 天
当前是否超出 95% 置信区间:否
有了这些上下文,值班人员才能判断是否需要干预。只报一个「回撤 6.2%」的数字,接收者无法决策。
5. 盘中异常处置
盘中异常的处置必须有预案、可执行,不能临场发挥。常见异常与对应动作:
| 异常 | 现象 | 处置 |
|---|---|---|
| 行情中断 | 长时间无 tick | 停止开仓,等待恢复 |
| 报单超时 | 报单无回报 | 主动查询订单状态 |
| 持仓偏离 | 实际与目标差异大 | 暂停策略,人工确认 |
| 亏损超限 | 单日亏损触发硬限 | 熔断,人工介入 |
| 通道断开 | 连接状态异常 | 切换备用通道 |
| 策略异常 | 报单频率暴增 | 立即停止策略 |
处置动作要分级且可逆:
class IncidentHandler:
def on_market_data_stale(self, age_ms):
if age_ms > 30000:
self.emergency_stop('行情中断超 30 秒') # 全停
elif age_ms > 5000:
self.pause_new_positions('行情延迟超 5 秒') # 只停开仓
def emergency_stop(self, reason):
self.cancel_all_orders() # 撤所有挂单
self.disable_strategy() # 停止策略
self.alert('CRITICAL', reason) # 告警
「停止开仓但允许平仓」是最常用的中间档。它能在行情异常时控制风险,又不至于让持仓失去管理。
6. 故障切换与高可用
交易系统的高可用有两个层次:进程级和通道级。
| 层次 | 故障 | 切换方式 | 目标 RTO |
|---|---|---|---|
| 进程级 | 策略进程崩溃 | 主备进程 + 心跳 | < 5 秒 |
| 通道级 | 报单通道断开 | 主备通道 | < 10 秒 |
| 机房级 | 托管机房故障 | 异地切换 | 分钟级 |
主备进程的状态同步是关键。备用进程必须实时同步主进程的订单状态,否则切换后会重复下单:
class StandbySync:
def __init__(self, standby):
self.standby = standby
def on_state_change(self, event):
self.standby.apply(event) # 每个状态变更都同步到备用进程
def heartbeat(self):
self.standby.last_heartbeat = time.time() # 超时则由备用进程接管
切换的幂等性至关重要:切换后第一件事是查询所有未完成订单的真实状态,而不是直接用本地缓存的状态继续。这与 分布式幂等与可靠性 中的恢复逻辑一致。
切换必须演练。没有演练过的切换流程,在真实故障时大概率会失败(备用进程配置过期、切换脚本路径错误、权限不足)。
7. 日终复盘与归因
日终复盘回答三个问题:赚在哪、亏在哪、与预期差多少。
def daily_review(actual, expected):
return {
'pnl_actual': actual.pnl,
'pnl_expected': expected.pnl, # 按模型预期
'deviation': actual.pnl - expected.pnl,
'by_symbol': group_pnl(actual.trades, 'symbol'), # 分标的
'by_factor': group_pnl(actual.trades, 'factor'), # 分因子
'by_hour': group_pnl(actual.trades, 'hour'), # 分时段
'cost': {
'commission': actual.commission,
'slippage': actual.slippage,
'impact': actual.impact,
},
}
成本归因是复盘的必备项。如果某天亏损主要来自滑点,说明执行有问题(可能是拆单过粗或流动性判断错误),而不是策略有问题。区分这两者决定了修复方向。
| 归因项 | 含义 | 修复方向 |
|---|---|---|
| Alpha 亏损 | 选股/择时失效 | 策略研究 |
| 成本超支 | 滑点/手续费超预期 | 执行优化 |
| 偏离亏损 | 未按信号执行 | 运维排查 |
| 暴露亏损 | 风格暴露反向 | 组合中性化 |
8. 策略衰减与下线决策
所有策略都会衰减。原因有三:市场结构变化、竞争者涌入、因子被套利掉。识别衰减并及时下线,比研究新策略同样重要。
衰减的信号:
1. 滚动 60 日 Sharpe 持续低于 0.5(回测为 1.5)
2. IC 均值连续 3 个月下降
3. 策略容量下降(同样的资金,滑点上升)
4. 与同类的相关性上升(说明在承担相同的风险)
5. 因子暴露漂移(实际持仓的风格暴露偏离目标)
def decay_score(stats):
score = 0
if stats.rolling_sharpe_60d < 0.5:
score += 2
if stats.ic_trend_3m < 0:
score += 2
if stats.slippage_trend > 0.3: # 滑点上升 30%
score += 1
if stats.style_drift > 0.2: # 风格漂移
score += 1
return score # >= 4 建议下线;>= 3 降档观察
下线的决策要果断。很多团队在策略已经明显失效后仍继续运行,理由是「等它恢复」。这个心理陷阱的代价是持续亏损。预设的规则是:衰减分数达到阈值,自动降档;连续两周未改善,下线。
9. 值班与应急预案
实盘运维需要值班机制,尤其是在极端行情下。值班的核心是明确的升级路径:
一级(自动化):熔断、停止开仓、切换通道 —— 无需人工
二级(值班工程师):确认异常、执行预案 —— 5 分钟内响应
三级(策略负责人):决策是否下线策略 —— 15 分钟内响应
四级(管理层):重大损失的处理决策 —— 30 分钟内响应
应急预案必须书面化且定期演练:
场景 1:行情中断超过 30 秒
动作:暂停开仓 → 确认行情源状态 → 切换备用源 → 恢复
负责人:值班工程师
场景 2:策略异常报单(频率暴增)
动作:立即停止策略 → 撤销所有挂单 → 排查代码/数据 → 恢复
负责人:值班工程师 + 策略负责人
场景 3:单日亏损超过硬限
动作:自动熔断 → 通知负责人 → 人工决策是否继续
负责人:策略负责人
告警的通道要分级且冗余:一般告警走 IM,严重告警走短信 + 电话。告警通道本身也要有监控(心跳检测),否则告警系统挂了都不知道。相关实践参考 告警设计与事故响应 。
权衡取舍
| 维度 | 保守运维 | 激进运维 |
|---|---|---|
| 资金递增 | 慢(月级) | 快(周级) |
| 熔断阈值 | 低(易触发) | 高(少干扰) |
| 人工干预 | 频繁 | 少 |
| 收益速度 | 慢 | 快 |
| 事故风险 | 低 | 高 |
保守与激进的取舍取决于资金属性和团队能力。自有资金可以激进,客户资金必须保守;团队有成熟的自动化处置能力可以激进,依赖人工的必须保守。
监控粒度的取舍同样明显:监控太粗会漏掉问题,太细会被噪声淹没。判断标准是告警的可执行性——如果一个告警发出后值班人员不知道该做什么,这个告警就不该存在。
常见坑清单
- 跳过仿真盘直接实盘:柜台拒单、部分成交等问题只能在仿真盘暴露。
- 一次全量上线:没有灰度,出问题时损失是全量的。
- 只监控盈亏:信号数量、成交率、延迟等指标更早预警。
- 回撤告警无上下文:只报数字,接收者无法判断是否需要干预。
- 切换未演练:真实故障时备用进程配置过期、脚本失败。
- 切换后直接用本地状态:未查询订单真实状态,可能重复下单。
- 成本不归因:分不清是策略问题还是执行问题,修复方向错误。
- 策略衰减无规则:凭感觉决定是否下线,往往下得太晚。
- 告警通道单一:IM 挂了告警就发不出,严重事故必须走电话。
- 应急预案只写在文档里:从未演练,真实事故时无人知道流程。
小结
实盘运维的核心是把「人的判断」尽可能变成「系统的规则」。什么时候降档、什么时候熔断、什么时候下线,都应该有明确的量化标准。人的判断在亏损时会被情绪影响,而规则不会。
判断运维体系是否成熟,最快的检验是故障演练:随机制造行情中断、通道断开、进程崩溃等场景,看系统能否自动处置、告警能否触达、恢复流程是否顺畅。演练中暴露的问题,比真实事故中暴露的便宜得多。
下一步建议阅读 交易合规与监管要求 ,看运维过程中产生的日志与数据如何满足监管要求。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。