1. 这节究竟解决什么实际问题?¶
0.0 量化交易的数学:线性代数 的具体任务是:用向量、矩阵和 PCA 把多资产共同变化压缩为少数可解释的风险方向。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
会使用百分比和基础代数;建议先完成环境配置。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
示例优先使用仓库固定的日频复权收盘价;它不是实时行情,也不代表完整市场。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 0.0 量化交易的数学:线性代数 的输入、计算步骤、输出和局限;尤其能说明:用向量、矩阵和 PCA 把多资产共同变化压缩为少数可解释的风险方向。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
价格表示资产在特定时点的水平,收益率表示两个时点之间的相对变化。多数跨资产比较使用收益率。
所有公式都先说明分子、分母、单位与时间尺度;年化前必须确认数据频率。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
NumPy/Pandas 帮你算数和对齐索引;统计库帮你估计参数,但不替你解释经济含义。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“用向量、矩阵和 PCA 把多资产共同变化压缩为少数可解释的风险方向。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把百分比和小数混用、忘记 dropna、把日频波动率直接叫作年化波动率、把相关性当因果。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
概念演示也要避免事后选择样本或只展示支持结论的时间段。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/01_financial_concepts/00_math_foundations_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/01_financial_concepts/00_math_foundations_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Investments(Bodie, Kane, Marcus)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 01_price_and_return。本节的概念会成为理解它的输入,而不是孤立的名词。
1. 向量与矩阵:从“单挑”到“群殴”¶
在只有一只股票时,我们处理的是标量(单个数字)。但现实中我们面对的是整个市场。
向量(Vector)是什么?¶
想象你有一个投资组合,持有了苹果(AAPL)、微软(MSFT)和谷歌(GOOG)。 你可以把它们的权重写成一个权重向量 $w$: $$w = [0.4, 0.3, 0.3]$$ 这代表你 40% 的钱买苹果,剩下两家各 30%。
矩阵(Matrix)是什么?¶
矩阵就是把多个向量叠在一起。比如这三只股票过去 5 天的日收益率,就是一个 $5 \times 3$ 的矩阵:
| 日期 | AAPL | MSFT | GOOG |
|---|---|---|---|
| Day 1 | 0.01 | -0.01 | 0.02 |
| Day 2 | -0.02 | 0.00 | 0.01 |
| ... | ... | ... | ... |
| Day 5 | -0.01 | 0.04 | 0.05 |
核心直觉:矩阵就是一张 Excel 表,它是量化代码处理数据的“基本单位”。
import numpy as np
import pandas as pd
# 用权重向量计算组合收益率
weights = np.array([0.4, 0.3, 0.3]) # 权重向量
returns = np.array([0.01, -0.02, 0.03]) # 三只股票当天的收益率向量
# 组合收益 = 权重1*收益1 + 权重2*收益2 + ...
# 在线性代数里,这就是“点积”(Dot Product)
portfolio_return = np.dot(weights, returns)
print(f"今日组合收益率: {portfolio_return:.4%}")
今日组合收益率: 0.7000%
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
# 模拟 5 天 3 只股票的收益率矩阵
np.random.seed(42)
return_matrix = np.random.normal(0.001, 0.02, (5, 3))
print("收益率矩阵 (5天 x 3只股票):")
print(return_matrix)
# 一次性计算 5 天的组合收益
portfolio_returns_5d = np.dot(return_matrix, weights)
print("\n5天组合收益序列:")
print(portfolio_returns_5d)
收益率矩阵 (5天 x 3只股票): [[ 0.01093428 -0.00176529 0.01395377] [ 0.0314606 -0.00368307 -0.00368274] [ 0.03258426 0.01634869 -0.00838949] [ 0.0118512 -0.00826835 -0.0083146 ] [ 0.00583925 -0.0372656 -0.03349836]] 5天组合收益序列: [ 0.00803026 0.0103745 0.01542146 -0.0002344 -0.01889349]
3. 特征值与特征向量:看透市场的“骨架”¶
想象标普 500 指数里的 500 只股票。它们每天乱跳,看起来有 500 种不同的动向。但实际上,大多数股票是跟着“大盘”走的,或者跟着“科技板块”走的。
特征分解(Eigen-decomposition) 可以把协方差矩阵分解为若干相互正交的共同变化方向:
- 特征向量 代表了市场运动的“方向”(比如:全市场上涨、价值股领先、科技股崩盘)。
- 特征值 代表了这个方向有多重要(影响力有多大)。
在量化里,我们常说的 PCA(主成分分析) 就是利用这个原理,把 500 个混乱的维度压缩成少数几个“核心变量”。
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import yfinance as yf
# 获取三只高相关科技股的数据
tickers = ['AAPL', 'MSFT', 'GOOGL']
data = yf.download(tickers, start='2023-01-01', end='2024-01-01', progress=False)['Close']
rets = data.pct_change().dropna() # 移除了nan值的涨跌幅
# 使用 PCA 提取第一个主成分(通常代表“市场共同因子”)
pca = PCA(n_components=1)
rets_pca = pca.fit_transform(rets)
# 默认使用固定样本,保证课程与 CI 不依赖实时接口。
USE_OFFLINE_DATA = True
if USE_OFFLINE_DATA:
from pathlib import Path
import sys
docs_root = next(parent for parent in [Path.cwd(), *Path.cwd().parents]
if (parent / 'utils' / 'offline_data.py').exists())
sys.path.insert(0, str(docs_root))
from utils.offline_data import configure_yfinance_offline
configure_yfinance_offline(yf)
else:
print('在线下载为扩展步骤;请核对数据字段、时区和下载日期。')
data, data.pct_change(), data.pct_change().dropna, rets_pca.shape
(Ticker AAPL GOOGL MSFT Date 2023-01-03 123.096016 88.451683 233.452805 2023-01-04 124.365677 87.419487 223.240829 2023-01-05 123.046814 85.553589 216.624512 2023-01-06 127.574203 86.685028 219.177475 2023-01-09 128.095856 87.359932 221.311462 ... ... ... ... 2023-12-22 191.609451 140.428970 368.236603 2023-12-26 191.065140 140.458755 368.315247 2023-12-27 191.164078 139.317368 367.735260 2023-12-28 191.589706 139.178406 368.924805 2023-12-29 190.550446 138.642456 369.671906 [250 rows x 3 columns], Ticker AAPL GOOGL MSFT Date 2023-01-03 NaN NaN NaN 2023-01-04 0.010314 -0.011670 -0.043743 2023-01-05 -0.010605 -0.021344 -0.029638 2023-01-06 0.036794 0.013225 0.011785 2023-01-09 0.004089 0.007786 0.009736 ... ... ... ... 2023-12-22 -0.005548 0.007620 0.002784 2023-12-26 -0.002841 0.000212 0.000214 2023-12-27 0.000518 -0.008126 -0.001575 2023-12-28 0.002227 -0.000997 0.003235 2023-12-29 -0.005424 -0.003851 0.002025 [250 rows x 3 columns], <bound method DataFrame.dropna of Ticker AAPL GOOGL MSFT Date 2023-01-03 NaN NaN NaN 2023-01-04 0.010314 -0.011670 -0.043743 2023-01-05 -0.010605 -0.021344 -0.029638 2023-01-06 0.036794 0.013225 0.011785 2023-01-09 0.004089 0.007786 0.009736 ... ... ... ... 2023-12-22 -0.005548 0.007620 0.002784 2023-12-26 -0.002841 0.000212 0.000214 2023-12-27 0.000518 -0.008126 -0.001575 2023-12-28 0.002227 -0.000997 0.003235 2023-12-29 -0.005424 -0.003851 0.002025 [250 rows x 3 columns]>, (249, 1))
plt.figure(figsize=(12, 6))
plt.plot(rets.index, rets.mean(axis=1), label='三只股票平均收益', alpha=0.5)
plt.plot(rets.index, rets_pca, label='第一主成分 (PCA1)', linewidth=2, color='red')
plt.title("PCA 提取的市场共同动力")
plt.legend()
plt.show()
print(f"第一主成分解释了这三只股票变动的 {pca.explained_variance_ratio_[0]:.2%}")
print("这说明这三只股票虽然名字不同,但 80% 以上的时间是在跳同一支舞。")
第一主成分解释了这三只股票变动的 70.37% 这说明这三只股票虽然名字不同,但 80% 以上的时间是在跳同一支舞。
练习¶
- 创建一个 5×5 的随机收益率矩阵,用矩阵乘法计算等权组合(权重各占 20%)每天的收益率。
- 使用
np.linalg.eig()手动计算一个 3×3 协方差矩阵的特征值和特征向量,并与 PCA 结果对比。 - 对标普 500 中的 10 只股票跑 PCA,看第一主成分解释了多少比例的总方差?
下一节 → 01_price_and_return.ipynb (我们将把这些数学应用到真实的价格计算中)