σμ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.16·~40 minIntermedio

Detección de valores atípicos

Posición en la rutaMódulo 2 de 11 · Tema 16 de 20
2.16 Detección de valores atípicos
—Puntuación del lab— / 100XP total—Racha—

Un valor que no puede ser correcto

Un rendimiento diario de +30 % en una gran capitalización es una OPA o un error de datos, como un split olvidado o una coma mal puesta. Un valor atípico está lo bastante lejos de los demás como para exigir una decisión: conservarlo, corregirlo o quitarlo. Nunca lo borres solo porque estorba, ya que los choques reales son justo lo que los modelos de riesgo deben ver. El primer paso es siempre hacerlos visibles con una regla explícita y repetible.

La regla del z-score y por qué puede fallar

La regla clásica marca los valores a más de 2 o 3 desviaciones típicas de la media: z = (x - mean) / std. Es simple y funciona con datos limpios. Pero el propio atípico infla la media y sobre todo la desviación típica, así que se esconde de la regla. En el ejemplo siguiente el día de +30 % solo obtiene un z-score de 2,85 y con un umbral de 3 no se marcaría. Cuanto más extremo es el error, más se protege.

import pandas as pd

returns = pd.Series([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.30, -0.001, 0.0, 0.002])

z = (returns - returns.mean()) / returns.std()
print(z.round(2).tolist())
# [-0.28, -0.34, -0.31, -0.29, -0.36, -0.3, 2.85, -0.33, -0.32, -0.3]

print(returns.mean().round(4), returns.std().round(4))   # 0.0305 0.0947   one day moved the mean tenfold
print(returns.drop(6).std().round(4))                    # 0.0026          the std without that day

Reglas robustas: IQR y MAD

Las estadísticas robustas apenas se mueven por los atípicos que persiguen. El rango intercuartílico IQR = Q3 - Q1 mide la dispersión de la mitad central de los datos, y los valores más allá de Q1 - 1.5 * IQR o Q3 + 1.5 * IQR quedan fuera de los límites de un diagrama de caja. El MAD (desviación absoluta mediana) sustituye la desviación típica por una mediana, y el z-score modificado 0.6745 * (x - median) / MAD se lee como un z-score normal (3,5 es un corte habitual). Ambos marcan sin problema el día de +30 %.

# The fences of a box plot
q1, q3 = returns.quantile(0.25), returns.quantile(0.75)
spread = q3 - q1
lower, upper = q1 - 1.5 * spread, q3 + 1.5 * spread
print(round(lower, 4), round(upper, 4))                    # -0.006 0.008
print(returns[(returns < lower) | (returns > upper)].to_dict())   # {6: 0.3}

# The modified z-score, built on the median
median = returns.median()
mad = (returns - median).abs().median()
modified_z = 0.6745 * (returns - median) / mad
print(modified_z.round(1).tolist())   # [0.8, -1.2, -0.2, 0.5, -1.9, 0.2, 100.7, -0.8, -0.5, 0.2]

Winsorización: recortar en lugar de borrar

Borrar una fila también borra la información de sus otras columnas y rompe el espaciado de las fechas. La winsorización conserva todas las filas y acerca los valores extremos a cuantiles elegidos, por ejemplo los de 1 % y 99 %: lo que está por debajo del cuantil de 1 % pasa a ser ese cuantil, lo que está por encima del de 99 % pasa a ser este. En pandas es clip(lower, upper) con los límites tomados de quantile. La forma de la serie sobrevive y los extremos pierden su influencia sobre medias y regresiones.

low, high = returns.quantile(0.1), returns.quantile(0.9)
print(round(low, 4), round(high, 4))             # -0.0022 0.0336

clipped = returns.clip(lower=low, upper=high)
print(clipped.round(4).tolist())
# [0.004, -0.002, 0.001, 0.003, -0.0022, 0.002, 0.0336, -0.001, 0.0, 0.002]
#                                 ^ raised                  ^ the 30% day pulled back to the 90% quantile
# Flag with a rule, then look at what is flagged before deciding anything
mask = z.abs() > 2
print(returns[mask].to_dict())                 # {6: 0.3}        the +30% day
print(returns[~mask].std().round(4))           # 0.0026          the volatility of the remaining days
Decide con una regla y anótalaUna regla de atípicos es una decisión de modelado, no un hecho. Comprueba qué son los días marcados (una noticia, un split, un mal tick) antes de cambiar nada, conserva la columna original y anota el umbral usado. Otro umbral da otro backtest.
  • La regla del z-score abs((x - mean) / std) > k es simple, pero los atípicos inflan la media y la desviación y pueden esconderse.
  • Las reglas robustas usan cuantiles y medianas: límites IQR (Q1 - 1.5 IQR, Q3 + 1.5 IQR) y z-score modificado basado en el MAD.
  • clip(lower, upper) con límites de cuantiles (winsorización) conserva las filas; nunca borres un choque sin saber qué es.

A continuación: las operaciones corporativas, los splits y dividendos que hacen que un precio antiguo no sea comparable con el de hoy.

Lab: qué se califica

  • Marcar los rendimientos lejanos con zscore_outliers25 pts
  • Calcular los límites del diagrama de caja con iqr_bounds25 pts
  • Marcar atípicos con una puntuación robusta con mad_outliers25 pts
  • Domar los extremos con winsorize25 pts
Explorador · lab-49-1
▾ lab-49-1
…
bash · en el navegador
learner@quantcraft:~/lab-49-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-49-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