σμ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.3·~45 minPrincipiante

Vectorización

Posición en la rutaMódulo 2 de 11 · Tema 3 de 20
2.3 Vectorización
—Puntuación del lab— / 100XP total—Racha—

Pensar en arreglos completos, no en elementos

La vectorización consiste en escribir un cálculo como operaciones sobre arreglos completos y dejar que NumPy ejecute el bucle en código compilado rápido. a + b suma dos arreglos elemento a elemento, a * b los multiplica, a ** 2 eleva al cuadrado cada elemento y np.sqrt(a) calcula cada raíz cuadrada. Son funciones universales (ufuncs). El resultado es más corto de escribir, más legible y, con datos grandes, a menudo cincuenta veces más rápido que el bucle for de Python equivalente, que paga la sobrecarga del intérprete en cada elemento.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([10.0, 20.0, 30.0])

print(a + b)             # [11. 22. 33.]
print(a * b)             # [10. 40. 90.]
print(a ** 2)            # [1. 4. 9.]
print(np.sqrt(a))        # [1.         1.41421356 1.73205081]

# The loop version of a * b, which vectorisation replaces
result = np.empty(3)
for i in range(3):
    result[i] = a[i] * b[i]
print(result)            # [10. 40. 90.]

Rendimientos sin bucle: desplazar con porciones

Un cálculo que compara cada elemento con su vecino necesita una segunda copia del arreglo desplazada una posición, y las porciones la dan gratis. p[1:] es el precio de hoy y p[:-1] el de ayer, ambos de longitud n - 1, así que los rendimientos simples son p[1:] / p[:-1] - 1. El mismo par da diferencias (p[1:] - p[:-1], o np.diff(p)). Las sumas y productos acumulados son np.cumsum y np.cumprod; una trayectoria de crecimiento es np.cumprod(1 + returns). Las comparaciones de arreglos completos (r > 0) y np.where(condición, si_cierto, si_falso) reemplazan el if dentro de un bucle.

import numpy as np

p = np.array([100.0, 102.0, 101.0, 103.0])

returns = p[1:] / p[:-1] - 1                 # today / yesterday - 1, no loop
print(np.round(returns, 4))                  # [ 0.02   -0.0098  0.0198]
print(np.diff(p))                            # [ 2. -1.  2.]
print(np.cumprod(1 + np.array([0.01, 0.02])))   # [1.01   1.0302]

# np.where replaces if/else inside a loop
r = np.array([0.01, -0.02, 0.03, -0.01])
print(np.where(r > 0, "up", "down"))         # ['up' 'down' 'up' 'down']
print(np.where(r > 0, r, 0.0))               # [0.01 0.   0.03 0.  ]

Agregaciones a lo largo de un eje y productos matriciales

Funciones como sum, mean, std, min y max colapsan un arreglo. Con un arreglo 2-D de rendimientos (una fila por día, una columna por activo), axis=0 colapsa las filas y da un valor por columna (el promedio de cada activo en el tiempo), mientras que axis=1 da un valor por fila (el promedio de cada día entre activos). El producto matricial R @ w, de un arreglo (días, activos) por un vector de pesos de longitud activos, da el rendimiento de cartera de cada día en una sola llamada: es el producto escalar del tema de vectores hecho para todas las filas a la vez.

import numpy as np

# 2 days x 3 assets
R = np.array([[0.02, -0.01, 0.03],
              [0.01,  0.00, 0.02]])
w = np.array([0.5, 0.3, 0.2])

print(R.shape)            # (2, 3)
print(R.mean(axis=0))     # [ 0.015 -0.005  0.025]   one value per asset
print(R.sum(axis=1))      # [0.04 0.03]              one value per day
print(R @ w)              # [0.013 0.009]            portfolio return each day

# Standardise: how many standard deviations from the mean?
z = np.array([1.0, 2.0, 3.0, 4.0])
print((z - z.mean()) / z.std())   # [-1.34164079 -0.4472136   0.4472136   1.34164079]

Medias móviles con cumsum y los límites de vectorizar

Una media móvil sobre una ventana w parece necesitar un bucle, pero con un total acumulado no: pon un 0 delante de np.cumsum(x) y la suma de w valores consecutivos es una diferencia de dos totales acumulados, cs[w:] - cs[:-w]; divide entre w. El resultado tiene len(x) - w + 1 valores. Vectoriza la aritmética, no la lógica que depende del resultado anterior (una posición que depende de la ejecución de ayer, por ejemplo): eso se queda en un bucle o pasa más adelante a herramientas compiladas como Numba. Vigila también la memoria, porque cada arreglo intermedio es una copia completa.

import numpy as np

x = np.array([1.0, 2.0, 4.0, 8.0, 16.0])
w = 3

cs = np.concatenate(([0.0], np.cumsum(x)))   # [0, 1, 3, 7, 15, 31]
print((cs[w:] - cs[:-w]) / w)                # [2.33333333 4.66666667 9.33333333]

# Check against the plain definition
print([x[i:i + w].mean() for i in range(len(x) - w + 1)])   # three values, the same
Vectoriza y luego compara con la versión lentaUn cálculo rápido pero erróneo es peor que uno lento pero correcto. Conserva la versión obvia con bucle, escribe la vectorizada y compáralas con np.allclose(rápida, lenta) sobre datos aleatorios antes de fiarte. Cuida también los bordes: un arreglo vacío, un solo elemento, una serie constante (desviación típica cero, así que un z-score divide por cero) y valores nan merecen una decisión, no un fallo ni un número erróneo en silencio.
  • Las operaciones sobre arreglos completos (a + b, a ** 2, np.sqrt(a)) reemplazan los bucles; compara con np.where.
  • Porciones desplazadas: p[1:] / p[:-1] - 1; cumsum y cumprod para totales acumulados y crecimiento.
  • axis=0 colapsa filas (por columna), axis=1 colapsa columnas (por fila); R @ w es un producto escalar por fila.

A continuación: broadcasting, la regla que permite que arreglos de formas distintas trabajen juntos.

Lab: qué se califica

  • Calcular todos los rendimientos de una vez con simple_returns25 pts
  • Obtener el rendimiento de la cartera de cada día con portfolio_returns25 pts
  • Suavizar una serie sin bucle con moving_average25 pts
  • Estandarizar valores con zscore25 pts
Explorador · lab-36-1
▾ lab-36-1
…
bash · en el navegador
learner@quantcraft:~/lab-36-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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