Comme le dit souvent un de mes youtubeurs préférés, il est important de “comprendre ce qu’il se passe” (merci Tibo!).
Un leitmotiv à se répéter en boucle tous les matins…
Si vous en êtes arrivé au point où vous avez un robot qui applique votre stratégie, si vous regardez sur la courbe l’historique des signaux, vous verrez invariablement des trades qui ont été ouverts à des moments où ils n’avaient objectivement que très peu de chance de réussite.
Il faut parfois changer d’échelle de temps pour s’en rendre compte. Un trade peut sembler pertinent en M15, puis qu’en on passe en H4 ou D1, on va s’apercevoir qu’on est bien en dehors des bandes de Bollinger, avec des signes évidents de retracement en cours au niveau du macd ou du stochastique.
Ainsi, pour arriver à “comprendre ce qu’il se passe”, la première chose est d’aller contrôler les différentes échelles de temps, surtout les échelles de temps supérieures (MN1, D1, H4).
Le fait d’observer un trade en particulier, pris isolément des autres trades générés par votre robot, n’a toutefois que peu d’importance. Ce qui nous intéresse, c’est la probabilité de succès ou d’échec d’un trade dans une configuration de marché donnée.
Pour connaître cette probabilité, il est indispensable de savoir générer un listing des trades émis, avec pour chacun d’eux la valeur de nos indicateurs préférés, les points gagnés ou perdus, ainsi que toutes les informations dont on peut avoir besoin. Notre ami Excel ou tout autre tableur de votre choix vous aidera alors à tirer toutes les conclusions dont vous avez besoin.
Cette possibilité de générer des fichiers de reporting est l’un des gros avantages de Metatrader sur les autres plateformes TradingView ou ProRealTime.
En MQL, la génération de fichiers est très simple.
On commence par la génération de l’entête au niveau du OnInit(), dont voici un exemple ci-dessous:
m_handle_file_trade = FileOpen(“Analyse\Pegase_Trade.csv”, FILE_WRITE|FILE_CSV);
FileWrite(m_handle_file_trade,
“Début “,
“Fin”,
“Symbol”,
“Volume”,
“Commentaire”,
“Position Type”,
“Swap (EUR)”,
“Profit (EUR)”,
“Profit (Pts)”,
“Points max (Pts)”,
“Points max (%)”,
“Pertes max (Pts)”,
“Pertes max (%)”,
“Signal de sortie”,
// mn1
“Dist. upper(buy)/lower(sell).mn1”,
“Dist. ma4.mn1”,
“Dist. ma8.mn1”,
“Pente ma4.mn1”,
“Pente ma8.mn1”,
“Delta sto.mn1”,
… etc …
);
Le fichier sera généré dans un répertoire du type:
C:\Users\smelo\AppData\Roaming\MetaQuotes\Tester\D0E8209F77C8CF37AD8BF550E51FF075\Agent-127.0.0.1-3000\MQL5\Files\Analyse
Ce répertoire est vidé à chaque lancement d’une nouvelle simulation, donc si il disparaît, c’est normal. Si ce mode de fonctionnement ne vous convient pas, vous pouvez utiliser l’option “FILE_COMMON”, ainsi vos fichiers seront créés dans un répertoire permanent.
L’écriture des données spécifiques à chaque trade émis doit se faire au moment de la clôture du dit trade si l’on veut savoir notamment si ce trade est gagnant ou pas…
Comme on souhaite récupérer les informations de marché au moment de l’ouverture du trade, cela signifie donc que l’on doit stocker ces informations dès l’ouverture du trade, pour être en mesure de les restituer plus tard.
Ainsi, au niveau des fonctions permettant l’ouverture d’un trade achat/vente, on va venir mettre à jour un tableau conservé en sessions, avec toutes les données nous intéressant.
void OpenBuy(string commentary)
{
double price, volume;
price = m_symbol.Ask();
// Calcul du volume
volume = dblLotsRisk( p_stop_loss );
if(m_trade.Buy(volume, m_symbol.Name(), m_symbol.Ask(), 0, 0, commentary))
{
// mn1
m_data_infos[0] = MathRound((m_bollinger_upper_mn1[0] – m_symbol.Ask())/m_bollinger_upper_mn1[0]10000); m_data_infos[1] = MathRound((buffer_ma4_mn1[0] – m_symbol.Ask())/buffer_ma4_mn1[0]10000);
m_data_infos[2] = MathRound((buffer_ma8_mn1[0] – m_symbol.Ask())/buffer_ma8_mn1[0]10000); m_data_infos[3] = MathRound(((buffer_ma4_mn1[0] – buffer_ma4_mn1[1])/buffer_ma4_mn1[1])10000)/10;
m_data_infos[4] = MathRound(((buffer_ma8_mn1[0] – buffer_ma8_mn1[1])/buffer_ma8_mn1[1])10000)/10; m_data_infos[5] = MathRound((sto_main_mn1[0] – sto_signal_mn1[0])10)/10;
… etc …
}
}
Il ne reste plus qu’à voir la mise à jour du fichier au moment de la clôture du trade.
Pour cela il est nécessaire de pouvoir exécuter une fonction à chaque fois qu’un trade se ferme, ce qui va s’avérer impossible si notre trade est fermé automatiquement par StopLoss ou TakeProfit. On doit donc pouvoir “hacker” ce fonctionnement, du moins pour les simulations.
Il y a probablement plusieurs façons de procéder, peut-être plus élégantes que celle que je vous propose ici, le principal est que cela fonctionne.
J’ai l’habitude de rajouter un paramètre de lancement “Désactiver stop loss (pour data report)”, variable “p_desactiver_stoploss”. Quand ce paramètre est activé, les stop loss/take profit ne sont pas définis au niveau des trades ouverts.
Voici ce que cela donne au niveau de la fonction de fermeture des trades pour la gestion des stop loss:
if (PositionGetDouble(POSITION_SL) == 0 && !p_desactiver_stoploss)
{
m_trade.PositionModify(ticket,
MathRound(PositionGetDouble(POSITION_PRICE_OPEN) – p_stop_loss * m_symbolPointVal),
PositionGetDouble(POSITION_TP)
);
}
// Stop loss automatique au cas où la mise à jour du SL ait échoué
if (points_en_cours < -1 * p_stop_loss)
{
if (PositionClose(ticket, “SL”))
UpdateReporting(currentTrade, “SL”);
}
Il reste à créer la fonction de mise à jour du fichier de reporting:
void UpdateReporting(STrade &closeTrade, string exitSignal)
{
double points_en_cours;
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
points_en_cours = m_symbol.Ask() – PositionGetDouble(POSITION_PRICE_OPEN);
else
points_en_cours = PositionGetDouble(POSITION_PRICE_OPEN) – m_symbol.Bid();
FileWrite(m_handle_file_trade,
datetime(PositionGetInteger(POSITION_TIME)),
TimeCurrent(),
Symbol(),
PositionGetDouble(POSITION_VOLUME),
PositionGetString(POSITION_COMMENT),
PositionGetInteger(POSITION_TYPE),
PositionGetDouble(POSITION_SWAP),
PositionGetDouble(POSITION_PROFIT),
points_en_cours,
closeTrade.points_max,
closeTrade.points_max / rates_d1[0].open * 100,
closeTrade.pertes_max,
closeTrade.pertes_max / rates_d1[0].open * 100,
exitSignal,
DoubleToString(m_data_infos[0]),
DoubleToString(m_data_infos[1]),
DoubleToString(m_data_infos[2]),
DoubleToString(m_data_infos[3]),
DoubleToString(m_data_infos[4]),
DoubleToString(m_data_infos[5]),
…. etc …
);
}
Ce n’est bien entendu qu’un exemple, à vous de constituer ce reporting avec les informations dont vous avez besoin, vos indicateurs préférés, etc…
Pour ma part j’aime bien avoir un aperçu des points max/pertes max durant la vie de chaque trade, car cela permet de voir les optimisations à apporter au niveau de la stratégie de sortie.
J’utilise aussi des commentaires à l’ouverture du trade mais aussi à la fermeture afin d’identifier les éléments déclencheurs d’une ouverture et d’une fermeture dans le cas de stratégies avancées.
Je pense avoir dit ici le principal.
Cet outil assez simple est particulièrement puissant et incontournable. Je l’utilise systématiquement pour être en mesure d’identifier les différentes configurations de marché, notamment celles qu’il vaut mieux éviter.
Avec l’habitude, on arrive parfois à lire le marché juste sur la base de ces données et sans même regarder la courbe, même si l’aspect graphique s’avère indispensable dans de nombreuses situations. Cela permet de détecter rapidement la présence d'”outliers” sur certains indicateurs, c’est à dire des valeurs inhabituelles permettant de comprendre que nous sommes dans une situation de marché particulière.
C’est cette complémentarité entre “datas” et “lecture d’un graphe” qui donne toute sa puissance à cette méthode. Cela permet d’apprendre au fil de l’expérience les configurations qui fonctionnent, et celles qui fonctionnent beaucoup moins. On découvre aussi que rien n’est aussi simple qu’il n’y paraît. Une configuration de marché peut nous sembler particulièrement dangereuse en analysant un cas particulier, mais en regardant de manière globale, sur un historique de 10 ans, et sur plusieurs actifs, on peut se rendre compte que c’est beaucoup plus subtil que cela, et que finalement, cela se passe plutôt bien dans la plupart des cas. C’est là où l’analyse des données collectées va nous aider à comprendre ce qui différentie un cas “qui marche”, d’un cas “qui ne marche pas”. Ce sera peut-être en regardant les gradients de moyennes mobiles, les distances à ces moyennes mobiles, ou d’autres indicateurs.
J’espère que cela vous aura été utile.