σμ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 11 · Sistemas de produção e modelos de ponta›Tópico 11.2·~1 hEspecialista

CI/CD para código quant

Posição no roteiroMódulo 11 de 11 · Tópico 2 de 13
11.2 CI/CD para código quant
—Nota do lab— / 100XP total—Sequência—

Verificações que rodam sozinhas

Testes só protegem se forem executados, sempre, por algo que não seja a sua memória. A integração contínua (CI) é um servidor que, a cada push e pull request, baixa o código em um ambiente limpo e executa as verificações definidas: lint, checagem de tipos, testes, um pequeno backtest. Se algo falhar, a mudança é bloqueada. A entrega/implantação contínua (CD) vai além: quando as verificações passam na branch principal, a nova versão é construída e publicada automaticamente. No trabalho quantitativo o risco é concreto: uma refatoração que desloca em silêncio um preço em um ponto-base deve ser pega por uma máquina antes de chegar a uma carteira.

# A test matrix: every operating system with every Python version, minus the combinations you skip
from itertools import product

os_list = ["linux", "macos", "windows"]
py_versions = ["3.11", "3.12"]
exclude = {("windows", "3.11")}

jobs = [(os_name, version) for os_name, version in product(os_list, py_versions) if (os_name, version) not in exclude]
print(len(jobs))                                     # 5
print(jobs[0], jobs[-1])                             # ('linux', '3.11') ('windows', '3.12')

O que um pipeline quantitativo verifica

Um pipeline típico tem etapas que falham rápido: o lint (estilo e erros óbvios) e a checagem de tipos levam segundos, depois os testes unitários, depois verificações mais lentas como um pequeno backtest de regressão comparado com um resultado guardado. Torne as verificações determinísticas: sementes fixas, sem rede, sem depender da data de hoje e com dados de um pequeno instantâneo versionado em vez de um feed ao vivo. Rode-as numa matriz de versões do Python e sistemas que diz suportar. Para dar retorno rápido, rode só os testes que uma mudança pode afetar (seleção de testes), com recuo seguro para a suíte inteira quando não for possível saber.

# Test selection: run only the tests a change can affect; an unknown file means "run everything"
def select_tests(changed_files, mapping):
    chosen = set()
    for path in changed_files:
        matches = [tests for prefix, tests in mapping.items() if path.startswith(prefix)]
        if not matches:
            return sorted({test for tests in mapping.values() for test in tests})
        for tests in matches:
            chosen.update(tests)
    return sorted(chosen)

mapping = {"src/pricing/": ["tests/test_bs.py", "tests/test_greeks.py"], "src/risk/": ["tests/test_var.py"]}
print(select_tests(["src/risk/var.py"], mapping))                    # ['tests/test_var.py']
print(select_tests(["src/risk/var.py", "setup.cfg"], mapping))       # all three tests: setup.cfg could affect anything

Portões, segredos e o arquivo de workflow

O pipeline é descrito em um arquivo guardado no repositório, por exemplo .github/workflows/ci.yml no GitHub Actions: quando roda (on:), os jobs, a matriz e os passos, cada passo ou uses: uma ação pronta ou run: um comando. Trate o arquivo como código, revisado como qualquer outro. Adicione um portão de deploy: só a branch main faz deploy, e só quando todo job exigido teve sucesso (um job pulado ou ausente não é sucesso). Proteja a branch para que não seja mesclada sem verificações aprovadas. Guarde chaves de API e senhas nos segredos do sistema de CI, nunca no repositório, e dê a cada job só as permissões de que precisa. Publique primeiro num ambiente de paper trading ou de homologação.

# The deploy gate: main branch only, and every required job must have succeeded
def can_deploy(results, required, branch):
    return branch == "main" and all(results.get(job) == "success" for job in required)

print(can_deploy({"lint": "success", "tests": "success"}, ["lint", "tests"], "main"))     # True
print(can_deploy({"lint": "success", "tests": "skipped"}, ["lint", "tests"], "main"))     # False  skipped is not success
print(can_deploy({"lint": "success"}, ["lint", "tests"], "main"))                         # False  a missing job is not success
print(can_deploy({"lint": "success", "tests": "success"}, ["lint", "tests"], "dev"))      # False  only main deploys
# The workflow file is plain text, so it can be generated and checked like any other output
def workflow_yaml(name, python_versions, command):
    versions = ", ".join(f'"{version}"' for version in python_versions)
    lines = [f"name: {name}", "on: [push, pull_request]", "jobs:", "  test:", "    runs-on: ubuntu-latest",
             "    strategy:", "      matrix:", f"        python-version: [{versions}]", "    steps:",
             "      - uses: actions/checkout@v4", "      - uses: actions/setup-python@v5", "        with:",
             "          python-version: ${{ matrix.python-version }}", "      - run: pip install -e .[test]",
             f"      - run: {command}"]
    return "\n".join(lines) + "\n"

text = workflow_yaml("ci", ["3.11", "3.12"], "pytest -q")
print(text.splitlines()[7])                          #         python-version: ["3.11", "3.12"]
print(text.splitlines()[-1])                         #       - run: pytest -q
Um pipeline verde vale o que valem seus testesA CI prova que as verificações passam, não que a estratégia é boa. Mantenha a suíte rápida para que a rodem, conserte um teste instável ou apague-o (um teste que falha ao acaso ensina todos a ignorar o vermelho) e trate um build quebrado como a primeira prioridade da equipe. Fixe as versões das ferramentas usadas na CI e mantenha um jeito de rodar exatamente as mesmas verificações na sua máquina, para que «funciona na minha máquina» e «falha na CI» nunca sejam um mistério.
  • A CI roda lint, checagem de tipos e testes em um ambiente limpo a cada push; o CD só publica automaticamente se passarem na main.
  • Torne as verificações determinísticas (sementes, instantâneos, sem rede), rode-as numa matriz de versões e sistemas e selecione os testes pelo que mudou.
  • Descreva o pipeline em um arquivo de workflow revisado, condicione os deploys à main e aos jobs exigidos, guarde segredos no sistema de CI e conserte testes instáveis.

A seguir: pipelines agendados, executar tarefas de dados e de trading na hora certa e na ordem certa.

Lab: o que é avaliado

  • Expandir a matriz de testes com matrix_jobs25 pts
  • Rodar só o que importa com select_tests25 pts
  • Proteger o lançamento com can_deploy25 pts
  • Gerar o workflow com workflow_yaml25 pts
Explorador · lab-131-1
▾ lab-131-1
…
bash · no navegador
learner@quantcraft:~/lab-131-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

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