σμ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 11 · Sistemas de producción y modelos de vanguardia›Tema 11.3·~1 hExperto

Pipelines programados

Posición en la rutaMódulo 11 de 11 · Tema 3 de 13
11.3 Pipelines programados
—Puntuación del lab— / 100XP total—Racha—

Trabajo que ocurre según un reloj

Una operación de trading está llena de trabajos que deben ocurrir a horas fijas: descargar los precios de cierre cuando cierra el mercado, rehacer el informe de riesgo a las 06:00, reequilibrar a las 15:55, archivar los registros el domingo por la noche. Un planificador los inicia sin una persona, y uno bueno conoce además el orden: el informe de riesgo necesita antes los precios y las posiciones. Bastan dos ideas para entender cualquiera: *cuándo* ejecutar (una regla de calendario como cron) y *qué debe ir primero* (un grafo de dependencias). Las herramientas difieren (cron, temporizadores de systemd, Airflow, Prefect, Dagster, planificadores en la nube), pero todas se basan en esto.

# A cron expression has five fields: minute hour day-of-month month day-of-week
expression = "30 9 * * 1-5"
minute, hour, day_of_month, month, day_of_week = expression.split()
print(minute, hour, day_of_week)                     # 30 9 1-5   09:30 on Monday to Friday
print(list(range(1, 6)))                             # [1, 2, 3, 4, 5]   day-of-week 0 is Sunday, so 1-5 is Monday to Friday

Leer cron: campos, rangos y pasos

Cada uno de los cinco campos selecciona valores: * todos, a uno, a-b un rango, */n uno de cada n, y las comas unen elementos (1,15,20-22). Así */15 * * * * es cada quince minutos y 0 9 * * 1-5 son las 09:00 en días laborables. Un instante coincide cuando los cinco campos lo seleccionan. Para hallar la próxima ejecución, avanza minuto a minuto desde la hora actual hasta que algo coincida; es lento en teoría pero trivial para un calendario, y maneja gratis fines de mes y años bisiestos. El cron real tiene una rareza (cuando día del mes y día de la semana están ambos restringidos, se ejecuta si *cualquiera* coincide); el laboratorio usa la regla más simple de *ambos*.

# Which values does a cron field select? Ranges, steps and lists
def field_values(field, low, high):
    chosen = set()
    for item in field.split(","):
        body, _, step = item.partition("/")
        step = int(step) if step else 1
        if body == "*":
            first, last = low, high
        elif "-" in body:
            first, last = (int(part) for part in body.split("-"))
        else:
            first = int(body)
            last = high if "/" in item else first
        chosen.update(range(first, last + 1, step))
    return sorted(chosen)

print(field_values("*/15", 0, 59))                   # [0, 15, 30, 45]
print(field_values("1,15,20-22", 1, 31))             # [1, 15, 20, 21, 22]
print(field_values("10-30/10", 0, 59))               # [10, 20, 30]
# The next run: step forward minute by minute until the five fields all match
from datetime import datetime, timedelta

def matches(minute_set, hour_set, weekday_set, when):
    return when.minute in minute_set and when.hour in hour_set and (when.weekday() + 1) % 7 in weekday_set

def next_run(after):
    moment = after.replace(second=0, microsecond=0)
    while True:
        moment += timedelta(minutes=1)
        if matches({0}, {9}, {1, 2, 3, 4, 5}, moment):          # "0 9 * * 1-5"
            return moment

print(next_run(datetime(2024, 3, 8, 17, 0)))         # 2024-03-11 09:00:00   Friday evening: the next weekday 09:00 is Monday
print(next_run(datetime(2024, 3, 11, 8, 59, 30)))    # 2024-03-11 09:00:00   seconds are ignored

Dependencias, reintentos y zonas horarias

Los trabajos forman un grafo dirigido acíclico: una flecha de prices a risk significa que risk espera a prices. Un orden de ejecución válido (una ordenación topológica) pone cada trabajo después de sus dependencias; los independientes pueden correr en paralelo, y un ciclo de dependencias es un error que informar, no que ejecutar. Haz cada trabajo idempotente: ejecutarlo dos veces para la misma fecha da el mismo resultado, porque habrá reintentos y reejecuciones manuales. Reintenta los fallos transitorios con un retraso creciente (como con los websockets) pero avisa tras el último intento; impide que un trabajo arranque mientras su ejecución anterior sigue en marcha. Y piensa en el tiempo: guarda y programa en UTC o en la zona horaria del mercado a propósito, recuerda los cambios de horario de verano y usa un calendario de mercado para no lanzar un trabajo de fin de día en un festivo.

# A valid run order: every job after its dependencies, the alphabetically first among those that are ready
def run_order(tasks):
    done, remaining = [], set(tasks)
    while remaining:
        ready = sorted(name for name in remaining if all(dep in done for dep in tasks[name]))
        if not ready:
            raise ValueError("cycle detected")
        done.append(ready[0])
        remaining.remove(ready[0])
    return done

tasks = {"report": ["risk"], "risk": ["prices", "positions"], "prices": [], "positions": []}
print(run_order(tasks))                              # ['positions', 'prices', 'risk', 'report']
try:
    run_order({"a": ["b"], "b": ["a"]})
except ValueError as error:
    print(error)                                     # cycle detected
El verdadero peligro es el fallo silenciosoUn trabajo que falla con estrépito es una molestia; uno que no se ejecuta en silencio es un desastre, porque nadie nota que los precios de ayer nunca se cargaron. Haz que cada trabajo informe de su éxito o fallo en un lugar vigilado, avisa cuando falta una ejecución esperada (no solo cuando una falla), registra las entradas y la fecha procesada y conserva una forma de recuperar un día perdido. Prueba la lógica de programación con horas fijas, como las de este laboratorio, en vez de esperar al reloj.
  • Una expresión cron tiene cinco campos (minuto, hora, día del mes, mes, día de la semana) con *, a, a-b, */n y comas; un instante coincide cuando los cinco lo seleccionan.
  • Halla la próxima ejecución avanzando minuto a minuto; los trabajos forman un grafo de dependencias, se ejecutan en orden topológico y un ciclo es un error.
  • Haz los trabajos idempotentes, reintenta con retroceso, evita ejecuciones solapadas, programa en una zona horaria explícita con un calendario de mercado y avisa de las ejecuciones que faltan.

A continuación: ejecución TWAP, dividir una orden grande en partes iguales a lo largo del tiempo.

Lab: qué se califica

  • Leer un campo cron con parse_cron_field25 pts
  • Comprobar un instante con cron_matches25 pts
  • Hallar la próxima ejecución con next_run25 pts
  • Ordenar los trabajos con run_order25 pts
Explorador · lab-132-1
▾ lab-132-1
…
bash · en el navegador
learner@quantcraft:~/lab-132-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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