2. 信号校验:风控规则与信号有效性检查
信号校验,说白了就是给交易信号做「体检」。
你想想看,策略生成了一个信号,说「现在买入」。你敢直接执行吗?我反正不敢。我在早期做量化的时候,就吃过这个亏——信号出来了,没做任何检查,直接怼进去,结果发现是数据源出了故障,信号完全无效。那笔亏损,到现在还记得。
所以,信号校验是策略到实盘之间的一道防火墙。它不负责判断信号对不对,只负责判断信号「能不能用」。
2.1 信号校验的核心目标
信号校验要回答三个问题:
- 信号是否合法?——格式、字段、时间戳对不对
- 信号是否合理?——价格、数量、方向有没有异常
- 信号是否安全?——有没有触发风控规则
这三个问题,任何一个不过关,信号就应该被拦截。我个人习惯是:宁可错杀一千,不可放过一个。因为一次错误执行,可能把之前所有的盈利都赔进去。
2.2 风控规则的分类与设计
风控规则,我一般分成三类。每一类都有它的应用场景和坑。
2.2.1 硬性风控规则
这类规则是「死线」,触碰就拦截,没有任何商量余地。
| 规则类型 | 说明 | 示例 |
|---|---|---|
| 最大持仓限制 | 单品种持仓不能超过设定值 | BTC 持仓 ≤ 10 张 |
| 最大单笔金额 | 单笔交易金额上限 | 单笔 ≤ 总资产的 5% |
| 交易时间限制 | 禁止在特定时间段交易 | 非交易时段拦截 |
| 品种黑名单 | 禁止交易某些品种 | ST 股票、低流动性币种 |
嗯,这里要注意:硬性规则一定要写在最前面检查。我曾经见过一个系统,先做信号有效性检查,再做风控检查。结果信号有效性检查通过了,但风控没通过,白白浪费了计算资源。
2.2.2 软性风控规则
软性规则是「黄线」。触碰了不会直接拦截,但会触发告警或降级处理。
- 价格偏离度检查:信号价格与当前市价偏离超过阈值(如 0.5%),触发告警
- 频率限制:同一方向信号间隔过短(如 < 1 秒),降级为「延迟执行」
- 滑点预估:预估滑点超过设定值,提示「可能无法成交」
我建议软性规则不要太多,3-5 条就够了。太多了反而容易产生「告警疲劳」,最后没人看告警了。
2.2.3 动态风控规则
这类规则会根据市场状态自动调整参数。比如:
- 波动率升高时,自动降低单笔最大金额
- 连续亏损 N 笔后,暂停交易 M 分钟
- 账户回撤超过 X%,自动切换为保守模式
动态规则是高级玩法。我在项目中遇到过一个问题:动态规则写得太复杂,导致信号校验耗时从 2ms 涨到了 50ms。对于高频交易来说,这完全不可接受。所以,动态规则一定要做性能测试。
2.3 信号有效性检查
信号有效性检查,关注的是信号本身的质量。它和风控规则是两回事。
2.3.1 数据完整性检查
信号必须包含哪些字段?我列一个最小集:
- 品种代码:如 BTCUSDT、AAPL
- 交易方向:买入/卖出/开仓/平仓
- 交易价格:限价单的价格
- 交易数量:合约张数或股票股数
- 信号时间戳:信号生成的时间
- 信号 ID:唯一标识,用于追踪
缺少任何一个字段,信号都应该被标记为「无效」。别想着「缺一个字段也能凑合用」,我吃过这个亏——有一次信号缺少时间戳,结果系统用了本地时间,和交易所时间差了 3 秒,导致订单全部被拒。
2.3.2 逻辑合理性检查
有些信号,字段都全了,但逻辑上不合理。比如:
- 买入价格是负数(明显数据错误)
- 数量为 0 或超过最大允许值
- 方向是「买入」,但当前持仓已经是满仓
- 信号时间戳比当前时间还晚(未来信号)
这些检查看起来简单,但很容易被忽略。我建议写一个专门的函数来做合理性检查,不要分散到各个地方。
def check_signal_validity(signal):
"""
信号有效性检查
返回: (is_valid, reason)
"""
# 1. 字段完整性检查
required_fields = ['symbol', 'direction', 'price', 'quantity', 'timestamp', 'signal_id']
for field in required_fields:
if field not in signal:
return False, f"缺少字段: {field}"
# 2. 逻辑合理性检查
if signal['price'] <= 0:
return False, "价格必须为正数"
if signal['quantity'] <= 0:
return False, "数量必须为正数"
if signal['quantity'] > MAX_QUANTITY:
return False, f"数量超过最大限制: {MAX_QUANTITY}"
if signal['timestamp'] > time.time() + 5: # 允许 5 秒误差
return False, "信号时间戳异常(未来信号)"
# 3. 方向检查
valid_directions = ['buy', 'sell', 'open_long', 'close_long', 'open_short', 'close_short']
if signal['direction'] not in valid_directions:
return False, f"无效的交易方向: {signal['direction']}"
return True, "信号有效"
2.4 信号校验的流程设计
一个完整的信号校验流程,应该是这样的:
- 接收信号:从策略模块获取原始信号
- 格式解析:将信号解析为统一的数据结构
- 硬性风控检查:触碰死线,直接拦截
- 信号有效性检查:检查数据完整性和逻辑合理性
- 软性风控检查:触碰黄线,触发告警或降级
- 动态风控检查:根据市场状态调整参数
- 输出结果:通过则进入执行队列,不通过则记录日志
这个流程,每一步都要有日志输出。为什么?因为出了问题,你要能回溯。我见过太多系统,信号被拦截了,但不知道是被哪个规则拦截的。这种系统,你敢用吗?
2.5 性能与可靠性
信号校验不能成为系统的瓶颈。我见过一些系统,校验逻辑写得特别复杂,结果信号从生成到执行,延迟从 1ms 涨到了 100ms。对于高频交易来说,这简直是灾难。
几点建议:
- 校验逻辑要轻量:不要在信号校验里做复杂的计算,比如机器学习预测
- 规则要可配置:不要硬编码规则参数,用配置文件或数据库管理
- 要有熔断机制:如果校验模块本身出问题了(比如数据库连不上),要有降级方案
- 日志要异步:写日志不要阻塞主流程,用异步队列
信号校验,说白了就是「信任但验证」。策略是你的战友,但你不能完全信任它。每一笔交易,都要经过严格的检查。这不是不信任策略,而是对账户负责。
嗯,这一节就到这里。记住:信号校验不是可选项,是必选项。没有校验的系统,就像没有刹车的车——你敢开,我不敢坐。