σμ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 10 · Ingeniería Python de alto rendimiento›Tema 10.7·~1 hAvanzado

Cython

Posición en la rutaMódulo 10 de 11 · Tema 7 de 13
10.7 Cython
—Puntuación del lab— / 100XP total—Racha—

Python con tipos de C, compilado

Cython es un compilador para un lenguaje que es Python más declaraciones de tipos de C opcionales. Escribes un archivo .pyx, Cython lo traduce a C, un compilador de C lo convierte en un módulo de extensión y lo importas como cualquier otro módulo. El código Python simple compilado así ya va algo más rápido, pero la ganancia real llega al declarar tipos: cdef double total le dice a Cython que total es un número de máquina, no un objeto de Python, de modo que el bucle trabaja con números crudos sin sobrecarga del intérprete. Los bucles numéricos pesados pueden volverse de cincuenta a cien veces más rápidos, como con Numba, pero obtienes un módulo compilado normal que puedes distribuir.

# What a Cython file looks like: Python with cdef type declarations (shown here as text)
pyx = """\
cpdef double dot(double[:] a, double[:] b):
    cdef double total = 0.0
    cdef long i
    for i in range(a.shape[0]):
        total += a[i] * b[i]
    return total
"""
print(pyx.splitlines()[0])                           # cpdef double dot(double[:] a, double[:] b):
print(sum("cdef" in line for line in pyx.splitlines()))     # 2   two typed declarations inside the function

Declarar tipos: cdef, cpdef y vistas de memoria

cdef declara una variable de C (cdef double total, cdef long i) o una función que solo el código C puede llamar. cpdef declara una función invocable desde Python y desde C, lo que conviene a los puntos de entrada de un módulo. def sigue siendo una función normal de Python. Los arrays se pasan como vistas de memoria tipadas, como double[:] para un array unidimensional de doubles: aceptan arrays de NumPy sin copiar y se indexan a la velocidad de C. Una firma como cpdef double mean(double[:] x, long n) sustituye así los tipos dinámicos de Python por tipos exactos, y un tipo omitido, o declarado object, sigue siendo un objeto lento de Python.

# Building the declarations as text: one table from Python type names to C types
C_TYPES = {"int": "long", "float": "double", "bool": "bint", "array[float]": "double[:]"}

def c_type(py_type):
    return C_TYPES.get(py_type, "object")

def cdef_signature(name, params, returns):
    result = "void" if returns is None else c_type(returns)
    arguments = ", ".join(f"{c_type(t)} {p}" for p, t in params)
    return f"cpdef {result} {name}({arguments})"

print(cdef_signature("mean", [("x", "array[float]"), ("n", "int")], "float"))    # cpdef double mean(double[:] x, long n)
print(cdef_signature("log_event", [("message", "dict")], None))                  # cpdef void log_event(object message)

Construir el módulo y encontrar qué tipar

Un setup.py con cythonize(["pricing.pyx"]) indica a setuptools que traduzca y compile el archivo; python setup.py build_ext --inplace produce una biblioteca compartida junto a tu código y import pricing la carga. Para pruebas rápidas, pyximport compila al importar. Para ver qué sigue lento, ejecuta cython -a pricing.pyx: escribe una página HTML donde cada línea se sombrea de amarillo según la cantidad de maquinaria de Python que usa, y las líneas blancas son C puro. Tipa las variables de las líneas amarillas dentro de tus bucles críticos y luego desactiva las comprobaciones que hayas probado innecesarias con @cython.boundscheck(False) y @cython.wraparound(False) para que indexar cueste tan poco como en C.

# The build script is a few lines of text; this generates it for a list of modules
def build_setup(modules):
    files = ", ".join(f'"{module}.pyx"' for module in modules)
    return "from setuptools import setup\nfrom Cython.Build import cythonize\n\n" + f"setup(ext_modules=cythonize([{files}]))\n"

print(build_setup(["pricing", "risk"]), end="")
# from setuptools import setup
# from Cython.Build import cythonize
#
# setup(ext_modules=cythonize(["pricing.pyx", "risk.pyx"]))
# The loop a .pyx file would hold, as plain Python: same result, compiled once types are added
def dot(a, b):
    total = 0.0
    for i in range(len(a)):
        total += a[i] * b[i]
    return total

prices = [184.25, 410.5, 99.0]
quantities = [100.0, 50.0, 200.0]
print(dot(prices, quantities))                       # 58750.0  the compiled version returns exactly this number
print(dot(prices, quantities) == sum(p * q for p, q in zip(prices, quantities)))     # True
¿Cython, Numba o ninguno?Numba es la opción ligera: un decorador, sin paso de compilación, ideal para funciones numéricas. Cython cuesta un paso de construcción y un segundo tipo de archivo, pero da un módulo normal que se distribuye sin arranque JIT y permite llamar bibliotecas C y mezclar libremente código C y Python. Prueba primero la vectorización con NumPy, luego Numba, luego Cython; y en cada caso compara la salida con la versión Python simple antes de fiarte.
  • Cython compila un archivo .pyx (Python más tipos de C opcionales) a C y luego a un módulo de extensión que se import normalmente.
  • cdef declara variables de C y funciones solo de C, cpdef una función invocable desde Python y C; los arrays son vistas de memoria tipadas como double[:].
  • Construye con cythonize en setup.py, usa cython -a para hallar las líneas amarillas (lentas) en bucles críticos y verifica con el resultado de Python simple.

A continuación: llamar a C++ con pybind11, exponer código C++ existente a Python.

Lab: qué se califica

  • Asociar tipos de Python y de C con c_type25 pts
  • Escribir la cabecera de una función con cdef_signature25 pts
  • Tipar las variables locales con declare_locals25 pts
  • Escribir el script de compilación con build_setup25 pts
Explorador · lab-123-1
▾ lab-123-1
…
bash · en el navegador
learner@quantcraft:~/lab-123-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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