σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academy学习路线
Ctrl K
AO
+1.2k 位学习者
登录加入 ↗
学习路线
AO
+1.2k 位学习者
登录
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python 学习路线›模块 11 · 生产系统与前沿模型›主题 11.5·~1小时专家

VWAP 执行

路线进度模块 11/11 · 主题 5/13
11.5 VWAP 执行
—实验得分— / 100总 XP—连续天数—

市场交易时才交易

成交量加权平均价格是所有成交的平均价格,每笔成交按其大小加权:sum(price * volume) / sum(volume)。它是机构交易中最常见的基准,因为它代表当天一个普通参与者付出的价格。VWAP 算法通过*按市场成交量的比例*交易来争取达到或超过它:市场活跃时(开盘、收盘)多做,安静时(午间)少做。TWAP 跟随时钟,VWAP 则跟随人群,所以你的每笔订单只占其他人所做交易的类似比例,对价格的推动更小。

# The benchmark: each price counts as much as the volume that traded at it
def vwap(prices, volumes):
    return sum(p * v for p, v in zip(prices, volumes)) / sum(volumes)

prices = [100.0, 101.0, 102.0]
volumes = [100, 300, 600]
print(round(vwap(prices, volumes), 4))               # 101.5     most volume traded near 102, so the VWAP leans that way
print(sum(prices) / len(prices))                     # 101.0     the plain average ignores how much traded at each price

成交量分布及跟随它的计划

你无法提前知道今天的成交量,所以 VWAP 算法使用根据历史估计的成交量分布:比如过去 20 个交易日里,一天成交量中在各个区间成交的平均占比。日内成交量通常呈 U 形,开盘和收盘时高。计划随后给每个区间分配 total * 占比 股。这些很少是整数,把每个都四舍五入可能多出或少掉股数。最大余数法解决了这个问题:先给每个区间取整,再把剩下的股数每次一股地分给小数部分最大的区间,这样计划的总和始终等于订单量。

# From volumes to a profile, then to whole shares by the largest remainder method
import math

def volume_profile(volumes):
    total = sum(volumes)
    return [v / total for v in volumes]

def vwap_schedule(total, profile):
    exact = [total * share for share in profile]
    shares = [math.floor(value + 1e-9) for value in exact]
    leftover = total - sum(shares)
    for index in sorted(range(len(profile)), key=lambda i: (-(exact[i] - shares[i]), i))[:leftover]:
        shares[index] += 1
    return shares

profile = volume_profile([300, 100, 100, 200, 500, 800])       # a U-shape: busy at the open and the close
print([round(p, 3) for p in profile])                # [0.15, 0.05, 0.05, 0.1, 0.25, 0.4]
print(vwap_schedule(1003, profile))                  # [151, 50, 50, 100, 251, 401]   adds up to 1003
print(sum(vwap_schedule(1003, profile)))             # 1003

参与率上限与跟踪基准

计划只是计划,市场可能比分布预测的更安静。参与率上限把你在每个区间的交易量限制在该区间市场实际成交量的某个比例(比如 10%)之内,这样你永远不会成为交易流的主体。没能成交的股数会结转到之后的区间,因此到最后可能仍有部分未执行;那时你要决定是承担更多风险、延长窗口还是停止。一天结束后,把你的平均价与市场 VWAP 相比:以基点计的差额就是算法的跟踪误差。在上涨的市场里靠晚交易跑赢 VWAP 并不是本事;要用多日的滑点来评判算法。

# A participation cap: never more than a fraction of the market's volume, carrying the rest forward
import math

def cap_participation(schedule, market_volumes, rate):
    trades, carry = [], 0
    for scheduled, volume in zip(schedule, market_volumes):
        wanted = scheduled + carry
        done = min(wanted, math.floor(rate * volume + 1e-9))
        trades.append(done)
        carry = wanted - done
    return trades, carry

print(cap_participation([100, 100, 100], [500, 1000, 1000], 0.1))      # ([50, 100, 100], 50)
print(cap_participation([100, 0, 0, 0], [500, 1000, 400, 800], 0.1))   # ([50, 50, 0, 0], 0)   the carry is traded when the market allows it
# Following the clock (TWAP) and following the volume (VWAP) give different average prices on the same day
prices = [100.0, 100.2, 100.1, 100.4, 100.3, 100.5]
volumes = [300, 100, 100, 200, 500, 800]               # busy at the open and the close

market_vwap = sum(p * v for p, v in zip(prices, volumes)) / sum(volumes)
twap_average = sum(prices) / len(prices)                # equal pieces at every time
print(round(market_vwap, 4), round(twap_average, 4))    # 100.33 100.25
print(round((twap_average - market_vwap) / market_vwap * 10000, 1))      # -8.0   a TWAP buy beat the VWAP here, by trading light when volume was heavy
VWAP 是基准,而不是目标跟上 VWAP 只说明你像普通参与者一样交易,如果是你自己的订单推高了价格,这可能是个糟糕的结果(你的成交本身就是用来比较的 VWAP 的一部分)。还要对照到达价格检查算法,在没有用来估计的日子上测试分布,并预料分布在新闻日、半天交易日和指数再平衡日会失效。为每个子订单保留可读的日志,便于之后分析。
  • VWAP 是 sum(price * volume) / sum(volume),常用的基准;VWAP 算法按市场成交量的比例交易。
  • 根据历史估计成交量分布,并用最大余数法按它拆分订单,使整数股数正好加总。
  • 给每个区间的参与度设上限并把剩余结转;用多日的跟踪误差和滑点来评判算法。

下一节:做市,双边报价并管理由此产生的库存。

实验:评分内容

  • 用 vwap 计算基准25 分
  • 用 volume_profile 描出一天的形状25 分
  • 用 vwap_schedule 拆分订单25 分
  • 用 cap_participation 尊重市场25 分
资源管理器 · lab-134-1
▾ lab-134-1
…
bash · 浏览器内
learner@quantcraft:~/lab-134-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-134-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续