Tout investisseur, que ce soit en cryptomonnaies ou en fiats rêve d’être en mesure d’en prévoir le cours: “buy low, sell high” est le crédo de l’investisseur !
Dans cet article, nous allons utiliser la puissance du langage de script Python afin, dans un premier temps, de calculer la valeur du MACD d’une cryptomonnaie donnée et d’en étudier la relation avec les fluctuation du cours et, dans un second temps, d’exploiter cette connaissance afin de vérifier si nous pouvons utiliser ce MACD pour établir une analyse prévisionnelle des cours gagnante.
Cet article ne constitue pas un une proposition pour un investissement ou un avis financier. Il ne s’agit que d’un article technique explorant les possibilités offertes par Python dans le domaine de l’analyse financière. Je ne peux pas être tenu pour responsable des conséquences qui découleraient de l’utilisation des scripts présentés !
Qu’est-ce que le MACD ?
Cet indicateur qui signifie en anglais “Moving Average Convergence/Divergence” est utilisé lors de l’analyse technique qui a pour objectif de prévoir la tendance d’un cours. La MACD permet donc de visualiser des signaux d’achat ou de vente. Un histogramme sont associés afin de travailler sur les divergences.
Comment calculer le MACD ?
Pour calculer la MACD il faut faire la différence entre deux moyennes mobiles exponentielles (MME) ayant des périodes différentes. De manière générale, il est convenu de faire la différence entre une moyenne mobile exceptionnelle à 12 jours et une autre à 26 jours. On l’écrit donc MACD(e12,e26). Une fois calculée et représentée graphiquement, on peut parler de ligne de MACD.
Comment utiliser le MACD ?
Visuellement parlant, il est aisé de prévoir une tendance:
- chaque croisement des lignes des moyennes indique un changement de tendance
- si la moyenne sur 12 jours (e12) dépasse la moyenne sur 24 jours, cela signifier que nous avons une tendance haussière
- si la moyenne sur 24 jours (e24) dépasse la moyenne sur 12 jours, cela signifier que nous avons une tendance baissière
Programatiquement, nous utiliserons le calcul e12 – e24 en observant son trajet autour de 0
Au boulot…
Première étape. Installer les dépendances nécessaires
Nous aurons besoin de diverses bibliothèques de fonctions:
ccxt
qui nous donne accès aux données financières des différents exchanges négociant les cryptomonnaies: voir GitHub;pandas
qui nous fourni la conversion de nos données en format dictionnaire vers un format dataframe : il s’agit d’un format semblable à celui d’une feuille de calcul EXCEL, ce qui facilite la manipulation des données;matplotlib
qui nous permet facilement de visualiser graphiquement les résultats de nos calculs et le cours des cryptomonnaies. Nous utiliserons la classematplotlib.pyplot
Pour ceux qui débutent en Python: voyez la documention de ‘pip’ afin d’installer localement ces différentes bibliothèques de fonctions.
Commençons notre script par:
#!/usr/bin/env python3 # encoding: utf-8 # article-MACD # Created by xtof on 20/07/2018. import pandas as pd import ccxt import matplotlib.pyplot as plt if __name__ == '__main__': pass
Deuxième étape. Télécharger les données financière d’un marché
Utilisons à présent l’excellent ccxt
afin de télécharger les données du cours d’un marché, disons par exemple : ETH/BTC (Ethereum / Bitcoin) sur l’exchange BITTREX et pour le cours journalier (timeframe = ‘1d’). Pour ce faire, créons une fonction getCCXTData(market)
. Celle-ci prend un marché en paramètre et nous retourne un dataframe avec les données de cours :
def getCCXTData(market): exchange = ccxt.bittrex() dataFile = exchange.fetch_ohlcv(market, timeframe = '1d') dataFrame = pd.DataFrame(data=dataFile, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']) return dataFrame
et appelons cette fonction au lancement du script afin de voir son résultat:
if __name__ == '__main__': df = getCCXTData('ETH/BTC') print(df)
Un dataframe des cours de ‘ETH/BTC’ nous est retourné:
timestamp open high low close volume 0 1439510400000 0.006900 0.080000 0.006667 0.007000 3123.423352 1 1439596800000 0.006740 0.007240 0.005950 0.006954 3060.474329 2 1439683200000 0.006501 0.006900 0.004120 0.006051 8554.037732 3 1439769600000 0.006139 0.006139 0.004655 0.004798 6059.488321 ... ... ... ... ... ... ... 1069 1531872000000 0.068245 0.068741 0.064352 0.065201 8439.720603 1070 1531958400000 0.065050 0.065201 0.062175 0.062610 7261.326607 1071 1532044800000 0.062610 0.062900 0.060770 0.061226 4563.440498 [1072 rows x 6 columns]
Troisième étape. Calcul du MACD
Exploitons à présent ces données afin de calculer le MACD. Une petite fonction va nous y aider:
def createMACD(df): df['e26'] = pd.Series.ewm(df['close'], span=26).mean() df['e12'] = pd.Series.ewm(df['close'], span=12).mean() df['MACD'] = df['e12'] - df['e26'] return df
Cette fonction prend en argument le dataframe contenant les données de cours et y rajoute trois colonnes calculées: e12, e26 et MACD avant de le retourner. Testons la fonction:
if __name__ == '__main__': df = getCCXTData('ETH/BTC') df = createMACD(df) print(df)
Nous obtenons dataframe modifié:
timestamp open high ... e26 e12 MACD 0 1439510400000 0.006900 0.080000 ... 0.007000 0.007000 0.000000 1 1439596800000 0.006740 0.007240 ... 0.006976 0.006975 -0.000001 2 1439683200000 0.006501 0.006900 ... 0.006644 0.006614 -0.000029 3 1439769600000 0.006139 0.006139 ... 0.006128 0.006041 -0.000087 ... ... ... ... ... ... ... ... 1069 1531872000000 0.068245 0.068741 ... 0.071088 0.069392 -0.001696 1070 1531958400000 0.065050 0.065201 ... 0.070460 0.068349 -0.002112 1071 1532044800000 0.062610 0.062900 ... 0.069779 0.067259 -0.002520 [1072 rows x 9 columns]
Quatrième étape. Visualisation des données
Ecrivons une fonction qui va nous permettre de visualiser nos données:
def plotData(df, columns): df[columns].plot() plt.grid(True) plt.show()
Cette fonction prend comme argument notre dataframe et l’intitulé des colonnes que nous souhaitons visualiser.
Testons la fonction:
if __name__ == '__main__': df = getCCXTData('ETH/BTC') df = createMACD(df) plotData(df, ['close', 'e26', 'e12'])
La fonction génère un graphique avec trois lignes: une par colonne demandée.
Effectuons un zoom sur une partie significative pour le propos: entre les lignes 400 et 520.
Nous pouvons ici constater comment la visualisation de e12 et e26 permet de prévoir la tendance d’un cours. Entre 400 et 420, e12 est situé au-dessus de e26 : la tendance est haussière. e12 croise e26 aux environs de 400 : changement de tendance, nous repartons vers la baisse. Vers 507, e1 remonte à la rencontre de e26: la tendance est haussière et est confirmée vers 511 !
Un autre exemple significatif:
Vers 978, e12 dépasse e26: le moment est venu d’acheter ! A 1018, e12 plonge en-dessous de e26: il est temps de vendre ! Nous avons acheté à 0.063 BTC, nous revendons à 0.078 BTC avec une marge de 0.013 BTC. Si nous y ajoutons les frais d’achat et de revente (0.025%) imposé par Bittrex, nous pouvons calculer notre profit net (avec le 1 BTC à 6000 $) :
achat: 0.063 + 0.025% =0,06301575 (378 $)
vente: 0.078 – 0.025% = 0,0779805 (467,8 $)
profit: 0,01496475 BTC (89,8 $)
Pas mal pour une seule opération !
Fort de ce constat, pouvons nous envisager une stratégie à terme qui nous garantirait un revenu constant, avec des risques minimisés ? Le rêve de l’investisseur…
Peut-être venons-nous de découvrir le Graal tant convoité ! Avant de commander la Lamborghini, il serait sage de procéder à une petite simulation pour vérifier nos conclusions… 😉
Cinquième étape. Simulation
Nous allons à présent simuler l’application notre stratégie sur les données dont nous disposons, soit 1072 jours (une rangée par jour).
Notre stratégie est la suivante:
- e12 passe au-dessus de e26 (MACD négative), nous achetons 1 ETH
- e12 passe en-dessous de e26 (MACD positive), nous vendons 1 ETH
Après chaque vente, nous calculons le profit (ou la perte) réalisé et nous le cumulons dans une variable ‘budget’.
Définissons quelques conditions de base nécessaires à notre simulation :
- nous supposons disposer d’un budget initial qui est suffisant pour un achat d’au moins 1 ETH (au cours actuel de 0,08 BTC)
- le premier mouvement effectué devra être un achat
- le dernier mouvement effectué devra être une vente
- nous considérons que nos demandes de vente et d’achat sont toutes acceptées et complètement honorée par l’exchange
Nous allons procéder en deux temps:
- nous allons parcourir chaque ligne de notre dataframe qui contient les données financières et la colonne de notre MACD, et rajouter dans une colonne supplémentaire ‘position’ ce que nous décidons d’effectuer: achat ‘buy’, vente ‘sell’ ou maintenir ‘hold’.
- ensuite nous allons parcourir chaque rangée en réalisant la ‘position’ calculée
A l’attaque !
Définissons notre fonction computeStrategy(dataframe).
Celle-ci prend un argument notre dataframe et retournera le profit (ou la perte) réalisé en fin d’exercice.
def computeStrategy(df): # initialisation de quelques variables profit = 0 move = 'buy' # drapeau qui servira à signaler le prochain mouvement 'buy' > 'sell' > 'buy' etc... # crée des colonnes vides dans laquelle nous placerons notre strategie et notre budget df['position'] = None df['budget'] = 0
Pour faciliter la lecture du dataframe, supprimons les colonnes superflues: seul la colonne ‘close’ (le prix de fermeture) nous intéresse.
# enlève les colonnes superflues del df['open'] del df['high'] del df['low'] del df['volume']
Parcourons les rangée du dataframe et appliquons notre théorie:
- si le MACD de la rangée est négatif et que le MACD de la rangée précédente est positif, il s’agit d’un signal d’achat. Effectuons celui-ci et soustrayons de notre budget le prix de l’achat.
- si le MACD de la rangée est positif et que le MACD de la rangée précédente est négatif, il s’agit d’un signal de vente. Effectuons celle-ci et rajoutons à notre budget le prix de la vente.
for row in range (len(df)): # conditions pour un achat if df['MACD'].iloc[row] < 0 and df['MACD'].iloc[row-1] > 0 and move == 'buy': df['position'].iloc[row] = 'buy' move = 'sell' df['budget'].iloc[row] = df['budget'].iloc[row-1] - df['close'].iloc[row] # conditions pour une vente elif df['MACD'].iloc[row] > 0 and df['MACD'].iloc[row-1] < 0 and move == 'sell': df['position'].iloc[row] = 'sell' move = 'buy' df['budget'].iloc[row] = df['budget'].iloc[row-1] + df['close'].iloc[row] # ni vente ni achat, nous tenons la position else: df['position'].iloc[row] = 'hold' df['budget'].iloc[row] = df['budget'].iloc[row-1]
Remarquez l’utilisation du drapeau ‘move’ qui oblige le script à effectuer une vente après un achat et un achat après une vente, pour éviter de cumuler les achats successifs, dans le cadre de cette simulation.
Testons notre script:
if __name__ == '__main__': df = getCCXTData('ETH/BTC') df = createMACD(df) profit = computeStrategy(df) plotData(df, ['close', 'e26', 'e12', 'budget'])
Nous constatons:
- que le dernier mouvement est un achat, qu’il convient d’annuler: en effet, dans le cadre de cette simulation celui-ci n’a pas de raison d’être puisque nous n’auront pas l’occasion de revendre l’ETH acheté. Après annulation notre profit est de -0.047215
- que même en ignorant ce dernier achat, notre stratégie est perdante, car notre profit est négatif !
Que s’est-il passé ? Pourtant nous avons suivi au pied de la lettre les indications du MACD !
Oui, mais MACD est un indicateur de tendance, pas un générateur de profit ! MACD ignore si le taux existant au moment d’un signal de vente est supérieur ou inférieur à celui de l’achat…
Effectuons un zoom sur la partie 680-760 :
Nous effectuons un achat d’1 ETH au taux de 0.101950 (rangée 691), car e26 passe au-dessus de e12
690 1499126400000 0.102931 0.113130 0.113371 2.408214e-04 hold 0.002642 691 1499212800000 0.101950 0.112302 0.111614 -6.881184e-04 buy -0.099308
et nous le revendons plus tard car e12 repasse au dessus de e26, mais au taux de 0.083790: donc à perte !
746 1503964800000 0.081438 0.077967 0.077566 -4.007387e-04 hold -0.099308 747 1504051200000 0.083790 0.078398 0.078524 1.254276e-04 sell -0.015518
Conclusion temporaire: ne jamais suivre aveuglement les signaux d’un indicateur !
Introduisons un garde-fou afin de nous prémunir de cette situation: nous ne vendons que si le prix de vente est supérieur au prix d’achat !
Utilisons pour cela une variable lastClose
afin de retenir le dernier prix d’achat et intégrons-là dans une condition supplémentaire pour la vente:
for row in range (len(df)): # conditions pour un achat if df['MACD'].iloc[row] < 0 and df['MACD'].iloc[row-1] > 0 and move == 'buy': df['position'].iloc[row] = 'buy' move = 'sell' lastClose = df['close'].iloc[row] df['budget'].iloc[row] = df['budget'].iloc[row-1] - df['close'].iloc[row] # conditions pour une vente elif df['MACD'].iloc[row] > 0 and df['MACD'].iloc[row-1] < 0 and move == 'sell' and df['close'].iloc[row] > lastClose: df['position'].iloc[row] = 'sell' move = 'buy' df['budget'].iloc[row] = df['budget'].iloc[row-1] + df['close'].iloc[row] # ni vente ni achat, nous tenons la position else: df['position'].iloc[row] = 'hold' df['budget'].iloc[row] = df['budget'].iloc[row-1]
En ignorant le dernier achat, qui doit être être annulé, nous avons effectué un profit de 0.099261 BTC sur un budget initial qui doit correspondre un montant du premier achat, soit 0.002332 BTC !
En d’autre termes, j’ai récupéré 42 fois ma mise de départ ! J’ai investit 17$ pour récupérer 728$ (au taux de change à l’écriture de cet article) sur une période 1072 jours (une rangée par jour dans le dataframe), soit presque trois ans…
Perspectives supplémentaires
Evidement, ce calcul ne prend pas en compte les frais d’achat et de revente, les crash boursiers, les exchanges qui se font hacker et voler leurs bitcoins…
La manière la plus sûre d’engranger des profits en investissant dans les cryptomonnaies et de viser le terme le plus court afin d’éviter les désagréments cités: prendre un (petit) profit et se retirer prudemment, puis recommencer…
Nous avons effectué cette simulation sur le marché ‘1d’ de la journée. Qu’en est-il du marché ‘1m’, celui de la minute ? Recommençons notre simulation en adaptant notre script:
dataFile = exchange.fetch_ohlcv(market, timeframe = '1m')
timestamp close e26 e12 MACD position budget 0 1531315260000 0.069364 0.069364 0.069364 0.000000e+00 hold 0.000000 1 1531315320000 0.069365 0.069365 0.069365 2.266026e-08 hold 0.000000 2 1531315380000 0.069368 0.069366 0.069366 1.222136e-07 hold 0.000000 3 1531315440000 0.069556 0.069419 0.069426 6.955776e-06 hold 0.000000 (...) 13967 1532178900000 0.062614 0.062550 0.062529 -2.181685e-05 hold -0.029076 13968 1532179020000 0.062436 0.062542 0.062514 -2.758565e-05 hold -0.029076 13969 1532179080000 0.062436 0.062534 0.062502 -3.179100e-05 hold -0.029076
Avec une mise initiale de 0.069383 BTC (509$) et en ignorant le dernier achat, nous obtenons donc après 10 jours un profit de 0.0422 BTC (309$).
Cependant, il s’agit ici d’un profit purement théorique, car si nous effectuons la simulation en tenant compte des frais d’achat et de vente retenus par Bittrex (0.25%), nous n’arrivons jamais à trouver un prix de vente qui couvre le prix d’achat et les frais qui vont avec ! En effet, sur le marché de la minute, les fluctuations sont minimales, et la probabilité de rencontrer un prix de vente qui couvre les frais est faible…
Conclusion
Oui, il est possible de gagner pas mal d’argent en suivant une stratégie appropriée et en suivant un principe simple: ‘buy low, sell high fast’. Les investissements à terme qui sont effectuées par la plupart des traders ne tient que peu compte de la réalité des marchés de cryptomonnaies, à savoir, volatilité, insécurité, manque de contrôle et j’en passe. Il convient donc de choisir un ou plusieurs indicateurs et, par simulation interposée, vérifier son application sur le marché choisit…
Des commentaires, des questions ? N’hésitez pas à me contacter via la page “Contact“