σμ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 2 · NumPy e pandas para dados de mercado›Tópico 2.3·~45 minIniciante

Vetorização

Posição no roteiroMódulo 2 de 11 · Tópico 3 de 20
2.3 Vetorização
—Nota do lab— / 100XP total—Sequência—

Pensar em arrays inteiros, não em elementos

Vetorização significa escrever um cálculo como operações sobre arrays inteiros e deixar o NumPy executar o laço em código compilado rápido. a + b soma dois arrays elemento a elemento, a * b os multiplica, a ** 2 eleva cada elemento ao quadrado e np.sqrt(a) calcula cada raiz quadrada. Essas são funções universais (ufuncs). O resultado é mais curto de escrever, mais legível e, em dados grandes, muitas vezes cinquenta vezes mais rápido que o laço for equivalente do Python, que paga o custo do interpretador em cada elemento.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([10.0, 20.0, 30.0])

print(a + b)             # [11. 22. 33.]
print(a * b)             # [10. 40. 90.]
print(a ** 2)            # [1. 4. 9.]
print(np.sqrt(a))        # [1.         1.41421356 1.73205081]

# The loop version of a * b, which vectorisation replaces
result = np.empty(3)
for i in range(3):
    result[i] = a[i] * b[i]
print(result)            # [10. 40. 90.]

Retornos sem laço: deslocar com fatias

Um cálculo que compara cada elemento com o vizinho precisa de uma segunda cópia do array deslocada de uma posição, e as fatias a dão de graça. p[1:] é o preço de hoje e p[:-1] o de ontem, ambos de comprimento n - 1, então os retornos simples são p[1:] / p[:-1] - 1. O mesmo par dá diferenças (p[1:] - p[:-1], ou np.diff(p)). Somas e produtos acumulados são np.cumsum e np.cumprod; uma trajetória de crescimento é np.cumprod(1 + returns). Comparações de arrays inteiros (r > 0) e np.where(condição, se_verdadeiro, se_falso) substituem o if dentro de um laço.

import numpy as np

p = np.array([100.0, 102.0, 101.0, 103.0])

returns = p[1:] / p[:-1] - 1                 # today / yesterday - 1, no loop
print(np.round(returns, 4))                  # [ 0.02   -0.0098  0.0198]
print(np.diff(p))                            # [ 2. -1.  2.]
print(np.cumprod(1 + np.array([0.01, 0.02])))   # [1.01   1.0302]

# np.where replaces if/else inside a loop
r = np.array([0.01, -0.02, 0.03, -0.01])
print(np.where(r > 0, "up", "down"))         # ['up' 'down' 'up' 'down']
print(np.where(r > 0, r, 0.0))               # [0.01 0.   0.03 0.  ]

Agregações ao longo de um eixo e produtos matriciais

Funções como sum, mean, std, min e max colapsam um array. Com um array 2-D de retornos (uma linha por dia, uma coluna por ativo), axis=0 colapsa as linhas e dá um valor por coluna (a média de cada ativo no tempo), enquanto axis=1 dá um valor por linha (a média de cada dia entre os ativos). O produto matricial R @ w, de um array (dias, ativos) por um vetor de pesos de comprimento ativos, dá o retorno da carteira de cada dia numa só chamada: é o produto escalar do tópico de vetores feito para todas as linhas de uma vez.

import numpy as np

# 2 days x 3 assets
R = np.array([[0.02, -0.01, 0.03],
              [0.01,  0.00, 0.02]])
w = np.array([0.5, 0.3, 0.2])

print(R.shape)            # (2, 3)
print(R.mean(axis=0))     # [ 0.015 -0.005  0.025]   one value per asset
print(R.sum(axis=1))      # [0.04 0.03]              one value per day
print(R @ w)              # [0.013 0.009]            portfolio return each day

# Standardise: how many standard deviations from the mean?
z = np.array([1.0, 2.0, 3.0, 4.0])
print((z - z.mean()) / z.std())   # [-1.34164079 -0.4472136   0.4472136   1.34164079]

Médias móveis com cumsum e os limites de vetorizar

Uma média móvel sobre uma janela w parece precisar de um laço, mas com um total acumulado não precisa: ponha um 0 na frente de np.cumsum(x) e a soma de w valores consecutivos é uma diferença de dois totais acumulados, cs[w:] - cs[:-w]; divida por w. O resultado tem len(x) - w + 1 valores. Vetorize a aritmética, não a lógica que depende do resultado anterior (uma posição que depende da execução de ontem, por exemplo): isso fica num laço ou passa depois para ferramentas compiladas como o Numba. Atenção à memória também, pois cada array intermediário é uma cópia completa.

import numpy as np

x = np.array([1.0, 2.0, 4.0, 8.0, 16.0])
w = 3

cs = np.concatenate(([0.0], np.cumsum(x)))   # [0, 1, 3, 7, 15, 31]
print((cs[w:] - cs[:-w]) / w)                # [2.33333333 4.66666667 9.33333333]

# Check against the plain definition
print([x[i:i + w].mean() for i in range(len(x) - w + 1)])   # three values, the same
Vetorize e depois confira com a versão lentaUm cálculo rápido mas errado é pior que um lento e certo. Mantenha a versão óbvia com laço, escreva a vetorizada e compare-as com np.allclose(rápida, lenta) em dados aleatórios antes de confiar. Cuide também das bordas: um array vazio, um único elemento, uma série constante (desvio padrão zero, então um z-score divide por zero) e valores nan merecem uma decisão, não uma falha nem um número errado em silêncio.
  • Operações sobre arrays inteiros (a + b, a ** 2, np.sqrt(a)) substituem laços; compare com np.where.
  • Fatias deslocadas: p[1:] / p[:-1] - 1; cumsum e cumprod para totais acumulados e crescimento.
  • axis=0 colapsa linhas (por coluna), axis=1 colapsa colunas (por linha); R @ w é um produto escalar por linha.

A seguir: broadcasting, a regra que permite que arrays de formatos diferentes trabalhem juntos.

Lab: o que é avaliado

  • Calcular todos os retornos de uma vez com simple_returns25 pts
  • Obter o retorno da carteira de cada dia com portfolio_returns25 pts
  • Suavizar uma série sem laço com moving_average25 pts
  • Padronizar valores com zscore25 pts
Explorador · lab-36-1
▾ lab-36-1
…
bash · no navegador
learner@quantcraft:~/lab-36-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

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