σμ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 学习路线›模块 1 · 面向量化的 Python 基础›主题 1.33·~45分钟入门

随机变量与期望

路线进度模块 1/11 · 主题 33/33
1.33 随机变量与期望
—实验得分— / 100总 XP—连续天数—

一个取值尚未确定的数

随机变量是取值取决于偶然性的量:骰子的点数、明天的收益率、股票到期时的价格。离散随机变量取一列可能的值,每个值带有一个概率。这些值和概率合起来就是它的分布。概率不会为负,且总和为 1。在 Python 中它就是两个等长的列表 values 和 probs。一个公平骰子是 values = [1, 2, 3, 4, 5, 6],probs = [1/6] * 6。

import math

values = [1, 2, 3, 4, 5, 6]
probs = [1 / 6] * 6

# A valid distribution: no negatives, and the probabilities add up to 1
print(all(p >= 0 for p in probs))             # True
print(math.isclose(sum(probs), 1.0))          # True (compare floats with isclose)

# A stock tomorrow: down 5%, up 2% or up 8%
returns = [-0.05, 0.02, 0.08]
chances = [0.2, 0.5, 0.3]
print(math.isclose(sum(chances), 1.0))        # True

期望:以概率加权的平均值

期望值 E[X] 是多次重复后得到的平均:把每个值乘以它的概率再相加,sum(v * p)。骰子的 E[X] = 3.5,这是它永远掷不出的数,但没关系:它是长期平均,而不是对单次结果的预测。期望是线性的:E[a*X + b] = a*E[X] + b。函数的期望 E[g(X)] 先对每个值应用 g:sum(g(v) * p)。注意:一般来说 E[g(X)] 不等于 g(E[X])。期权的收益 max(S - K, 0) 就是典型例子。

def expected_value(values, probs):
    return sum(v * p for v, p in zip(values, probs))

die = [1, 2, 3, 4, 5, 6]
fair = [1 / 6] * 6
print(round(expected_value(die, fair), 4))            # 3.5

# E[g(X)]: apply g to each value first. Call payoff with strike 100:
prices = [90, 100, 110, 120]
chances = [0.1, 0.4, 0.3, 0.2]
mean_price = expected_value(prices, chances)
print(mean_price)                                      # 106.0
print(expected_value([max(s - 100, 0) for s in prices], chances))  # 7.0  = E[max(S - K, 0)]
print(max(mean_price - 100, 0))                        # 6.0  = max(E[S] - K, 0), not the same

方差:离散程度有多大

两项投资可以有相同的预期收益,却有截然不同的风险。方差衡量围绕均值的离散程度:Var(X) = E[(X - mu)^2],也可算作 E[X^2] - E[X]^2。标准差是它的平方根,单位与 X 相同;对收益率来说就是波动率。对骰子,Var = 35/12 = 2.9167。两者都由 X 的函数的期望构成,所以写好一个 expected_of(g, values, probs),均值、二阶矩及后续的一切就都有了。

def expected_value(values, probs):
    return sum(v * p for v, p in zip(values, probs))

def expected_of(g, values, probs):
    return expected_value([g(v) for v in values], probs)

def variance(values, probs):
    mean = expected_of(lambda v: v, values, probs)
    return expected_of(lambda v: v * v, values, probs) - mean ** 2

die, fair = [1, 2, 3, 4, 5, 6], [1 / 6] * 6
print(round(variance(die, fair), 4))                   # 2.9167

returns, chances = [-0.05, 0.02, 0.08], [0.2, 0.5, 0.3]
print(round(expected_value(returns, chances), 4))      # 0.024
print(round(variance(returns, chances), 6))            # 0.002044

模拟与大数定律

也可以通过抽样来估计期望。rng.choices(values, weights=probs, k=n) 返回 n 次抽样,随着 n 增大,样本均值会接近 E[X]:这就是大数定律,也是蒙特卡罗定价的核心。使用你自己的 random.Random(seed),让运行可重复,也不干扰其他代码。抽样少时均值噪声大;误差只按 n 的平方根的倒数缩小,所以抽样次数增加 100 倍,精度只提高 10 倍。

import random

die, fair = [1, 2, 3, 4, 5, 6], [1 / 6] * 6

def sample_mean(values, probs, n, seed):
    rng = random.Random(seed)                    # private, repeatable generator
    draws = rng.choices(values, weights=probs, k=n)
    return sum(draws) / n

for n in (10, 1000, 100000):
    print(n, round(sample_mean(die, fair, n, 1), 4))
# 10 3.1
# 1000 3.598
# 100000 3.5017       (closing in on 3.5)

print(sample_mean(die, fair, 1000, 1) == sample_mean(die, fair, 1000, 1))  # True: same seed, same answer
使用前先检查分布如果一个分布的概率之和不为 1、含有负数或与取值长度不一致,就会在没有任何报错的情况下给出毫无意义的期望。要先校验:长度相等、没有负数、math.isclose(sum(probs), 1.0),否则引发 ValueError。还要记住,期望是长期平均:期望为正的策略在某一天仍可能亏钱。
  • 分布由 values 和 probs 构成(无负数、和为 1);E[X] = sum(v * p)。
  • E[g(X)] = sum(g(v) * p),不等于 g(E[X]);Var = E[X^2] - E[X]^2。
  • 样本均值收敛到 E[X](大数定律);使用你自己的 random.Random(seed)。

下一节:NumPy 数组与 dtype,本课程其余部分所依赖的快速、带类型的数组。

实验:评分内容

  • 用 expected_value 按概率对结果加权平均25 分
  • 用 expected_of 计算收益的期望25 分
  • 用 variance 衡量离散程度25 分
  • 用 sample_mean 通过模拟估计期望25 分
资源管理器 · lab-33-1
▾ lab-33-1
…
bash · 浏览器内
learner@quantcraft:~/lab-33-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

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

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

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

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

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