一套策略能跑通回测,不代表能安全活过实盘。在量化里,最贵的教训往往不是 alpha 失效,而是合规与审计的盲区—下单越权、持仓超限、成交对不上账、监管函件措手不及。这些问题的共同点是:它们都发生在”收益曲线”之外,等发现时已经既亏钱又违规。
本文把量化合规拆成四个层次,并用 Python 把三件最该自动化的事跑出来:交易前风控校验、交易后持仓核对与审计追踪、扫单异常检测。
一个常被忽视的事实是:合规事故的代价是非线性的。一次越权下单可能只是小亏,但一旦伴随信披违规或操纵嫌疑,带来的就是账户冻结、产品清盘、甚至从业资格处罚—损失量级从”万”跳到”亿”。所以合规投入的性价比,远高于它看起来那点工程成本。

一、量化合规的四个层次#
合规不是法务部门的事,它贯穿策略全生命周期,至少覆盖四层:
| 层次 | 核心问题 | 典型失控后果 |
|---|---|---|
| 数据合规 | 数据源是否授权、个人隐私是否脱敏 | 数据侵权、监管约谈 |
| 模型合规 | 因子/模型是否可解释、是否过拟合 | 黑箱决策、误导性信号 |
| 交易合规 | 持仓/杠杆/黑名单是否越界 | 超限额、内幕或操纵嫌疑 |
| 报告披露 | 绩效与风险披露是否及时、可追溯 | 信披违规、问责无据 |
关键洞察:交易合规和报告披露是最容易用代码”卡死”的两层,因为它们有明确的数值边界(限额、杠杆、持仓),适合自动化拦截与留痕;而数据合规和模型合规更依赖流程与人工复核。
在落地时,四层的自动化程度差异很大。交易合规和报告披露天然适合用”数值边界 + 代码拦截”:持仓、杠杆、黑名单、参与率都是硬数字,写进 OMS 和执行引擎即可实时卡死;数据合规要把数据源授权、字段脱敏、数据血缘(这条数据从哪个接口来、何时入库)登记到元数据系统,出了问题能一路回溯;模型合规则更依赖流程—因子变更要走审批、模型版本要可复现、上线要留评审记录。一个实用的经验是:凡是能用数字定义的约束,就不要留给人工判断。
二、Python 实战 1:交易前风控校验器#
交易前风控的目标只有一个—在订单发出前就把违规挡住。下面这个 PreTradeChecker 校验三类硬约束:禁投黑名单、单一标的持仓上限、总杠杆上限。
from dataclasses import dataclass, field
from typing import Dict, Tuple
@dataclass
class PreTradeChecker:
"""交易前风控校验器:下单前拦截违规订单。"""
max_single_weight: float = 0.08 # 单一标的持仓上限 8%
max_gross_leverage: float = 1.0 # 总杠杆(多头满仓)上限
blacklist: set = field(default_factory=set)
def check(self, order: Dict, weights: Dict[str, float]) -> Tuple[bool, str]:
code = order["code"]; side = order["side"]; tw = order["target_weight"]
if code in self.blacklist:
return False, f"{code} 在禁投黑名单"
new_w = weights.get(code, 0.0) + (tw if side == "BUY" else -tw)
if new_w > self.max_single_weight:
return False, f"单一持仓 {new_w:.1%} 超上限 {self.max_single_weight:.0%}"
new_weights = dict(weights); new_weights[code] = new_w
gross = sum(v for v in new_weights.values() if v > 0)
if gross > self.max_gross_leverage + 1e-9:
return False, f"总杠杆 {gross:.1%} 超上限 {self.max_gross_leverage:.0%}"
return True, "通过"
chk = PreTradeChecker(blacklist={"600519.SH"})
w = {"000858.SZ": 0.06, "300750.SZ": 0.05}
print(chk.check({"code": "600519.SH", "side": "BUY", "target_weight": 0.03}, w))
print(chk.check({"code": "000858.SZ", "side": "BUY", "target_weight": 0.05}, w))python跑出来的两条结果分别是 ('600519.SH 在禁投黑名单') 和 ('单一持仓 11.0% 超上限 8%')—两笔订单都被正确拦截。真正重要的是:这类校验必须发生在 OMS 下单接口之前,而不是事后对账才发现。

三、Python 实战 2:交易后持仓核对与审计追踪#
下单后,成交价、成交量常常和预期有偏差(部分成交、滑点)。审计的第一条铁律是:每一笔成交都必须留痕,且能和目标持仓对账。下面用 AuditTrail 记录成交并对账。
from dataclasses import dataclass, field
from datetime import datetime
@dataclass
class AuditTrail:
"""交易后核对与审计追踪:记录每笔成交并核对持仓。"""
ledger: list = field(default_factory=list)
def fill(self, code, filled_qty, filled_px, ts=None):
self.ledger.append({"code": code, "qty": filled_qty,
"px": filled_px, "ts": ts or datetime.now()})
def reconcile(self, target_weights: dict, current_weights: dict) -> list:
"""返回偏离超 1% 容差的标的列表。"""
issues = []
for code, tw in target_weights.items():
cw = current_weights.get(code, 0.0)
if abs(cw - tw) > 0.01:
issues.append((code, tw, cw, cw - tw))
return issues
at = AuditTrail()
at.fill("000858.SZ", 1000, 180.5)
print("对账异常:", at.reconcile({"000858.SZ": 0.06}, {"000858.SZ": 0.045}))python输出 对账异常: [('000858.SZ', 0.06, 0.045, -0.015)],说明实际持仓比目标低了 1.5%—可能是部分成交或同步漏单,需要追溯补单或下调目标。审计追踪的价值,就是让这种偏差可追溯、可归因、可问责。
四、Python 实战 3:扫单异常检测#
监管的红线之一,是”影响交易价格或交易量的异常交易行为”。一个简单可量化的信号是单笔参与率(participation = 成交量 / 日均成交量 ADV):一把吃掉盘口、单笔参与率过高,容易被认定为扫单或拉抬。
import pandas as pd
trades = pd.DataFrame({
"code": ["300750.SZ"]*4, "side": ["BUY"]*4,
"qty": [2000, 1500, 3000, 2500], "adv": [50000]*4,
"ts": pd.to_datetime(["09:31","09:32","09:33","09:34"]),
})
trades["participation"] = trades["qty"] / trades["adv"]
# 单笔参与率 > 5% 视为异常扫单
breaches = trades[trades["participation"] > 0.05]
print("扫单异常笔数:", len(breaches))
print(breaches[["ts", "qty", "participation"]])python结果标出 09:33 那笔 3000 股、参与率 6% 为异常。实际系统里,这条规则会接入实时成交流,超阈值即告警甚至暂停下单——它防的不是亏钱,而是违规。在监管科技(RegTech)语境下,这类规则正是“可疑交易报告”的量化地基,券商与基金公司都需按日上报,自己先把关比被查到再补救强得多。

五、合规规则也要回测(但不能前视)#
合规规则不是写完就完事,你同样要问:这条限额在历史上有没有误杀过好单?参与率阈值设 5% 还是 3% 更合适?答案是—用历史成交流回测,但必须严格遵守 signal-on-i / execute-on-i+1 的执行时序,绝不能拿事后才披露的信息(如某标的盘后才被加入黑名单)去回测规则有效性,否则会严重高估规则的准确率。
更稳妥的做法是”离线复算”:每天收盘后,用当天已知的持仓与成交,重跑一遍交易前校验器,统计被拦截的订单如果当时放行会怎样。这能持续校准阈值,又不会引入前视偏差。把合规回测纳入策略研究流程,它才从”应付检查”变成”真实的风险护栏”。
六、常见陷阱#
- 把审计当形式:很多团队的”审计”只是月底导出一份 Excel,违规早已发生。审计必须实时或准实时,且违规要能触发动作,而非仅记录。
- 人工补单 / 线下改账:实盘发现偏差后,私下手工调整持仓却不留痕,等于主动制造合规黑洞。所有调整都应走同一套带时间戳的接口。
- 只看收益不看分布:合规报告里只放收益率曲线,却不展示持仓集中度、杠杆、参与率分布,等于主动隐瞒风险结构。
- 前视偏差进合规:用”未来才知道”的标的状态(如事后才披露的黑名单)去回测合规规则,会高估规则有效性。合规回测必须严格 signal-on-i / execute-on-i+1。
- 边界写死:限额写死在代码里,市场或监管变化时无人更新。建议把限额、黑名单、参与率阈值放进配置,而非散落在逻辑中。
小结#
量化合规与审计的本质,是给策略装上** brakes 和 black box recorder**:交易前校验是刹车,交易后追踪是黑匣子。两者都用可量化的数值边界驱动,才能从”靠人盯”升级为”靠代码卡”。在监管趋严、问责到人的环境下,这套自动化内控不是成本,而是策略能长期生存的门票。
落地清单(可直接照抄进你们的执行系统):
- 交易前:黑名单、单一持仓上限、总杠杆上限,三道校验必须在 OMS 下单接口之前串行执行。
- 交易后:每笔成交写审计账本,盘后自动对账,偏离 >1% 自动告警。
- 实时:单笔参与率超阈值即告警/暂停,防范扫单与拉抬。
- 校准:合规规则纳入历史回测与每日离线复算,阈值放进配置而非代码。
- 留痕:所有调整(补单、改仓、改限额)走同一套带时间戳的接口,禁止线下改账。