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。对于高频交易来说,这完全不可接受。所以,动态规则一定要做性能测试。

避坑指南:我曾经把动态规则和硬性规则混在一起写,结果调试的时候根本分不清是哪个规则拦截了信号。后来我强制要求:每个规则必须有独立的 ID 和日志输出。这样排查问题,一眼就能看出来。

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, "信号有效"
小技巧:我习惯在信号校验函数里加一个「调试模式」。当 debug=True 时,即使信号无效,也会返回详细的检查过程。这样在开发和测试阶段,可以快速定位问题。

2.4 信号校验的流程设计

一个完整的信号校验流程,应该是这样的:

  1. 接收信号:从策略模块获取原始信号
  2. 格式解析:将信号解析为统一的数据结构
  3. 硬性风控检查:触碰死线,直接拦截
  4. 信号有效性检查:检查数据完整性和逻辑合理性
  5. 软性风控检查:触碰黄线,触发告警或降级
  6. 动态风控检查:根据市场状态调整参数
  7. 输出结果:通过则进入执行队列,不通过则记录日志

这个流程,每一步都要有日志输出。为什么?因为出了问题,你要能回溯。我见过太多系统,信号被拦截了,但不知道是被哪个规则拦截的。这种系统,你敢用吗?

信号校验流程 ① 接收信号 ② 格式解析 ③ 硬性风控检查 拦截 ❌ 记录日志,丢弃信号 ④ 信号有效性检查 无效 ❌ 记录日志,丢弃信号 ⑤ 软性风控检查 ⑥ 动态风控检查 ✅ 进入执行队列 📝 全流程日志

2.5 性能与可靠性

信号校验不能成为系统的瓶颈。我见过一些系统,校验逻辑写得特别复杂,结果信号从生成到执行,延迟从 1ms 涨到了 100ms。对于高频交易来说,这简直是灾难。

几点建议:

  • 校验逻辑要轻量:不要在信号校验里做复杂的计算,比如机器学习预测
  • 规则要可配置:不要硬编码规则参数,用配置文件或数据库管理
  • 要有熔断机制:如果校验模块本身出问题了(比如数据库连不上),要有降级方案
  • 日志要异步:写日志不要阻塞主流程,用异步队列
我的习惯:我会给每个规则设置一个「超时时间」。如果某个规则执行超过 10ms,就跳过该规则,并记录告警。这样即使某个规则出了问题,也不会拖垮整个系统。

信号校验,说白了就是「信任但验证」。策略是你的战友,但你不能完全信任它。每一笔交易,都要经过严格的检查。这不是不信任策略,而是对账户负责。

嗯,这一节就到这里。记住:信号校验不是可选项,是必选项。没有校验的系统,就像没有刹车的车——你敢开,我不敢坐。


专注资料整理