σμ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 1 · Fundamentos de Python para quants›Tópico 1.29·~40 minIniciante

Derivadas em código

Posição no roteiroMódulo 1 de 11 · Tópico 29 de 33
1.29 Derivadas em código
—Nota do lab— / 100XP total—Sequência—

Com que rapidez muda?

A derivada de uma função f em x é sua taxa de variação instantânea: a inclinação da curva naquele ponto. Em finanças quase toda sensibilidade é uma derivada. O delta de uma opção é quanto seu preço se move por unidade de movimento do ativo subjacente, seu gama é a rapidez com que o próprio delta muda, e a duração de um título é uma derivada do preço em relação ao rendimento. O cálculo dá fórmulas exatas para funções simples (a derivada de x**2 é 2*x), mas funções de precificação reais costumam ser complicadas demais ou existir só como código. Então estimamos a derivada numericamente.

# A function is just a Python function
def f(x):
    return x * x

# Slope between two nearby points approaches the true slope (2x = 6 at x = 3)
x = 3.0
for h in (1.0, 0.1, 0.001):
    slope = (f(x + h) - f(x)) / h
    print(h, round(slope, 4))
# 1.0 7.0
# 0.1 6.1
# 0.001 6.001

Diferenças finitas: para a frente e central

A diferença para a frente olha um pequeno passo h adiante: (f(x + h) - f(x)) / h. Seu erro diminui em proporção a h. A diferença central olha um passo para cada lado: (f(x + h) - f(x - h)) / (2 * h). Os erros dos dois lados se cancelam, então seu erro diminui como h ao quadrado e ela é exata para quadráticas. A segunda derivada (gama) combina três pontos: (f(x + h) - 2 * f(x) + f(x - h)) / h**2. Passe a própria função como argumento (f), uma habilidade dos tópicos de funções.

import math

def forward_diff(f, x, h=1e-6):
    return (f(x + h) - f(x)) / h

def central_diff(f, x, h=1e-5):
    return (f(x + h) - f(x - h)) / (2 * h)

def second_derivative(f, x, h=1e-4):
    return (f(x + h) - 2 * f(x) + f(x - h)) / h**2

print(round(central_diff(math.sin, 0.0), 6))            # 1.0  (cos 0)
print(round(central_diff(lambda x: x**3, 2.0), 4))      # 12.0 (3 x^2)
print(round(second_derivative(lambda x: x**3, 2.0), 3)) # 12.0 (6 x)

# A quadratic: central is exact even for a big step, forward is not
print(central_diff(lambda x: x * x, 3.0, h=0.5))  # 6.0
print(forward_diff(lambda x: x * x, 3.0, h=0.5))  # 6.5

Escolher o tamanho do passo

Um h menor reduz o erro de truncamento da fórmula, mas subtrair dois números quase iguais perde dígitos: esse erro de arredondamento cresce quando h diminui, e numa segunda derivada cresce mais rápido porque também dividimos por h**2. Um h pequeno demais (como 1e-12) pode dar lixo. Um bom compromisso é cerca de 1e-5 para primeiras derivadas com diferenças centrais e cerca de 1e-4 para segundas derivadas. No código de precificação real, «bump and reprice» é exatamente isto: reexecutar o precificador com o spot aumentado e diminuído de uma pequena quantia.

import math

def central_diff(f, x, h):
    return (f(x + h) - f(x - h)) / (2 * h)

# True derivative of exp at 0 is 1; watch the error as h shrinks
for h in (1e-1, 1e-3, 1e-5, 1e-8, 1e-12):
    error = abs(central_diff(math.exp, 0.0, h) - 1.0)
    print(f"h={h:g} error={error:.1e}")
# h=0.1 error=1.7e-03
# h=0.001 error=1.7e-07
# h=1e-05 error=1.2e-11
# h=1e-08 error=5.3e-10     (rounding error takes over)
# h=1e-12 error=3.3e-05     (much worse: too small)

Usar derivadas: o método de Newton

Uma derivada indica em que direção ir para alcançar uma raiz, um ponto onde f(x) = 0. O método de Newton parte de um palpite x e o substitui repetidamente por x - f(x) / f'(x), seguindo a tangente até zero. Converge muito rápido perto de uma raiz, por isso é usado para obter uma volatilidade implícita a partir de um preço de opção. Pare quando abs(f(x)) ficar abaixo de uma tolerância, desista após um número máximo de iterações e gere um erro se a derivada for zero ou o laço não convergir, em vez de devolver um número errado.

def central_diff(f, x, h=1e-5):
    return (f(x + h) - f(x - h)) / (2 * h)

def newton_root(f, x0, tol=1e-10, max_iter=50):
    x = x0
    for _ in range(max_iter):
        if abs(f(x)) < tol:
            return x
        slope = central_diff(f, x)
        if slope == 0:
            raise ValueError("flat slope: Newton cannot continue")
        x = x - f(x) / slope
    raise ValueError("did not converge")

# The square root of 2 is the positive root of x^2 - 2
root = newton_root(lambda x: x * x - 2, 1.0)
print(round(root, 8))           # 1.41421356

# x^2 + 1 never reaches zero
try:
    newton_root(lambda x: x * x + 1, 1.0)
except ValueError:
    print("no root found")      # no root found
Exato quando possível, numérico quando necessárioSe você conhece a fórmula de uma derivada, use-a: é mais rápida e exata. Derivadas numéricas são para funções que você não pode ou não quer derivar à mão, e sempre têm algum ruído, então compare com um caso de resposta conhecida (como x**2 ou sin) antes de confiar nelas num precificador. Bibliotecas como numpy e scipy oferecem versões testadas; saber como funcionam indica quando confiar nelas.
  • Para a frente (f(x+h) - f(x)) / h; central (f(x+h) - f(x-h)) / (2h); segunda (f(x+h) - 2f(x) + f(x-h)) / h**2.
  • Nem grande nem pequeno demais: cerca de 1e-5 para primeiras e 1e-4 para segundas derivadas.
  • Newton: x = x - f(x) / f'(x) até abs(f(x)) ficar minúsculo; gere um erro se não convergir.

A seguir: integrais em código, somar muitas fatias finas para achar áreas, totais e valores esperados.

Lab: o que é avaliado

  • Estimar uma inclinação olhando adiante com forward_diff25 pts
  • Estimar uma inclinação dos dois lados com central_diff25 pts
  • Estimar a curvatura (gama) com second_derivative25 pts
  • Encontrar onde uma função se anula com newton_root25 pts
Explorador · lab-29-1
▾ lab-29-1
…
bash · no navegador
learner@quantcraft:~/lab-29-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-29-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