σμ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 AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 2 · NumPy y pandas para datos de mercado›Tema 2.14·~40 minIntermedio

Ventanas móviles

Posición en la rutaMódulo 2 de 11 · Tema 14 de 20
2.14 Ventanas móviles
—Puntuación del lab— / 100XP total—Racha—

Una ventana que se desliza

El precio de hoy significa poco por sí solo; los traders lo comparan con el pasado reciente. Una ventana móvil conserva las últimas n observaciones y avanza un paso cada vez, de modo que cada fecha recibe una estadística calculada con su propio pasado reciente: una media móvil, una volatilidad reciente, un máximo reciente. En pandas, series.rolling(n) describe la ventana y una estadística como .mean() la calcula en cada posición a la vez.

import pandas as pd

prices = pd.Series(
    [100.0, 102, 101, 105, 107, 106, 110],
    index=pd.bdate_range("2026-03-02", periods=7),
    name="close",
)

moving_average = prices.rolling(3).mean()
print(moving_average.round(2).tolist())
# [nan, nan, 101.0, 102.67, 104.33, 106.0, 107.67]

print(prices.rolling(3).max().tolist())   # [nan, nan, 102.0, 105.0, 107.0, 107.0, 110.0]   the 3-day high

El comienzo de la serie

Las primeras n - 1 posiciones aún no tienen una ventana completa, así que el resultado es NaN. Es honesto: la media de dos precios no es una media de 3 días. Si de verdad quieres un valor antes, min_periods=1 calcula con lo disponible, y expanding() usa todo desde el inicio en lugar de una ventana fija. Las estadísticas que necesitan varios puntos, como la desviación típica, son NaN hasta que llegan suficientes observaciones. Una serie que ya empieza con NaN, como los rendimientos simples, retrasa un paso la primera ventana válida.

# min_periods=1: use whatever is available at the start
print(prices.rolling(3, min_periods=1).mean().round(2).tolist())   # [100.0, 101.0, 101.0, 102.67, 104.33, 106.0, 107.67]

# expanding(): everything since the first day, not a fixed window
print(prices.expanding().mean().round(2).tolist())   # [100.0, 101.0, 101.0, 102.0, 103.0, 103.5, 104.43]

# Rolling volatility of the returns: the first return is NaN, so three returns exist only from the 4th day
returns = prices.pct_change()
print(returns.rolling(3).std().round(4).tolist())   # [nan, nan, nan, 0.0249, 0.0248, 0.0246, 0.0237]

De las ventanas a las señales

Las estadísticas móviles se convierten en señales de trading cuando se comparan. Un z-score dice a cuánta distancia está el precio de hoy de su propia media reciente, en unidades de su desviación típica reciente: (price - rolling mean) / rolling std. Construye el objeto móvil una vez (r = prices.rolling(3)) y úsalo dos veces. Un cruce de medias móviles compara una media rápida con una lenta: mientras la rápida está por encima, la tendencia reciente es alcista. Una comparación da booleanos, y astype("int64") los convierte en una señal 0/1. Las comparaciones con NaN son False, así que la señal es 0 hasta que existen ambas medias.

# z-score of each price against its own 3-day window
rolling = prices.rolling(3)
zscore = (prices - rolling.mean()) / rolling.std()
print(zscore.round(2).tolist())   # [nan, nan, 0.0, 1.12, 0.87, 0.0, 1.12]
# Moving-average crossover: 1 while the 2-day average is above the 4-day average
fast = prices.rolling(2).mean()
slow = prices.rolling(4).mean()
signal = (fast > slow).astype("int64")
print(signal.tolist())   # [0, 0, 0, 1, 1, 1, 1]

# Trade on the next day: the signal of today's close cannot be traded before tomorrow
print(signal.shift(1).fillna(0).astype("int64").tolist())   # [0, 0, 0, 0, 1, 1, 1]
No mires al futuroUna ventana móvil termina en la fila actual, así que solo usa el pasado. center=True pone la fila actual en medio de la ventana y usa precios futuros: queda genial en un gráfico y no se puede operar. Una señal calculada con el cierre de hoy debe aplicarse mañana, de ahí shift(1).
  • series.rolling(n).mean() (o std(), max()...) calcula una estadística sobre las últimas n observaciones en cada posición.
  • Las primeras n - 1 entradas son NaN; min_periods y expanding() lo cambian a propósito.
  • Los z-scores y los cruces de medias móviles convierten ventanas en señales; desplaza una señal antes de operarla y evita center=True.

A continuación: el manejo de datos faltantes, qué hacer con los valores NaN que las tablas de precios reales siempre contienen.

Lab: qué se califica

  • Suavizar los precios con moving_average25 pts
  • Medir el riesgo reciente con rolling_volatility25 pts
  • Estandarizar cada precio frente a su ventana con rolling_zscore25 pts
  • Convertir dos medias en una señal con ma_crossover25 pts
Explorador · lab-47-1
▾ lab-47-1
…
bash · en el navegador
learner@quantcraft:~/lab-47-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-47-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub