第 5 · 20 分钟
订单类型与执行
市价、限价、止损 — 以及 PortfolioEngine 如何模拟成交。
订单是给券商的指令。新手容易被术语淹没——市价、限价、止损、收盘市价——但在系统化交易中,你通常用 `set_target_weights` 表达*意图*,再由基础设施把它翻译成具体订单。
Definition: Execute now at the best available price.
Certainty of fill (if liquidity exists).
Price uncertainty — you pay the spread + slippage.
Quant Buffet: PortfolioEngine rebalances at the daily close with slippage applied — similar to a market-on-close intent.
真正重要的四种订单
| 订单类型 | 保证什么 | 放弃什么 | 量化何时使用 |
|---|---|---|---|
| 市价单 | 一定成交 | 价格 | 流动 ETF 上的小额订单 |
| 限价单 | 价格上限 / 下限 | 是否成交 | 清淡标的,或相对成交量偏大的订单 |
| 止损(市价) | 会尝试离场 | 完全放弃价格 | 很少用 — 参见闪崩那一课 |
| 收盘市价单(MOC) | 官方收盘价 | 盘中选择权 | 信号本身*就是*收盘价的日频策略 |
收盘市价单值得特别关注,因为它最接近实验室的模拟方式。收盘集合竞价通常是美股全天流动性最好的时刻——往往占到全天成交量的约十分之一——所以基于日收盘信号并提交 MOC 订单的策略,恰好是在深度最佳时成交。这是少见的「现实做法同时也是方便做法」的情形。
Quant Buffet 在代码中如何执行
def on_day(engine, dt):
# 目标 60% SPY、40% TLT —— 引擎自动买卖以达到该权重
engine.set_target_weights(dt, {"SPY": 0.6, "TLT": 0.4})- 以日收盘价作为调仓参考价。
- 滑点 2 bps:买入略贵、卖出略便宜。
- 佣金 5 bps,按每笔成交额收取。
- 先卖后买,确保买入时现金充足。
- 权重被下限截断为 0 —— 负权重变成 0(仅做多)。
- 权重之和超过 1.0 时会按比例缩放,因此你无法意外使用杠杆。
- 目标权重未变时整笔调仓被跳过,所以每日调用的策略并不会每日付费。
一次完整的调仓计算
初始 10 万美元现金。SPY 收于 500 美元,TLT 收于 90 美元,目标 60/40。
| 步骤 | SPY | TLT |
|---|---|---|
| 目标金额 | 60,000 美元 | 40,000 美元 |
| 成交价(+2 bps 滑点) | 500.10 美元 | 90.018 美元 |
| 买入股数 | 约 119.98 | 约 444.35 |
| 佣金(5 bps) | 约 30 美元 | 约 20 美元 |
这次调仓的总摩擦约为 10 万美元上的 62 美元,即约 6.2 bps,一次性成本约 0.06%——可以忽略。但如果你的信号每天都完全翻转呢?
真实示例:换手率如何吃掉一个好信号
| 策略风格 | 每年往返次数 | 大致年化成本拖累 |
|---|---|---|
| SPY 上的 200 日均线趋势 | 2–6 | 约 0.03–0.09% |
| 月度动量轮动 | 约 12 | 约 0.2% |
| 周度轮动 | 约 50 | 约 0.7% |
| 日度均值回归、满仓翻转 | 约 250 | 约 3.5% 或更高 |
算术非常直白:在实验室模型下,每次完整往返约耗 14 bps(卖出与买入各承担 5 bps 佣金 + 2 bps 滑点)。250 次约等于每年 3.5%,且尚未计税。许多已发表的均值回归 edge 毛收益仅值每年 2%–4%——也就是说,仅执行成本就足以吞掉整个信号。每当日频策略表现疲弱,先检查换手率,再怀疑想法本身。
# 降低换手率的廉价做法:只在变化足够大时才调仓
state = {"weights": {}}
def on_day(engine, dt):
target = compute_weights(dt) # 你的信号
current = state["weights"] # 上一次设定的目标权重
keys = set(target) | set(current)
drift = sum(abs(target.get(s, 0.0) - current.get(s, 0.0)) for s in keys)
if drift < 0.05: # 总偏移小于 5%
return # 直接跳过本次调仓
state["weights"] = dict(target)
engine.set_target_weights(dt, target)仅做多的权重算术
| 权重 | 含义 |
|---|---|
{"SPY": 1.0} | 100% SPY,0% 现金 |
{"SPY": 0.5, "TLT": 0.5} | 满仓,等权两分 |
{"SPY": 0.6, "TLT": 0.3} | 90% 投资,10% 现金拖累 |
{"SPY": 0.8, "TLT": 0.8} | 缩放为 50/50 —— 不允许杠杆 |
{"SPY": -0.5} | 截断为 0 —— 不支持做空 |
{} 或全为 0 | 清仓为现金 |
进入第 6 课前 — 您应能够
- 说清四种订单各自放弃了什么。
- 根据权重、价格、滑点与佣金计算一次调仓的成本。
- 由每年往返次数估算年化成本拖累。
- 用偏移阈值替代无脑调仓,从而降低换手率。