σμ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 学习路线›模块 8 · 随机模型与机器学习›主题 8.8·~1小时高级

用于收益率的 scikit-learn 流水线

路线进度模块 8/11 · 主题 8/10
8.8 用于收益率的 scikit-learn 流水线
—实验得分— / 100总 XP—连续天数—

基于收益率的机器学习

监督学习拟合从特征 X 到目标 y 的函数。在金融中,目标通常是未来收益率,特征是事先已知的东西:过去的收益率、波动率、成交量。scikit-learn 提供统一的接口:每个模型都有 fit(X, y) 和 predict(X)。与教科书问题的一个关键区别是信噪比:收益率几乎是纯噪声,即使好模型也只能解释极小一部分,而草率的设置会发现并不存在的规律。

import numpy as np

returns = np.array([0.01, -0.02, 0.015, 0.005, -0.01, 0.02])
n_lags = 2

# Row i holds the two returns before the target; y[i] is the return that follows them
windows = np.lib.stride_tricks.sliding_window_view(returns, n_lags)
X, y = windows[:-1], returns[n_lags:]
print(X)
print(y)                                             # the targets are one step ahead of the features

由滞后项构造特征

收益率最简单的特征就是它之前的收益率。构建滞后矩阵:第 i 行是 returns[i : i + n_lags],目标是 returns[i + n_lags],即下一个值。滑动窗口视图可以不用循环得到它。一行中的所有内容都必须在预测时刻已知,这就是应用到模型上的前视规则:某个特征若朝错误方向多移了一位,模型就能看到答案。

import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# Pipeline: scale each feature, then a ridge regression (a linear model with a penalty on large coefficients)
pipe = Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=10.0))])
print([name for name, _ in pipe.steps])

rng = np.random.default_rng(1)
returns = np.zeros(400)
noise = rng.normal(0, 0.01, size=400)
for t in range(1, 400):
    returns[t] = 0.3 * returns[t - 1] + noise[t]                    # a series with a little memory

windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
pipe.fit(X[:300], y[:300])                                          # fit on the first 300 rows
print(np.round(pipe.named_steps["model"].coef_, 4))                 # the last lag weighs most

流水线与信息泄露

Pipeline 把缩放器和模型打包在一起,使 fit 只从训练行学习缩放统计量(各特征的均值和标准差),predict 再把它们原样用到新行上。若先对整个数据集缩放再切分,测试期的均值和离散程度就会泄露进训练:这是同样的前视偏差,只是换了个地方。时间序列的切分还必须按时间顺序:用较早的行训练、较晚的行测试,绝不能随机打乱。

import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

def build_pipeline(alpha):
    return Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))])

rng = np.random.default_rng(1)
returns = np.zeros(600)
noise = rng.normal(0, 0.01, size=600)
for t in range(1, 600):
    returns[t] = 0.3 * returns[t - 1] + noise[t]

windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
model = build_pipeline(10.0).fit(X[:400], y[:400])                  # chronological split: early rows to train
pred = model.predict(X[400:])                                       # later rows to test
print(round(float(np.mean(np.sign(y[400:]) == np.sign(pred))), 3))  # direction accuracy on unseen data

诚实地评判结果

对交易信号而言,方向往往最重要,所以一个简单的评分是方向准确率:预测收益与实际收益符号相同的天数占比。50% 相当于抛硬币。真实的收益预测器能达到 51% 到 53%,测试数据上出现 60% 更可能是泄露而不是发现。相信任何结果之前,先与平凡的基准比较(总是预测上涨、预测为零)。

import numpy as np

y_true = np.array([0.01, -0.02, 0.015, -0.005, 0.02, -0.01])
y_pred = np.array([0.004, 0.001, 0.002, -0.001, 0.003, 0.002])        # the model is right on 4 of 6 signs

accuracy = float(np.mean(np.sign(y_true) == np.sign(y_pred)))
always_up = float(np.mean(np.sign(y_true) == 1))                      # the trivial baseline: always predict a rise
print(round(accuracy, 3), round(always_up, 3))                        # compare the model with the baseline
简单模型,诚实验证噪声这么大时,带正则化的线性模型(岭回归、Lasso)通常优于深层决策树或神经网络这类灵活模型,后者会记住训练期。惩罚强度 alpha 是需要调的旋钮,但要在验证期上调,绝不能在测试期上调。接下来几节会构建特征和滚动前向检验,让这一切变得诚实。
  • 滞后矩阵(sliding_window_view)由每个目标之前的收益率生成特征;一行中的所有内容在预测时必须已知。
  • Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))]) 只在训练行上学习缩放;按时间切分,绝不随机。
  • 用平凡基准评估方向(符号相同的占比);51% 到 53% 才现实,60% 暗示泄露;带正则化的线性模型适合噪声数据。

下一节:金融数据的特征工程,构造比原始滞后收益率更好的输入。

实验:评分内容

  • 用 lag_matrix 把序列变成特征25 分
  • 用 build_pipeline 串联各步骤25 分
  • 用 fit_predict 用过去训练、预测其余部分25 分
  • 用 direction_accuracy 评估方向判断25 分
资源管理器 · lab-109-1
▾ lab-109-1
…
bash · 浏览器内
learner@quantcraft:~/lab-109-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

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

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

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

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

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