σμ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 8 · Modelos estocásticos y aprendizaje automático›Tema 8.9·~1 hAvanzado

Ingeniería de variables para datos financieros

Posición en la rutaMódulo 8 de 11 · Tema 9 de 10
8.9 Ingeniería de variables para datos financieros
—Puntuación del lab— / 100XP total—Racha—

Dar al modelo entradas comparables

Un precio de 250 no significa nada para un modelo: depende de la acción y tiende a subir con el tiempo, así que los valores de 2020 nunca se parecen a los de 2010. La ingeniería de variables convierte los datos en bruto en entradas comparables en el tiempo y entre activos: rendimientos en vez de precios, volatilidad en vez de rangos, puntuaciones estandarizadas en vez de niveles. Las buenas variables aportan más al resultado que la elección del modelo, y cada una debe describir solo lo que se sabía ese día.

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])

window = 3
momentum = np.full(len(prices), np.nan)
momentum[window:] = prices[window:] / prices[:-window] - 1          # the return over the last 3 days
print(np.round(momentum, 4))                                        # nan for the first 3 days: no price 3 days back

Ventanas que miran atrás

La mayoría de las variables son estadísticos de una ventana retrospectiva: los últimos n valores hasta hoy inclusive. El momentum compara hoy con hace n días; la volatilidad móvil es la desviación típica de los últimos n rendimientos; el z-score móvil dice a cuántas desviaciones de la ventana está hoy respecto a la media de la ventana. Con sliding_window_view se obtienen todas las ventanas como filas de una matriz y se calcula el estadístico a lo largo de un eje, sin bucle. Los primeros n - 1 días no tienen ventana completa y quedan nan.

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
returns = np.diff(prices) / prices[:-1]
window = 3

windows = np.lib.stride_tricks.sliding_window_view(returns, window)        # every 3-day window as a row
vol = np.full(len(returns), np.nan)
vol[window - 1:] = windows.std(axis=1, ddof=1)
print(np.round(vol, 4))                                                    # nan nan then one value per complete window

p_windows = np.lib.stride_tricks.sliding_window_view(prices, window)
z = np.full(len(prices), np.nan)
z[window - 1:] = (prices[window - 1:] - p_windows.mean(axis=1)) / p_windows.std(axis=1, ddof=1)
print(np.round(z, 3))                                                      # how unusual each price is for its last 3 days
# Features put prices of different stocks on the same footing
import numpy as np

cheap = np.array([5.0, 5.2, 5.1, 5.4, 5.5])
dear = np.array([500.0, 520.0, 510.0, 540.0, 550.0])           # ten times the price, the same movements
print(cheap[-1] / cheap[-3] - 1, dear[-1] / dear[-3] - 1)        # identical momentum, about 0.0784 for both
print(dear[-1] - dear[-3], cheap[-1] - cheap[-3])                # price changes (40.0 and 0.4) would not be comparable

El objetivo mira hacia delante

Lo que predice el modelo es el objetivo, normalmente el rendimiento de los siguientes h días: prices[t + h] / prices[t] - 1. Es la única columna autorizada a mirar hacia delante, y es nan en los últimos h días porque el futuro aún no ha ocurrido; elimina esas filas antes de entrenar. Un error común es una variable que contiene el objetivo por accidente, como una ventana centrada, un estadístico calculado con todos los datos o un rendimiento futuro reutilizado como entrada. La prueba es simple: una variable del día t no debe cambiar al añadir días posteriores.

import numpy as np

def forward_return(prices, horizon):
    out = np.full(len(prices), np.nan)
    out[:-horizon] = prices[horizon:] / prices[:-horizon] - 1
    return out

def momentum(prices, window):
    out = np.full(len(prices), np.nan)
    out[window:] = prices[window:] / prices[:-window] - 1
    return out

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
print(np.round(momentum(prices, 2), 4))                 # looks back: known on each day
print(np.round(forward_return(prices, 2), 4))           # looks forward: the target, nan for the last 2 days

# The prefix test: a feature computed on the first 5 days equals the first 5 values of the full computation
print(np.allclose(momentum(prices[:5], 2), momentum(prices, 2)[:5], equal_nan=True))      # True
Cuidado con las variables que son secretamente el objetivoUna variable que predice demasiado bien es una señal de alarma. Un momentum medido con ventana centrada, una volatilidad que incluye el rendimiento de mañana, un z-score estandarizado con toda la muestra: cada uno hace brillar los backtests y falla en real. Construye cada variable para que pueda calcularse en producción la mañana del día t, y compruébala con la prueba del prefijo de arriba.
  • Convierta los precios en variables comparables: momentum prices[t] / prices[t - n] - 1, volatilidad móvil, z-score móvil, cada uno sobre una ventana retrospectiva que incluye hoy.
  • sliding_window_view da cada ventana como fila, así que los estadísticos no necesitan bucle; los primeros n - 1 días quedan nan.
  • El rendimiento futuro prices[t + h] / prices[t] - 1 es el objetivo, nunca una variable; compruebe cada variable con la prueba del prefijo.

A continuación: la validación walk-forward, probar un modelo en el orden en que realmente fluye el tiempo.

Lab: qué se califica

  • Medir la tendencia con momentum25 pts
  • Seguir el riesgo reciente con rolling_vol25 pts
  • Decir cuán inusual es hoy con rolling_zscore25 pts
  • Definir el objetivo con forward_return25 pts
Explorador · lab-110-1
▾ lab-110-1
…
bash · en el navegador
learner@quantcraft:~/lab-110-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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