Votre edge vient-il du signal ou du sizing ?
Publié : 09 sept. 2026, 12:38
Le sizing peut-il rendre profitable une stratégie qui ne l'est pas ?
C'est une idée qu'on croise souvent sur les forums, généralement formulée ainsi : « peu importe le point d'entrée, ce qui compte c'est la gestion ». Elle est à la fois vraie et fausse, et l'écart entre les deux mérite d'être précisé, parce que c'est exactement là que se logent les martingales et les comptes vidés.
Je vais partir d'un cas réel — le mien — et remonter jusqu'aux mathématiques, qui sont plus simples qu'il n'y paraît.
Le point de départ
J'ai analysé 214 positions prises sur GER40 et NAS100 en deux mois de compte réel. Résultat brut : environ 860 € de gain.
Puis j'ai refait le calcul en imaginant que j'avais pris exactement les mêmes positions, aux mêmes moments, dans le même sens, mais toujours avec un lot identique. Résultat équipondéré : +8,60 €.
Autrement dit, mon signal d'entrée ne rapporte rien. Zéro. Le rendement moyen par position est de +0,0003 % du prix, avec un intervalle de confiance à 95 % qui va de −0,020 % à +0,020 %. Je ne peux même pas exclure que ma stratégie soit légèrement perdante.
Et pourtant le compte est en gain, parce que mes grosses positions et mes petites positions ne sont pas réparties au hasard. Voilà le sujet.
Ce que le sizing ne peut pas faire
Commençons par ce qui est mathématiquement impossible, parce que c'est là que la plupart des raisonnements dérapent.
Multiplier toutes vos positions par un même coefficient multiplie votre résultat par ce même coefficient. Si votre espérance est nulle, elle reste nulle. Si elle est négative, elle reste négative et l'écart-type augmente. Trader 3 lots au lieu de 1 ne transforme pas une stratégie plate en stratégie gagnante, ça transforme un bruit de 100 € en bruit de 300 €.
Deuxième impossibilité, plus insidieuse : la martingale, sous toutes ses formes. Doubler après une perte, moyenner à la baisse, ajouter sur une position perdante. Ces méthodes ne changent pas l'espérance. Elles changent la forme de la distribution : elles transforment une série de résultats moyens en une longue suite de petits gains, ponctuée d'une perte catastrophique. L'espérance reste identique, mais elle est désormais concentrée dans un événement rare dont on ne sait pas quand il arrive. Avec les frais, elle devient même strictement inférieure.
Troisième point, celui qu'on oublie presque toujours : une stratégie à espérance arithmétique nulle est une stratégie perdante dès qu'on la compose. Si vous risquez une fraction f de votre capital à chaque trade, votre taux de croissance approche f×μ − f²σ²/2. Quand μ vaut zéro, il ne reste que le terme négatif.
J'ai simulé 4 000 comptes de 250 trades sur une stratégie d'espérance exactement nulle :
Retenons donc : le sizing peut détruire une bonne stratégie, il ne peut pas créer d'espérance à partir de rien. Sauf d'une seule manière.
Ce que le sizing peut réellement faire
Toute l'affaire tient dans une identité de deux lignes. Si X est le résultat d'une position exprimé en points, et v la taille que vous lui avez donnée, alors votre gain est :
E[v × X] = E[v] × E[X] + Cov(v, X)
Lisez bien le second terme. Si votre espérance en points E[X] est nulle, le premier terme disparaît et l'intégralité de votre résultat est la covariance entre votre taille et votre résultat. Rien d'autre.
C'est-à-dire : est-ce que vous mettez plus gros, précisément, sur les trades qui marchent mieux ?
Si oui, vous gagnez de l'argent sur une stratégie d'espérance nulle, et ce n'est pas de la magie. C'est simplement que la variable de taille contient de l'information que le signal d'entrée ne contient pas. Formulé autrement : vous n'avez pas une stratégie qu'on redimensionne, vous avez deux stratégies différentes — les petites positions et les grosses — et vous appelez « sizing » ce qui est en réalité un second signal.
C'est le cas dans mon historique. Mes positions de plus de 2 lots, au nombre de 31, portent la quasi-totalité du résultat. Ce ne sont pas des entrées prises au hasard qu'on aurait grossies : ce sont mes reprises, celles que j'ouvre après qu'une première position a établi le sens du mouvement. Elles bénéficient d'une information que les autres n'ont pas.
C'est aussi ce que formalise le critère de Kelly : la taille optimale n'est pas une constante, c'est une fonction de l'edge du moment. Miser identiquement sur toutes les situations, c'est jeter de l'information.
Le piège : distinguer un sizing informatif d'une simple chance
Voici le vrai problème, et il est presque toujours passé sous silence.
Constater après coup que vos grosses positions ont mieux marché ne prouve rien. Sur 200 trades, il faut bien que la répartition des tailles tombe d'une certaine manière, et elle tombera par hasard sur les gagnants dans un bon nombre de cas.
Le test qui tranche est simple à mettre en œuvre : gardez exactement le même jeu de tailles et le même jeu de résultats, mais redistribuez les tailles au hasard sur les trades. Refaites-le vingt mille fois. Vous obtenez la distribution de ce qu'un sizing aveugle aurait produit. Il ne reste plus qu'à regarder où se situe votre résultat réel dans cette distribution.
Sur mon historique :
Notez surtout l'écart-type : 838 €, pour un résultat de 861 €. Le simple fait de faire varier les tailles, même sans aucune information, génère une dispersion de résultats du même ordre de grandeur que le gain total. Sur un tel historique, on ne peut essentiellement rien conclure — et c'est précisément dans ce brouillard que naissent les convictions les plus solides.
Les limites du raisonnement
Même en admettant que votre sizing soit réellement informatif, plusieurs objections restent debout.
La formule vaut d'être gardée en tête, parce qu'elle range tout le débat :
E[v × X] = E[v] × E[X] + Cov(v, X)
Le premier terme, c'est votre stratégie. Le second, c'est votre gestion. Beaucoup de traders qui pensent avoir un edge d'entrée ont en réalité un terme de covariance, et beaucoup de traders qui pensent gérer intelligemment ont un terme de covariance nul et un compte qui survit grâce à un marché porteur.
Le seul moyen de savoir dans quel cas on est, c'est de refaire le calcul à taille constante. C'est cinq minutes de travail sur un export de rapport, et le résultat est rarement flatteur.
Pour ma part, il ne l'a pas été : à taille constante, deux mois de trading valent huit euros et soixante centimes. Ça remet le reste en perspective.
Si certains d'entre vous font l'exercice sur leur propre historique, le chiffre qui m'intéresse est le rapport entre le résultat réel et le résultat équipondéré. Chez moi il est de 100 pour 1. J'aimerais bien savoir si c'est une anomalie ou la norme.
Plusieurs d'entre vous m'ont demandé de publier l'outil. Le voici, en Python, sous licence domaine public. Il fonctionne sur n'importe quel rapport d'historique MetaTrader 5, quels que soient vos instruments et la langue de votre terminal.
Il répond à trois questions, dans cet ordre :
Dans MT5, onglet Historique en bas de l'écran, clic droit n'importe où dans la liste, puis Rapport, puis XLSX. Pensez à afficher la période complète que vous voulez analyser avant d'exporter, sinon vous n'obtiendrez que le mois en cours.
Installation et lancement
Le bloc 1 vous donne, par instrument, vos points cumulés et votre résultat ramené à 1 lot partout. C'est votre stratégie nue, débarrassée de tout effet de taille.
Le bloc 2 compare ce résultat équipondéré à votre résultat réel. Si l'écart est énorme, votre performance ne vient pas de vos entrées. Il affiche aussi l'intervalle de confiance à 95 % de votre rendement moyen par position, exprimé en pourcentage du prix pour rester comparable entre instruments. Si zéro tombe dans cet intervalle, vous ne pouvez pas démontrer d'edge d'entrée sur cet échantillon. Ce n'est pas la même chose que ne pas en avoir, mais c'est la seule chose que vos données autorisent à dire.
Le bloc 3 est le cœur du sujet. Le script garde exactement votre jeu de tailles et votre jeu de résultats, puis redistribue les tailles au hasard sur les trades, vingt mille fois. Vous obtenez ainsi la distribution de ce qu'un dimensionnement aveugle aurait produit sur votre propre historique. Il ne reste qu'à regarder où tombe votre résultat réel.
Deux chiffres à surveiller dans ce bloc, et le second est plus important que le premier :
Deux mois, 210 positions sur GER40 et NAS100 :
Je serais curieux de voir vos chiffres, en particulier le rapport entre votre résultat réel et votre résultat équipondéré.
Une note sur le calcul
Le script ne vous demande aucune spécification de contrat. Il déduit la valeur du point de chaque symbole à partir de vos propres trades, par la médiane du rapport entre le profit enregistré et le produit points multiplié par lots. Ça fonctionne sur n'importe quel instrument, y compris ceux dont vous ne connaissez pas les specs.
Les points sont calculés à partir des prix d'ouverture et de clôture du rapport, donc déjà nets du spread : MT5 enregistre l'entrée à l'ask et la sortie au bid pour un achat, et l'inverse pour une vente. En revanche les commissions et les swaps ne sont pris en compte que dans la ligne « résultat réel net », pas dans le calcul en points.
Le code
Limites connues
Le script lit le bloc Positions du rapport, donc il raisonne position par position. Si vous travaillez en compte netting plutôt qu'en hedge, ou si vous fermez vos positions par tranches, l'unité d'analyse ne sera pas exactement celle que vous avez en tête.
Le test de permutation suppose que vos trades sont indépendants les uns des autres. Si vous ouvrez plusieurs positions corrélées en même temps, ce qui est le cas de beaucoup d'entre nous, il surestime légèrement la significativité. Autrement dit, le vrai p est probablement un peu moins bon que celui qui s'affiche.
Enfin, un résultat au 99e centile sur 60 trades ne vaut pas grand-chose. C'est la taille de l'échantillon qui commande, comme toujours.
Signalez-moi les bugs, je corrigerai le message.
C'est une idée qu'on croise souvent sur les forums, généralement formulée ainsi : « peu importe le point d'entrée, ce qui compte c'est la gestion ». Elle est à la fois vraie et fausse, et l'écart entre les deux mérite d'être précisé, parce que c'est exactement là que se logent les martingales et les comptes vidés.
Je vais partir d'un cas réel — le mien — et remonter jusqu'aux mathématiques, qui sont plus simples qu'il n'y paraît.
Le point de départ
J'ai analysé 214 positions prises sur GER40 et NAS100 en deux mois de compte réel. Résultat brut : environ 860 € de gain.
Puis j'ai refait le calcul en imaginant que j'avais pris exactement les mêmes positions, aux mêmes moments, dans le même sens, mais toujours avec un lot identique. Résultat équipondéré : +8,60 €.
Autrement dit, mon signal d'entrée ne rapporte rien. Zéro. Le rendement moyen par position est de +0,0003 % du prix, avec un intervalle de confiance à 95 % qui va de −0,020 % à +0,020 %. Je ne peux même pas exclure que ma stratégie soit légèrement perdante.
Et pourtant le compte est en gain, parce que mes grosses positions et mes petites positions ne sont pas réparties au hasard. Voilà le sujet.
Ce que le sizing ne peut pas faire
Commençons par ce qui est mathématiquement impossible, parce que c'est là que la plupart des raisonnements dérapent.
Multiplier toutes vos positions par un même coefficient multiplie votre résultat par ce même coefficient. Si votre espérance est nulle, elle reste nulle. Si elle est négative, elle reste négative et l'écart-type augmente. Trader 3 lots au lieu de 1 ne transforme pas une stratégie plate en stratégie gagnante, ça transforme un bruit de 100 € en bruit de 300 €.
Deuxième impossibilité, plus insidieuse : la martingale, sous toutes ses formes. Doubler après une perte, moyenner à la baisse, ajouter sur une position perdante. Ces méthodes ne changent pas l'espérance. Elles changent la forme de la distribution : elles transforment une série de résultats moyens en une longue suite de petits gains, ponctuée d'une perte catastrophique. L'espérance reste identique, mais elle est désormais concentrée dans un événement rare dont on ne sait pas quand il arrive. Avec les frais, elle devient même strictement inférieure.
Troisième point, celui qu'on oublie presque toujours : une stratégie à espérance arithmétique nulle est une stratégie perdante dès qu'on la compose. Si vous risquez une fraction f de votre capital à chaque trade, votre taux de croissance approche f×μ − f²σ²/2. Quand μ vaut zéro, il ne reste que le terme négatif.
J'ai simulé 4 000 comptes de 250 trades sur une stratégie d'espérance exactement nulle :
- f = 1 % du capital par trade : capital médian ×0,99, 53 % des comptes en perte
- f = 5 % : capital médian ×0,71, 66 % des comptes en perte
- f = 10 % : capital médian ×0,30, 78 % des comptes en perte
- f = 20 % : capital médian ×0,005, 94 % des comptes en perte
Retenons donc : le sizing peut détruire une bonne stratégie, il ne peut pas créer d'espérance à partir de rien. Sauf d'une seule manière.
Ce que le sizing peut réellement faire
Toute l'affaire tient dans une identité de deux lignes. Si X est le résultat d'une position exprimé en points, et v la taille que vous lui avez donnée, alors votre gain est :
E[v × X] = E[v] × E[X] + Cov(v, X)
Lisez bien le second terme. Si votre espérance en points E[X] est nulle, le premier terme disparaît et l'intégralité de votre résultat est la covariance entre votre taille et votre résultat. Rien d'autre.
C'est-à-dire : est-ce que vous mettez plus gros, précisément, sur les trades qui marchent mieux ?
Si oui, vous gagnez de l'argent sur une stratégie d'espérance nulle, et ce n'est pas de la magie. C'est simplement que la variable de taille contient de l'information que le signal d'entrée ne contient pas. Formulé autrement : vous n'avez pas une stratégie qu'on redimensionne, vous avez deux stratégies différentes — les petites positions et les grosses — et vous appelez « sizing » ce qui est en réalité un second signal.
C'est le cas dans mon historique. Mes positions de plus de 2 lots, au nombre de 31, portent la quasi-totalité du résultat. Ce ne sont pas des entrées prises au hasard qu'on aurait grossies : ce sont mes reprises, celles que j'ouvre après qu'une première position a établi le sens du mouvement. Elles bénéficient d'une information que les autres n'ont pas.
C'est aussi ce que formalise le critère de Kelly : la taille optimale n'est pas une constante, c'est une fonction de l'edge du moment. Miser identiquement sur toutes les situations, c'est jeter de l'information.
Le piège : distinguer un sizing informatif d'une simple chance
Voici le vrai problème, et il est presque toujours passé sous silence.
Constater après coup que vos grosses positions ont mieux marché ne prouve rien. Sur 200 trades, il faut bien que la répartition des tailles tombe d'une certaine manière, et elle tombera par hasard sur les gagnants dans un bon nombre de cas.
Le test qui tranche est simple à mettre en œuvre : gardez exactement le même jeu de tailles et le même jeu de résultats, mais redistribuez les tailles au hasard sur les trades. Refaites-le vingt mille fois. Vous obtenez la distribution de ce qu'un sizing aveugle aurait produit. Il ne reste plus qu'à regarder où se situe votre résultat réel dans cette distribution.
Sur mon historique :
- Distribution des 20 000 réallocations aléatoires : moyenne +6 €, écart-type 838 €, 5e centile à −1 538 €, 95e centile à +1 198 €
- Mon résultat réel, +861 €, se situe au 87e centile
- p unilatéral = 0,13, soit 2 623 tirages aléatoires sur 20 000 qui font aussi bien ou mieux
Notez surtout l'écart-type : 838 €, pour un résultat de 861 €. Le simple fait de faire varier les tailles, même sans aucune information, génère une dispersion de résultats du même ordre de grandeur que le gain total. Sur un tel historique, on ne peut essentiellement rien conclure — et c'est précisément dans ce brouillard que naissent les convictions les plus solides.
Les limites du raisonnement
Même en admettant que votre sizing soit réellement informatif, plusieurs objections restent debout.
- Un edge qui vit entièrement dans le dimensionnement discrétionnaire n'est ni automatisable, ni transmissible, ni robuste. Il dépend de votre état du jour. Un signal d'entrée, on peut le coder et le backtester ; « j'ai senti qu'il fallait mettre plus gros », non.
- La covariance entre taille et résultat est beaucoup plus instable dans le temps que l'espérance d'un signal. Elle peut disparaître sans prévenir, et vous ne le saurez qu'après plusieurs centaines de trades.
- Concentrer le capital sur quelques situations concentre aussi le risque. Dans mon cas, ce sont exactement les grosses positions qui ont produit jusqu'à 55 % du solde du compte en perte flottante sur une seule séance. Le rendement et le risque viennent du même endroit.
- Les frais ne se redimensionnent pas dans le bon sens. Le spread se paie proportionnellement à la taille, mais il pèse d'autant plus que l'espérance par trade est faible. Sur une stratégie à espérance nulle en brut, elle est franchement négative en net.
- Enfin, la taille d'échantillon. Pour établir un edge de l'ordre de +0,02 % par trade avec la dispersion habituelle de l'intraday indices, il faut compter 1 500 à 2 000 positions. En dessous, on mesure du bruit.
La formule vaut d'être gardée en tête, parce qu'elle range tout le débat :
E[v × X] = E[v] × E[X] + Cov(v, X)
Le premier terme, c'est votre stratégie. Le second, c'est votre gestion. Beaucoup de traders qui pensent avoir un edge d'entrée ont en réalité un terme de covariance, et beaucoup de traders qui pensent gérer intelligemment ont un terme de covariance nul et un compte qui survit grâce à un marché porteur.
Le seul moyen de savoir dans quel cas on est, c'est de refaire le calcul à taille constante. C'est cinq minutes de travail sur un export de rapport, et le résultat est rarement flatteur.
Pour ma part, il ne l'a pas été : à taille constante, deux mois de trading valent huit euros et soixante centimes. Ça remet le reste en perspective.
Si certains d'entre vous font l'exercice sur leur propre historique, le chiffre qui m'intéresse est le rapport entre le résultat réel et le résultat équipondéré. Chez moi il est de 100 pour 1. J'aimerais bien savoir si c'est une anomalie ou la norme.
Plusieurs d'entre vous m'ont demandé de publier l'outil. Le voici, en Python, sous licence domaine public. Il fonctionne sur n'importe quel rapport d'historique MetaTrader 5, quels que soient vos instruments et la langue de votre terminal.
Il répond à trois questions, dans cet ordre :
- Quelle est l'espérance de votre stratégie à taille constante, c'est-à-dire en imaginant que vous ayez pris exactement les mêmes positions avec toujours le même lot ?
- Quelle part de votre résultat réel vient du dimensionnement plutôt que du signal d'entrée ?
- Ce dimensionnement porte-t-il réellement de l'information, ou fait-il aussi bien qu'une répartition au hasard des mêmes tailles ?
Dans MT5, onglet Historique en bas de l'écran, clic droit n'importe où dans la liste, puis Rapport, puis XLSX. Pensez à afficher la période complète que vous voulez analyser avant d'exporter, sinon vous n'obtiendrez que le mois en cours.
Installation et lancement
- pip install pandas numpy openpyxl
- python edge_vs_sizing.py MonRapport.xlsx
- --symboles GER40.p NAS100.p pour ne garder qu'une famille d'instruments homogène. Indispensable si vous mélangez indices et forex : un lot d'indice et un lot de paire de devises n'ont rien de comparable, et le script vous avertit quand il détecte le cas.
- --tirages 50000 pour augmenter la précision du test de permutation. Le défaut de 20 000 suffit largement.
- --graphique pour afficher l'histogramme des permutations avec votre résultat en pointillés. Beaucoup plus parlant que le centile, si vous avez matplotlib installé.
Le bloc 1 vous donne, par instrument, vos points cumulés et votre résultat ramené à 1 lot partout. C'est votre stratégie nue, débarrassée de tout effet de taille.
Le bloc 2 compare ce résultat équipondéré à votre résultat réel. Si l'écart est énorme, votre performance ne vient pas de vos entrées. Il affiche aussi l'intervalle de confiance à 95 % de votre rendement moyen par position, exprimé en pourcentage du prix pour rester comparable entre instruments. Si zéro tombe dans cet intervalle, vous ne pouvez pas démontrer d'edge d'entrée sur cet échantillon. Ce n'est pas la même chose que ne pas en avoir, mais c'est la seule chose que vos données autorisent à dire.
Le bloc 3 est le cœur du sujet. Le script garde exactement votre jeu de tailles et votre jeu de résultats, puis redistribue les tailles au hasard sur les trades, vingt mille fois. Vous obtenez ainsi la distribution de ce qu'un dimensionnement aveugle aurait produit sur votre propre historique. Il ne reste qu'à regarder où tombe votre résultat réel.
Deux chiffres à surveiller dans ce bloc, et le second est plus important que le premier :
- Le centile. Au-delà du 95e, votre sizing est difficile à attribuer au hasard. Entre le 80e et le 95e, c'est encourageant sans être concluant. En dessous, il n'apporte rien de démontrable.
- L'écart-type des réallocations. Il mesure le bruit généré par le seul fait de faire varier les tailles, sans aucune information. S'il est du même ordre de grandeur que votre gain total, votre historique ne permet de conclure à rien, quel que soit le centile affiché. C'est mon cas, et c'est probablement le cas de la majorité d'entre nous.
Deux mois, 210 positions sur GER40 et NAS100 :
- Résultat réel : +782,21 €
- Résultat équipondéré à 1 lot : −16,54 €
- Rendement moyen par position : −0,0001 %, intervalle de confiance [−0,0203 % ; +0,0200 %]
- Permutation : 85,5e centile, p = 0,145, soit 2 902 tirages au hasard sur 20 000 qui font aussi bien ou mieux
- Écart-type des réallocations : 840 €, pour un gain de 782 €
Je serais curieux de voir vos chiffres, en particulier le rapport entre votre résultat réel et votre résultat équipondéré.
Une note sur le calcul
Le script ne vous demande aucune spécification de contrat. Il déduit la valeur du point de chaque symbole à partir de vos propres trades, par la médiane du rapport entre le profit enregistré et le produit points multiplié par lots. Ça fonctionne sur n'importe quel instrument, y compris ceux dont vous ne connaissez pas les specs.
Les points sont calculés à partir des prix d'ouverture et de clôture du rapport, donc déjà nets du spread : MT5 enregistre l'entrée à l'ask et la sortie au bid pour un achat, et l'inverse pour une vente. En revanche les commissions et les swaps ne sont pris en compte que dans la ligne « résultat réel net », pas dans le calcul en points.
Le code
Code : Tout sélectionner
#!/usr/bin/env python3
-- coding: utf-8 --
"""
edge_vs_sizing.py - Votre edge vient-il du signal ou du dimensionnement ?
Prend en entree un rapport d'historique MetaTrader 5 (onglet Historique ->
clic droit -> Rapport -> XLSX) et repond a trois questions :
Quelle est l'esperance de votre strategie A TAILLE CONSTANTE ?
Quelle part de votre resultat reel vient du dimensionnement ?
Ce dimensionnement est-il informatif, ou indistinguable du hasard ?
Le point 3 est un test de permutation : on garde exactement le meme jeu de
tailles et le meme jeu de resultats, on redistribue les tailles au hasard sur
les trades, et on regarde ou tombe le resultat reel dans la distribution
obtenue. Si votre resultat est noye dans le tas, votre sizing n'apporte rien
de demontrable.
Usage :
python edge_vs_sizing.py MonRapport.xlsx
python edge_vs_sizing.py MonRapport.xlsx --tirages 50000 --graphique
Dependances : pandas, numpy, openpyxl (matplotlib seulement pour --graphique)
pip install pandas numpy openpyxl matplotlib
Licence : domaine public. Publie sur le forum VideoBourse.
"""
import argparse
import sys
import warnings
import numpy as np
import pandas as pd
warnings.filterwarnings("ignore", module="openpyxl")
Libelles de la section "Positions" en FR et EN, pour que le script
fonctionne quelle que soit la langue du terminal MT5.
MARQUEURS_DEBUT = ("positions",)
MARQUEURS_FIN = ("ordres", "orders", "transactions", "deals")
COL_ACHAT = ("buy", "achat")
def charger_positions(chemin):
"""Extrait le bloc 'Positions' d'un rapport d'historique MT5 (.xlsx)."""
brut = pd.read_excel(chemin, header=None)
def premiere_cellule(i):
v = brut.iloc[i, 0]
return str(v).strip().lower() if pd.notna(v) else ""
debut = fin = None
for i in range(len(brut)):
cell = premiere_cellule(i)
if debut is None and cell in MARQUEURS_DEBUT:
debut = i + 2 # ligne de titre = debut+1, donnees a partir de debut+2
elif debut is not None and cell in MARQUEURS_FIN:
fin = i
break
if debut is None:
sys.exit("Section 'Positions' introuvable. S'agit-il bien d'un rapport "
"d'historique MT5 au format xlsx ?")
if fin is None:
fin = len(brut)
# Colonnes du bloc Positions, dans l'ordre, quelle que soit la langue :
# ouverture | ticket | symbole | type | volume | prix | S/L | T/P |
# cloture | prix | commission | swap | profit
noms = ["t_open", "ticket", "sym", "type", "vol", "p_open", "sl", "tp",
"t_close", "p_close", "comm", "swap", "profit"]
d = brut.iloc[debut:fin, :len(noms)].copy()
d.columns = noms
d = d[d["sym"].notna() & d["type"].notna()]
for c in ("vol", "p_open", "p_close", "comm", "swap", "profit"):
d[c] = pd.to_numeric(d[c], errors="coerce")
d = d.dropna(subset=["vol", "p_open", "p_close", "profit"])
d["type"] = d["type"].astype(str).str.strip().str.lower()
d = d[d["type"].isin(("buy", "sell", "achat", "vente"))]
return d.reset_index(drop=True)
def preparer(d):
"""Calcule points, valeur du point et resultat net par position."""
d = d.copy()
d["sens"] = np.where(d["type"].isin(COL_ACHAT), 1, -1)
# Points gagnes/perdus, deja nets du spread : MT5 enregistre l'entree a
# l'ask et la sortie au bid pour un achat, et l'inverse pour une vente.
d["pts"] = (d["p_close"] - d["p_open"]) * d["sens"]
# Valeur du point par lot, deduite des trades eux-memes plutot que
# codee en dur : evite d'avoir a connaitre les specs de chaque symbole.
ecart = d["pts"] * d["vol"]
implicite = (d["profit"] / ecart).replace([np.inf, -np.inf], np.nan)
ref = implicite.groupby(d["sym"]).median()
d["vpp"] = d["sym"].map(ref)
d = d[d["vpp"].notna() & (d["vpp"] > 0)]
d["net"] = d["profit"] + d["comm"].fillna(0) + d["swap"].fillna(0)
d["eur_1lot"] = d["pts"] * d["vpp"] # resultat a 1 lot partout
d["eur_reel"] = d["pts"] * d["vol"] * d["vpp"] # resultat brut reconstitue
d["pct"] = d["pts"] / d["p_open"] * 100 # normalise, comparable entre
return d.reset_index(drop=True) # instruments
def resume_par_symbole(d):
g = d.groupby("sym").agg(
n=("pts", "size"),
points=("pts", "sum"),
pts_par_trade=("pts", "mean"),
reussite=("pts", lambda s: (s > 0).mean() * 100),
eur_1lot=("eur_1lot", "sum"),
eur_reel=("net", "sum"),
)
return g.round(2)
def test_permutation(d, tirages, graine=None):
"""Redistribue les memes tailles au hasard sur les memes trades."""
rng = np.random.default_rng(graine)
pts_vpp = (d["pts"] * d["vpp"]).to_numpy()
tailles = d["vol"].to_numpy()
reel = float((pts_vpp * tailles).sum())
sims = np.empty(tirages)
for i in range(tirages):
sims[i] = (pts_vpp * rng.permutation(tailles)).sum()
centile = float((sims < reel).mean() * 100)
return reel, sims, centile
def intervalle_confiance(x):
x = pd.Series(x).dropna()
demi = 1.96 * x.std(ddof=1) / np.sqrt(len(x))
return x.mean() - demi, x.mean() + demi
def main():
ap = argparse.ArgumentParser(
description="Signal ou sizing ? Analyse d'un rapport d'historique MT5.")
ap.add_argument("rapport", help="fichier .xlsx exporte depuis MT5")
ap.add_argument("--symboles", nargs="+", default=None, metavar="SYM",
help="ne garder que ces symboles (ex : --symboles GER40.p "
"NAS100.p). Recommande des que vous melangez des "
"familles d'instruments differentes.")
ap.add_argument("--tirages", type=int, default=20000,
help="nombre de permutations (defaut : 20000)")
ap.add_argument("--graine", type=int, default=None,
help="graine aleatoire, pour resultats reproductibles")
ap.add_argument("--graphique", action="store_true",
help="affiche l'histogramme des permutations")
args = ap.parse_args()
d = preparer(charger_positions(args.rapport))
if args.symboles:
d = d[d["sym"].isin(args.symboles)].reset_index(drop=True)
if d.empty:
sys.exit("Aucune position pour les symboles demandes.")
if len(d) < 30:
print("Attention : moins de 30 positions exploitables, "
"les resultats ci-dessous n'ont aucune valeur statistique.\n")
print("=" * 68)
print(" %d positions | %s -> %s" % (len(d), d["t_open"].iloc[0],
d["t_close"].iloc[-1]))
print("=" * 68)
vpp = d.groupby("sym")["vpp"].first()
if len(vpp) > 1 and vpp.max() / vpp.min() > 20:
print("\nAVERTISSEMENT : vos instruments ont des valeurs de point tres\n"
"differentes (indices et forex, par exemple). Le calcul a taille\n"
"constante additionne alors des lots qui n'ont rien de comparable.\n"
"Relancez avec --symboles pour ne garder qu'une famille homogene.")
print("\n[1] RESULTAT PAR INSTRUMENT, A TAILLE CONSTANTE (1 lot partout)\n")
print(resume_par_symbole(d).to_string())
print("\n[2] SIGNAL OU SIZING ?\n")
reel_net = d["net"].sum()
plat = d["eur_1lot"].sum()
print(" Resultat reel (tailles reelles, net) : %+10.2f EUR" % reel_net)
print(" Resultat equipondere (1 lot partout) : %+10.2f EUR" % plat)
if abs(reel_net) > 1e-9:
print(" Part du resultat due au dimensionnement : %9.1f %%"
% ((reel_net - plat) / reel_net * 100))
bas, haut = intervalle_confiance(d["pct"])
print("\n Rendement moyen par position, en %% du prix : %+.4f %%"
% d["pct"].mean())
print(" Intervalle de confiance a 95 %% : [%+.4f %% ; %+.4f %%]"
% (bas, haut))
if bas < 0 < haut:
print(" -> Zero est dans l'intervalle : aucun edge d'entree "
"demontrable sur cet echantillon.")
elif bas > 0:
print(" -> Edge d'entree positif et significatif sur cet echantillon.")
else:
print(" -> Edge d'entree negatif et significatif sur cet echantillon.")
print("\n[3] TEST DE PERMUTATION : votre sizing est-il informatif ?\n")
reel, sims, centile = test_permutation(d, args.tirages, args.graine)
p = 1 - centile / 100
print(" Resultat reel (brut, hors frais) : %+10.2f EUR" % reel)
print(" Moyenne des %d reallocations aleatoires : %+10.2f EUR"
% (args.tirages, sims.mean()))
print(" Ecart-type de ces reallocations : %10.2f EUR" % sims.std())
print(" 5e centile / mediane / 95e centile : %+.0f / %+.0f / %+.0f EUR"
% (np.percentile(sims, 5), np.median(sims), np.percentile(sims, 95)))
print("\n Votre resultat se situe au %.1f e centile (p = %.4f)"
% (centile, p))
print(" %d tirages au hasard sur %d font aussi bien ou mieux."
% ((sims >= reel).sum(), args.tirages))
if p < 0.05:
verdict = ("Votre dimensionnement porte de l'information : il est "
"difficile de l'attribuer au hasard.")
elif p < 0.20:
verdict = ("Resultat encourageant mais non concluant. Il faut plus de "
"trades pour trancher.")
else:
verdict = ("Votre dimensionnement est indistinguable d'une repartition "
"au hasard des memes tailles.")
print("\n -> " + verdict)
print("\n Rappel : l'ecart-type ci-dessus mesure le bruit genere par le "
"seul fait\n de faire varier les tailles. S'il est du meme ordre "
"que votre resultat,\n votre historique ne permet pas de conclure, "
"quel que soit le centile.\n")
if args.graphique:
try:
import matplotlib.pyplot as plt
except ImportError:
sys.exit("matplotlib n'est pas installe : pip install matplotlib")
plt.figure(figsize=(9, 5))
plt.hist(sims, bins=80)
plt.axvline(reel, linestyle="--", linewidth=2)
plt.title("Resultat reel vs %d reallocations aleatoires des memes tailles"
% args.tirages)
plt.xlabel("Resultat (EUR)")
plt.ylabel("Nombre de tirages")
plt.tight_layout()
plt.show()
if name == "main":
main()
Le script lit le bloc Positions du rapport, donc il raisonne position par position. Si vous travaillez en compte netting plutôt qu'en hedge, ou si vous fermez vos positions par tranches, l'unité d'analyse ne sera pas exactement celle que vous avez en tête.
Le test de permutation suppose que vos trades sont indépendants les uns des autres. Si vous ouvrez plusieurs positions corrélées en même temps, ce qui est le cas de beaucoup d'entre nous, il surestime légèrement la significativité. Autrement dit, le vrai p est probablement un peu moins bon que celui qui s'affiche.
Enfin, un résultat au 99e centile sur 60 trades ne vaut pas grand-chose. C'est la taille de l'échantillon qui commande, comme toujours.
Signalez-moi les bugs, je corrigerai le message.