σμ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.1·~45 minAvanzado

Código limpio

Posición en la rutaMódulo 10 de 11 · Tema 1 de 13
10.1 Código limpio
—Puntuación del lab— / 100XP total—Racha—

El código se lee mucho más de lo que se escribe

Una base de código de trading vive años y la modifican personas que no la escribieron, a menudo bajo presión y a menudo tú mismo seis meses después. El código limpio es el que se entiende rápido y se puede cambiar con seguridad. No trata de reglas de estilo, sino de hacer visible un cambio equivocado. Una función mal nombrada, que hace tres cosas y esconde un límite dentro de una fórmula, es donde viven los errores caros, como un límite de riesgo que nadie recordaba que estaba fijo en el código.

# The same calculation, written two ways
def f(d, e, m, w):
    r = []
    if sum([abs(x) for x in d.values()]) / e > m:
        r.append("LEVERAGE")
    for k in sorted(d):
        if abs(d[k]) / sum([abs(x) for x in d.values()]) > w:
            r.append("CONCENTRATION:" + k)
    return r

book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(f(book, 50_000.0, 1.5, 0.5))                  # works, but what is d? e? m? w?

Nombres, funciones pequeñas, sin números ocultos

Tres hábitos hacen la mayor parte. Nombra las cosas por lo que son: gross_exposure en lugar de f, max_leverage en lugar de m. Mantén las funciones pequeñas y de un solo propósito: cada una hace un trabajo que se nombra en pocas palabras, así se prueba sola y se reutiliza, y una función mayor se construye llamándolas. No entierres números: un límite como 1.5 escrito dentro de una fórmula es un número mágico que nadie puede encontrar ni cambiar con seguridad; pásalo como argumento (o ponle nombre de constante). Calcular la exposición bruta una vez en su propia función también elimina la expresión repetida.

# Small functions with names that say what they return; the limits come in as arguments
def gross_exposure(positions):
    return sum(abs(value) for value in positions.values())

def leverage(positions, equity):
    return gross_exposure(positions) / equity

def risk_flags(positions, equity, max_leverage, max_weight):
    flags = []
    if leverage(positions, equity) > max_leverage:
        flags.append("LEVERAGE")
    gross = gross_exposure(positions)
    for symbol in sorted(positions):
        if abs(positions[symbol]) / gross > max_weight:
            flags.append(f"CONCENTRATION:{symbol}")
    return flags

book = {"AAPL": 80_000.0, "MSFT": -10_000.0, "NVDA": 10_000.0}
print(risk_flags(book, 50_000.0, 1.5, 0.5))         # ['LEVERAGE', 'CONCENTRATION:AAPL']
print(risk_flags(book, 50_000.0, 3.0, 0.9))         # []  the same code, looser limits

Proteger los bordes y decir por qué

Trata primero la entrada mala y devuelve o lanza de inmediato (una cláusula de guarda), para que el camino principal se lea recto, sin anidar. Un apalancamiento gross / equity con capital cero debe ser un ValueError claro, no un ZeroDivisionError desde el fondo, y una cartera vacía debe dar 0.0 y no un fallo. Los comentarios deben explicar el porqué, que el código no puede decir (# la bolsa redondea al centavo), no repetir el qué que el código ya muestra. Las docstrings dicen qué recibe y devuelve una función. Por último, prefiere las funciones puras, que dependen solo de sus argumentos y no tienen efectos secundarios: son las que se prueban con unas pocas entradas.

# A guard clause stops bad input at the door and says exactly what is wrong
def leverage(positions, equity):
    if equity <= 0:
        raise ValueError("equity must be positive")
    return sum(abs(value) for value in positions.values()) / equity

print(leverage({"AAPL": 60_000.0, "MSFT": -30_000.0}, 45_000.0))     # 2.0
print(leverage({}, 45_000.0))                                         # 0.0  an empty book is fine

try:
    leverage({"AAPL": 60_000.0}, 0.0)
except ValueError as error:
    print("rejected:", error)                                         # rejected: equity must be positive
# A pure function answers the same way every time; hidden state does not
history = []

def running_notional(price, quantity):          # remembers earlier calls: the answer depends on the past
    history.append(price * quantity)
    return sum(history)

def notional(price, quantity):                  # depends only on its arguments
    return price * quantity

print(running_notional(10.0, 5), running_notional(10.0, 5))     # 50.0 100.0  same call, different answers
print(notional(10.0, 5), notional(10.0, 5))                     # 50.0 50.0   always the same
Limpio no es ingeniosoNo comprimas el código para parecer listo: un bucle de tres líneas que todos entienden vale más que un truco de una línea que necesita un comentario. Tampoco lo partas en decenas de funciones diminutas; para cuando una función se lea como un paso claro. Y la limpieza es un cambio distinto de un cambio de comportamiento: refactoriza con las pruebas pasando antes y después, de modo que cualquier diferencia venga de la nueva lógica y no del orden.
  • Nombra las cosas por lo que son, mantén las funciones pequeñas y de un solo propósito, y construye las mayores llamándolas.
  • Sin números mágicos: los límites llegan como argumentos o constantes con nombre; calcula una vez un valor repetido.
  • Protege primero contra la entrada mala con un error claro, comenta el porqué y no el qué, y prefiere funciones puras que puedas probar.

A continuación: anotaciones de tipo y comprobación estática, dejar que las herramientas detecten errores antes de ejecutar el código.

Lab: qué se califica

  • Netear la cartera con net_exposure25 pts
  • Sumar cada posición con gross_exposure25 pts
  • Proteger el denominador en leverage25 pts
  • Componer las comprobaciones en risk_flags25 pts
Explorador · lab-117-1
▾ lab-117-1
…
bash · en el navegador
learner@quantcraft:~/lab-117-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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