一个取值尚未确定的数
随机变量是取值取决于偶然性的量:骰子的点数、明天的收益率、股票到期时的价格。离散随机变量取一列可能的值,每个值带有一个概率。这些值和概率合起来就是它的分布。概率不会为负,且总和为 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 answermath.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 分