4.3.2.1 日常监控:上线只是开始

上线那天你最自信。三个月后你最慌。中间那段叫"没人看"。

策略上线了。

你发了个朋友圈。

"量化交易,启动。"

一堆人点赞。

你飘了。

然后------

第一周,每天看八遍。

第二周,每天看两遍。

第三周------

不看了。

一个月后。

策略悄悄吃了个跌停。

你都不知道。

等到发现的时候------

已经亏了六位数。

操。

这就是99%的量化新手的死法。

不是策略不行。

没人看着

策略上线只是开始。

监控,才是日常。

一、监控的三个维度

很多人以为监控就是------

每天看看赚了多少。

太浅了。

监控有三个维度。

缺一个,迟早出事。

第一维度:策略层面

你最关心的。

每日收益。

持仓情况。

交易信号。

最大回撤。

夏普比率。

这些数字------

每天必须看。

不是每周。

不是每月。

每天。

为什么?

因为策略会"漂移"。

什么意思?

你回测的时候------

策略在沪深300上表现牛逼。

年化30%。

跑了一个月------

沪深300风格变了。

策略开始频繁止损。

胜率从65%掉到45%。

你没发现。

两个月后------

账户亏了15%。

你品品。

策略还是那个策略。

代码没改一行。

但市场变了。

策略和市场不匹配了。

这就是"策略漂移"。

不监控,你永远不知道。

具体看什么?

每日收益。

别光看绝对数。

要看------跟回测的日均收益比,偏离了多少。

偏离超过2倍标准差,就该警觉了。

持仓集中度。

你的策略是分散持仓的。

突然某天------

单只股票仓位占了40%。

出bug了。

信号频率。

策略一天发2个信号,正常。

突然一天发了20个信号------

不是市场炸了,是代码炸了。

策略不会背叛你,但市场会改变它的性格。监控,就是听它有没有变声。

第二维度:系统层面

策略层面看的是"赚没赚"。

系统层面看的是"跑没跑"。

网络延迟。

订单状态。

错误日志。

进程存活。

这些东西------

你看不到钱。

但一旦出问题------

钱就没了。

真实案例。

有个哥们,策略跑得挺好。

QMT客户端挂在服务器上。

每天自动生成信号、自动下单。

三个月后的一天------

服务器网卡掉了。

QMT断线了。

策略还在跑。

信号还在发。

但订单------

一个都送不出去。

那天正好有个大涨的票。

策略发了买入信号。

订单没出去。

等网络恢复了------

票已经涨停了。

少赚了八万。

更要命的是------

策略以为订单成交了。

持仓记录里多了这个票。

但实际上------

没买到。

后面的逻辑全乱了。

这就是系统层面的问题。

不监控,就是裸奔。

具体看什么?

进程存活。

你的策略脚本是不是还在跑。

别笑。

Python脚本崩溃是家常便饭。

内存泄漏。

网络超时。

一个没捕获的异常------

进程就死了。

订单状态。

每一笔订单------

报了吗?

成了吗?

部分成交了吗?

撤了吗?

这些状态必须实时记录。

错误日志。

每一个warning。

每一个error。

都要留痕。

平时不看。

出事了------

翻日志。

没有日志------

你就是个瞎子。

代码不会无缘无故地坏。但它会悄悄地坏。错误日志就是它的遗书。

第三维度:数据层面

最容易被忽视的。

数据更新了吗?

数据质量怎么样?

有没有异常值?

量化策略的燃料------

就是数据。

数据出了问题------

策略就是个吃错药的疯子。

案例。

2024年某个周五。

某数据源的系统升级。

收盘数据延迟了4个小时。

某量化团队的策略------

6点钟开始跑。

拿到的收盘价------

昨天的

策略一看------

哎?这个票今天没动?

信号没了。

但实际呢?

这个票今天跌了5%。

策略基于昨天的数据做了判断。

第二天开盘------

直接亏了。

更离谱的------

数据源偶尔会返回负数价格

某只股票的价格字段:-3.5。

你没做校验。

策略直接买了。

因为------

-3.5的PE。

"太便宜了"。

不用我说了吧。

具体监控什么?

数据更新时间。

每天收盘后------

数据在几点更新的?

超过18:00还没更新,就该打电话了。

数据完整性。

5000只股票。

今天只更新了4800只。

少了200只。

那200只------

你的策略要不要交易?

异常值检测。

价格 > 昨日收盘价 × 1.2?(非涨停板的情况下)

成交量 = 0?(停牌但标记为正常交易?)

这些都要自动检查。

垃圾进,垃圾出。你的策略再聪明,喂它屎,它就拉屎。

二、告警阈值设置:让系统替你盯着

人靠不住。

你会忘。

你会懒。

你会觉得"今天不看了,应该没事"。

"应该没事"这四个字------是亏钱的开始。

所以------

要让系统替你盯着。

出事了------

自动报警。

怎么写?

完整代码,能跑:

// python

pip安装依赖:

pip install pandas numpy requests

import pandas as pd
import numpy as np
import datetime
import logging
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

============================================

配置区 - 根据你的实际情况修改

============================================

邮件告警配置(以QQ邮箱为例)

EMAIL_CONFIG = {
"smtp_server": "smtp.qq.com",
"smtp_port": 465,
"sender": "your_email@qq.com", # 你的发件邮箱
"password": "your_auth_code", # 邮箱授权码(不是登录密码)
"receiver": "your_email@qq.com", # 收件邮箱
}

策略监控阈值

STRATEGY_THRESHOLDS = {
"max_daily_loss_pct": -3.0, # 单日最大亏损百分比
"max_drawdown_pct": -10.0, # 最大回撤百分比
"min_sharpe_ratio": 0.5, # 最低夏普比率(近20日滚动)
"max_position_pct": 25.0, # 单只股票最大仓位百分比
"max_daily_signals": 10, # 单日最大信号数
"signal_freq_std_multiplier": 2.0, # 信号频率偏离均值的标准差倍数
}

系统监控阈值

SYSTEM_THRESHOLDS = {
"max_latency_seconds": 5, # 最大网络延迟(秒)
"max_error_count_per_hour": 3, # 每小时最大错误次数
"max_order_pending_minutes": 10, # 订单最大挂起时间(分钟)
}

数据监控阈值

DATA_THRESHOLDS = {
"max_data_delay_hours": 2, # 数据最大延迟时间(小时)
"min_data_completeness_pct": 99.0, # 数据最低完整率百分比
"price_change_alert_pct": 15.0, # 价格异常波动百分比
}

============================================

日志配置

============================================

logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
handlers= logging.FileHandler("monitor.log", encoding="utf-8"), logging.StreamHandler()
)
logger = logging.getLogger("QuantMonitor")

============================================

告警发送

============================================

class Alerter:
"""告警发送器"""

def init(self, config: dict):
self.config = config

def send_email(self, subject: str, body: str):
"""发送邮件告警"""
try:
msg = MIMEMultipart()
msg"From" = self.config"sender"
msg"To" = self.config"receiver"
msg"Subject" = f"【量化告警】{subject}"
msg.attach(MIMEText(body, "plain", "utf-8"))

with smtplib.SMTP_SSL(
self.config"smtp_server",
self.config"smtp_port"
) as server:
server.login(self.config"sender", self.config"password")
server.send_message(msg)

logger.info(f"告警邮件已发送: {subject}")
except Exception as e:
logger.error(f"告警邮件发送失败: {e}")

def alert(self, level: str, dimension: str, message: str):
"""
统一告警入口
level: WARNING / CRITICAL
dimension: 策略 / 系统 / 数据
"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
subject = f"{level} {dimension} - {timestamp}"

body = f"""告警级别: {level}
告警维度: {dimension}
告警时间: {timestamp}
告警内容: {message}


量化监控系统自动发送
"""
logger.warning(f"{level}{dimension} {message}")
self.send_email(subject, body)

============================================

策略层面监控

============================================

class StrategyMonitor:
"""策略层面监控"""

def init(self, thresholds: dict, alerter: Alerter):
self.thresholds = thresholds
self.alerter = alerter

def check_daily_pnl(self, daily_pnl_pct: float):
"""
检查每日收益
daily_pnl_pct: 当日收益百分比,如 -2.5 表示亏损2.5%
"""
if daily_pnl_pct <= self.thresholds"max_daily_loss_pct":
self.alerter.alert(
"CRITICAL", "策略",
f"单日亏损 {daily_pnl_pct:.2f}% 超过阈值 "
f"{self.thresholds'max_daily_loss_pct'}%"
)

def check_drawdown(self, current_drawdown_pct: float):
"""
检查回撤
current_drawdown_pct: 当前回撤百分比(负数),如 -8.0
"""
if current_drawdown_pct <= self.thresholds"max_drawdown_pct":
self.alerter.alert(
"CRITICAL", "策略",
f"当前回撤 {current_drawdown_pct:.2f}% 超过阈值 "
f"{self.thresholds'max_drawdown_pct'}%"
)

def check_sharpe(self, returns_series: pd.Series):
"""
检查近20日滚动夏普比率
returns_series: 日收益率序列
"""
if len(returns_series) < 20:
return

recent = returns_series.tail(20)
sharpe = (recent.mean() / recent.std()) * np.sqrt(252) if recent.std() > 0 else 0

if sharpe < self.thresholds"min_sharpe_ratio":
self.alerter.alert(
"WARNING", "策略",
f"近20日滚动夏普比率 {sharpe:.2f} 低于阈值 "
f"{self.thresholds'min_sharpe_ratio'}"
)

def check_position_concentration(
self, positions: dict, total_value: float
):
"""
检查持仓集中度
positions: {股票代码: 持仓市值}
total_value: 账户总资产
"""
for stock, value in positions.items():
pct = (value / total_value) * 100
if pct > self.thresholds"max_position_pct":
self.alerter.alert(
"WARNING", "策略",
f"{stock} 仓位占比 {pct:.1f}% 超过阈值 "
f"{self.thresholds'max_position_pct'}%"
)

def check_signal_frequency(self, today_signals: int, history_signals: list):
"""
检查信号频率是否异常
today_signals: 今日信号数
history_signals: 过去30日每日信号数列表
"""
if len(history_signals) < 10:
return

mean = np.mean(history_signals)
std = np.std(history_signals)

if today_signals > self.thresholds"max_daily_signals":
self.alerter.alert(
"WARNING", "策略",
f"今日信号数 {today_signals} 超过上限 "
f"{self.thresholds'max_daily_signals'}"
)
elif std > 0 and abs(today_signals - mean) > self.thresholds "signal_freq_std_multiplier" * std:
self.alerter.alert(
"WARNING", "策略",
f"今日信号数 {today_signals} 偏离历史均值 "
f"{mean:.1f} 超过 {self.thresholds'signal_freq_std_multiplier'} 倍标准差"
)

============================================

数据层面监控

============================================

class DataMonitor:
"""数据层面监控"""

def init(self, thresholds: dict, alerter: Alerter):
self.thresholds = thresholds
self.alerter = alerter

def check_data_update_time(self, last_update_time: datetime.datetime):
"""
检查数据更新时间
last_update_time: 数据最后更新时间
"""
now = datetime.datetime.now()
delay_hours = (now - last_update_time).total_seconds() / 3600

if delay_hours > self.thresholds"max_data_delay_hours":
self.alerter.alert(
"CRITICAL", "数据",
f"数据已 {delay_hours:.1f} 小时未更新,"
f"最后更新: {last_update_time.strftime('%Y-%m-%d %H:%M:%S')}"
)

def check_data_completeness(self, total_count: int, updated_count: int):
"""
检查数据完整性
total_count: 应更新的股票总数
updated_count: 实际更新的股票数
"""
completeness = (updated_count / total_count) * 100
if completeness < self.thresholds"min_data_completeness_pct":
missing = total_count - updated_count
self.alerter.alert(
"WARNING", "数据",
f"数据完整率 {completeness:.1f}%,缺失 {missing} 只股票"
)

def check_price_anomaly(
self, stock_code: str, current_price: float, prev_close: float
):
"""
检查价格异常值
"""
if prev_close <= 0:
self.alerter.alert(
"CRITICAL", "数据",
f"{stock_code} 昨收价异常: {prev_close}"
)
return

change_pct = abs((current_price - prev_close) / prev_close * 100)

if current_price < 0:
self.alerter.alert(
"CRITICAL", "数据",
f"{stock_code} 价格异常: {current_price}(负数)"
)
elif change_pct > self.thresholds"price_change_alert_pct":
self.alerter.alert(
"WARNING", "数据",
f"{stock_code} 价格波动 {change_pct:.1f}%,"
f"昨收 {prev_close} -> 今 {current_price}"
)

============================================

每日监控主流程

============================================

class DailyMonitor:
"""每日监控主入口"""

def init(self):
self.alerter = Alerter(EMAIL_CONFIG)
self.strategy_monitor = StrategyMonitor(STRATEGY_THRESHOLDS, self.alerter)
self.data_monitor = DataMonitor(DATA_THRESHOLDS, self.alerter)

def run_daily_check(self):
"""
执行每日检查。
实际使用时,将下面的模拟数据替换为你的真实数据源。
"""
logger.info("===== 开始每日监控检查 =====")

--- 1. 策略层面 ---

示例:当日收益 -1.5%,回撤 -6%,近20日收益率序列

self.strategy_monitor.check_daily_pnl(daily_pnl_pct=-1.5)
self.strategy_monitor.check_drawdown(current_drawdown_pct=-6.0)

模拟近20日收益率

np.random.seed(42)
returns = pd.Series(np.random.normal(0.001, 0.02, 20))
self.strategy_monitor.check_sharpe(returns)

模拟持仓

positions = {"600519": 250000, "000858": 80000, "601318": 50000}
total_value = 500000
self.strategy_monitor.check_position_concentration(positions, total_value)

模拟信号频率

self.strategy_monitor.check_signal_frequency(
today_signals=3,
history_signals=2, 3, 1, 2, 4, 2, 3, 1, 2, 3, 2, 1, 3, 2, 4, 2, 1, 3, 2, 2
)

--- 2. 数据层面 ---

self.data_monitor.check_data_update_time(
last_update_time=datetime.datetime.now() - datetime.timedelta(hours=1)
)
self.data_monitor.check_data_completeness(
total_count=5000, updated_count=4980
)
self.data_monitor.check_price_anomaly(
stock_code="600519", current_price=1800.0, prev_close=1780.0
)

模拟异常数据检测

self.data_monitor.check_price_anomaly(
stock_code="000001", current_price=-3.5, prev_close=10.5
)

logger.info("===== 每日监控检查完成 =====")

============================================

运行

============================================

if name == "main":
monitor = DailyMonitor()
monitor.run_daily_check()

把这段代码存成 monitor.py

改一下邮箱配置。

跑一下试试。

// bash
pip install pandas numpy
python monitor.py

你会收到一封告警邮件------

因为模拟数据里故意放了个负数价格

看出区别没?

这不是玩具代码。

这是生产级 的监控骨架。

你往里填真实数据就行。

三、日常运维 Checklist

光有代码不够。

你得有个习惯

每天固定时间。

对着清单。

一项一项打勾。

像飞行员起飞前一样。

飞行员不检查仪表------

会死人。

你不检查策略------

会亏钱。

道理一样。

盘前(9:00前)

|--------|-------------------|----------------|
| 序号 | 检查项 | 确认 |
| 1 | 昨晚数据是否全部更新完成 | □ |
| 2 | 数据质量检查是否通过(无异常值) | □ |
| 3 | 策略进程是否存活(`ps aux | grep python`) |
| 4 | 昨日报错日志是否已检查 | □ |
| 5 | 今日是否有停牌/涨跌停影响持仓 | □ |
| 6 | 网络连通性是否正常 | □ |
| 7 | QMT客户端是否已登录 | □ |

盘中(9:30-15:00)

|--------|-----------------|--------|
| 序号 | 检查项 | 确认 |
| 8 | 开盘后30分钟内信号是否正常 | □ |
| 9 | 订单是否正常成交(无异常挂单) | □ |
| 10 | 盘中策略收益是否在预期范围内 | □ |
| 11 | 持仓集中度是否正常 | □ |

盘后(15:30后)

|--------|----------------|--------|
| 序号 | 检查项 | 确认 |
| 12 | 收盘数据是否开始更新 | □ |
| 13 | 当日交易记录是否完整录入 | □ |
| 14 | 当日收益是否与预期偏离过大 | □ |
| 15 | 是否需要调整参数(记录原因) | □ |
| 16 | 监控日志是否已归档 | □ |

没有checklist的策略,就像没有仪表盘的飞机。你觉得自己飞得挺稳,其实已经偏航了。

16项。

每天10分钟。

你嫌烦?

那你等着亏钱的时候------

就不只是10分钟了。

四、监控日志记录模板

监控不能只看。

记下来

为什么?

因为你要复盘

三个月后------

你回头看。

哪天出了问题。

哪天收益异常。

哪天信号发多了。

全在日志里。

没有日志------

你就是条没有记忆的金鱼。

日志模板:

============ 监控日志 ============
日期:2025-01-15
记录人:自动监控 / 人工补充

【策略层面】
当日收益:+0.85%
当月累计收益:+3.2%
最大回撤(近20日):-4.1%
夏普比率(近20日滚动):1.32
今日信号数:3
持仓数量:8只
最大单只仓位:招商银行 12.3%

【系统层面】
策略进程状态:正常运行(PID 12345)
QMT客户端状态:已登录
订单总数:5
成交:4 | 撤单:1 | 挂单:0
错误日志:无
网络延迟:平均 0.3s

【数据层面】
数据更新时间:16:15
数据完整率:100%(5000/5000)
异常值检测:通过

【异常记录】
无异常。

【备注】
市场整体偏强。沪深300 +1.2%。
策略表现符合预期。

============ 日志结束 ============

每天一份。

存成文件。

按日期命名。

monitor_2025-01-15.log

monitor_2025-01-16.log

...

三个月后------

你就有了一座金矿。

回头看------

哪天策略漂移了。

哪天数据出了问题。

哪天系统出了bug。

全有据可查。

日志不是写给今天看的,是写给三个月后的自己看的。那时候你会感谢今天的勤快。

五、进阶:自动化运维

人力有时而穷。

上面那些------

是起步。

真正的老手------

全自动化。

数据更新?自动检查。

进程挂了?自动重启。

信号异常?自动暂停策略。

怎么做?

两个方向:

第一,定时任务。

Linux用cron。Windows用任务计划。

每天固定时间跑检查脚本。

不用人记。

// bash

Linux crontab 示例

每个交易日16:30自动执行盘后检查

30 16 * * 1-5 python /home/user/quant/monitor.py >> /home/user/quant/cron.log 2>&1

第二,进程守护。

策略进程挂了------

自动拉起来。

用 supervisor 或者 systemd。

// ini

/etc/supervisor/conf.d/quant_strategy.conf

program:quant_strategy

command=python /home/user/quant/strategy.py
directory=/home/user/quant
autostart=true
autorestart=true
stderr_logfile=/home/user/quant/strategy_err.log
stdout_logfile=/home/user/quant/strategy_out.log
user=user

配好了------

进程死了。

秒级重启。

你连告警都来不及收到。

这才是真正的"无人值守"。

但------

无人值守的前提------

有人盯过

前几个月。

每天看。

每周看。

看到你对这个策略了如指掌。

然后------

你才敢放手。

自动化不是让你变懒,是让你把精力花在值得花的地方。但前提是你先花过那个精力。

六、几个血的教训

最后,讲几个真实案例。

都是------

没做好监控。

案例一:周末忘了关策略。

某个量化新手。

策略挂在本地电脑上。

周五收盘------

没关。

周六凌晨。

某数据源更新了测试数据。

策略拿到了。

以为是正常数据。

基于测试数据------

下了单。

好在------

周末不能交易。

订单没出去。

但------

策略的持仓记录------

乱了。

周一开盘。

策略以为自己有持仓。

实际上没有。

后面的逻辑------

全错了。

案例二:一次手动干预,毁了三个月的回测。

有个老手。

策略跑得挺好。

但某天------

手痒了。

手动改了个参数。

"这个阈值太高了,调低一点应该更好。"

调了。

跑了两周。

亏了。

调回去。

又跑了两周。

还是亏了。

为什么?

因为他调参的时候------

用的是那两周的数据。

过拟合了。

三个月的回测成果------

毁于一次手痒。

教训------

参数调整必须记录。

什么时候调的。

调了什么。

为什么调。

全写进日志。

不然------

你都不知道自己做过什么。

案例三:一个warning,被忽略了47天。

服务器上。

策略每天输出一个warning:

WARNING: Data source returned NULL for stock 002XXX

运维的人------

看了一眼。

"应该是个别股票,没关系。"

47天后。

那个warning变成了------

200只股票返回NULL。

策略在这47天里------

一直基于不完整的数据做决策。

亏了多少钱?

没人知道。

因为------

没人认真看过日志。

每一个warning都是策略在喊疼。你不听,它就忍着。忍到那天------它不喊了,你才知道它死了。

总结

监控三个维度。

策略层面------赚没赚,偏没偏。

系统层面------跑没跑,死没死。

数据层面------对不对,全不全。

告警要自动化。

checklist要每天做。

日志要每天记。

上线只是开始。

监控,才是日常。

你花三个月写策略。

但你得花每一天 看策略。

这就是量化的真相。

不是写完代码就完了。

是写完代码------

才刚刚开始。

量化交易最贵的东西不是代码,是注意力。你的注意力在哪里,你的钱就在哪里。

相关推荐
爱吃鱼的喵️1 小时前
云数据库怎么选?主流云厂商横向对比与选型指南
数据库·阿里云·云计算
the局外人1 小时前
学习 FastAPI 的 Day 2:用异步 ORM 完成增删改查
后端·python·fastapi
智搜广告1 小时前
GEO优化公司怎么选?智搜广告从三个维度帮你判断
大数据·人工智能·python·elasticsearch·microsoft·geo
zhengqweasd1 小时前
网站卡死、接口超时的隐性根源
运维·服务器·网络
AIGS0011 小时前
工艺参数散在Excel和纸质文件里,能不能统一管、随查随用
服务器·数据库·excel·经验沉淀·知识管理·工艺知识·本体语义
裕晟资质规划1 小时前
军工保密资质二级申报的四个可量化硬条件:条文位置、数值口径与西安配套企业实务要点
java·服务器·网络·数据库·算法
wanglei2007081 小时前
软件架构设计中,各个组件之间的通信方式有哪些?
python
会飞的拖把1 小时前
Python 面向对象编程详解:从类与对象到动态属性方法
开发语言·python
kaixin_啊啊1 小时前
PandaWiki 本地 AI 知识库实战:文档导入、智能问答与远程访问
linux·服务器·人工智能·windows·ai