σμ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.2·~1 hExperto

CI/CD para código quant

Posición en la rutaMódulo 11 de 11 · Tema 2 de 13
11.2 CI/CD para código quant
—Puntuación del lab— / 100XP total—Racha—

Comprobaciones que se ejecutan solas

Las pruebas solo te protegen si se ejecutan, siempre, por algo que no sea tu memoria. La integración continua (CI) es un servidor que, en cada push y pull request, descarga el código en un entorno limpio y ejecuta las comprobaciones definidas: lint, comprobación de tipos, pruebas, un pequeño backtest. Si algo falla, el cambio se bloquea. La entrega/despliegue continuo (CD) va más allá: cuando las comprobaciones pasan en la rama principal, la nueva versión se construye y se publica automáticamente. En trabajo cuantitativo lo que está en juego es concreto: una refactorización que desplaza en silencio un precio un punto básico debe ser detectada por una máquina antes de llegar a una cartera.

# A test matrix: every operating system with every Python version, minus the combinations you skip
from itertools import product

os_list = ["linux", "macos", "windows"]
py_versions = ["3.11", "3.12"]
exclude = {("windows", "3.11")}

jobs = [(os_name, version) for os_name, version in product(os_list, py_versions) if (os_name, version) not in exclude]
print(len(jobs))                                     # 5
print(jobs[0], jobs[-1])                             # ('linux', '3.11') ('windows', '3.12')

Qué comprueba una canalización cuantitativa

Una canalización típica tiene etapas que fallan rápido: el lint (estilo y errores evidentes) y la comprobación de tipos tardan segundos, luego las pruebas unitarias, luego comprobaciones más lentas como un pequeño backtest de regresión comparado con un resultado guardado. Haz las comprobaciones deterministas: semillas fijas, sin red, sin depender de la fecha de hoy y con datos de una pequeña instantánea versionada en lugar de un feed en vivo. Ejecútalas en una matriz de versiones de Python y sistemas que dices admitir. Para dar respuesta rápida, ejecuta solo las pruebas que un cambio puede afectar (selección de pruebas), con un respaldo seguro a toda la suite cuando no puedas saberlo.

# Test selection: run only the tests a change can affect; an unknown file means "run everything"
def select_tests(changed_files, mapping):
    chosen = set()
    for path in changed_files:
        matches = [tests for prefix, tests in mapping.items() if path.startswith(prefix)]
        if not matches:
            return sorted({test for tests in mapping.values() for test in tests})
        for tests in matches:
            chosen.update(tests)
    return sorted(chosen)

mapping = {"src/pricing/": ["tests/test_bs.py", "tests/test_greeks.py"], "src/risk/": ["tests/test_var.py"]}
print(select_tests(["src/risk/var.py"], mapping))                    # ['tests/test_var.py']
print(select_tests(["src/risk/var.py", "setup.cfg"], mapping))       # all three tests: setup.cfg could affect anything

Barreras, secretos y el archivo de flujo de trabajo

El flujo se describe en un archivo guardado en el repositorio, por ejemplo .github/workflows/ci.yml para GitHub Actions: cuándo se ejecuta (on:), los trabajos, la matriz y los pasos, y cada paso uses: una acción ya hecha o run: un comando. Trata el archivo como código, revisado como cualquier otro. Añade una barrera de despliegue: solo la rama main despliega, y solo cuando todos los trabajos requeridos tuvieron éxito (un trabajo omitido o ausente no es un éxito). Protege la rama para que no se fusione sin comprobaciones superadas. Guarda las claves de API y contraseñas en los secretos del sistema de CI, nunca en el repositorio, y da a cada trabajo solo los permisos que necesita. Publica primero en un entorno de paper trading o de preproducción.

# The deploy gate: main branch only, and every required job must have succeeded
def can_deploy(results, required, branch):
    return branch == "main" and all(results.get(job) == "success" for job in required)

print(can_deploy({"lint": "success", "tests": "success"}, ["lint", "tests"], "main"))     # True
print(can_deploy({"lint": "success", "tests": "skipped"}, ["lint", "tests"], "main"))     # False  skipped is not success
print(can_deploy({"lint": "success"}, ["lint", "tests"], "main"))                         # False  a missing job is not success
print(can_deploy({"lint": "success", "tests": "success"}, ["lint", "tests"], "dev"))      # False  only main deploys
# The workflow file is plain text, so it can be generated and checked like any other output
def workflow_yaml(name, python_versions, command):
    versions = ", ".join(f'"{version}"' for version in python_versions)
    lines = [f"name: {name}", "on: [push, pull_request]", "jobs:", "  test:", "    runs-on: ubuntu-latest",
             "    strategy:", "      matrix:", f"        python-version: [{versions}]", "    steps:",
             "      - uses: actions/checkout@v4", "      - uses: actions/setup-python@v5", "        with:",
             "          python-version: ${{ matrix.python-version }}", "      - run: pip install -e .[test]",
             f"      - run: {command}"]
    return "\n".join(lines) + "\n"

text = workflow_yaml("ci", ["3.11", "3.12"], "pytest -q")
print(text.splitlines()[7])                          #         python-version: ["3.11", "3.12"]
print(text.splitlines()[-1])                         #       - run: pytest -q
Una canalización en verde vale lo que valen sus pruebasLa CI demuestra que las comprobaciones pasan, no que la estrategia sea buena. Mantén la suite rápida para que se ejecute, arregla una prueba inestable o bórrala (una prueba que falla al azar enseña a todos a ignorar el rojo) y trata una compilación rota como la primera prioridad del equipo. Fija las versiones de las herramientas usadas en CI y conserva una forma de ejecutar exactamente las mismas comprobaciones en tu máquina, para que «en mi máquina funciona» y «falla en CI» nunca sean un misterio.
  • La CI ejecuta lint, comprobación de tipos y pruebas en un entorno limpio en cada push; la CD solo publica automáticamente si pasan en main.
  • Haz las comprobaciones deterministas (semillas, instantáneas, sin red), ejecútalas en una matriz de versiones y sistemas y selecciona las pruebas según lo que cambió.
  • Describe la canalización en un archivo de flujo revisado, condiciona los despliegues a main y a los trabajos requeridos, guarda los secretos en el sistema de CI y arregla las pruebas inestables.

A continuación: canalizaciones programadas, ejecutar tareas de datos y de trading en el momento y orden correctos.

Lab: qué se califica

  • Expandir la matriz de pruebas con matrix_jobs25 pts
  • Ejecutar solo lo que importa con select_tests25 pts
  • Proteger el lanzamiento con can_deploy25 pts
  • Generar el flujo de trabajo con workflow_yaml25 pts
Explorador · lab-131-1
▾ lab-131-1
…
bash · en el navegador
learner@quantcraft:~/lab-131-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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