Décomposition : tendance#
Objectif
modéliser la tendance avec différents modèles
soustraire la tendance
comparer les modèles
Modèles choisis
Régression linéaire
Régression polynomiale
Moyenne mobile
Moyenne mobile exponentielle
B-Splines
Critères d’évaluation
AIC
MSE
graphiquement (la courbe ne doit pas faire “n’importe quoi”)
Imports et lecture des données#
Imports#
1import matplotlib.pyplot as plt
2import numpy as np
3import pandas as pd
4from sklearn.metrics import mean_squared_error
5from statsmodels.graphics.tsaplots import plot_acf
6
7from src.functions.detrend import (
8 BSplinesDetrend,
9 ExponentialMADetrend,
10 LinearMADetrend,
11 LinearRegressionDetrend,
12 PolynomialRegressionDetrend,
13)
14from src.utils import init_notebook
1init_notebook()
1detrend_dict = {}
Lecture des données#
1data_folder = "data/raw_data"
1stock_name = "AAPL"
1df = pd.read_csv(
2 f"{data_folder}/{stock_name}.csv", parse_dates=["Date"], index_col="Date"
3)
4print(f"{df.shape = }")
df.shape = (756, 6)
Soustraction de la tendance#
La tendance est, pour une série chronologique, sa composante première, inhérente à la nature des données. Elle peut le plus souvent se représenter par une droite, à la hausse ou à la baisse. Dans le cas des cours boursiers, le prix a une tendance généralement haussière, qui s’explique par le concept de croissance économique.
1df["Close"].plot(title="Closing price plot (daily) with trend", figsize=(12, 6))
<Axes: title={'center': 'Closing price plot (daily) with trend'}, xlabel='Date'>
Régression linéaire#
Soit le vecteur \(Y = (y_1, \dots, y_N)\) des prix de clôture du cours aux temps (ici, des jours) \(1, \dots, N\).
L’objectif est de s’ajuster aux données avec un modèle de régression linéaire simple donné par l’équation :
où \((\beta_0, \beta_1)\) est le vecteur des paramètres à estimer
et \(X = (1, \dots, N)\) est le vecteur time dummy qui représente l’avancement linéaire du temps.
Puis il faut soustraire au vecteur de données originales \(Y\) le vecteur des prédictions \(Y_{pred}\) :
1# Define time series to detrend
2y = df["Close"]
1# Define detrend model
2detrend_model = LinearRegressionDetrend()
3
4# Fit model to time series
5y_fitted = detrend_model.fit(y)
6
7# Substract adjusted values to original time series to get detrend data
8y_detrend = detrend_model.predict(y)
9
10# Plot the result
11detrend_model.fancy_plot(xticklabels=df.index.strftime("%Y-%m-%d"))
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:43: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[0].set_xticklabels(xticklabels)
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[1].set_xticklabels(xticklabels)
1def aic(y_true, y_detrend, nb_parameters: int) -> float:
2 k = nb_parameters
3 n = len(y_true)
4
5 mse = mean_squared_error(y_true, y_detrend)
6 aic = n * np.log(mse) + 2 * k
7
8 return aic
1aic_detrend = aic(y, y_detrend, 2)
2mse_detrend = mean_squared_error(y, y_detrend)
3detrend_dict["Régression linéaire"] = [aic_detrend, mse_detrend]
1# Input detrend close price into dataframe
2df["Close_detrend"] = y_detrend
1# acf plot before / after
2
3lag = len(df) - 1
4
5_, axs = plt.subplots(2, 1, figsize=(16, 10))
6_ = plot_acf(df["Close"], ax=axs[0], lags=lag, title="ACF plot - Close price")
7_ = plot_acf(
8 df["Close_detrend"], ax=axs[1], lags=lag, title="ACF plot - Close price detrend"
9)
Les fortes autocorrélations dans le premier graphique montrent que la série temporelle contient une tendance.
Après déconstruction de la tendance, le deuxième graphique affiche cependant encore des autocorrélations importantes pour des lags petits. C’est un indice de saisonnalité ou de cyclicité.
Régression polynomiale par morceaux#
Soit \(k \in N^*\).
Pour la régression polynomiale d’ordre \(k\), le même principe que la régression linéaire simple est appliqué mais avec la particularité que des time dummies d’ordre \(1, \dots, k\) sont ajoutées comme variables explicatives au modèle.
L’équation de régression devient :
où pour tout \(i \in [1;k], \hspace{6px} X_i = (1^i, 2^i, \dots, N^i)\).
Cette méthode permet de capter une tendance polynomiale (croissante au carré du temps par exemple).
1# Choose parameters of segmented polynomial regression
2
3order = 3
4n_segments = 5
1# Define detrend model
2detrend_model = PolynomialRegressionDetrend(order=order, n_segments=n_segments)
3
4# Fit model to time series
5y_fitted = detrend_model.fit(y)
6
7# Substract adjusted values to original time series to get detrend data
8y_detrend = detrend_model.predict(y)
9
10# Plot the result
11detrend_model.fancy_plot(xticklabels=df.index.strftime("%Y-%m-%d"))
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:43: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[0].set_xticklabels(xticklabels)
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[1].set_xticklabels(xticklabels)
1aic_detrend = aic(y, y_detrend, 2)
2mse_detrend = mean_squared_error(y, y_detrend)
3detrend_dict["Régression polynomiale"] = [aic_detrend, mse_detrend]
Moyennes mobiles#
Linéaire#
On se munit de \((x_t)_{t \in \mathbb{N}}\) une série temporelle.
Soit \((k, t) \in \mathbb{N}^* \times \llbracket k~;~+\infty \rrbracket\),
la moyenne mobile linéaire à gauche de profondeur \(k\) pour \(x_t\) est :
1# Define detrend model
2detrend_model = LinearMADetrend(window=100)
3
4# Fit model to time series
5y_fitted = detrend_model.fit(y)
6
7# Substract adjusted values to original time series to get detrend data
8y_detrend = detrend_model.predict(y)
9
10# Plot the result
11detrend_model.fancy_plot(xticklabels=df.index.strftime("%Y-%m-%d"))
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:43: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[0].set_xticklabels(xticklabels)
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[1].set_xticklabels(xticklabels)
1aic_detrend = aic(y, y_detrend, 2)
2mse_detrend = mean_squared_error(y, y_detrend)
3detrend_dict["Moyenne mobile linéaire"] = [aic_detrend, mse_detrend]
Exponentielle#
La moyenne mobile exponentielle fait partie de la famille des moyennes mobiles pondérées.
On se munit de \((x_t)_{t \in \mathbb{N}}\) une série temporelle.
Soit \(t \in \mathbb{N}^*\) et \(\alpha \in [0;1]\) une constante de lissage.
On remarque que la moyenne mobile exponentielle à l’instant \(t\) prend en compte toutes les données qui précèdent. Toutefois, les valeurs très antérieures impactent très peu le résultat - d’autant plus que \(\alpha\) est grand - et peuvent donc être négligées.
1# Define detrend model
2detrend_model = ExponentialMADetrend(alpha=0.05)
3
4# Fit model to time series
5y_fitted = detrend_model.fit(y)
6
7# Substract adjusted values to original time series to get detrend data
8y_detrend = detrend_model.predict(y)
9
10# Plot the result
11detrend_model.fancy_plot(xticklabels=df.index.strftime("%Y-%m-%d"))
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:43: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[0].set_xticklabels(xticklabels)
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[1].set_xticklabels(xticklabels)
1aic_detrend = aic(y, y_detrend, 2)
2mse_detrend = mean_squared_error(y, y_detrend)
3detrend_dict["Moyenne mobile exponentielle"] = [aic_detrend, mse_detrend]
B-splines#
Étant donné \(m + 1\) nœuds \( \{t_i \in [0, 1]\}_{i \in [0, m]} \) indexés selon le temps,
une courbe spline de degré \(n \in \mathbb{N^*}\) est une fonction paramétrique définie par :
Les B-splines sont des fonctions utilisées pour l’interpolation ou l’approximation de fonctions continues à partir de données discrètes.
\(\forall k \in \mathbb{N^*}\), la fonction B-spline de degré k, notée \(B_{i, k}(t)\), est définie récursivement comme suit :
Pour \( i \in {0,1,…,n−k−2}\), où \(n\) est le nombre total de points de contrôle et \(k\) est l’ordre des B-splines.
1# Define parameters for B-splines
2
3interval_length = 50 # Days between two knots
4degree = 3
1# Define detrend model
2detrend_model = BSplinesDetrend(interval_length=interval_length, degree=degree)
3
4# Fit model to time series
5y_fitted = detrend_model.fit(y)
6
7# Substract adjusted values to original time series to get detrend data
8y_detrend = detrend_model.predict(y)
9
10# Plot the result
11detrend_model.fancy_plot(xticklabels=df.index.strftime("%Y-%m-%d"))
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:43: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[0].set_xticklabels(xticklabels)
/home/runner/work/stock-analysis/stock-analysis/src/functions/detrend_fancy_plot.py:51: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
axs[1].set_xticklabels(xticklabels)
1aic_detrend = aic(y, y_detrend, 2)
2mse_detrend = mean_squared_error(y, y_detrend)
3detrend_dict["B-Splines"] = [aic_detrend, mse_detrend]
La méthode des BSplines de degré 1 correspond à de l’interpolation linéaire :
1detrend_df = pd.DataFrame(detrend_dict).T
2detrend_df.columns = ["AIC", "MSE"]
3detrend_df["Graphiquement"] = "✅"
1# print(detrend_df.to_markdown())
1detrend_df[["AIC", "MSE"]].plot(kind="bar")
2plt.title("Comparaison des modèles de tendance")
3
4plt.xticks(rotation=45, ha="right")
(array([0, 1, 2, 3, 4]),
[Text(0, 0, 'Régression linéaire'),
Text(1, 0, 'Régression polynomiale'),
Text(2, 0, 'Moyenne mobile linéaire'),
Text(3, 0, 'Moyenne mobile exponentielle'),
Text(4, 0, 'B-Splines')])
Tableau. Comparaison des modèles de tendance
AIC |
MSE |
Graphiquement |
|
---|---|---|---|
Régression linéaire |
7005.56 |
10523.1 |
✅ |
Régression polynomiale |
7023.09 |
10770 |
✅ |
Moyenne mobile linéaire |
7014.76 |
10651.8 |
✅ |
Moyenne mobile exponentielle |
6973.26 |
10082.9 |
✅ |
B-Splines |
7005.56 |
10523.1 |
✅ |
Meilleur modèle : moyenne mobile exponentielle