σμ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 学习路线›模块 2 · 用于市场数据的 NumPy 与 pandas›主题 2.12·~40分钟中级

DatetimeIndex 与基于时间的索引

路线进度模块 2/11 · 主题 12/20
2.12 DatetimeIndex 与基于时间的索引
—实验得分— / 100总 XP—连续天数—

日期不是字符串

价格序列是时间序列:重要的是每个价格发生在何时。从文件读出的日期只是普通字符串,字符串并不知道三月在二月之后,也不知道星期一跟在星期日后面。用 pd.to_datetime 转换并把结果作为索引:DatetimeIndex 保存真正的时间戳,pandas 可以对其排序、切片、比较和拆解。务必对它排序(sort_index()),让最早的观测排在最前。

import pandas as pd

raw_dates = ["2026-03-04", "2026-03-02", "2026-03-03"]      # strings, in a messy order
closes = [102.0, 100.0, 101.0]

series = pd.Series(closes, index=pd.to_datetime(raw_dates), name="close").sort_index()
print(series)
# 2026-03-02    100.0
# 2026-03-03    101.0
# 2026-03-04    102.0
# Name: close, dtype: float64

print(isinstance(series.index, pd.DatetimeIndex))   # True

生成日历

你常常需要没人提供的日期:用来对比的网格,或回测的各个交易日。pd.date_range(start, periods=n, freq="D") 生成 n 个连续的自然日,pd.bdate_range 跳过周末,给出工作日(它不了解节假日)。freq 参数决定步长:"D" 是一天,"B" 是一个工作日,"h" 是一小时。

# Four calendar days, then six business days (the weekend is skipped)
print(pd.date_range("2026-03-01", periods=4, freq="D").strftime("%a %d %b").tolist())
# ['Sun 01 Mar', 'Mon 02 Mar', 'Tue 03 Mar', 'Wed 04 Mar']

days = pd.bdate_range("2026-02-26", periods=6)
print(days.strftime("%a %d %b").tolist())
# ['Thu 26 Feb', 'Fri 27 Feb', 'Mon 02 Mar', 'Tue 03 Mar', 'Wed 04 Mar', 'Thu 05 Mar']

按日期选择

对已排序的 DatetimeIndex,.loc 能理解字符串写成的日期。部分字符串如 "2026-03" 选出整个月,"2026" 选出整年,"2026-03-04" 选出某一天。切片 .loc["2026-03-04":"2026-03-06"] 选出一个范围,并且与按位置切片不同,两端都包含。端点不必出现在数据中:周末的日期只是匹配不到任何行,这正是你说「从月初起的全部」时想要的效果。

prices = pd.Series(
    [float(i) for i in range(20)],
    index=pd.bdate_range("2026-02-23", periods=20),   # Mon 23 Feb ... Fri 20 Mar
)

print(len(prices.loc["2026-03"]))      # 15   the business days of March in the data
print(prices.loc["2026-03-04"])        # 7.0  a single day

print(prices.loc["2026-03-04":"2026-03-06"])
# 2026-03-04    7.0
# 2026-03-05    8.0
# 2026-03-06    9.0
# Freq: B, dtype: float64

print(prices.loc["2026-03-01":"2026-03-03"].tolist())   # [5.0, 6.0]   Sunday the 1st matches nothing

日期的组成部分

DatetimeIndex 把每个日期的组成部分作为属性提供:year、month、day、dayofweek(星期一是 0,星期日是 6)。每个属性都是每行一个值的数组,可以当作掩码或分组键。这样就能不用循环地问「每个星期几各有多少观测?」或「星期一会发生什么?」。记得先对索引排序:对乱序的 DatetimeIndex 切片可能得到出人意料的结果,甚至报错。

# The parts of the dates, one entry per row
print(prices.index.dayofweek[:7].tolist())   # [0, 1, 2, 3, 4, 0, 1]   Monday is 0
print(prices.index.month[:7].tolist())       # [2, 2, 2, 2, 2, 3, 3]

# How many observations fall on each weekday?
print(prices.groupby(prices.index.dayofweek).size().to_dict())   # {0: 4, 1: 4, 2: 4, 3: 4, 4: 4}

# What do the Mondays look like?
print(prices[prices.index.dayofweek == 0].tolist())   # [0.0, 5.0, 10.0, 15.0]
留意范围的终点不带时间的日期字符串表示午夜。日线数据中 .loc["2026-03-04":"2026-03-06"] 包含 6 日,但日内数据会在 6 日 00:00 就结束,丢掉整天。当数据比一天更细时,请检查结果的最后一个时间戳。
  • pd.to_datetime(字符串) 生成 DatetimeIndex;sort_index() 让最早的在前;date_range 与 bdate_range 生成日历。
  • .loc["2026-03"] 选出一个月,.loc["2026-03-04":"2026-03-06"] 选出包含两端的范围。
  • index.year、index.month、index.dayofweek 给出每个日期的组成部分,无需循环即可筛选或分组。

下一节:重采样,把日度价格转换成周度、月度甚至更细的K线。

实验:评分内容

  • 用 parse_prices 构建带日期的价格序列25 分
  • 用 prices_in_month 选出一个月的数据25 分
  • 用 prices_between 选出一段日期范围25 分
  • 用 count_by_weekday 统计每个星期几的观测数25 分
资源管理器 · lab-45-1
▾ lab-45-1
…
bash · 浏览器内
learner@quantcraft:~/lab-45-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

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

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

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

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

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