Simon Depiets pour Trading-Automatique
Un réseau de neurones artificiel est un modèle de calcul dont la conception est très schématiquement inspirée du fonctionnement des neurones biologiques (humains ou non).Les réseaux de neurones sont généralement optimisés par des méthodes d’apprentissage de type statistique, si bien qu’ils sont placés d’une part dans la famille des applications statistiques, qu’ils enrichissent avec un ensemble de paradigmes permettant de générer de vastes espaces fonctionnels, souples et partiellement structurés, et d’autre part dans la famille des méthodes de l’intelligence artificielle qu’ils enrichissent en permettant de prendre des décisions s’appuyant davantage sur la perception que sur le raisonnement logique formel. En modélisation des circuits biologiques, ils permettent de tester les hypothèses fonctionnelles issues de la neurophysiologie ou de tester les conséquences de ces hypothèses afin de les comparer aux réseaux réels. (Wikipédia)
L’objectif de mon implémentation d’un réseau de neurones est l’apprentissage par celui-ci de conditions externes dynamiques, j’ai d’abord essayé de faire en sorte que chaque neurone représente une stratégie. Le problème était que j’orientais donc la prise de décision en éliminant les stratégies qui ne fonctionnent pas (autrement dit 99.9% des stratégies imaginables). J’obtenais un amalgame de stratégies et au final je n’en créais pas une nouvelle (même simple) et j’étais donc loin de mon objectif.
Cela faisait quelques mois déjà que j’avais l’intention de créer un tel algorithme de prise de décision, au départ MetaTrader 5 était la plateforme privilégiée. Faute de mode backtest, c’est finalement vers le tout nouvel Algodeal que je me suis tourné, une plateforme dont Nicolas vous a déjà parlé. Algodeal dispose d’une API en Java et j’avais donc tout loisir pour faire du code orienté objet dessus.
Les noeuds
Je suis donc parti sur la piste de créer un Nœud par Indicateur, le premier Nœud que j’ai implémenté est celui du RSI. Un nœud contient un ensemble de propriétés :
- spectrum : La variance de la loi normale de pondération que nous verrons plus tard (ne me demandez pas pourquoi spectrum)
- min : La valeur du plus petit sous-nœud
- max : La valeur du plus grand sous-nœud
- step : L’écart de valeur entre deux sous-nœuds
- vsn : Le Vector de Sous-Nœuds
- name : Le nom du nœud
Et un ensemble de méthodes :
- Node : Un constructeur qui crée aussi l’ensemble des sous-noeuds
- new_weight : Prend en paramètre le nombre de pips depuis l’UT précédente et value la valeur du précédent RSI et pondère les sous-nœuds.
- normal_law : qui calcule une loi normale, placé ici
- dump : Une fonction de débogage qui affiche toutes les poids des sous-nœuds
RSINode
Cette classe est une classe abstraite, je vais donc créer une classe RSINode pour implémenter mon nœud RSI. Cette classe contiendra quelques propriétés :
- rsi : L’indicateur rsi
- rsiLength : La longueur du RSI (14 UT dans l’exemple)
Et implémentera une seule méthode en plus de son constructeur :
- get_weight : Récupère le poids sur le bon sous-nœud, c’est le seul endroit avec new_weight où on a vraiment besoin de la valeur du RSI. Mais j’utilise une astuce de programmation pour ne pas avoir à recoder new_weight à chaque nouveau type de nœuds (l’ancien poids est stocké dans la partie abstraite lors d’un appel à get_weight).
SubNode
Le sous-nœud est le deuxième niveau du réseau de neurones (qui en compte pour l’instant 2). Pour chaque nœud, par exemple un NoeudRSI14, il y a (max – min) / step SubNodes. Par exemple avec min = 0, max = 100 et min = 1.0 on a un subnode à 0, un à 1, un à 2, etc. jusqu’à 100.
Les SubNodes ont 4 paramètres :
- value : est la valeur du SubNode (dans le cas du RSI 1, 2, 3, etc.)
- weight : est le poids du SubNode, le plus important, un poids négatif est un signal de vente, un poids positif un signal d’achat
- wu : Un coefficient multiplicateur lors d’une augmentation de poids
- wd : Un coefficient multiplicateur lors d’une diminution de poids
Ils ont également trois méthodes :
- weight : récupère le poids du SubNode
- weight_up : augmente le poids du SubNode
- weight_down : diminue le poids du SubNode
Fonctionnement
image : http://ks363797.kimsufi.com/images/rdn_workflow.pn... ou format inconnu
Initialisation
A l’initialisation les nœuds sont crées et stockés dans un vecteur, chaque nœud crée également ses sous-nœuds et leur donne un poids (nul ou issu de précédentes expériences).
Mise à jour du poids
Au début les nœuds ont un poids nul, on observe donc le marché sans agir, imaginons que le 1er Mars le FCE soit à 4000 points avec un RSI(14) à 60 et que le 2 Mars le FCE passe à 4020 points. Je souhaite donc faire connaître à mon SubNode(60) que le marché à tendance à monter quand le RSI est à 60. Je vais donc augmenter le poids de tous les SubNodes avec la formule suivante :
NouveauPoids = AncienPoids + CoefficientWU * Log(1+EcartPips) * LoiNormale(SubNode)
La loi normale me sert ici à augmenter non seulement le nœud 60, mais également dans une moindre mesure les nœuds alentours :
image : http://ks363797.kimsufi.com/images/rdn_normal.pngi... ou format inconnu
Loi normale
Cette formule est bien entendu à raffiner au fur et à mesure des expérimentations et des résultats mais ce n’est pas l’objet de ce Proof of Concept.
Récupération du poids
Je suis maintenant à 4020 et mon RSI est à 65, je vais récupérer la valeur du SubNode(65) , si celle-ci est positif je vais acheter, si celle-ci est négative je vais vendre. Afin de vérifier que mon apprentissage fonctionnait j’ai affiché les valeurs des différents SubNode à la fin de l’expérience (10 ans de backtest sur FCE).
image : http://ks363797.kimsufi.com/images/rdn_rsi14.pngin... ou format inconnu
Valeur des SubNodes RSI 14 après 10 ans de backtest
image : http://ks363797.kimsufi.com/images/rdn_rsi50.pngin... ou format inconnu
Valeur des SubNodes RSI 50 après 10 ans de backtest
On peut observer que globalement quand un RSI est supérieur à 50 le marché monte le jour suivant… attention la formule n’étant pas parfaite les anomalies statistiques sont très « puissantes ».
Améliorations possibles
La liste des améliorations possible est longue, voire infinie, puisqu’il me suffit de rajouter des Nœuds sur les milliers d’indicateurs existants. Si ils ne sont pas efficients (comprendre aléatoires) leur poids devrait de toute façon être négligeable par rapport aux autres. Parmi les améliorations notables :
- SubNodes à plusieurs valeurs (RSI hier et RSI avant-hier, ou MACD par exemple)
- Recherche de coefficients efficaces (wu, wd, variance de la loi normale)
- Pondération plus efficace pour éliminer rapidement les indicateurs peu efficaces et faire sortir du lot les indicateurs efficaces.
- MoneyManagement
Résultats
Les résultats n’ont aucune importance à ce stade mais les voici quand-même
image : http://ks363797.kimsufi.com/images/rdn_chart.pngin... ou format inconnu
Un beau suivi de tendance avec le RSI 50…
image : http://ks363797.kimsufi.com/images/rdn_pnl.pngintr... ou format inconnu
…avec le P&L classique de ce genre de stratégies
Un réseau de neurones artificiel est un modèle de calcul dont la conception est très schématiquement inspirée du fonctionnement des neurones biologiques (humains ou non).Les réseaux de neurones sont généralement optimisés par des méthodes d’apprentissage de type statistique, si bien qu’ils sont placés d’une part dans la famille des applications statistiques, qu’ils enrichissent avec un ensemble de paradigmes permettant de générer de vastes espaces fonctionnels, souples et partiellement structurés, et d’autre part dans la famille des méthodes de l’intelligence artificielle qu’ils enrichissent en permettant de prendre des décisions s’appuyant davantage sur la perception que sur le raisonnement logique formel. En modélisation des circuits biologiques, ils permettent de tester les hypothèses fonctionnelles issues de la neurophysiologie ou de tester les conséquences de ces hypothèses afin de les comparer aux réseaux réels. (Wikipédia)
L’objectif de mon implémentation d’un réseau de neurones est l’apprentissage par celui-ci de conditions externes dynamiques, j’ai d’abord essayé de faire en sorte que chaque neurone représente une stratégie. Le problème était que j’orientais donc la prise de décision en éliminant les stratégies qui ne fonctionnent pas (autrement dit 99.9% des stratégies imaginables). J’obtenais un amalgame de stratégies et au final je n’en créais pas une nouvelle (même simple) et j’étais donc loin de mon objectif.
Cela faisait quelques mois déjà que j’avais l’intention de créer un tel algorithme de prise de décision, au départ MetaTrader 5 était la plateforme privilégiée. Faute de mode backtest, c’est finalement vers le tout nouvel Algodeal que je me suis tourné, une plateforme dont Nicolas vous a déjà parlé. Algodeal dispose d’une API en Java et j’avais donc tout loisir pour faire du code orienté objet dessus.
Les noeuds
Je suis donc parti sur la piste de créer un Nœud par Indicateur, le premier Nœud que j’ai implémenté est celui du RSI. Un nœud contient un ensemble de propriétés :
- spectrum : La variance de la loi normale de pondération que nous verrons plus tard (ne me demandez pas pourquoi spectrum)
- min : La valeur du plus petit sous-nœud
- max : La valeur du plus grand sous-nœud
- step : L’écart de valeur entre deux sous-nœuds
- vsn : Le Vector de Sous-Nœuds
- name : Le nom du nœud
Et un ensemble de méthodes :
- Node : Un constructeur qui crée aussi l’ensemble des sous-noeuds
- new_weight : Prend en paramètre le nombre de pips depuis l’UT précédente et value la valeur du précédent RSI et pondère les sous-nœuds.
- normal_law : qui calcule une loi normale, placé ici
- dump : Une fonction de débogage qui affiche toutes les poids des sous-nœuds
RSINode
Cette classe est une classe abstraite, je vais donc créer une classe RSINode pour implémenter mon nœud RSI. Cette classe contiendra quelques propriétés :
- rsi : L’indicateur rsi
- rsiLength : La longueur du RSI (14 UT dans l’exemple)
Et implémentera une seule méthode en plus de son constructeur :
- get_weight : Récupère le poids sur le bon sous-nœud, c’est le seul endroit avec new_weight où on a vraiment besoin de la valeur du RSI. Mais j’utilise une astuce de programmation pour ne pas avoir à recoder new_weight à chaque nouveau type de nœuds (l’ancien poids est stocké dans la partie abstraite lors d’un appel à get_weight).
SubNode
Le sous-nœud est le deuxième niveau du réseau de neurones (qui en compte pour l’instant 2). Pour chaque nœud, par exemple un NoeudRSI14, il y a (max – min) / step SubNodes. Par exemple avec min = 0, max = 100 et min = 1.0 on a un subnode à 0, un à 1, un à 2, etc. jusqu’à 100.
Les SubNodes ont 4 paramètres :
- value : est la valeur du SubNode (dans le cas du RSI 1, 2, 3, etc.)
- weight : est le poids du SubNode, le plus important, un poids négatif est un signal de vente, un poids positif un signal d’achat
- wu : Un coefficient multiplicateur lors d’une augmentation de poids
- wd : Un coefficient multiplicateur lors d’une diminution de poids
Ils ont également trois méthodes :
- weight : récupère le poids du SubNode
- weight_up : augmente le poids du SubNode
- weight_down : diminue le poids du SubNode
Fonctionnement
Initialisation
A l’initialisation les nœuds sont crées et stockés dans un vecteur, chaque nœud crée également ses sous-nœuds et leur donne un poids (nul ou issu de précédentes expériences).
Mise à jour du poids
Au début les nœuds ont un poids nul, on observe donc le marché sans agir, imaginons que le 1er Mars le FCE soit à 4000 points avec un RSI(14) à 60 et que le 2 Mars le FCE passe à 4020 points. Je souhaite donc faire connaître à mon SubNode(60) que le marché à tendance à monter quand le RSI est à 60. Je vais donc augmenter le poids de tous les SubNodes avec la formule suivante :
NouveauPoids = AncienPoids + CoefficientWU * Log(1+EcartPips) * LoiNormale(SubNode)
La loi normale me sert ici à augmenter non seulement le nœud 60, mais également dans une moindre mesure les nœuds alentours :
Loi normale
Cette formule est bien entendu à raffiner au fur et à mesure des expérimentations et des résultats mais ce n’est pas l’objet de ce Proof of Concept.
Récupération du poids
Je suis maintenant à 4020 et mon RSI est à 65, je vais récupérer la valeur du SubNode(65) , si celle-ci est positif je vais acheter, si celle-ci est négative je vais vendre. Afin de vérifier que mon apprentissage fonctionnait j’ai affiché les valeurs des différents SubNode à la fin de l’expérience (10 ans de backtest sur FCE).
Valeur des SubNodes RSI 14 après 10 ans de backtest
Valeur des SubNodes RSI 50 après 10 ans de backtest
On peut observer que globalement quand un RSI est supérieur à 50 le marché monte le jour suivant… attention la formule n’étant pas parfaite les anomalies statistiques sont très « puissantes ».
Améliorations possibles
La liste des améliorations possible est longue, voire infinie, puisqu’il me suffit de rajouter des Nœuds sur les milliers d’indicateurs existants. Si ils ne sont pas efficients (comprendre aléatoires) leur poids devrait de toute façon être négligeable par rapport aux autres. Parmi les améliorations notables :
- SubNodes à plusieurs valeurs (RSI hier et RSI avant-hier, ou MACD par exemple)
- Recherche de coefficients efficaces (wu, wd, variance de la loi normale)
- Pondération plus efficace pour éliminer rapidement les indicateurs peu efficaces et faire sortir du lot les indicateurs efficaces.
- MoneyManagement
Résultats
Les résultats n’ont aucune importance à ce stade mais les voici quand-même
Un beau suivi de tendance avec le RSI 50…
…avec le P&L classique de ce genre de stratégies
Commentaire