⚠ Le trading comporte un risque important de perte. Tout le contenu de ce site est fourni à des fins éducatives uniquement et ne constitue pas un conseil financier.
Leçon 13 sur 13 · Gratuit

Un projet complet de notebook de recherche, des données au rapport, avec une liste de contrôle de revue de code contre le biais d'anticipation et le surapprentissage

Organisez une étude de stratégie complète sous forme de projet reproductible, menez-la des données brutes à un résultat rédigé, et relisez le code avec une liste de contrôle qui repère les erreurs flatteuses pour les backtests.

Auteur: Moustafa HijaziContenu relu le: 2026-10-308 min de lectureCours en autoformation

Ce que vous allez apprendre

  • Une organisation des dossiers et des notebooks qui rend une étude reproductible, par vous dans six mois ou par n'importe qui d'autre.
  • L'ordre de travail : données, nettoyage, variables, signaux, backtest, métriques, vérification hors échantillon, rapport.
  • Une liste de contrôle de revue de code, ligne par ligne, contre le biais d'anticipation, le biais du survivant et le surapprentissage.
  • Comment rédiger honnêtement un résultat, y compris ce qui a été essayé et n'a pas fonctionné.

Explication

Les tests hors échantillon, les plateaux de paramètres et le coût des essais multiples ont été traités dans Trading systématique (leçons 6 et 7). Cette leçon porte sur l'ingénierie qui permet à ces idées de tenir : une structure de projet, un déroulé de travail fixe et une revue qu'une autre personne pourrait refaire.

Organisation du projet

  • project/
  • data/raw/ # téléchargements, jamais modifiés à la main
  • data/clean/ # fichiers parquet produits par 01_data.ipynb
  • notebooks/ # 01_data, 02_features, 03_backtest, 04_report
  • src/ # indicators.py, engine.py, metrics.py (leçons 6, 8, 9)
  • tests/ # test_metrics.py, test_engine.py
  • config.yaml # tickers, dates, paramètres, hypothèses de coûts
  • requirements.txt # versions figées : pandas==2.x, numpy==…

Le code réutilisé vit dans src/ ; les notebooks se contentent d'appeler des fonctions et d'afficher les résultats. Figez les versions des paquets, car une mise à jour de pandas peut modifier un comportement par défaut (chaînes de fréquence, étiquettes de rééchantillonnage) et déplacer vos chiffres. Fixez la graine de tout élément aléatoire et indiquez dans le rapport la date de téléchargement des données.

Le déroulé de travail, dans l'ordre

  1. L'hypothèse d'abord. Une phrase de raisonnement économique et les règles exactes avant de toucher aux données (Trading systématique, leçon 2). Fixez dès maintenant la séparation entre échantillon d'apprentissage (in-sample) et échantillon de test (out-of-sample), et n'ouvrez pas le fichier hors échantillon avant l'étape 7.
  2. Données. Téléchargez, stockez les données brutes, documentez la source et la méthode d'ajustement (leçon 4). Une liste des « membres actuels de l'indice » testée sur dix ans en arrière constitue un biais du survivant.
  3. Nettoyer et valider. Trous, doublons, prix nuls, fuseaux horaires ; enregistrez dans data/clean/.
  4. Variables et signaux avec les fonctions testées de src/ ; chaque variable à la barre t n'utilise que des données allant jusqu'à t.
  5. Backtest avec le moteur de la leçon 8, modèle de coûts compris (Trading systématique, leçon 8).
  6. Métriques et graphiques (leçons 9 et 10), sur la période in-sample, avec une petite grille de paramètres, et non une recherche de la meilleure case.
  7. Exécution hors échantillon une seule fois, avec les paramètres figés. Consignez le résultat, quel qu'il soit.
  8. Rapport : hypothèse, données, règles, coûts, les deux tableaux de résultats, graphiques, nombre de variantes essayées, décision.

Liste de contrôle de revue de code

Relisez votre notebook comme s'il était celui de quelqu'un d'autre ; chaque ligne est une question fermée.

Domaine Vérification Correction type
Timing Chaque signal est-il décalé d'une barre avant de fixer une position ? pos = signal.shift(1)
Timing Les fenêtres glissantes utilisent-elles center=False (la valeur par défaut) et aucun bfill() ? Supprimer le centrage et les remplissages vers l'arrière
Timing Les normalisations (z-scores, rangs, scalers) sont-elles ajustées uniquement sur des données passées ? Ajustements cumulatifs ou glissants
Données L'univers est-il défini tel qu'il était connu à chaque date ? Listes de composants à date (point-in-time) ou réserve honnête
Données Les prix ajustés servent-ils aux rendements et les prix non ajustés aux distances de stop, le cas échéant ? Conserver les deux colonnes
Exécutions Les gaps sont-ils exécutés à l'ouverture, et non au prix du stop ou du signal ? min(open, stop)
Coûts Les commissions, le spread et le slippage sont-ils appliqués par trade et par sens ? Modèle de coûts dans la configuration
Surapprentissage Combien de jeux de paramètres, de règles et d'univers ont été essayés ? Ce nombre figure-t-il dans le rapport ? Journaliser chaque exécution
Surapprentissage Des paramètres voisins donnent-ils des résultats similaires (un plateau) ? Carte de chaleur de la grille
Surapprentissage Les données hors échantillon ont-elles été utilisées plus d'une fois ? Si oui, elles sont désormais in-sample ; trouvez de nouvelles données
Reproductibilité « Restart kernel and run all » reproduit-il les chiffres du rapport ? Corriger l'état caché et l'ordre des cellules

Règle empirique pour la significativité : la statistique t d'un ratio de Sharpe annualisé vaut à peu près Sharpe × √années. Un Sharpe de 0.8 sur quatre ans donne 1.6, sous le seuil habituel d'environ 2, et cela avant même de tenir compte du nombre de variantes essayées ; parmi vingt essais, le meilleur devrait paraître bon par simple hasard.

Exemple commenté

Exemple hypothétique, tous les chiffres sont inventés. Un trader teste « acheter une cassure des 100 jours, sortir sur un stop suiveur de 3 × ATR » sur 30 actions de grande capitalisation, de 2014 à 2025, avec des coûts de 0.1 % par sens.

  1. Séparation. In-sample 2014–2021 (8 ans), out-of-sample 2022–2025 (4 ans), décidé avant la première exécution.
  2. Grille. Longueurs de cassure 50, 100, 150 × multiples d'ATR 2, 3, 4 = 9 variantes, journalisées. Le Sharpe in-sample va de 0.6 à 1.1, la meilleure case étant (100, 3) = 1.1 ; ses voisines affichent 0.9 et 1.0, un plateau, le choix est donc conservé.
  3. Revue. La liste de contrôle révèle que la colonne ATR utilisait rolling(14).mean() sur l'étendue vraie du jour pour le stop du jour. Le moteur est modifié pour utiliser l'ATR de la barre précédente (leçon 8). Le Sharpe in-sample passe de 1.1 à 1.0 ; le drawdown maximal reste inchangé à −22 %.
  4. Hors échantillon. Une seule exécution : Sharpe de 0.6, CAGR de 7.2 %, drawdown maximal de −19 %, 118 trades. Significativité : 0.6 × √4 = 1.2 ; le chiffre in-sample donnait 1.0 × √8 = 2.8. Le résultat hors échantillon est positif mais statistiquement faible à lui seul.
  5. Décision dans le rapport. « La règle a conservé environ 60 % de son Sharpe in-sample hors échantillon (0.6 / 1.0), sur un plateau, avec 9 variantes essayées et un bug d'anticipation corrigé. Les preuves sont modestes ; l'étape suivante est six mois de trading simulé (paper trading) avec le script de la leçon 12, et non du capital réel. » Frais modélisés ; impôts et coûts de financement exclus.

Erreur fréquente

Jeter un œil à la période hors échantillon « juste pour vérifier »Chaque coup d'œil aux données mises de côté et chaque ajustement effectué ensuite les transforment en données in-sample. Si vous avez modifié le multiple d'ATR après avoir vu 2022–2025, les seules options honnêtes sont d'indiquer que le test n'est plus hors échantillon ou de trouver de nouvelles données, par exemple d'autres marchés ou les mois écoulés depuis. Inscrivez dans le rapport la date de séparation ainsi que la date à laquelle l'exécution hors échantillon a eu lieu.

Exercice pratique

Sur papier : relisez ce fragment de notebook et le résultat qui l'a suivi.

  • df['z'] = (df.close - df.close.mean()) / df.close.std()
  • df['signal'] = df.z < -1.5
  • df['pos'] = df.signal.astype(int)
  • df['ret'] = df.close.pct_change() * df.pos

Sharpe in-sample de 2.4 sur 6 ans ; Sharpe hors échantillon de −0.3 sur 2 ans. (a) Identifiez deux problèmes d'anticipation dans le code. (b) Calculez les statistiques t approximatives des ratios de Sharpe in-sample et hors échantillon. (c) Réécrivez les quatre lignes pour qu'elles soient exemptes d'anticipation (décrivez les modifications si vous préférez la prose).

Afficher la solution

(a) Premièrement, df.close.mean() et df.close.std() utilisent tout l'échantillon, prix futurs compris, si bien que le z-score à n'importe quelle date « connaît » la moyenne future. Deuxièmement, pos n'est pas décalé : le rendement du jour est obtenu sur un signal calculé à partir de la clôture du jour.

(b) In-sample : 2.4 × √6 = 5.9 ; hors échantillon : −0.3 × √2 = −0.4. La valeur in-sample élevée était produite par les bugs, c'est pourquoi elle s'est effondrée.

(c) Utilisez une fenêtre glissante ou cumulative et un décalage :

  • m = df.close.rolling(250).mean(); s = df.close.rolling(250).std()
  • df['z'] = (df.close - m) / s
  • df['signal'] = df.z < -1.5
  • df['pos'] = df.signal.shift(1).fillna(False).astype(int)
  • df['ret'] = df.close.pct_change() * df.pos

Limites : supprimer l'anticipation rend le test honnête, pas rentable ; la règle corrigée a encore besoin de coûts, d'un moteur avec stops et d'une nouvelle période hors échantillon.

Points clés à retenir

  • Conservez le code réutilisable dans src/ avec des tests, figez les versions, et faites en sorte que « run all » reproduise le rapport.
  • Fixez l'hypothèse, les règles et la séparation in-sample / out-of-sample avant le premier backtest ; n'exécutez le test hors échantillon qu'une seule fois.
  • Passez en revue le timing, les données, les exécutions, les coûts et le nombre d'essais à l'aide d'une liste de contrôle ; journalisez chaque variante essayée.
  • Indiquez la significativité approximativement par Sharpe × √années, le nombre de variantes et la décision, y compris « preuves insuffisantes ».

Étape suivante

Ainsi s'achève Python pour traders et investisseurs ; le cours suivant, « Prop firms, comptes financés et trading en tant qu'activité professionnelle », examine les règles d'évaluation, les types de drawdown ainsi que les aspects pratiques et fiscaux allemands du trading exercé comme activité professionnelle, pour lesquels un processus testé et scripté comme celui que vous avez construit ici constitue le point de départ.

Auto-évaluation

Répondez aux questions, puis vérifiez vos réponses. Vous pouvez recommencer autant de fois que vous le souhaitez.

1. Une stratégie affiche un ratio de Sharpe annualisé de 0.9 sur 5 ans hors échantillon. Avec la règle approximative t ≈ Sharpe × √années, quelle est la statistique t et comment faut-il l'interpréter ?

2. Au cours d'une étude, un chercheur exécute la période hors échantillon, constate un résultat faible, modifie la règle de sortie et relance le test avec un meilleur résultat. Quel est le statut de ce second résultat ?

3. Quelle ligne d'un notebook introduit le plus clairement un biais d'anticipation ?

Auto-évaluation automatique pour vos propres révisions. Vos réponses sont vérifiées dans votre navigateur ; nous ne les voyons pas et ne les évaluons pas. Ce n'est ni un examen ni une qualification.

Votre progression

« Terminée » signifie seulement que vous avez marqué la leçon comme lue. Ce n'est pas un test de votre compréhension. La progression et les notes sont enregistrées uniquement dans ce navigateur, sur cet appareil — pas dans un compte.