σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 11 · Systèmes de production et modèles de pointe›Sujet 11.2·~1 hExpert

CI/CD pour le code quant

Position dans le parcoursModule 11 sur 11 · Sujet 2 sur 13
11.2 CI/CD pour le code quant
—Score du lab— / 100XP total—Série—

Des contrôles qui s'exécutent tout seuls

Les tests ne vous protègent que s'ils sont exécutés, à chaque fois, par autre chose que votre mémoire. L'intégration continue (CI) est un serveur qui, à chaque push et pull request, récupère le code dans un environnement propre et exécute les contrôles définis : lint, vérification des types, tests, petit backtest. Si quelque chose échoue, la modification est bloquée. La livraison/déploiement continu (CD) va plus loin : quand les contrôles passent sur la branche principale, la nouvelle version est construite et publiée automatiquement. En quant, l'enjeu est concret : un refactoring qui décale discrètement un prix d'un point de base doit être attrapé par une machine avant d'atteindre un portefeuille.

# 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')

Ce que vérifie un pipeline quantitatif

Un pipeline typique a des étapes qui échouent vite : le lint (style et erreurs évidentes) et la vérification des types prennent quelques secondes, puis les tests unitaires, puis des contrôles plus lents comme un petit backtest de régression comparé à un résultat enregistré. Rendez les contrôles déterministes : graines aléatoires fixées, pas de réseau, pas de dépendance à la date du jour, et des données issues d'un petit instantané versionné plutôt que d'un flux en direct. Exécutez-les sur une matrice de versions de Python et de systèmes que vous prétendez prendre en charge. Pour un retour rapide, n'exécutez que les tests qu'une modification peut affecter (sélection de tests), avec un repli sûr sur toute la suite quand on ne sait pas.

# 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

Garde-fous, secrets et fichier de workflow

Le pipeline est décrit dans un fichier conservé dans le dépôt, par exemple .github/workflows/ci.yml pour GitHub Actions : quand il s'exécute (on:), les tâches, la matrice et les étapes, chaque étape faisant soit uses: une action toute prête, soit run: une commande. Traitez le fichier comme du code, relu comme tout autre. Ajoutez un garde-fou de déploiement : seule la branche main déploie, et seulement quand chaque tâche requise a réussi (une tâche sautée ou absente n'est pas un succès). Protégez la branche pour qu'elle ne soit pas fusionnée sans contrôles réussis. Gardez clés d'API et mots de passe dans les secrets du système de CI, jamais dans le dépôt, et donnez à chaque tâche seulement les permissions nécessaires. Publiez d'abord vers un environnement de paper trading ou de préproduction.

# 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
Un pipeline vert ne vaut que ce que valent ses testsLa CI prouve que les contrôles passent, pas que la stratégie est bonne. Gardez la suite rapide pour qu'on l'exécute, corrigez un test instable ou supprimez-le (un test qui échoue au hasard apprend à tous à ignorer le rouge), et traitez une construction en échec comme la première priorité de l'équipe. Figez les versions des outils utilisés en CI, et gardez un moyen d'exécuter exactement les mêmes contrôles sur votre machine, pour que « ça marche chez moi » et « ça échoue en CI » ne soient jamais un mystère.
  • La CI exécute lint, vérification des types et tests dans un environnement propre à chaque push ; la CD ne publie automatiquement que s'ils passent sur main.
  • Rendez les contrôles déterministes (graines, instantanés, pas de réseau), exécutez-les sur une matrice de versions et de systèmes, et sélectionnez les tests selon ce qui a changé.
  • Décrivez le pipeline dans un fichier de workflow relu, conditionnez les déploiements à main et aux tâches requises, gardez les secrets dans le système de CI et corrigez les tests instables.

À suivre : les pipelines planifiés, exécuter les tâches de données et de trading au bon moment, dans le bon ordre.

Lab : ce qui est noté

  • Développer la matrice de tests avec matrix_jobs25 pts
  • N'exécuter que l'utile avec select_tests25 pts
  • Garder la mise en production avec can_deploy25 pts
  • Générer le workflow avec workflow_yaml25 pts
Explorateur · lab-131-1
▾ lab-131-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-131-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-131-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub