σμ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 1 · Fundamentos de Python para quants›Tema 1.25·~40 minPrincipiante

Flujos de investigación reproducibles

Posición en la rutaMódulo 1 de 11 · Tema 25 de 33
1.25 Flujos de investigación reproducibles
—Puntuación del lab— / 100XP total—Racha—

Mismas entradas, misma respuesta

Un resultado es reproducible cuando tú, un colega o un revisor pueden ejecutar el mismo proyecto más tarde y obtener los mismos números. Un backtest que no se puede reconstruir no se puede auditar, depurar ni creer. Los resultados derivan por unas pocas razones: números aleatorios, versiones distintas de bibliotecas, datos que cambiaron o se editaron a mano, ajustes dispersos por el código y estado oculto, como un cuaderno ejecutado en desorden. Un flujo reproducible elimina cada fuente de deriva y registra lo que se usó.

import random

# Same seed, same "random" numbers
a = random.Random(42)
b = random.Random(42)
print([round(a.random(), 3) for _ in range(3)])   # [0.639, 0.025, 0.275]
print([round(b.random(), 3) for _ in range(3)])   # [0.639, 0.025, 0.275]

# A different seed gives a different but equally repeatable sequence
print(round(random.Random(7).random(), 3))        # 0.324

Las semillas y qué generador las posee

Las simulaciones y el bootstrap usan números aleatorios, que en realidad son una secuencia determinista que parte de una semilla (seed). Fija la semilla y la ejecución se repite exactamente. random.seed(42) inicializa el único generador global que comparte todo el programa, así que cualquier otro código que saque números desplaza tu secuencia. Es más seguro crear tu propio generador, rng = random.Random(42) (o numpy.random.default_rng(42)), pasarlo y sacar números de él. Así tus resultados dependen solo de tu semilla y tu inicialización no altera la aleatoriedad de los demás.

import random

# The global generator is shared: seeding it changes everyone's numbers
random.seed(1)
first = random.random()
random.seed(1)
print(random.random() == first)        # True: global seeding repeats...

# ...but a private generator leaves the global one untouched
random.seed(1)
before = random.getstate()
mine = random.Random(99)
mine.random()
print(random.getstate() == before)     # True: your draws did not disturb it

Huellas para datos y ajustes

¿Cómo sabes que el archivo de datos es el que usaste el mes pasado? Calcula una suma de verificación: un hash como SHA-256 convierte cualquier archivo en una cadena corta de longitud fija que cambia si cambia un solo byte. hashlib.sha256(data).hexdigest() la da; lee los archivos grandes por bloques. Los ajustes merecen el mismo trato: serializa la configuración con claves ordenadas (json.dumps(config, sort_keys=True)), aplica el hash a ese texto y la huella es idéntica para dos configuraciones equivalentes, sea cual sea el orden de las claves, y distinta en cuanto cambia un valor.

import hashlib
import json

# A checksum of bytes
print(hashlib.sha256(b"AAPL,184.25").hexdigest()[:12])   # 06ea4aa73ec1 (12 characters of 64)

# A fingerprint of settings, independent of key order
def fingerprint(config):
    text = json.dumps(config, sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(text.encode("utf-8")).hexdigest()[:12]

one = {"fee": 0.001, "window": 20}
two = {"window": 20, "fee": 0.001}
print(fingerprint(one) == fingerprint(two))              # True
print(fingerprint(one) == fingerprint({"fee": 0.002, "window": 20}))  # False

Anota lo que ejecutaste

Cada ejecución debería dejar un pequeño manifiesto junto a sus resultados: la semilla, la huella de la configuración, las versiones de Python y de las bibliotecas, la suma de verificación de cada archivo de entrada y el commit de Git del código. Con ese registro puedes decir exactamente qué produjo un número y reconstruirlo más tarde. Combínalo con los hábitos anteriores: un entorno fijado en requirements.txt, el código en Git, los ajustes en un solo archivo, un comando que ejecute todo desde los datos en bruto hasta las tablas finales y ninguna edición manual de los datos por el camino.

import json
import platform

# A manifest records what a run used
manifest = {
    "seed": 42,
    "config_fingerprint": "a1b2c3d4e5f6",
    "python": platform.python_version().split(".")[0] + ".x",
    "inputs": {"prices.csv": "9f86d081884c"},
}
print(json.dumps(manifest, indent=2, sort_keys=True))
# {
#   "config_fingerprint": "a1b2c3d4e5f6",
#   "inputs": {
#     "prices.csv": "9f86d081884c"
#   },
#   "python": "3.x",
#   "seed": 42
# }
Reproducible no es lo mismo que correctoUn error también se repite a la perfección. La reproducibilidad permite que otros comprueben tu trabajo, encuentren el error y confíen en la corrección; no sustituye las pruebas, las comprobaciones de coherencia de los datos ni una mirada crítica al resultado. Las semillas también ocultan la suerte: cuando un resultado depende de la semilla, ejecútalo con varias e informa de la dispersión.
  • Usa un random.Random(seed) propio para que los resultados dependan solo de tu semilla.
  • Huella para datos (suma de verificación de un archivo) y ajustes (JSON ordenado y luego SHA-256).
  • Guarda un manifiesto en cada ejecución; entorno fijado + commit de Git + un comando para reconstruir.

A continuación: clases y objetos, para agrupar datos y las funciones que los manejan en una unidad ordenada.

Lab: qué se califica

  • Generar rendimientos aleatorios repetibles con seeded_returns25 pts
  • Sacar la huella de un archivo de datos con file_checksum25 pts
  • Sacar la huella de una configuración con config_fingerprint25 pts
  • Registrar lo que usó una ejecución con run_manifest25 pts
Explorador · lab-25-1
▾ lab-25-1
…
bash · en el navegador
learner@quantcraft:~/lab-25-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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