σμ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 AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 10 · Engenharia Python de alto desempenho›Tópico 10.1·~45 minAvançado

Código limpo

Posição no roteiroMódulo 10 de 11 · Tópico 1 de 13
10.1 Código limpo
—Nota do lab— / 100XP total—Sequência—

O código é lido muito mais do que escrito

Uma base de código de trading vive anos e é alterada por pessoas que não a escreveram, muitas vezes sob pressão e muitas vezes por você mesmo, seis meses depois. Código limpo é o que se entende rápido e se altera com segurança. Não se trata de regras de estilo, mas de tornar visível uma mudança errada. Uma função mal nomeada, que faz três coisas e esconde um limite dentro de uma fórmula, é onde moram os bugs caros, como um limite de risco que ninguém lembrava estar fixo no 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?

Nomes, funções pequenas, sem números escondidos

Três hábitos fazem a maior parte. Dê nomes às coisas pelo que são: gross_exposure em vez de f, max_leverage em vez de m. Mantenha funções pequenas e de um só propósito: cada uma faz um trabalho que se nomeia em poucas palavras, então se testa sozinha e se reutiliza, e uma função maior se constrói chamando-as. Não enterre números: um limite como 1.5 digitado dentro de uma fórmula é um número mágico que ninguém consegue achar ou mudar com segurança; passe-o como argumento (ou dê-lhe nome de constante). Calcular a exposição bruta uma vez em sua própria função também elimina a expressão 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 as bordas e dizer por quê

Trate primeiro a entrada ruim e retorne ou levante de imediato (uma cláusula de guarda), para que o caminho principal se leia reto, sem aninhamento. Uma alavancagem gross / equity com patrimônio zero deve ser um ValueError claro, não um ZeroDivisionError vindo do fundo, e uma carteira vazia deve dar 0.0 e não uma falha. Comentários devem explicar o porquê, que o código não diz (# a bolsa arredonda ao centavo), não repetir o quê que o código já mostra. Docstrings dizem o que a função recebe e devolve. Por fim, prefira funções puras, que dependem só dos argumentos e não têm efeitos colaterais: são as que se testam com poucas 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
Limpo não é espertoNão comprima o código para parecer esperto: um laço de três linhas que todos entendem vale mais que um truque de uma linha que precisa de comentário. Também não o divida em dezenas de funções minúsculas; pare quando uma função se ler como um passo claro. E a limpeza é uma mudança separada de uma mudança de comportamento: refatore com os testes passando antes e depois, para que qualquer diferença venha da nova lógica e não da arrumação.
  • Dê nomes às coisas pelo que são, mantenha funções pequenas e de um só propósito, e construa as maiores chamando-as.
  • Sem números mágicos: os limites chegam como argumentos ou constantes nomeadas; calcule uma vez um valor repetido.
  • Proteja primeiro contra a entrada ruim com um erro claro, comente o porquê e não o quê, e prefira funções puras que possa testar.

A seguir: anotações de tipo e verificação estática, deixar as ferramentas pegarem erros antes de o código rodar.

Lab: o que é avaliado

  • Netear a carteira com net_exposure25 pts
  • Somar cada posição com gross_exposure25 pts
  • Proteger o denominador em leverage25 pts
  • Compor as verificações em risk_flags25 pts
Explorador · lab-117-1
▾ lab-117-1
…
bash · no navegador
learner@quantcraft:~/lab-117-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-117-1 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub