惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
I
InfoQ
The Register - Security
The Register - Security
L
LangChain Blog
H
Help Net Security
The GitHub Blog
The GitHub Blog
S
Schneier on Security
博客园 - 【当耐特】
W
WeLiveSecurity
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
The Cloudflare Blog
H
Heimdal Security Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
雷峰网
雷峰网
N
Netflix TechBlog - Medium
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
Lohrmann on Cybersecurity
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
V
Visual Studio Blog
博客园 - 聂微东
PCI Perspectives
PCI Perspectives
Last Week in AI
Last Week in AI
A
Arctic Wolf
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
SegmentFault 最新的问题
SecWiki News
SecWiki News
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AI
AI
Engineering at Meta
Engineering at Meta

土法炼钢兴趣小组的算法知识备份

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 排序基准测试:用数据说话
【量化交易】交易成本模型:冲击成本、滑点、TCA
2026-05-01 · via 土法炼钢兴趣小组的算法知识备份

很多人写策略,回测里夏普 3.0,上线之后跑成 0.5,复盘的时候归因到「市场风格变了」「最近行情不好」「机器学习模型衰减」。这些原因都可能成立,但更常见的、几乎所有中频以下策略上线第一年都会撞上的真实原因是另一个:回测假设的成交价根本不存在。回测里写的是「以收盘价成交,扣 5bps 手续费」,实盘里发出去的单子要排队、要吃多档、要被对手方主动让出来或者主动闪开,最终拿到的成交均价比回测里那个「收盘价」差几十甚至上百个 bps。这部分差额不是黑天鹅、不是因子失效,是工程上从一开始就没建模的交易成本。

交易成本这件事,在卖方研究员的研报里被一带而过——他们关心的是 alpha,把成本当作给基金经理算账的小问题;但在做市商、对冲基金、量化资管的执行台 (execution desk)、券商的电子交易部门里,成本是日常工作的全部内容:客户的钱亏在哪一档买卖盘上、自营单子如何拆才能不被前置交易(front running)、做市报价要给多少 spread 才覆盖逆向选择、机构客户的 TCA(Transaction Cost Analysis,交易成本分析)报告是不是又把锅甩给券商。这些问题没有一个能用「夏普」「IR」之类的策略指标回答,必须用一整套独立的成本模型与归因方法学来处理。

本文把交易成本从「报表上一行手续费」展开成一个四层结构:显性成本、滑点、冲击成本、机会成本。每一层各自有自己的测量方法、估计模型、监控手段。关键结论按公式与文献一一对照,所有 Python 代码片段可以直接拿去拟合自己的成交回报数据。最后一节给出跨市场(A 股、美股、CME、币安)的成本口径差异,以及在生产 TCA 系统里我们实际做的工程权衡。

风险提示与适用范围:本文不构成任何投资建议。所有代码、数据与示例数值均用于说明算法,未经过完整生产审计,直接套用产生的资金损失由使用者自负。文中冲击成本系数、滑点估计值仅用于教学,绝对不能直接当作生产环境的费率参数——真实参数应使用本机构的成交回报数据按本文方法重新拟合。TCA 报告涉及客户告知、最佳执行(best execution)合规义务的部分(如 MiFID II RTS 28、FINRA Rule 5310、SEC Rule 605/606、中国证监会《证券公司客户资产管理业务管理办法》等)有专门的口径与披露要求,本文不替代任何监管文档。


一、交易成本的分类

写代码之前先要把「成本」这个词拆清楚,否则后面所有的归因、归责、调参讨论都会陷入鸡同鸭讲。从机构成交回报数据里能直接看见的东西,按是否在结算单上落账分成两类:显性成本(explicit cost)隐性成本(implicit cost)

显性成本

显性成本是结算单、对账单、清算所流水上有具体一行的支出,跑不掉、抄进会计科目、按交易所与监管口径有明文规定。常见的几条:

第一是佣金(commission)。券商为客户提供经纪服务收取的费用,A 股按成交金额的固定比例收取(万 1.5 到万 3 是机构客户的常见区间,零售账户略高),美股机构经纪可以做到每股 USD 0.0005-0.002,币安现货按成交额收 0.1% 普通账户、0.075% 抵扣 BNB、做市商负费率最低到 -0.005%。佣金对量化策略的影响最容易看见,也最容易被高估——除了零佣金的零售账户,机构端佣金通常是所有成本里比例最低的一项。

第二是印花税(stamp duty / transfer tax)。中国大陆 A 股卖方按成交额 0.05%(2023 年 8 月起从 0.1% 减半),中国香港买卖双边 0.1%,英国伦敦买方 0.5%(Stamp Duty Reserve Tax,电子结算),法国对市值超过 10 亿欧元的本国上市股票买方收 0.3% 的金融交易税(FTT),美股不收印花税但 SEC 收 Section 31 卖方监管费(2024 年费率 USD 27.80 / USD 1,000,000 名义额)。印花税是策略换手率的硬约束,A 股 T+1 单边卖出收 5bps 这一项,意味着年换手 200% 的策略仅这一项就要交 10% 收益作税。

第三是过户费、清算费、交易所规费。沪深两市过户费按面额 0.001%(即百万分之十)双边收取,由中国结算公司统一收;美股 FINRA TAF(Trading Activity Fee)按成交股数 USD 0.000166 / 股(卖方上限 USD 8.30 每笔,2024 费率),CME 期货按合约收(小型 E-mini S&P 单合约约 USD 0.50 含交易所、清算、监管费),币安永续合约 maker 0.02% / taker 0.05% 是「佣金 + 清算 + 交易所」一票打包的「fee」,内部并不区分。

第四是借券费(borrow fee)与融资利息。做空必须借券,借券费按年化对名义市值收,A 股融券标的池窄、券源紧张、借券费可达年化 8%-12%,美股大盘股借券费 25-50bps、热门做空标的可以飙到三位数 bps,币安永续按资金费率(funding rate)每 8 小时结算(多空双方互付)。这些费用在持仓期摊销,是隐藏在「持仓成本」里的显性成本。

第五是外汇交割成本。跨市场交易需要把本币换成结算货币,外汇 spread 按规模与对手方差异在 5bps 到 50bps 之间。这一项常被国内策略团队忽略——做美股的策略应当在回测里直接用美元计价收益、把汇率波动剥离到独立的 FX hedging 子策略里。

第六是监管费、自律费。SEC Section 31 fee、FINRA TAF、欧盟 FTT、中国证券业协会规定的部分会费按业务规模计提。这部分金额小但口径杂,需要靠券商提供的费率表逐项核对。

显性成本的特点是:确定、可预测、可全额计入回测。在写策略的时候它们应该作为一个 cost_table 字典从配置文件里读进来,按交易所 + 品种 + 方向 + 客户等级查出来直接乘到名义额上。任何把显性成本只用一个「commission_bps=5」糊弄过去的回测都是在欺骗自己。

隐性成本

隐性成本是没有明确发票、不在结算单上单列、要靠「假设一个不存在的基准价格」才能算出来的成本。它包括:

  • 买卖价差成本(spread cost / half-spread):用 mid 价当公允价,主动单吃单要支付一半 spread;
  • 滑点(slippage):从决策那一刻的参考价到实际成交均价之间的差距,包含所有「不确定」的成分;
  • 冲击成本(market impact):自己的单子推动了价格朝不利方向走,导致后续成交逐档变贵;
  • 机会成本(opportunity cost):因为想避免冲击而拆得太散、太慢,没成交完就被市场跑走,剩下没成交的部分以更差的价格被动成交、或者干脆错过;
  • 延迟成本(delay cost):从信号触发到下单系统把单子真正发到交易所之间的时间窗口里,价格已经走掉的部分。

这五项之间的关系不是并列、而是连续覆盖一段时间轴上的不同区间。Perold 在 1988 年提出的 Implementation Shortfall(IS) 框架把它们统一到一个公式里,本文第五节会展开。先记住一个直觉:显性成本是固定支出,隐性成本是不确定的滑动支出,二者的总和才是策略真实承担的全成本

下面这张图是后面整篇文章的几何骨架,把决策、到达、执行、收盘四个时间点和三段成本(延迟、执行、机会)画在一条时间轴上。

Implementation Shortfall 分解结构

把这张图记牢。后面无论讨论 VWAP 算法、Almgren-Chriss 最优执行、TCA 归因,本质都在这条时间轴上做切分。


二、滑点的多种定义

「滑点」这个词在散户交易者的语言里指「下了一个限价单结果成交价比想象的差了一点」,定义松散且常和「点差」混用。在机构 TCA 里,滑点必须明确写出参考价(benchmark),否则讨论无法进行。同一笔成交相对不同的参考价,「滑点」的数值与符号都可能完全不同。

三种主流的参考价

到达价(arrival price)。下单系统接收到子任务(child order)的那一刻,市场上的参考价。一般取 mid(最优买一与卖一的中点),有时取 last(最近一笔成交),机构默认用 mid 以避免被「最近一笔大单成交价」污染。到达价滑点(arrival slippage)测量的是「我从把单子发出去那一刻起,相对当时市场的公允价,我多付/少付了多少」,是衡量执行算法本身好坏的最常用指标。

VWAP(Volume Weighted Average Price,成交量加权均价)。一段时间内全市场所有成交的「成交额 / 成交量」。VWAP 滑点是机构经纪与买方之间默认的考核指标——客户给券商一个母单(parent order),约定按当日 VWAP 结算,券商承诺执行价不差于 VWAP 多少 bps。VWAP 的好处是平滑、几乎不可被操纵(除非占据全天大部分成交量);坏处是它把自己的成交也算进了 VWAP,导致大单的「VWAP 滑点」被自己稀释(自我成就,self-fulfilling)。

TWAP(Time Weighted Average Price,时间加权均价)。一段时间内 mid 价按等时间间隔的均值,与成交量无关。TWAP 主要用在两类场景:(1) 流动性很差的品种,VWAP 容易被几笔大单偏倚;(2) 监管报告里要求时间均价的合规口径。

除此之外还有一些次要参考价:前一交易日收盘价(previous close)用于隔夜决策的策略;当日开盘价(open)用于「在开盘前生成的 alpha 信号」;决策时刻 mid(decision-time mid)用于 IS 中区分延迟成本与执行成本;逆向 mid (contra mid)——成交那一刻对手方价格——用于衡量做市策略的 mark-to-market loss。

滑点公式与符号约定

记买方向 \(s \in \{+1, -1\}\)(买入 +1、卖出 -1),成交量 \(q\),成交均价 \(\bar P\),参考价 \(P_b\)符号统一约定:滑点正值表示对自己不利

\[ \text{Slippage}_{\text{bps}} = s \cdot \frac{\bar P - P_b}{P_b} \cdot 10^4 \]

买入时若 \(\bar P > P_b\)(多付了),滑点为正;卖出时若 \(\bar P < P_b\)(少收了),滑点也为正。这条符号约定在生产 TCA 系统里必须严格执行——一旦买卖方向上的符号搞错,整张报表会变成「买入策略看起来在赚钱,卖出策略看起来在亏钱」的灾难。

主动单与被动单的滑点本质不同

把所有成交无脑混到一起算「平均滑点」是初级 TCA 的常见错误。同一笔母单在执行算法里会被拆成大量子单,子单按是否吃对手方分为:

  • 主动单(aggressive / taker):发送时吃掉对手方挂单,立刻成交,付半个 spread;
  • 被动单(passive / maker):挂在自己一侧排队,等别人来吃,省半个 spread 但承担排队风险与逆向选择;
  • 跨档单(marketable limit):限价吃多档,占用多档流动性,付的 spread 大于半个;
  • 冰山单(iceberg):被动挂单的一种,只显示部分量,用于隐藏总意图。

衡量主动单的好坏看「相对到达 mid 多付了多少 spread」;衡量被动单的好坏看「成交率(fill ratio)」与「逆向选择损失(adverse selection cost)」——后者指挂单成交后短时间内(一般 5 秒到 60 秒)价格反向跑掉的部分。一个 TCA 报表如果只报「平均滑点 X bps」却不区分 aggressive / passive,本质上就是把高频做市的「主动让利」和择时单的「主动吃单」混到一起算,结论无意义。

实测代码:计算到达价滑点与 VWAP 滑点

下面给一段可以直接跑的 Python 骨架。输入是一张子单成交回报表 fills(来自 FIX 引擎的 ExecutionReport,字段 parent_id, child_id, side, qty, price, timestamp, venue, liquidity_flag),以及一份按毫秒采样的 mid 行情 quotes

import pandas as pd
import numpy as np

def compute_slippage(fills: pd.DataFrame,
                     quotes: pd.DataFrame,
                     parent_meta: pd.DataFrame) -> pd.DataFrame:
    """
    fills: child-order fills, columns:
        parent_id, side(+1/-1), qty, price, ts(datetime64[ns]), liquidity_flag('A'/'P')
    quotes: mid price snapshots, columns:
        symbol, ts, mid
    parent_meta: parent orders, columns:
        parent_id, symbol, side, decision_ts, arrival_ts, target_qty
    Returns:
        per-parent TCA row with all slippage variants in bps.
    """
    # --- 1) 子单 reduce 到母单层 ---
    grp = fills.groupby('parent_id')
    exec_qty = grp['qty'].sum()
    exec_notional = (fills['qty'] * fills['price']).groupby(fills['parent_id']).sum()
    exec_vwap = exec_notional / exec_qty

    # --- 2) 取每个母单 arrival_ts 的 mid ---
    quotes_sorted = quotes.sort_values(['symbol', 'ts'])
    arrival_mid = pd.merge_asof(
        parent_meta.sort_values('arrival_ts'),
        quotes_sorted, by='symbol',
        left_on='arrival_ts', right_on='ts',
        direction='backward'
    )[['parent_id', 'mid']].set_index('parent_id')['mid']

    decision_mid = pd.merge_asof(
        parent_meta.sort_values('decision_ts'),
        quotes_sorted, by='symbol',
        left_on='decision_ts', right_on='ts',
        direction='backward'
    )[['parent_id', 'mid']].set_index('parent_id')['mid']

    # --- 3) 当日 VWAP(基于母单覆盖的时间窗)---
    # 用成交回报代表市场量在工程上不准确,生产中应使用 trades feed
    market_trades = quotes  # placeholder: 实际应是 trades 表,含 px 和 size
    # 占位:留给读者按本机构数据接入

    side = parent_meta.set_index('parent_id')['side']
    arrival_slip = side * (exec_vwap - arrival_mid) / arrival_mid * 1e4
    decision_slip = side * (exec_vwap - decision_mid) / decision_mid * 1e4

    out = pd.DataFrame({
        'exec_qty': exec_qty,
        'exec_vwap': exec_vwap,
        'arrival_mid': arrival_mid,
        'decision_mid': decision_mid,
        'arrival_slip_bps': arrival_slip,
        'decision_slip_bps': decision_slip,
    })
    return out

代码的意思都写在文档字符串里。两个工程要点:

第一,对齐参考价用 merge_asof 而不是简单 join。母单 arrival_ts 是连续时间,行情 quotes 是离散采样,必须用「向后查找最近的 quote」(direction='backward')才能拿到「下单瞬间市场上看得见的最后一个 mid」。在生产中如果用了 direction='nearest' 会偷看未来——这是 TCA 报告里最常见的口径错误。

第二,VWAP 必须独立估计。生产里不能用「fills 表本身」当 VWAP 基准,因为 fills 是自己的成交,会被自己污染。VWAP 必须从交易所成交回报流(market trades feed)里另算,再减去自己的成交(self-trade exclusion),剩下的才是「除我之外的市场 VWAP」(market-ex-self VWAP),这才是公允考核基准。


三、冲击成本模型

冲击成本(market impact)指自己的下单行为推动价格向不利方向移动所产生的成本。它和滑点的边界并不锐利——简单来说,滑点是观测量,冲击是滑点里「能归因到自己下单行为」的那部分。冲击模型回答两类问题:(1) 给定母单总量 \(Q\) 与时间窗 \(T\),预计承担多少冲击?(2) 给定冲击预算,最优拆单策略是什么?

线性冲击模型

最简单的形式假设瞬时冲击与下单速率成线性关系。记瞬时下单速率为 \(v_t = dx_t / dt\)(手 / 秒),日均成交速率为 \(V\)(手 / 秒),价格冲击为:

\[ \Delta P_t = \eta \cdot \sigma \cdot \frac{v_t}{V} \]

其中 \(\sigma\) 是当日波动(通常用 daily volatility),\(\eta\) 是临时冲击系数(temporary impact),无量纲。线性模型最早出现在 Bertsimas & Lo (1998)、Almgren & Chriss (2000) 的最优执行框架里。优点是数学处理简单,能解出 closed-form 最优执行轨迹;缺点是与实证不符——所有按成交回报数据拟合的实证文献(Torre 1997、Almgren et al. 2005、Frazzini, Israel & Moskowitz 2018)都报告冲击是凹函数而非线性。

平方根律

Barra 早在 1990 年代的 MIM(Market Impact Model)里就提出冲击系数与 \(\sqrt{Q/V}\) 成正比;Almgren et al. (2005) 在 Citigroup 的 70 万笔成交回报上做了完整拟合,得到经典形式:

\[ \text{Cost}_{\text{permanent}} = \gamma \cdot \sigma \cdot \left( \frac{Q}{V} \right) \]

\[ \text{Cost}_{\text{temporary}} = \eta \cdot \sigma \cdot \left( \frac{Q}{V \cdot T} \right)^{0.6} \]

永久冲击对累计量线性,临时冲击对下单速率近似 0.6 次方(论文给出 95% 置信区间 [0.55, 0.65],工程上取 0.5 也足够好)。把二者合到 IS 公式里,给定 \(Q\)\(T\),单位名义额冲击成本近似为:

\[ \text{IS}(Q, T) \approx \frac{1}{2} \gamma \sigma \frac{Q}{V} + \eta \sigma \left( \frac{Q}{VT} \right)^{0.6} \]

第一项与 \(T\) 无关、与 \(Q\) 线性;第二项与 \(T\) 有关、随 \(T\) 减小、随 \(Q\) 凹。后面 Almgren-Chriss 优化就是在这两项与时间风险之间做权衡。

为什么是平方根而不是线性?两个角度可以理解。一是有限流动性供给:限价簿里在 mid 附近 \(\delta\) 之内的累计挂单量近似与 \(\delta\) 线性,因此把 mid 推动 \(\delta\) 需要消耗 \(O(\delta^2)\) 的市价单,反之消耗 \(Q\) 单子产生 \(O(\sqrt Q)\) 的价格位移。二是Bouchaud 等人的 propagator 框架给出了更精细的解释(见下文)。

Bouchaud propagator 模型

Bouchaud, Gefen, Potters & Wyart (2004) 把冲击建模为线性响应函数:

\[ \Delta P_t = \sum_{s \le t} G(t - s) \cdot \epsilon_s \cdot f(v_s) + \text{noise} \]

其中 \(\epsilon_s = \pm 1\)\(s\) 时刻成交方向,\(f(v_s)\) 是对成交量的非线性变换(实证 \(f(v) \propto \log v\)\(f(v) \propto v^{0.5}\)),\(G(\tau)\) 是衰减核(propagator),实证上 \(G(\tau) \sim \tau^{-\beta}\)\(\beta \approx 0.4 \sim 0.6\)

关键观察是:冲击不是瞬时的,而是带长记忆衰减的。Bouchaud 框架下「方向流(order flow)有强自相关,价格序列接近随机游走」这两个事实可以同时成立——后续相反方向的成交把前一笔的冲击抵消掉一部分,留下来的「永久部分」远小于即刻冲击。这与 Hasbrouck (1991) 的「永久 vs 临时」VAR 分解是相通的。

工程上 propagator 模型主要用在做市与高频策略:拟合自己的下单序列对 mid 的响应,反推 \(G(\tau)\),用于挂单价位与撤单时机的策略调参。中频策略一般不需要自己拟合 propagator,直接用平方根律即可。

Almgren-Chriss 最优执行

Almgren & Chriss (2000) 把执行问题写成「成本期望 + 风险厌恶 × 成本方差」的均值-方差最优化:

\[ \min_{x_t} \quad \mathbb{E}[C(x)] + \lambda \cdot \mathrm{Var}[C(x)] \]

其中 \(x_t\)\(t\) 时刻剩余持仓,\(C(x)\) 是从 \(0\)\(T\) 的累计执行成本,\(\lambda\) 是风险厌恶系数。令永久冲击系数为 \(\gamma\)、临时冲击为 \(\eta\)、价格波动为 \(\sigma\),离散时间下:

\[ x_k = X \cdot \frac{\sinh(\kappa (T - t_k))}{\sinh(\kappa T)}, \quad \kappa = \sqrt{\frac{\lambda \sigma^2}{\eta}} \]

风险厌恶 \(\lambda \to 0\) 时退化为 TWAP(线性轨迹);\(\lambda\) 大则前重后轻(front-loaded),尽快交易避免持仓暴露。

下面给一段实现 Almgren-Chriss 解的 Python 代码,可以直接喂母单参数得到最优拆单计划:

import numpy as np

def almgren_chriss_schedule(X: float, T: float, N: int,
                            sigma: float, eta: float, gamma: float,
                            lam: float) -> np.ndarray:
    """
    X: 母单总量(股 / 手 / 张)
    T: 执行时间窗(秒、分钟均可,需与 sigma 单位一致)
    N: 拆分成 N 段
    sigma: 价格波动(与 T 单位一致)
    eta: 临时冲击系数(USD per (unit/time))
    gamma: 永久冲击系数(USD per unit)
    lam: 风险厌恶系数 (1/USD)
    返回长度 N+1 的数组,x_k = 第 k 段开始时剩余仓位
    """
    tau = T / N
    # 离散化的 kappa
    kappa_tilde2 = lam * sigma**2 / (eta * (1 - 0.5 * gamma * tau / eta))
    kappa = np.arccosh(0.5 * kappa_tilde2 * tau**2 + 1) / tau
    t = np.arange(N + 1) * tau
    # 持仓轨迹 x_k:从 X 衰减到 0
    x = X * np.sinh(kappa * (T - t)) / np.sinh(kappa * T)
    return x

def schedule_to_trades(schedule: np.ndarray) -> np.ndarray:
    """把持仓轨迹 x_k 转换为每段成交量 n_k = x_{k-1} - x_k"""
    return -np.diff(schedule)

# 例:100 万股,30 分钟,5 分钟一段 6 段;σ=2bps/分钟,η/γ 为典型值
X, T, N = 1_000_000, 30, 6
sigma = 0.02 / np.sqrt(390)  # 假设日波动 2%、240/390 分钟
eta = 2.5e-7   # 拟合得到,量纲:USD per (share/minute)
gamma = 2.5e-8
lam_low, lam_high = 1e-8, 1e-6

x_low = almgren_chriss_schedule(X, T, N, sigma, eta, gamma, lam_low)
x_high = almgren_chriss_schedule(X, T, N, sigma, eta, gamma, lam_high)
print('风险厌恶低(接近 TWAP):', schedule_to_trades(x_low))
print('风险厌恶高(前重后轻):', schedule_to_trades(x_high))

实现要点:

第一,\(\eta\)\(\gamma\) 必须按本机构成交回报数据拟合。论文里给的数值是 Citigroup 2001-2003 的美股拟合值,直接套到 A 股或币安永续上无意义。第六节给出按平方根律拟合的最小代码。

第二,\(\lambda\) 的量纲容易搞错。Almgren-Chriss 原始论文里 \(\lambda\) 量纲是 1 / 货币(即 1/USD),实际工程里更直观的做法是直接指定「半轨迹时间」\(T_{1/2} = \log 2 / \kappa\) 或者「能容忍的执行价波动 std」反推 \(\lambda\)

第三,实盘里 Almgren-Chriss 是计划,不是命令。计划给出每分钟应当成交的目标量,真正的子单仍由更下层的 child router 在 100ms 时间尺度上决定挂被动单还是主动单、挂在哪一档、挂多少。两层之间用「执行进度容忍度(schedule deviation tolerance)」连接,进度落后超过阈值则切到主动单赶进度,进度超前则切回被动单省 spread。

Kyle λ 与微观结构视角

把冲击放回到信息经济学的基础上看,Kyle (1985) 的连续拍卖模型给出了一个最经典的线性形式:

\[ \Delta P = \lambda \cdot Q \]

其中 \(\lambda\) 是「Kyle 系数」,刻画了「每单位订单流推动 mid 多少」的逆向选择强度。Kyle 模型的核心假设是市场上存在一个掌握私人信息的 informed trader 与一组 noise trader,做市商通过观察总订单流推断信息含量并相应调价;\(\lambda\) 越大,意味着市场对订单流越敏感、流动性越差。

Kyle 模型的线性结论与实证的平方根律似乎冲突,二者其实在不同尺度成立:

  • 极短时间(秒级以内):Kyle 线性是合理近似,对应做市商的 inventory-based quote adjustment;
  • 中等时间(分钟到小时):平方根律是更好的拟合,因为限价簿深度被「翻新」(fresh liquidity 进入),冲击的边际成本随累计量减小;
  • 长时间(日级别以上):永久冲击近似线性,对应 Kyle 模型的稳态。

工程上 \(\lambda\) 的估计可以由 Hasbrouck (1991) 的 VAR 框架给出:把订单流(signed volume)与 mid return 拟合一个二变量 VAR,永久部分对累计签名量的偏导即 \(\lambda\)

import numpy as np
import statsmodels.tsa.api as smt

def estimate_kyle_lambda(returns: np.ndarray,
                         signed_volume: np.ndarray,
                         lags: int = 5) -> float:
    """
    Hasbrouck VAR:returns_t 与 signed_volume_t 双变量 VAR。
    冲击响应函数(IRF)的 long-run 累积值 / 单位 signed_volume = Kyle lambda。
    """
    data = np.column_stack([returns, signed_volume])
    model = smt.VAR(data)
    result = model.fit(lags)
    irf = result.irf(periods=200)
    # 第二个变量 (signed_volume) 对第一个变量 (returns) 的累积响应
    cum = irf.cum_effects[-1, 0, 1]
    return cum

VAR 残差通常厚尾、非正态,标准的 t 检验置信区间偏窄,工程里要配合 bootstrap 给 95% 区间。

永久冲击与临时冲击的分离

工程上经常需要把冲击拆成「永久」与「临时」两部分,因为二者对策略的意义不同:

  • 永久冲击(permanent impact):自己下单永久改变了 mid,相当于自己把价格水位推高了一段,不会回退。这部分对自己是真实损失,对市场是「信息泄露」。
  • 临时冲击(temporary impact / transient impact):自己下单瞬间消耗了一档流动性,mid 被推高,但订单结束后 mid 会逐步回归。这部分对自己是损失(多付了),但对市场是无信息的「流动性消费」。

按 Almgren et al. (2005) 的拟合,永久冲击约占总冲击的 30%-50%,剩下是临时冲击。Bouchaud propagator 框架则给出更细:临时冲击按 \(\tau^{-0.4}\) 缓慢衰减,5-10 分钟后大部分回归。

工程上拆分的方法:把母单结束后 30 分钟内的 mid 回归量记为 \(\Delta P_{\text{revert}}\),则:

\[ \text{Permanent} \approx (\bar P - P_a) - \Delta P_{\text{revert}}, \quad \text{Temporary} \approx \Delta P_{\text{revert}} \]

这条估计的工程价值在于:永久冲击对应「策略 alpha 的 information leakage」,可以通过更隐蔽的拆单(dark pool、随机化、隐藏量)压低;临时冲击对应「执行算法的 spread loss」,可以通过更被动的挂单策略压低。两条优化路径不同,必须先拆开再分别处理。

平方根律的实证拟合

一段最小代码,给一张母单 + 成交回报样本表 parents(字段 Q, V, T, sigma, IS_bps),拟合 \(\text{IS} = a + b \cdot \sigma \cdot (Q/(V T))^{\beta}\)

import numpy as np
from scipy.optimize import curve_fit

def sqrt_law(X, b, beta):
    sigma, q_over_vt = X
    return b * sigma * np.power(q_over_vt, beta)

def fit_impact(parents):
    """
    parents: DataFrame with columns Q, V, T, sigma_daily, IS_bps
    Q: 母单数量
    V: 当日 ADV(同一单位)
    T: 执行时长(占当日交易时间的比例,0~1)
    sigma_daily: 日波动(小数,如 0.02 = 2%)
    IS_bps: 实测 IS(bps,正值表示成本)
    """
    sigma = parents['sigma_daily'].values * 1e4  # 转 bps
    q_over_vt = (parents['Q'] / (parents['V'] * parents['T'])).values
    y = parents['IS_bps'].values
    popt, pcov = curve_fit(sqrt_law, (sigma, q_over_vt), y, p0=[0.1, 0.6])
    b_hat, beta_hat = popt
    err = np.sqrt(np.diag(pcov))
    return {'b': b_hat, 'beta': beta_hat, 'b_se': err[0], 'beta_se': err[1]}

工程经验值:经过样本筛除(剔除极端母单、流动性受限品种、停牌前后异常单)后,A 股全市场拟合 \(\beta\) 通常落在 [0.55, 0.70],美股大盘股 \(\beta \approx 0.55\)、小盘股 \(\beta \approx 0.65\),币安主流币永续 \(\beta \approx 0.5\)(更接近平方根,市场流动性更线性)。

下面这张图把三种典型假设(线性、\(\sqrt{}\)\(q^{0.6}\))的成本曲线画在同一组坐标上,便于直观对比。

成本随委托量增长的曲线

四、机会成本

机会成本(opportunity cost)是 IS 框架里最容易被回测忽略的一项:该买没买到、该卖没卖出去,于是市场跑掉一段,你少赚的那部分。它的存在挑战了一个朴素直觉——拆得越散、走得越慢,冲击成本越小,是不是就一定越便宜?答案是否定的:拆散降低了冲击与滑点,但抬高了机会成本,二者之和有最优点。

未成交部分的机会成本

记母单目标量 \(Q\),实际成交 \(Q_{\text{exec}}\),未成交比例 \(1 - f = 1 - Q_{\text{exec}} / Q\),决策价 \(P_d\),期末参考价 \(P_l\)(一般取期末 mid 或当日收盘)。未成交部分的机会成本为:

\[ C_{\text{opp}} = (1 - f) \cdot s \cdot (P_l - P_d) \cdot Q \]

注意这里也按方向 \(s\) 归一化:买入时如果 \(P_l > P_d\)(市场涨了,没买够),机会成本为正(你少赚了);卖出时如果 \(P_l < P_d\)(市场跌了,没卖完),机会成本同样为正(你少跑了)。

延迟成本

延迟成本(delay cost)是信号触发到子单真正发到交易所之间那段时间里价格走掉的部分:

\[ C_{\text{delay}} = s \cdot (P_a - P_d) \cdot Q \]

延迟成本的来源细分有:

  • 信号生成延迟:从行情数据到达本机到策略产生 alpha 信号的算力延迟,毫秒到分钟不等;
  • 风控审批延迟:策略发出母单到风控/合规系统放行的延迟,机构端通常 < 100ms,部分券商定制接入 < 10μs;
  • 路由延迟:从下单 API 到 FIX 引擎到交易所网关的网络延迟;
  • 决策保留延迟:人工干预环节(PM 审核、夜盘/早盘集合竞价等待)造成的几分钟到一小时级别延迟。

延迟成本看起来是工程问题(拼网络延迟),但它的真正治理在策略侧:只要 alpha 信号衰减时间常数大于延迟时间常数,延迟成本就有限。如果 alpha 半衰期是 30 分钟、下单延迟 10ms,延迟成本可以忽略;如果 alpha 半衰期是 10 秒、下单延迟 50ms,延迟成本会吃掉一半信号。

时间风险与冲击的权衡

把上面三块(执行、机会、延迟)加在一起,再叠加显性 fee,写成 Implementation Shortfall 完整形式:

\[ \text{IS} = f \cdot s \cdot (\bar P - P_d) \cdot Q + (1 - f) \cdot s \cdot (P_l - P_d) \cdot Q + \text{fees} \]

第一项进一步拆为延迟 + 执行:

\[ f \cdot s \cdot (\bar P - P_d) = f \cdot s \cdot (P_a - P_d) + f \cdot s \cdot (\bar P - P_a) \]

前者是延迟成本(成交那部分),后者是执行成本(含滑点 + 冲击 + 时间风险)。所有三项都按符号 \(s\) 与方向归一化为「正值 = 成本」。这是 Perold (1988) 给出的标准分解,也是后面所有 TCA 报告的口径基础。

时间风险(timing risk)是 Almgren-Chriss 框架下的「成本方差」对应物:拉长执行时间窗 \(T\) 降低冲击,但价格的随机游走会让最终成交价的方差增大。其量化形式为:

\[ \mathrm{Var}[C(T)] \approx \sigma^2 \int_0^T x_t^2 \, dt \]

剩余持仓 \(x_t\) 越大、暴露时间越长,时间风险越大。这一项不是「期望成本」,而是成本的不确定性,必须配合风险厌恶系数 \(\lambda\) 进入决策。

取消单与重新下单的核算

未成交部分常见三种处理:(1) 彻底放弃——母单到点撤单,剩余量不再尝试;(2) 滚动到下一窗——挂单失败的部分自动进入下一个执行窗;(3) 强制 catch-up——剩余量切主动单,立即吃完。三种处理对应三种机会成本核算口径:

def opportunity_cost(parent_meta, fills, end_quote):
    """
    parent_meta: parent_id -> {target_qty, side, decision_px, expire_ts}
    fills:        per-parent executed_qty
    end_quote:    parent_id -> reference price at end-of-window
    返回未成交部分的机会成本(按 side 归一化为正值表示成本)
    """
    out = {}
    for pid, meta in parent_meta.items():
        executed = fills.get(pid, 0.0)
        unfilled = meta['target_qty'] - executed
        if unfilled <= 0:
            out[pid] = 0.0
            continue
        delta = end_quote[pid] - meta['decision_px']
        # side: +1 buy, -1 sell
        out[pid] = meta['side'] * delta * unfilled
    return out

工程要点:核算口径必须由策略而非执行台决定。如果策略本身允许放弃未成交(如做市策略),那未成交不应被计入策略 PnL 也不应计入 TCA 机会成本(策略已经主动选择不要这部分敞口);如果策略要求必须成交(如指数追踪、ETF 申购赎回),未成交必须按上述公式扣作机会成本。混淆这两类口径会导致 TCA 报告把执行台的好处理(按时撤单)当成执行台的坏处理(没成交完)。


TCA(Transaction Cost Analysis,交易成本分析)是把上面四节内容工程化为可定期产出的报表与仪表盘的活动。机构里 TCA 同时服务于三类干系人:

  1. 基金经理 / 策略组:评估自己的信号是否在执行后还有 alpha 剩下;
  2. 执行台 / 经纪商关系:评估每个执行算法、每家经纪、每个流动性场所(venue)的优劣;
  3. 合规 / 监管报送:MiFID II(欧)、FINRA Rule 5310 / SEC Rule 606(美)、《证券公司客户资产管理业务管理办法》(中)规定的最佳执行义务,需要按季 / 半年披露。

三类干系人的成本口径并不相同。基金经理关心 IS 与决策价的差距(决策价之前发生的事不计入),执行台关心 arrival slippage(接到母单之后的事),合规关心一系列硬性指标(与全市场 NBBO 比较、与基准价格对比的偏离比例等)。一份合格的生产 TCA 系统必须同时支持三套口径的并行报表。

Implementation Shortfall 主报表

主报表的字段骨架:

字段 含义 量纲
parent_id 母单标识 文本
symbol 标的 文本
side +1/-1 整型
target_qty 母单目标量 股/手/张
decision_ts 决策时刻 时间
arrival_ts 到达时刻 时间
end_ts 收盘 / 母单到期 时间
decision_px 决策价
arrival_px 到达价 mid
exec_vwap 自身成交均价
end_px 期末参考价
exec_qty 已成交量 同上
fees 显性费用合计 货币
delay_bps 延迟成本 bps
exec_bps 执行成本 bps
opp_bps 机会成本 bps
fees_bps 显性 bps
IS_bps 合计 bps

下面这段代码是把上面 fills、parent_meta、quotes 三张表 reduce 成一张 IS 报表的核心:

def implementation_shortfall(parent_meta: pd.DataFrame,
                             fills: pd.DataFrame,
                             quotes: pd.DataFrame,
                             fee_table: dict) -> pd.DataFrame:
    """
    标准 IS 四段式分解。所有 bps 列正值代表对持有人不利。
    """
    # 子单 reduce
    grp = fills.groupby('parent_id')
    exec_qty = grp['qty'].sum().rename('exec_qty')
    exec_vwap = (grp.apply(lambda d: (d['qty'] * d['price']).sum() / d['qty'].sum())
                    .rename('exec_vwap'))

    df = parent_meta.join(exec_qty, on='parent_id').join(exec_vwap, on='parent_id')
    df['exec_qty'] = df['exec_qty'].fillna(0)
    df['fill_ratio'] = df['exec_qty'] / df['target_qty']

    # 价格基准
    df['decision_px'] = lookup_quote(quotes, df['symbol'], df['decision_ts'])
    df['arrival_px']  = lookup_quote(quotes, df['symbol'], df['arrival_ts'])
    df['end_px']      = lookup_quote(quotes, df['symbol'], df['end_ts'])

    side = df['side']

    # 各项成本(货币)
    df['delay_cost']   = df['fill_ratio']         * side * (df['arrival_px'] - df['decision_px']) * df['target_qty']
    df['exec_cost']    = df['fill_ratio']         * side * (df['exec_vwap']  - df['arrival_px'])  * df['target_qty']
    df['opp_cost']     = (1 - df['fill_ratio'])   * side * (df['end_px']     - df['decision_px']) * df['target_qty']
    df['fees']         = df.apply(lambda r: compute_fees(r, fee_table), axis=1)

    # 转 bps(按决策价归一)
    notional = df['target_qty'] * df['decision_px']
    for c in ['delay_cost', 'exec_cost', 'opp_cost', 'fees']:
        df[c.replace('_cost', '_bps').replace('fees', 'fees_bps')] = df[c] / notional * 1e4

    df['IS_bps'] = df['delay_bps'] + df['exec_bps'] + df['opp_bps'] + df['fees_bps']
    return df

骨架要点:

第一,fill_ratio 必须乘到 delay 与 exec 项上,机会成本则乘 1 - fill_ratio。许多自实现的 TCA 漏掉这个权重,结果把没成交的那部分也按已成交计延迟与执行成本,IS 总和被高估。

第二,decision_px 需要明确取哪一个。Perold 原始定义是「策略生成信号那一刻的可见价格」。如果策略在收盘后跑模型、第二天开盘下单,则 decision_px 是前一日收盘 mid;如果是日内信号、立即下单,则 decision_px = arrival_px(此时延迟成本为 0)。

第三,end_px 取期末 mid 而非期末 last。last 价容易被最后一笔大单污染,实证里能差出 5-10bps 的口径噪声。

IS attribution

把 IS 拆出来之后,下一步是把它归因到不同维度:策略、经纪商、算法、时段、品种、流动性条件、订单大小桶。归因的工程实现是「按维度 group by 之后对 IS 做 quantile + ANOVA」:

def is_attribution(is_df: pd.DataFrame, dim: str) -> pd.DataFrame:
    """
    按 dim 维度(如 strategy / broker / algo / venue / hour / size_bucket)
    汇总 IS 与其分量。
    """
    agg = (is_df.groupby(dim)
                .agg(n=('parent_id', 'count'),
                     notional=('target_qty', lambda s: (s * is_df.loc[s.index, 'decision_px']).sum()),
                     delay_bps=('delay_bps', 'mean'),
                     exec_bps=('exec_bps', 'mean'),
                     opp_bps=('opp_bps', 'mean'),
                     fees_bps=('fees_bps', 'mean'),
                     IS_bps_mean=('IS_bps', 'mean'),
                     IS_bps_p50=('IS_bps', 'median'),
                     IS_bps_p95=('IS_bps', lambda s: np.quantile(s, 0.95)))
                .sort_values('notional', ascending=False))
    return agg

实战经验:

  • 仅看均值会低估尾部成本。p95mean 更能暴露执行算法的失败模式。
  • size_bucket 必须按 ADV 分位(如 [0, 0.5%], [0.5%, 2%], [2%, 5%], [5%, +∞)),按绝对名义额分桶在跨品种比较时无意义。
  • hour 维度上要把开盘前 30 分钟与收盘前 30 分钟单独拎出来——两段尾盘的成本结构与中间时段差距巨大。
  • A 股要按「集合竞价 / 连续竞价 / 收盘集合」三段分别看,连续竞价段内再按「9:30-10:00 / 10:00-11:30 / 13:00-14:30 / 14:30-15:00」四个小段。

报表骨架的最小可运行版

class TCAReport:
    def __init__(self, parent_meta, fills, quotes, fee_table):
        self.is_df = implementation_shortfall(parent_meta, fills, quotes, fee_table)

    def by(self, dim):
        return is_attribution(self.is_df, dim)

    def summary(self):
        return {
            'n_parents': len(self.is_df),
            'total_notional': (self.is_df['target_qty'] * self.is_df['decision_px']).sum(),
            'avg_IS_bps': self.is_df['IS_bps'].mean(),
            'p95_IS_bps': self.is_df['IS_bps'].quantile(0.95),
            'fill_ratio_mean': self.is_df['fill_ratio'].mean(),
            'top_loss_parents': self.is_df.nlargest(20, 'IS_bps')[['parent_id', 'symbol', 'IS_bps']],
        }

    def render_pdf(self, out_path):
        # 占位:留给具体绘图库(matplotlib + reportlab / weasyprint)
        raise NotImplementedError

生产 TCA 系统在这个骨架上还要叠加:当日实时滚动报告(5 分钟刷新一次)、跨日趋势图(30 天移动均值)、客户专属维度(每家券商一份独立 PDF)、异常告警(IS 超过 3σ 自动钉钉/Slack)。这些都是工程问题,公式与口径就是上面这套。


六、回测中嵌入成本

回测里如何近似真实成本,是策略上线前最后一个关键问题。错误的成本假设比错误的 alpha 更要命——alpha 的失效你能在样本外回测里观察到,错误的成本假设直到真金白银上场才会暴露。下面分两条主流回测路径讨论。

向量化回测的成本嵌入

向量化回测在每日(或每分钟)持仓变化层面工作,输入是「目标权重时间序列」,按价格序列求 PnL。成本以「换手 × 单位成本」形式扣减:

\[ \text{Cost}_t = \sum_i |\Delta w_{i,t}| \cdot c_i(\,|\Delta w_{i,t}| \cdot \text{Equity}_t\,) \]

其中 \(c_i(\cdot)\) 是单位名义额成本函数,依赖于换手量。最常见的两层模型:

  • 常数 bps 模型\(c_i = c_0\),最简单,零成本拟合,绝大多数公开教程用这个。
  • 平方根模型\(c_i = a + b \sqrt{q_i / V_i}\),按本机构数据拟合 \(a, b\),能对换手大小敏感。

向量化代码示例:

def apply_costs_vectorized(weights: pd.DataFrame,
                           prices: pd.DataFrame,
                           adv: pd.DataFrame,
                           sigma: pd.DataFrame,
                           equity_series: pd.Series,
                           fee_bps: float = 5.0,
                           sqrt_b: float = 0.10,
                           sqrt_beta: float = 0.6) -> pd.DataFrame:
    """
    weights: T×N 目标权重
    prices:  T×N 收盘价
    adv:     T×N 当日 ADV(货币计量)
    sigma:   T×N 日波动
    返回每日成本(占总资产比例)
    """
    dw = weights.diff().abs().fillna(0.0)
    # 当日换手对应的名义额(货币)
    notional = dw.mul(equity_series, axis=0)
    # ADV 占比
    q_over_v = (notional / adv).clip(upper=0.5).fillna(0.0)
    # 隐性成本(bps):sqrt 律
    impact_bps = sqrt_b * sigma * 1e4 * np.power(q_over_v, sqrt_beta)
    # 显性 + 隐性
    total_bps = fee_bps + impact_bps
    daily_cost = (dw * total_bps / 1e4).sum(axis=1)
    return daily_cost

工程要点:

第一,q_over_v 必须 clip。某些异常日单只换手会被算成 ADV 的 100% 甚至更多(数据错误或停复牌),不 clip 会让冲击成本爆炸到几百 bps。clip 上限取 30%-50% 并触发告警。

第二,保守原则。回测里默认乘一个安全系数(× 1.5 到 × 2.0),避免「拟合得刚好的成本」在实盘里被市场状态变化打破。这条经验来自实务:策略上线第一年成本几乎一定比回测高。

第三,stamp duty 必须按方向计。A 股印花税单边卖出,向量化代码里要写 dw_sell = (-weights.diff().clip(upper=0)).abs(),再单独乘印花税率。把印花税并入「平均费率」会高估买入成本、低估卖出成本。

事件驱动回测的成本嵌入

事件驱动(event-driven)回测在订单簿(或者 trades feed)层面工作,每个母单按事件序列模拟拆单、挂单、撤单、成交。它的成本不是后验计算出来的,而是在模拟成交那一刻按当时的订单簿状态自然产生的

事件驱动模型的关键是「成交假设」。三种主流写法:

  • last-trade fill:按 trades feed 里下一笔成交价直接成交,不考虑队列、不考虑深度。最乐观,最常见的初级实现。
  • next-tick mid + spread:成交价 = 下一 tick mid ± half-spread,按订单方向加 spread 损失。中等保守。
  • L2 book walking:限价单按队列位置等候、市价单沿订单簿逐档吃,吃不完的部分按 latency-aware 时间补足。最贴近实盘,但需要完整 L2 数据与队列模型。

A 股交易所(沪深)的 L2 数据成本高昂、且不公开队列位置(只公开聚合后的 10 档量),队列模型只能近似(如 Cont-Kukanov-Stoikov 的随机泊松流模型)。美股 NASDAQ/NYSE 的 ITCH/PITCH 数据公开,可以重建逐笔订单簿;CME 的 MDP 3.0 同样公开;币安提供 L2 + trades 的 WebSocket,但不提供 L3(无法重建队列)。

事件驱动回测的成本 = 显性 fee + 模拟成交价与 arrival mid 的差距,二者天然合在一起,无需额外加冲击模型。 L2 walking 不会模拟「如果我没下这个单,市场后续会是什么样」——这是冲击的反事实问题。处理方法两种:

  • 被动假设:默认自己不影响市场,所有成交假设市场会按历史 L2 演化。适用于小单(占 ADV < 0.1%)。
  • 冲击叠加:在 L2 walking 之外再叠加一个统计冲击模型,按 \(\sigma \sqrt{Q/V}\) 抬一下成交价。适用于中单到大单。
def event_driven_fill(book_snapshot, order_side, order_qty, latency_ms):
    """
    book_snapshot: list of (px, qty) on opposite side, sorted best-first
    order_side: 'BUY' / 'SELL'
    order_qty: 总量
    latency_ms: 模拟下单延迟,决定看的是哪个 tick 的 book
    Returns: (fill_qty, vwap, residual_qty)
    """
    remaining = order_qty
    notional = 0.0
    for px, level_qty in book_snapshot:
        take = min(remaining, level_qty)
        notional += take * px
        remaining -= take
        if remaining <= 0:
            break
    fill_qty = order_qty - remaining
    vwap = notional / fill_qty if fill_qty > 0 else float('nan')
    return fill_qty, vwap, remaining

这是最朴素的「市价单 walk book」实现。生产里要再叠加:(1) latency 之间 book 的更新(不能用「下单瞬间的 book」,要用「下单后 latency_ms 时刻的 book」);(2) 同一时刻其他单的影响(multi-agent 模拟);(3) 冲击叠加层。

两种回测路径的取舍

向量化路径快、易并行、易扫超参,但成本是后验近似;事件驱动慢、需要 L2 数据、调试复杂,但成本贴合实盘。生产里两个都要:用向量化扫超参选出策略候选,用事件驱动做最终确认与上线前 dry-run。中间的过渡:把事件驱动跑出的成本回拟合为向量化的 \(a + b \sqrt{q/v}\) 公式,让二者结果在中等换手区间一致。

保守原则的工程化

回测成本估计的最大风险不是「估错」而是「估得太精」——精确拟合的成本曲线在实盘里被市场状态变化打破后没有缓冲。工程上的几条保守做法:

  1. 乘 1.5-2.0 的安全系数。回测拟合出 \(a, b\) 后,实际跑回测时乘 \(\alpha = 1.5\)
  2. 加常数下限cost_bps = max(fee_bps + impact_bps, 8 bps),避免极小单被当成零成本。
  3. 不同市场分别拟合。A 股、美股、CME、币安四套独立参数,绝不混拟合。
  4. 每月重新拟合。市场流动性按月、按季度变化,固定参数 6 个月以上一定漂移。
  5. 跨日 IS 飘移监控。把实盘 IS 的 30 天滚动均值与回测预测对比,差距超过阈值触发参数复盘。

七、跨市场成本差异

到此为止讨论的都是通用结构,下面把四个主要市场(A 股、美股、CME、币安)的成本口径差异列清楚。这部分的具体数值是 2024 年常见费率,使用前应核对最新规则。

A 股(沪深)

显性费用:

  • 佣金:机构客户万 1.5 - 万 3,零售万 2 - 万 5(已含规费);
  • 印花税:单边卖出 0.05%(2023 年 8 月起);
  • 过户费:双边 0.001%(百万分之十,由中国结算统一收);
  • 经手费、证管费:合计约 0.00687%(已并入券商佣金)。

隐性成本特性:

  • T+1 制度导致同日不能买卖,套利策略无法在日内完成,机会成本结构与 T+0 市场不同。
  • 涨跌停(10%、ST 5%、创业板/科创板 20%)造成「板上无成交」事件,机会成本不可控。
  • 集合竞价(9:15-9:25、9:25 撮合、14:57-15:00 收盘集合竞价)成交价为单一价格,与连续竞价口径不同。
  • 北上资金(沪深港通)有独立的额度与申报口径,成本结构含港交所端的费用。

冲击成本拟合经验值:\(\beta \approx 0.55-0.70\),散户参与度高的中小盘股更接近 0.7(凹性更弱)。

美股

显性费用:

  • 佣金:机构 USD 0.0005-0.002 / 股,零售零佣金(Robinhood / IBKR Lite);
  • SEC Section 31 fee:卖方 USD 27.80 / USD 1,000,000(2024);
  • FINRA TAF:卖方 USD 0.000166 / 股,单笔最大 USD 8.30;
  • 不收印花税(除纽约州 STT 法律存在但豁免缴纳);
  • ATS / dark pool:另有按 venue 的额外 fee 与 rebate。

隐性成本特性:

  • Reg NMS 强制 NBBO(National Best Bid Offer),订单可被 reroute 到任意场所,TCA 必须按 venue 拆分。
  • T+1 结算(自 2024 年 5 月 28 日起,从 T+2 缩短);
  • Maker-taker rebate 模型导致挂单可获得返佣(典型 NASDAQ 0.0029 USD/股 maker rebate),TCA 报表里 maker 单的「成本」可能是负值。
  • 盘前盘后(pre-market 4:00-9:30,after-hours 16:00-20:00)流动性降至日均的 5%-15%,成本翻 3-5 倍。

冲击成本拟合:大盘 \(\beta \approx 0.55\),小盘 \(\beta \approx 0.65\),sector ETF \(\beta \approx 0.5\)

CME(期货)

显性费用:

  • 交易所费用:按合约固定,小型 E-mini S&P 500(ES)约 USD 0.32 / 合约;
  • NFA fee:USD 0.02 / 合约;
  • 清算费:USD 0.05-0.15 / 合约;
  • 经纪商佣金:机构 USD 0.10-0.30 / 合约 / 双边。

隐性成本特性:

  • 单一场所、深度集中(CME Globex),无 dark pool,TCA 的 venue 维度退化;
  • 合约规格固定(ES 一手 USD 50 × 指数点),按手数下单而非按金额;
  • 跨期价差(calendar spread)与 BTIC(Basis Trade at Index Close)有专门撮合机制,成本口径独立于 outright;
  • 流动性按时段波动剧烈:亚洲早盘流动性约美东盘中的 1/5,开盘前 30 分钟流动性最差;
  • 滚仓成本:合约到期日前的 roll 操作集中在 8 个交易日内(roll period),冲击成本是平时的 1.5-2 倍。

冲击成本:ES、NQ、CL、GC 等主力合约 \(\beta \approx 0.5\),深度极高,冲击系数低;二线合约(TY 长债、6E 欧元期货)\(\beta \approx 0.55\)

币安(现货 + 永续)

显性费用:

  • 现货:普通 0.1% / 0.1%(maker/taker),抵扣 BNB 0.075% / 0.075%,VIP 9 级 0.020% / 0.040%;
  • 永续 USDT 本位:0.02% / 0.05%(VIP 0),VIP 9 级 -0.005% / 0.017%(maker 负费率,做市补贴);
  • Funding rate:每 8 小时按 USDT 永续合约 mark-index spread 结算,由多空互付(不流向交易所)。Funding 是「持仓成本」而非「交易成本」,但持仓策略必须计入。
  • 没有印花税、没有过户费。

隐性成本特性:

  • 24/7 全天候交易,流动性沿一日 U 型分布(亚盘最深、欧盘次之、美盘晚上较稀);
  • API 限频:现货 6000 weight/分钟,永续 2400 order/10秒,过载会导致部分单子被拒(机会成本来源);
  • WebSocket 推送有 2-5ms 抖动,HFT 必须 collocated;
  • 订单簿存在 wash trading 与 layering,L2 数据噪声大,需要 self-trade 与异常订单过滤;
  • 主流币(BTCUSDT、ETHUSDT)冲击系数极低,long-tail altcoin 冲击可达主流币的 5-10 倍。

冲击成本:BTCUSDT \(\beta \approx 0.5\),ETHUSDT \(\beta \approx 0.5\),市值前 50 之外 \(\beta \approx 0.55-0.65\)

跨市场建模总结

把上面四个市场的关键参数列在一张表里:

市场 主要显性 fee 隐性 β 时段特性 TCA 关键维度
A 股 万 1.5 + 卖印 0.05% 0.55-0.70 T+1, 涨跌停, 集合竞价 时段、停牌前后
美股 监管 fee 极低, maker rebate 0.50-0.65 Reg NMS, 盘前盘后 venue, liquidity flag
CME 单合约固定 fee ≈ 0.5 24x6, 深度集中 合约月份, roll period
币安 现货 0.075%, 永续 0.02/0.05 0.50-0.65 24/7, U 型流动性 时区, BTC vs altcoin

八、监控与改进

回测里把成本估对、上线时把成本控住,仅是开始。生产里更难的是持续监控成本是否漂移、按维度归因找到改进抓手、与执行算法形成反馈闭环

实时 TCA

生产 TCA 不能只跑日终批处理。理由有三:

  1. 执行算法的失误(如挂错档位、被前置交易、触发交易所限频)必须在小时级别发现,否则一天可以亏掉一周的 alpha。
  2. 市场状态突变(流动性骤降、波动飙升)要求执行参数动态调整,参数调整需要实时成本反馈作输入。
  3. 监管要求(MiFID II RTS 27 季度披露、Rule 605 月度披露)的报送数据需从实时系统汇集而非事后重组。

实时 TCA 的工程要求:

  • 流式架构:Kafka 接入 fills + quotes,Flink / Spark Structured Streaming 做窗口聚合,5 分钟滚动 IS。
  • 延迟容忍:成交回报到 IS 落地端到端延迟 < 30 秒。
  • 可重放:所有原始数据(fills / quotes / orderbook snapshots)持久化到时序数据库(KDB+ / ClickHouse / Druid),随时回放。
  • 告警阈值:单只标的 IS 超 3σ、母单 fill ratio 低于 80%、自身 spread loss 占比超 50% 等触发。

分维度归因

实时 TCA 仪表盘上必须看到的几个维度:

  • 按策略:策略 A 的执行成本是不是比策略 B 高?为什么?是 alpha 弱(信号小,被成本吃掉)还是单子大(占 ADV 高)?
  • 按算法:VWAP / TWAP / IS / POV / Implementation Shortfall 各算法的成本对比,哪个在哪个市场状态下最优。
  • 按经纪 / venue:同一个母单在不同经纪、不同 venue 上的成本,用于砍掉表现差的对手方。
  • 按时段:开盘前 30 分钟 vs 中段 vs 收盘前 30 分钟。
  • 按订单大小桶:占 ADV 0.1%-0.5% / 0.5%-2% / 2%-5% / 5%+ 四档的成本。
  • 按市场状态:高波动日(IV percentile > 80)vs 低波动日,验证冲击系数是否随波动放大。

每个维度的归因输出都要进入策略侧的反馈:经纪 X 的执行总比经纪 Y 差 5bps → 把母单分配比例从 X:Y = 50:50 调到 30:70;高波动日 IS 算法成本比 VWAP 多 8bps → 高波动日切回 VWAP;占 ADV 5% 以上的母单成本不可控 → 拒绝单一信号生成超过 ADV 5% 的目标,强制策略层切日内分散。

与执行算法的联动

成本监控不能只产出 PDF 报告,必须形成调参闭环。下面是两种最有效的反馈:

反馈一:动态切算法。维护「市场状态 → 算法选择」的查找表,按当日开盘时的实现波动、bid-ask spread、ADV 占比预估自动切换算法。

def select_algo(market_state, parent):
    iv_pct = market_state['iv_percentile']  # 0-100
    spread_bps = market_state['spread_bps']
    q_over_adv = parent['target_qty'] / market_state['adv_estimate']

    if q_over_adv > 0.05:
        return 'POV_5'  # Percentage of Volume,限制不超过 5% 参与率
    if iv_pct > 80 and spread_bps > 20:
        return 'VWAP_FULLDAY'  # 高波动 + 宽 spread,拉长执行
    if iv_pct < 20 and parent['urgency'] == 'high':
        return 'IS_AGGRESSIVE'  # 低波动且急单,前置执行
    return 'VWAP_DEFAULT'

反馈二:动态调冲击系数。冲击系数 \(\eta, \gamma\) 不是固定值,按本机构最近 30 天成交回报每日重新拟合。生产里用 EWMA 让新数据权重更高:

def update_impact_params(history_fills, history_params, alpha=0.05):
    """
    每日收盘后调用:把当日 fills 拟合得到的当日参数,
    按 EWMA 系数 alpha 与历史参数混合。
    """
    today_params = fit_impact(history_fills.tail_today())
    new_params = {}
    for k in ['b', 'beta']:
        new_params[k] = (1 - alpha) * history_params[k] + alpha * today_params[k]
    return new_params

工程经验值:\(\alpha = 0.05\)(半衰期约 14 天)能在「跟上市场变化」与「不被单日噪声放大」之间取得平衡。重大事件(FOMC、国内政治会议、加密黑天鹅)窗口期内 \(\alpha\) 临时调到 0.2。

改进抓手:从 TCA 到策略

最有意义的 TCA 输出是策略层的改进而不是执行台的改进。三类常见改进:

  1. alpha-cost 联合优化:原来策略层是「最大化夏普」,改成「最大化 (夏普 − 0.5 × IS_bps × 换手)」,换手在策略目标函数里被惩罚而不只是事后扣减。这个变化对中频策略效果显著,能把策略生命周期从 6 个月延长到 18 个月。

  2. alpha-decay 与执行时长的联动:原来执行时长由风控固定(如「30 分钟内必须执行完」),改成按 alpha 半衰期反推。半衰期 1 小时的 alpha,30 分钟执行窗合理;半衰期 5 分钟的 alpha,30 分钟窗会让执行成本吃掉一半。让执行时长 = α 半衰期 × 系数。

  3. 拒绝低质量信号:策略层产生的信号,预估 IS 成本超过预期 alpha 的 70% 直接拒单(不发到执行台)。这个简单规则能切掉 20%-40% 的低质量单子,对总 PnL 是显著正贡献。

一个完整的实时 TCA 数据流

把上面所有要素串起来,生产里实时 TCA 的数据流大致如下:

策略层 ─┐                    ┌─→ 风控审批 ─→ FIX 引擎 ─→ 交易所
        │                    │                              │
        └─→ 母单 (parent) ─┐ │                              │
                           │ │                              ↓
                           ↓ │                          成交回报
                       Order Manager                    (ExecReport)
                           │ │                              │
                           ↓ │                              │
                       拆单算法                              │
                       (VWAP/IS)                            │
                           │                                │
                           ↓                                │
                       子单 (child) ───────────────────────┘
                           │
        ┌──────────────────┴──────────────────┐
        ↓                                     ↓
    Kafka: fills                        Kafka: quotes (L1/L2)
        │                                     │
        └──────────────┬──────────────────────┘
                       ↓
               Flink / Spark Streaming
                       │
        ┌──────────────┼─────────────────────────────────┐
        ↓              ↓                                 ↓
    实时 IS       告警 (3σ / fill_ratio < X)         冷数据归档
    (5min 滚动)   ──→ Slack/钉钉                     (ClickHouse / KDB+)
                                                          │
                                                          ↓
                                                  日终批处理 TCA
                                                  (按算法/经纪/品种归因)
                                                          │
                                                          ↓
                                                  反馈到策略 / 执行调参

工程上几个关键点:

第一,fills 与 quotes 必须时间戳对齐。FIX 引擎用机器时间,交易所返回的 transaction time 用交易所时间,两者差几十毫秒到几百毫秒不等。生产里以交易所时间为准做 IS 计算,机器时间仅用作 latency 监控的辅助字段。

第二,孤儿单(orphan fills)必须告警。fills 里出现没有对应 parent 的成交(一般是 order modify / cancel/replace 后状态没同步),TCA 要单独记录并触发对账,不能直接丢掉。

第三,rerouted 母单的 IS 归并。一个母单被风控拒了之后被策略层重新生成新母单,二者在 TCA 里要按「策略意图」合并归一(按 strategy_intent_id 关联),而不是按 parent_id 拆成两笔独立的失败单。

第四,多账户聚合。同一策略在多个账户(自营、客户、专户)跑,IS 报表的「策略层口径」要按 strategy_id 聚合,而「合规层口径」要按 account_id 拆分,两套口径并行。

与执行算法选型的反馈

最后给一张实战上常用的「成本 - 算法 - 市场状态」三维查找表(数值仅为某机构 A 股中盘股近一年实测中位数,不代表通用结论):

市场状态  算法 VWAP TWAP IS-AC POV-5% POV-10%
低波动、宽 spread 12 15 18 11 14
低波动、窄 spread 7 10 13 8 12
高波动、宽 spread 25 22 19 28 35
高波动、窄 spread 15 14 12 18 24
趋势日(单边) 18 15 9 22 28

读法:每个格子是相对到达 mid 的 IS(bps)。读出几条经验:

  • 低波动条件下 VWAP / POV 类算法占优,因为时间风险小、慢拆能省 spread;
  • 高波动 + 趋势日 IS-AC 显著占优,因为前重后轻能 hedge 时间风险;
  • POV 类算法在窄 spread 情境下本身没有 spread 优势,反而因强制「保持参与率」被市场利用;
  • TWAP 在窄 spread 低波动下与 VWAP 接近,但任何其他情境下都次于 VWAP,是「永远不是最优、永远不是最差」的稳健选择。

把这张表沉淀进算法选择器,再叠加策略层的 urgency 标签(「这个信号必须 30 分钟内执行」vs「全天均匀执行即可」),就构成了完整的算法选型反馈闭环。表本身不是结论,是工程方法——每家机构应该按自己的成交回报数据把这张表填出来,按月刷新。

TCA 系统的反模式

最后列几条生产 TCA 系统里见过的反模式,避坑:

  • 只看均值、不看尾部:均值 IS 8bps 看起来不错,p95 却 35bps,意味着每周有几笔母单严重失败,长期下来这部分尾部成本累积超过总成本的 40%。
  • 报表口径每月变:TCA 报表的字段定义、聚合维度、benchmark 选择频繁变化,跨月对比失去意义。生产里口径要冻结至少 6 个月,新口径作为并行报表 A/B 跑 3 个月再切换。
  • 把 TCA 当合规工具不当工程工具:合规交付的 TCA 只回答「我们执行符合最佳执行义务吗」,工程意义的 TCA 必须回答「下个月哪几笔母单可能成本超标」。两套报表共存,不要混用。
  • 把 venue 归因做得太细:把 venue 分到 50 个 dark pool 并对每个独立估均值 IS,每个桶样本数过少(< 30 笔/月),均值估计标准误大于均值本身,结论无意义。先按场所类型聚类(lit / dark / wholesaler / SDP)再细分。
  • 不区分 algo 内部子算法:报表里只有「VWAP」一种,实际生产 VWAP 算法内部有十几个小开关(aggressive aggression 0-100、catch-up 阈值、被动单档位偏移、参与率上限),不区分这些参数的话「VWAP 算法的成本」是十几个版本的混合均值,归因没法落地。

九、合规与风险声明

最后再次重申合规边界与风险声明:本文所有代码、数值与示例均为教学用途,未经过完整生产审计。商用 TCA 系统应使用机构内部授权的成交回报数据按本文方法重新拟合参数,绝对不能直接套用文中的 \(\beta\)\(\eta\)\(\gamma\) 经验值。冲击成本与滑点估计的数值差异可以达到一个数量级,套用错误参数产生的资金损失由使用者自负。

监管口径方面,最佳执行(best execution)义务在不同司法辖区有不同要求:

  • 欧盟 MiFID II RTS 27(场所执行质量)、RTS 28(投资公司前 5 大执行场所)、Article 27 Principal Adverse Impact 披露;
  • 美国 SEC Rule 605(市场中心执行质量月度报告)、Rule 606(订单路由季度报告)、FINRA Rule 5310(最佳执行)、Reg NMS Rule 611(订单保护规则);
  • 英国 FCA COBS 11.2A(最佳执行,与 MiFID II 平行);
  • 中国证监会《证券公司客户资产管理业务管理办法》、《证券期货经营机构落实资产管理业务「八条底线」禁止行为细则》对最佳执行义务的相关条款;
  • 中国证券业协会《证券公司客户资产管理业务规范》对成本披露的具体要求。

上述监管文档的口径、字段定义、报送频率、披露范围与本文教学口径有差异,正式合规系统必须按监管文档原文实现,本文不替代任何正式监管文档。任何把示例代码部署到生产环境或合规报送系统的尝试都需要经过完整审计与法律审核,作者不对此承担任何责任。

涉及个人投资者的部分还要遵守适当性管理、客户告知、风险揭示等要求。本文提到的所有费率(佣金、印花税、监管费等)以撰写时的公开规则为准,可能随交易所、监管机构的政策调整而变化,使用前必须核对最新规则。币安、CME、A 股、美股的具体费率口径以官方公告为准。


参考文献

  1. Perold, A. F. (1988). “The Implementation Shortfall: Paper Versus Reality.” Journal of Portfolio Management, 14(3).
  2. Almgren, R., & Chriss, N. (2000). “Optimal Execution of Portfolio Transactions.” Journal of Risk, 3(2).
  3. Almgren, R., Thum, C., Hauptmann, E., & Li, H. (2005). “Direct Estimation of Equity Market Impact.” Risk, 18(7).
  4. Bertsimas, D., & Lo, A. W. (1998). “Optimal Control of Execution Costs.” Journal of Financial Markets, 1(1).
  5. Bouchaud, J.-P., Gefen, Y., Potters, M., & Wyart, M. (2004). “Fluctuations and Response in Financial Markets: The Subtle Nature of ‘Random’ Price Changes.” Quantitative Finance, 4(2).
  6. Bouchaud, J.-P., Farmer, J. D., & Lillo, F. (2009). “How Markets Slowly Digest Changes in Supply and Demand.” In Handbook of Financial Markets: Dynamics and Evolution, North-Holland.
  7. Hasbrouck, J. (1991). “Measuring the Information Content of Stock Trades.” Journal of Finance, 46(1).
  8. Cont, R., Kukanov, A., & Stoikov, S. (2014). “The Price Impact of Order Book Events.” Journal of Financial Econometrics, 12(1).
  9. Frazzini, A., Israel, R., & Moskowitz, T. J. (2018). “Trading Costs.” Working Paper, AQR Capital Management.
  10. Kyle, A. S. (1985). “Continuous Auctions and Insider Trading.” Econometrica, 53(6).
  11. Glosten, L. R., & Milgrom, P. R. (1985). “Bid, Ask and Transaction Prices in a Specialist Market with Heterogeneously Informed Traders.” Journal of Financial Economics, 14(1).
  12. Hasbrouck, J. (2007). Empirical Market Microstructure: The Institutions, Economics, and Econometrics of Securities Trading. Oxford University Press.
  13. Harris, L. (2003). Trading and Exchanges: Market Microstructure for Practitioners. Oxford University Press.
  14. Cartea, Á., Jaimungal, S., & Penalva, J. (2015). Algorithmic and High-Frequency Trading. Cambridge University Press.
  15. Gatheral, J. (2010). “No-Dynamic-Arbitrage and Market Impact.” Quantitative Finance, 10(7).
  16. Obizhaeva, A. A., & Wang, J. (2013). “Optimal Trading Strategy and Supply/Demand Dynamics.” Journal of Financial Markets, 16(1).
  17. Tóth, B., Lemperiere, Y., Deremble, C., De Lataillade, J., Kockelkoren, J., & Bouchaud, J.-P. (2011). “Anomalous Price Impact and the Critical Nature of Liquidity in Financial Markets.” Physical Review X, 1(2).
  18. Madhavan, A. (2002). “VWAP Strategies.” Trading, 2002(1).
  19. Kissell, R. (2013). The Science of Algorithmic Trading and Portfolio Management. Academic Press.
  20. Johnson, B. (2010). Algorithmic Trading & DMA: An Introduction to Direct Access Trading Strategies. 4Myeloma Press.
  21. SEC (2005). “Regulation NMS.” 17 CFR Parts 200, 201, 230, 240, 242, 249, and 270.
  22. ESMA (2017). “Regulatory Technical Standards on the Application of Position Limits to Commodity Derivatives.” MiFID II RTS 27 / RTS 28.
  23. FINRA (2024). “Rule 5310: Best Execution and Interpositioning.”
  24. CME Group (2024). “CME Globex Reference Guide and Fee Schedule.”
  25. Binance (2024). “Spot and Futures Fee Schedule.” Binance Documentation.
  26. 中国证券业协会 (2023). “证券公司客户资产管理业务规范.”
  27. 中国证券登记结算公司 (2023). “结算业务实施细则.”
  28. 上海证券交易所 (2024). “交易规则.”
  29. Torre, N. G. (1997). “BARRA Market Impact Model Handbook.” BARRA Inc.
  30. Grinold, R. C., & Kahn, R. N. (1999). Active Portfolio Management. McGraw-Hill, 2nd edition.

系列导航

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-01 · quant

【量化交易】市场微结构:订单簿、价差、流动性、冲击

系统讲解市场微结构的核心概念与可计算工具:限价订单簿的数据模型、报价/有效/已实现价差、Roll 模型、四维流动性度量、Kyle's lambda、订单流不平衡(OFI)、Almgren-Chriss 框架下的临时与永久冲击、PIN 与 VPIN、Hawkes 过程,并给出基于 polars 的 L2 增量处理与系数估计代码。

2026-05-01 · quant

【量化交易】量化交易全景:从信号到订单的工程链路

量化交易不是策略写得好就能赚钱,更难的是把数据、特征、因子、信号、组合、执行、风控、复盘这八段链路在工程上连成一条不漏数据、不串时间、不丢订单的流水线。本文是【量化交易】系列的总目录与读图,给出八段链路的输入输出、失败模式、不变量清单,并用研究流程图把从一个想法到一笔实盘订单之间所有该过的卡点串起来。

2026-05-01 · quant

【量化交易】市场结构:交易所、做市商、暗池、ECN

系统梳理全球市场结构(Market Structure)的工程图景:从证券交易所、衍生品交易所、加密交易所,到做市商、暗池、ECN/ATS,再到 Maker-Taker 收费、PFOF、Reg NMS 与 MiFID II 的监管影响;给出量化策略选择交易场所的判断框架与基于 ccxt 的多交易所行情聚合代码。

2026-05-01 · quant

【量化交易】订单类型与执行语义:限价、市价、IOC、FOK、冰山

把 Limit、Market、IOC、FOK、Iceberg、Stop、MOO/MOC 这些常被混为一谈的订单类型还原为价格、数量、时效、可见性、触发五个独立维度,并对照 A 股、港股、美股、CME、Binance 五个市场的实际语义差异,给出量化系统中的订单工厂、状态机与风控前置校验的工程实现。