0% ont trouvé ce document utile (0 vote)
23 vues60 pages

Analyse des Séries Temporelles Financières

Ce document présente une méthodologie pour l'analyse des séries temporelles financières, en se concentrant sur les actions d'AKDITAL. Il décrit les étapes allant de la définition du problème, la collecte et l'exploration des données, jusqu'à la sélection et l'évaluation des modèles de prédiction. Des recommandations pour des améliorations futures et des extensions de l'analyse sont également proposées.

Transféré par

ismail bouachrine
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
23 vues60 pages

Analyse des Séries Temporelles Financières

Ce document présente une méthodologie pour l'analyse des séries temporelles financières, en se concentrant sur les actions d'AKDITAL. Il décrit les étapes allant de la définition du problème, la collecte et l'exploration des données, jusqu'à la sélection et l'évaluation des modèles de prédiction. Des recommandations pour des améliorations futures et des extensions de l'analyse sont également proposées.

Transféré par

ismail bouachrine
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Analyse des Séries Temporelles Financieres et Application

February 3, 2025

[108]: #!pip install sktime


#!pip install dask[dataframe]
#!pip install mplfinance
#!pip install pmdarima
#!pip install pyts

1 Flux de travail pour l’Analyse des Séries Temporelles Financières


- AKDITAL STOCKS
1.1 1. Définition du Problème
• Identifier le problème principal lié aux actions d’AKDITAL.
• Objectif principal : Analyser les séries temporelles pour extraire des tendances et des infor-
mations clés.
• Sous-objectifs :
– Étudier les performances historiques des actions.
– Évaluer des indicateurs clés tels que la volatilité, le volume des échanges et les cours
ajustés.
– Envisager une prédiction des tendances futures si applicable.

1.2 2. Collecte de Données


• Source des données : Site web “Bourse de Casablanca”.
• Description des données :
– Colonnes : Séance, Instrument, Ticker, Ouverture, Dernier Cours, +haut du
jour, +bas du jour, Nombre de titres échangés, Volume des échanges, Nombre de
contrats, Capitalisation, Cours ajusté.
– Type de données : Historique des actions d’AKDITAL.

1.3 3. Exploration des Données


• Examiner la structure des données : [Link](), [Link]().
• Identifier des statistiques descriptives importantes.
• Visualiser les données :
– Évolution des cours ajustés (Cours ajusté) dans le temps.
– Volume des échanges (Volume des échanges) au fil du temps.
• Identifier des relations :
– Corrélation entre le volume des échanges et les mouvements de prix.

1
1.4 4. Prétraitement des Données
• Convertir la colonne Séance en DatetimeIndex pour l’analyse des séries temporelles.
• Supprimer les colonnes non pertinentes : Instrument, Ticker.
• Gérer les données manquantes ou incohérentes :
– Imputation ou suppression des lignes avec des valeurs manquantes.
• Renommer les colonnes pour une meilleure clarté (par exemple, Dernier Cours → Close).

1.5 5. Ingénierie des Caractéristiques


• Créer des caractéristiques dérivées :
– Rendements quotidiens : ((Close{t} - Close{t-1}) / Close_{t-1}).
– Volatilité : Écart-type glissant des rendements.
– Moyennes mobiles : Calculer les moyennes mobiles sur 7 jours, 30 jours, etc.
• Normaliser ou mettre à l’échelle certaines variables si nécessaire (par exemple, Min-Max Scal-
ing).

1.6 6. Sélection du Modèle


• Envisager différents modèles en fonction de l’objectif :
– Modèles statistiques pour séries temporelles :
∗ ARIMA, SARIMA, ou GARCH pour prédire les prix ou modéliser la volatilité.
– Modèles d’apprentissage automatique :
∗ Régression pour prédire le prix ajusté (Cours ajusté).
∗ Clustering pour identifier des phases de marché.

1.7 7. Entraînement du Modèle


• Diviser les données en ensembles d’entraînement et de test (par exemple, 80% pour
l’entraînement, 20% pour le test).
• Entraîner les modèles sur les données pertinentes, en utilisant les caractéristiques dérivées.
• Optimiser les hyperparamètres via validation croisée.

1.8 8. Évaluation du Modèle


• Utiliser des métriques adaptées :
– Séries temporelles : Erreur absolue moyenne (MAE), Erreur quadratique moyenne
(RMSE).
– Prédiction de direction : Taux de précision.
• Comparer plusieurs modèles pour sélectionner le plus performant.

1.9 9. Interprétation des Résultats


• Analyser les tendances et les schémas identifiés dans les données.
• Évaluer la pertinence des prédictions ou des insights obtenus.
• Poser des questions clés :
– Les résultats sont-ils cohérents avec le comportement du marché réel ?
– Quels enseignements peuvent être tirés pour AKDITAL ?

2
1.10 10. Visualisation
• Créer des graphiques pour communiquer efficacement les résultats :
– Graphique en ligne pour les tendances des prix (Close, Cours ajusté).
– Volatilité et moyennes mobiles dans le temps.
– Histogramme des rendements quotidiens.
– Diagrammes de dispersion pour visualiser les corrélations (par exemple, volume vs prix).

1.11 11. Documentation


• Documenter le processus complet :
– Ajouter des commentaires au code.
– Résumer les méthodologies, les résultats et les conclusions.
• Préparer un rapport expliquant les étapes et les résultats principaux.

1.12 12. Étapes Futures


• Propositions d’améliorations ou d’extensions :
– Intégrer d’autres actions cotées à la Bourse de Casablanca.
– Tester des modèles plus avancés (par exemple, LSTM ou Transformers pour séries tem-
porelles).
– Inclure des facteurs externes tels que des indicateurs macroéconomiques ou des sentiments
issus des actualités.

[110]: import pandas as pd


import yfinance as yf
import numpy as np
import [Link] as plt
import torch
import [Link] as models
import [Link] as transforms
from [Link] import ARIMA
from [Link] import mean_absolute_percentage_error
from PIL import Image
import [Link] as nn
from [Link] import Variable
from [Link] import tqdm
import [Link] as F
from [Link] import DataLoader,ConcatDataset,␣
,→TensorDataset,Subset,Dataset

from sklearn.model_selection import train_test_split


import datetime
from [Link] import GramianAngularField as GADF
import plotly.graph_objs as go
import [Link] as pio
from [Link] import StandardScaler,MinMaxScaler
from [Link] import GramianAngularField
from [Link] import resnet18
import [Link] as optim

3
import seaborn as sns
import warnings
[Link]("ignore")

Analyse de séries temporelles - Modèle Auto-régressif Mobile (ARIMA) Données Explorer les don-
nées collectées sur Bourse Casablanca. Période : Janvier 2022 à 2025. Environ 3 anées Source du
DataFrame Python : Bourse Casablanca avec les variables/colonnes suivantes : Date : Représente
la date des données financières. Open : Indique le prix d’ouverture de l’action. High : Désigne le
prix le plus élevé atteint pendant la période de négociation. Low : Représente le prix le plus bas
atteint pendant la période de négociation. Close : Indique le prix de clôture de l’action. Adj Close
: Reflète le prix de clôture ajusté, tenant compte des actions corporatives. Volume : Représente le
volume de négociation (nombre d’actions échangées) à la date donnée

[9]: from [Link] import drive


[Link]('/content/drive')

file_path = '/content/drive/MyDrive/FinancialTS/[Link]'
df = pd.read_excel(file_path)
# Drop the 'Instrument' and 'Ticker' columns
[Link](columns=['Instrument', 'Ticker'], inplace=True)

# Define the mapping for column renaming


column_mapping = {
'Séance': 'Date',
'Ouverture': 'Open',
'Dernier Cours': 'Close',
'+haut du jour': 'High',
'+bas du jour': 'Low',
'Nombre de titres échangés': 'Shares Traded',
'Volume des échanges': 'Trading Volume',
'Nombre de contrats': 'Contracts',
'Capitalisation': 'Market Capitalization',
'Cours ajusté': 'Adj Close'
}

# Rename the columns


[Link](columns=column_mapping, inplace=True)

save_df = df
data = [Link]()
# Display the first few rows to confirm changes
print([Link]())

---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[9], line 1
----> 1 from [Link] import drive
2 [Link]('/content/drive')

4
4 file_path = '/content/drive/MyDrive/FinancialTS/[Link]'

ModuleNotFoundError: No module named '[Link]'

[10]: import pandas as pd

file_path = r'C:\Users\user\OneDrive\Documents\C\[Link]'

# Read the Excel file


df = pd.read_excel(file_path)

# Drop the 'Instrument' and 'Ticker' columns if they exist


columns_to_drop = ['Instrument', 'Ticker']
[Link](columns=[col for col in columns_to_drop if col in [Link]],␣
,→inplace=True)

# Define the mapping for column renaming


column_mapping = {
'Séance': 'Date',
'Ouverture': 'Open',
'Dernier Cours': 'Close',
'+haut du jour': 'High',
'+bas du jour': 'Low',
'Nombre de titres échangés': 'Shares Traded',
'Volume des échanges': 'Trading Volume',
'Nombre de contrats': 'Contracts',
'Capitalisation': 'Market Capitalization',
'Cours ajusté': 'Adj Close'
}

# Rename the columns


[Link](columns=column_mapping, inplace=True)

# Create copies for further use


save_df = [Link]()
data = [Link]()

# Display the first few rows to confirm changes


print([Link]())

Date Open Close High Low Shares Traded Trading Volume \


0 22/01/2025 1122.0 1134.0 1134.0 1095.0 11790.0 13099580.0
1 21/01/2025 1140.0 1134.0 1144.0 1125.0 14523.0 16513287.0
2 20/01/2025 1151.0 1144.0 1166.0 1143.0 5012.0 5757666.0
3 17/01/2025 1165.0 1150.0 1165.0 1150.0 18533.0 21403345.0
4 16/01/2025 1164.0 1150.0 1170.0 1150.0 6658.0 7703536.0

5
Contracts Market Capitalization Adj Close
0 240 1.605654e+10 1134.0
1 185 1.605654e+10 1134.0
2 125 1.619813e+10 1144.0
3 188 1.628309e+10 1150.0
4 199 1.628309e+10 1150.0

[11]: ## statistics clefs:


[Link]()

[11]: Open Close High Low Shares Traded \


count 522.000000 522.000000 522.000000 522.000000 522.000000
mean 605.140038 605.539559 611.393391 599.602969 14933.082375
std 278.179573 277.317468 281.289240 274.663556 19290.105780
min 270.000000 270.500000 275.000000 270.000000 7.000000
25% 320.050000 325.125000 325.712500 320.012500 3077.000000
50% 510.550000 511.050000 514.950000 505.050000 7794.000000
75% 797.500000 807.475000 810.000000 787.625000 18457.250000
max 1181.000000 1163.000000 1199.000000 1161.000000 166323.000000

Trading Volume Contracts Market Capitalization Adj Close


count 5.220000e+02 522.000000 5.220000e+02 522.000000
mean 9.882594e+06 121.618774 8.038006e+09 605.539559
std 1.473440e+07 127.648218 4.085638e+09 277.317468
min 2.069950e+03 3.000000 3.426334e+09 270.500000
25% 1.510817e+06 45.000000 4.118251e+09 325.125000
50% 4.913342e+06 86.500000 6.473302e+09 511.050000
75% 1.174534e+07 157.000000 1.022802e+10 807.475000
max 1.452388e+08 1554.000000 1.646716e+10 1163.000000

La moyenne des prix ajustés de clôture est d’environ 605.53 Dh , indiquant un niveau de prix
moyen. Cependant, l’écart-type élevé de 277.31 Dh suggère une variabilité importante, indiquant
des fluctuations notables autour de la moyenne. Le prix de clôture minimum dans le jeu de données
est d’environ 270.5 Dh, et le maximum est de 1163 Dh, reflétant la plage observée. Ces informations
sont cruciales pour les investisseurs, soulignant à la fois la performance moyenne et le potentiel de
mouvements de prix substantiels.

[12]: # Reset the index to ensure it's a RangeIndex


df.reset_index(drop=True, inplace=True) # incomment if needed.

# Display the DataFrame's properties to confirm


[Link], [Link], [Link]

[12]: (RangeIndex(start=0, stop=522, step=1),


Index(['Date', 'Open', 'Close', 'High', 'Low', 'Shares Traded',
'Trading Volume', 'Contracts', 'Market Capitalization', 'Adj Close'],
dtype='object'),
(522, 10))

6
[13]: print(f"Downloaded {len(df)} rows of data.")
[Link]()

Downloaded 522 rows of data.

[13]: Date Open Close High Low Shares Traded Trading Volume \
0 22/01/2025 1122.0 1134.0 1134.0 1095.0 11790.0 13099580.0
1 21/01/2025 1140.0 1134.0 1144.0 1125.0 14523.0 16513287.0
2 20/01/2025 1151.0 1144.0 1166.0 1143.0 5012.0 5757666.0
3 17/01/2025 1165.0 1150.0 1165.0 1150.0 18533.0 21403345.0
4 16/01/2025 1164.0 1150.0 1170.0 1150.0 6658.0 7703536.0

Contracts Market Capitalization Adj Close


0 240 1.605654e+10 1134.0
1 185 1.605654e+10 1134.0
2 125 1.619813e+10 1144.0
3 188 1.628309e+10 1150.0
4 199 1.628309e+10 1150.0

1.13 4. Prétraitement des Données


• Convertir la colonne Séance en DatetimeIndex pour l’analyse des séries temporelles.
• Supprimer les colonnes non pertinentes : Instrument, Ticker.
• Gérer les données manquantes ou incohérentes :
– Imputation ou suppression des lignes avec des valeurs manquantes.
• Renommer les colonnes pour une meilleure clarté (par exemple, Dernier Cours → Close).

[14]: df['Date'] = pd.to_datetime(df['Date'], format='%d/%m/%Y', errors='coerce')


# Set 'Date' column as the index
df.set_index('Date', inplace=True)
## verification des valeurs nulles
[Link]().sum()

[14]: Open 0
Close 0
High 0
Low 0
Shares Traded 0
Trading Volume 0
Contracts 0
Market Capitalization 0
Adj Close 0
dtype: int64

[15]: def fill_zero_with_ma(


df,
target_cols, # Column(s) to process (str or list)
window='7D',

7
min_periods=1
):
"""

Args:
df ([Link]): Input DataFrame with DatetimeIndex
target_cols (str/list): Column name(s) to process (e.g., 'Open' or␣
,→['Open', 'Close'])

window (str): Rolling window size (default: '7D' for 1 week)


min_periods (int): Minimum observations for valid average (default: 1)

Returns:
[Link]: DataFrame with zeros replaced by moving averages
"""
# Validate index type
if not isinstance([Link], [Link]):
raise ValueError("Index must be a DatetimeIndex")

# Work on a sorted copy to ensure rolling windows are chronological


df = [Link]().sort_index()

# Ensure target_cols is a list


target_cols = [target_cols] if isinstance(target_cols, str) else target_cols

for col in target_cols:


if col not in [Link]:
raise ValueError(f"Column '{col}' not found in DataFrame")

# Identify zeros
zero_mask = df[col] == 0

# Replace zeros with NaN for calculation


temp_series = df[col].replace(0, [Link])

# Compute moving average (uses datetime-aware window)


moving_avg = temp_series.rolling(window, min_periods=min_periods).mean()

# Fill zeros with moving average


df[col] = [Link](zero_mask, moving_avg, df[col])

return df

[16]: zero_count = (df['Open'] == 0).sum()


print(f"Number of zeros in 'Open': {zero_count}")

Number of zeros in 'Open': 0

8
[17]: df_cleaned = fill_zero_with_ma(
df,
target_cols=['Open', 'High', 'Low', 'Close'], # List of columns
window='7D' # Optional: 7-day moving average
)

[18]: df_cleaned.head()

[18]: Open Close High Low Shares Traded Trading Volume \


Date
2022-12-14 300.0 302.0 308.00 300.0 82716.0 25118798.75
2022-12-15 303.0 303.0 303.00 302.0 34731.0 10514971.10
2022-12-16 303.0 300.0 303.90 294.0 54480.0 16404661.80
2022-12-19 300.0 301.4 301.95 300.0 62817.0 18870458.60
2022-12-20 300.0 300.0 301.00 297.0 48741.0 14610350.25

Contracts Market Capitalization Adj Close


Date
2022-12-14 249 3.825334e+09 302.0
2022-12-15 190 3.838001e+09 303.0
2022-12-16 315 3.800001e+09 300.0
2022-12-19 313 3.817734e+09 301.4
2022-12-20 210 3.800001e+09 300.0

[19]: # Handling Missing Values


# Imputation Strategies
df_filled = [Link](method='linear') # Linear interpolation

# Missing Values Indicator


missing_values_indicator = [Link]().astype(int)

# Replace original DataFrame with the filled one


df = df_filled

Methode 6σ

[20]: # df,df_var, df_rolling


df_var = df_filled
df_var["rtn"] = df_var["Adj Close"].pct_change()
df_var = df_var[["rtn"]].copy()
df_var.head()

[20]: rtn
Date
2025-01-22 NaN
2025-01-21 0.000000
2025-01-20 0.008818
2025-01-17 0.005245

9
2025-01-16 0.000000

[21]: df_rolling = df_var[["rtn"]].rolling(window=7) \


.agg(["mean", "std"])
df_rolling.columns = df_rolling.[Link]()
df_rolling

[21]: mean std


Date
2025-01-22 NaN NaN
2025-01-21 NaN NaN
2025-01-20 NaN NaN
2025-01-17 NaN NaN
2025-01-16 NaN NaN
... ... ...
2022-12-20 0.012653 0.021664
2022-12-19 0.013372 0.021240
2022-12-16 0.008033 0.020237
2022-12-15 0.011373 0.017909
2022-12-14 0.005395 0.013852

[522 rows x 2 columns]

[22]: df2 = [Link](df_rolling)


df2 = [Link]()
[Link]()

[22]: Open Close High Low Shares Traded Trading Volume \


Date
2025-01-10 1181.0 1139.0 1181.0 1135.0 37197.0 43035914.0
2025-01-09 1170.0 1161.0 1199.0 1161.0 39951.0 47231629.0
2025-01-08 1120.0 1160.0 1175.0 1120.0 43835.0 50703382.0
2025-01-07 1123.0 1120.0 1124.0 1090.0 29645.0 32686876.0
2025-01-06 1130.0 1120.0 1140.0 1120.0 55155.0 62159077.0

Contracts Market Capitalization Adj Close rtn mean \


Date
2025-01-10 284 1.612734e+10 1139.0 -0.014706 0.000663
2025-01-09 362 1.643884e+10 1161.0 0.019315 0.003423
2025-01-08 403 1.642468e+10 1160.0 -0.000861 0.002040
2025-01-07 226 1.585831e+10 1120.0 -0.034483 -0.003636
2025-01-06 309 1.585831e+10 1120.0 0.000000 -0.003636

std
Date
2025-01-10 0.008967
2025-01-09 0.011377

10
2025-01-08 0.011199
2025-01-07 0.017562
2025-01-06 0.017562

[23]: N_SIGMAS = 3
df2["upper"] = df2["mean"] + N_SIGMAS * df2["std"]
df2["lower"] = df2["mean"] - N_SIGMAS * df2["std"]
#Mask
df2["outlier"] = (
(df2["rtn"] > df2["upper"]) | (df2["rtn"] < df2["lower"])
)

[24]: fig, ax = [Link]()


df2[["rtn", "upper", "lower"]].plot(ax=ax)
[Link]([Link][df2["outlier"]].index,
[Link][df2["outlier"], "rtn"],
color="black", label="outlier")
ax.set_title("AKDITAL's stock returns (6σ)")
[Link](loc="center left", bbox_to_anchor=(1, 0.5))
[Link]()

output_22_0.png

[25]: fig, ax = [Link]()


df2[["Adj Close", "upper", "lower"]].plot(ax=ax)
[Link]([Link][df2["outlier"]].index,
[Link][df2["outlier"], "Adj Close"],
color="black", label="outlier")
ax.set_title("AKDITAL's Adj Close (6σ)")
[Link](loc="center left", bbox_to_anchor=(1, 0.5))
[Link]()

11
[26]: # Valeurs manquantes
nouvel_indicateur_valeurs_manquantes = [Link]().astype(int)

# Visualisons des lacunes


[Link](figsize=(12, 6))
[Link](nouvel_indicateur_valeurs_manquantes, cmap="viridis", cbar=False)
[Link]("Carte de chaleur des valeurs manquantes avec de larges lacunes")
[Link]()

12
[27]: # De nouvelle valeurs puevent etre crées par exemple
df_cleaned["simple_rtn"] = df_cleaned["Adj Close"].pct_change()
df_cleaned["log_rtn"] = [Link](df_cleaned["Adj Close"]/df_cleaned["Adj Close"].
,→shift(1))

print(df_cleaned.loc[:, ["Adj Close", "simple_rtn", "log_rtn"]])

Adj Close simple_rtn log_rtn


Date
2022-12-14 302.0 NaN NaN
2022-12-15 303.0 0.003311 0.003306
2022-12-16 300.0 -0.009901 -0.009950
2022-12-19 301.4 0.004667 0.004656
2022-12-20 300.0 -0.004645 -0.004656
... ... ... ...
2025-01-16 1150.0 -0.011178 -0.011241
2025-01-17 1150.0 0.000000 0.000000
2025-01-20 1144.0 -0.005217 -0.005231
2025-01-21 1134.0 -0.008741 -0.008780
2025-01-22 1134.0 0.000000 0.000000

[522 rows x 3 columns]

[28]: def realized_volatility(x):


return [Link]([Link](x**2))

13
df = df_cleaned.copy()
df_rv = (
[Link]([Link](freq="ME"))
.apply(realized_volatility)
.rename(columns={"log_rtn": "rv"})
)
df_rv.rv = df_rv["rv"] * [Link](12)
fig, ax = [Link](2, 1, sharex=True)
ax[0].plot(df)
ax[0].set_title("Rendements logarithmiques d'AKDITAL (2022-2025)")
ax[1].plot(df_rv)
ax[1].set_title("Volatilité annualisée")
[Link]()

output_26_0.png

1.13.1 Remarque :
Nous pouvons constater que les pics de la volatilité réalisée coïncident avec certains rende

[29]: [Link](figsize=(14, 8))


[Link](df['Adj Close'], label='AKDITAL Adj Close')
[Link]("Série temporelle AKDITAL après prétraitement")
[Link]("Date")
[Link]("Prix")
[Link]()
[Link]()

output_28_0.png

[30]: from [Link].outlier_detection import HampelFilter

hampel_detector = HampelFilter(window_length=10,

14
return_bool=True)
df["outlier"] = hampel_detector.fit_transform(df["Adj Close"])
df["rtn"] = df["Adj Close"].pct_change()

[31]: fig, ax = [Link]()


df2[["Adj Close"]].plot(ax=ax)
[Link]([Link][df2["outlier"]].index,
[Link][df2["outlier"], "Adj Close"],
color="black", label="outlier")
ax.set_title("AKDITAL's stock price")

[31]: Text(0.5, 1.0, "AKDITAL's stock price")

output_30_1.png

[32]: df2 = df2.sort_index()

df2["outlier_rtn"] = hampel_detector.fit_transform(df2["rtn"])

fig, ax = [Link]()
df2[["rtn"]].plot(ax=ax)
[Link]([Link][df2["outlier_rtn"]].index,
[Link][df2["outlier_rtn"], "rtn"],
color="black", label="outlier")
ax.set_title("AKDITAL's stock returns")
[Link](loc="center left", bbox_to_anchor=(1, 0.5))
[Link]()

output_31_0.png

15
1.13.2 Exploring Financial Time Series Data

[33]: # definition d'une fonction pour la visualisation des donnees


def plot_data(data):
# Noms des colonnes
col_names_copy = [Link]
# creation de la figure
fig_copy = [Link](figsize=(24, 24))
# Boucle pour crdeder les sous graphiques
for i in range(4):
ax_copy = fig_copy.add_subplot(4, 1, i + 1)
ax_copy.plot([Link][:, i], label=col_names_copy[i])
[Link][:, i].rolling(7).mean().plot(label='Rolling Mean')
ax_copy.set_title(col_names_copy[i], fontsize=18)
ax_copy.set_xlabel('Date')
ax_copy.set_ylabel('Price')
ax_copy.patch.set_edgecolor('black')
[Link]('fivethirtyeight')
[Link](prop={'size': 12})
[Link]('fivethirtyeight')
# ejustement de la disposition des sous graphiques
fig_copy.tight_layout(pad=3.0)
# affichage de la figure
[Link]()
plot_data(df)

output_33_0.png

[34]: # definition d'une fonction pour la visualisation des donnees


def plot_data(data):
# Noms des colonnes
col_names_copy = [Link]
# creation de la figure
fig_copy = [Link](figsize=(24, 24))
# Boucle pour crdeder les sous graphiques
for i in range(4):
ax_copy = fig_copy.add_subplot(4, 1, i + 1)
ax_copy.plot([Link][:, i], label=col_names_copy[i])
[Link][:, i].rolling(30).mean().plot(label='Rolling Mean')
ax_copy.set_title(col_names_copy[i], fontsize=18)
ax_copy.set_xlabel('Date')

16
ax_copy.set_ylabel('Price')
ax_copy.patch.set_edgecolor('black')
[Link]('fivethirtyeight')
[Link](prop={'size': 12})
[Link]('fivethirtyeight')
# ejustement de la disposition des sous graphiques
fig_copy.tight_layout(pad=3.0)
# affichage de la figure
[Link]()
plot_data(df)

17
[35]: def dessiner_graphique_personnalise(df, x_col, y_col=None, titre="", xlabel="",␣
,→ylabel="", hue_col=None, palette="viridis", style="whitegrid", figsize=(12,␣

,→6)):

"""Dessine un graphique personnalisé avec différentes options de␣


,→personnalisation."""

# Configuration du style seaborn


sns.set_theme(style=style, palette=palette)

# Configuration de la taille de la figure


[Link](figsize=figsize)

# Choix du type de graphique (univarié ou bivarié)


if y_col is None:
# Graphique univarié
[Link](data=df, x=x_col, hue=hue_col, kde=True)
[Link](titre)
[Link](xlabel)
[Link](ylabel)
else:
# Graphique bivarié
[Link](data=df, x=x_col, y=y_col, hue=hue_col)
[Link](titre)
[Link](xlabel)
[Link](ylabel)

# Affichage du graphique
[Link]()

[36]: # Initial Exploration


print("Initial Exploration:")
print([Link]())
print("\nSummary Statistics:")
print([Link]())

Initial Exploration:
<class '[Link]'>
DatetimeIndex: 522 entries, 2022-12-14 to 2025-01-22
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Open 522 non-null float64
1 Close 522 non-null float64
2 High 522 non-null float64
3 Low 522 non-null float64
4 Shares Traded 522 non-null float64
5 Trading Volume 522 non-null float64
6 Contracts 522 non-null int64

18
7 Market Capitalization 522 non-null float64
8 Adj Close 522 non-null float64
9 simple_rtn 521 non-null float64
10 log_rtn 521 non-null float64
11 outlier 522 non-null bool
12 rtn 521 non-null float64
dtypes: bool(1), float64(11), int64(1)
memory usage: 53.5 KB
None

Summary Statistics:
Open Close High Low Shares Traded \
count 515.000000 515.000000 515.000000 515.000000 515.00000
mean 597.681748 598.176019 603.930777 592.317961 15000.04466
std 272.539860 271.844992 275.748980 269.253440 19403.64617
min 270.000000 270.500000 275.000000 270.000000 7.00000
25% 320.000000 321.025000 323.475000 318.525000 3026.00000
50% 505.100000 505.000000 509.000000 499.000000 7779.00000
75% 756.500000 770.500000 775.500000 749.050000 18732.00000
max 1181.000000 1161.000000 1199.000000 1161.000000 166323.00000

Trading Volume Contracts Market Capitalization Adj Close \


count 5.150000e+02 515.000000 5.150000e+02 515.000000
mean 9.861212e+06 120.852427 7.926460e+09 598.176019
std 1.481829e+07 128.256695 3.998728e+09 271.844992
min 2.069950e+03 3.000000 3.426334e+09 270.500000
25% 1.473796e+06 44.000000 4.066318e+09 321.025000
50% 4.795062e+06 85.000000 6.396668e+09 505.000000
75% 1.166551e+07 156.500000 9.759669e+09 770.500000
max 1.452388e+08 1554.000000 1.643884e+10 1161.000000

rtn mean std upper lower


count 515.000000 515.000000 515.000000 515.000000 515.000000
mean -0.002489 -0.002500 0.012782 0.035845 -0.040845
std 0.015084 0.006275 0.007424 0.021091 0.025022
min -0.079650 -0.025419 0.001929 0.002389 -0.125437
25% -0.008736 -0.005510 0.007608 0.020823 -0.055463
50% -0.000581 -0.001058 0.010853 0.030788 -0.033542
75% 0.004285 0.001253 0.017140 0.045294 -0.022437
max 0.087604 0.013926 0.037114 0.119305 -0.005465

[37]: import seaborn as sns


# Histogramme univarié
dessiner_graphique_personnalise(df, x_col='Adj Close', titre='Distribution des␣
,→prix', xlabel='Valeurs', ylabel='Fréquence')

# Nuage de points bivarié

19
dessiner_graphique_personnalise(df, x_col='log_rtn', y_col='Trading Volume',␣
,→titre='Nuage de points', xlabel='Rendements logarithmiques', ylabel='Volume',␣

,→palette='Set2')

output_37_0.png

output_37_1.png

[38]: start_date = '2023-01-01'


end_date = '2023-02-01'
year = '2024'
interval_df = [Link][year]
interval_df["Adj Close"].plot(title=f"AKDITAL stock in {year}- Profile annuel")

[38]: <Axes: title={'center': 'AKDITAL stock in 2024- Profile annuel'}, xlabel='Date'>

20
[39]: (
interval_df[["Adj Close", "simple_rtn"]]
.plot(subplots=True, sharex=True, # sharex=True : pour garantir que les␣
,→sous-graphes partagent le même axe x..

title=f"AKDITAL stock in {year}")


)

[39]: array([<Axes: xlabel='Date'>, <Axes: xlabel='Date'>], dtype=object)

[40]: import seaborn as sns


corr_matrix = [Link]()
[Link](corr_matrix, cmap='coolwarm')
[Link]("Matrice de Corrélation")
[Link]()

output_40_0.png

21
[41]: corr_matrix = df[['Adj Close', 'Trading Volume', 'simple_rtn', 'Open', 'Trading␣
,→Volume', 'Contracts', 'Market Capitalization']].corr(method='pearson')

[Link](corr_matrix, annot=True, cmap='coolwarm')


[Link]("Matrice de Corrélation")
[Link]()

[42]: import plotly.graph_objects as go


import mplfinance as mpf
fig = [Link](data=
[Link](x=df_cleaned.index,
open=df_cleaned["Open"],
high=df_cleaned["High"],
low=df_cleaned["Low"],
close=df_cleaned["Close"])
)
fig.update_layout(

22
title="AKDITAL's stock prices in ",
yaxis_title="Prix (Dh)"
)
[Link]()

1.14 Introduction :
La modélisation des séries chronologiques financières est essentielle pour analyser l’évolution des
prix des actions de la Banque CIH et pour effectuer des prévisions. Chaque étape dans ce processus
permet d’améliorer la qualité des prévisions et de mieux comprendre les dynamiques du marché.

1.14.1 1. Décomposition de la série chronologique


1. Objectif : Décomposer la série chronologique en composantes distinctes, telles que la ten-
dance, la saisonnalité et les résidus.
2. Méthode : Utilisation de techniques de décomposition comme la décomposition additive ou
multiplicative, pour extraire la tendance et la saisonnalité de la série.
3. Formule : Série chronologique = Tendance + Saisonnalité + Résidus ### Test de station-
narité dans les séries chronologiques
4. Objectif: Tester la stationnarité de la série chronologique.
5. Méthode: Utilisation de tests statistiques comme le test Augmented Dickey-Fuller (ADF).
6. Formule: H0: La série n’est pas stationnaire, H1: La série est stationnaire. ### Correction
de la stationnarité dans les séries chronologiques
7. Objectif: Rendre la série chronologique stationnaire.
8. Méthode: Différenciation, transformation logarithmique, etc.
9. Formule: Série stationnaire = Différence(série chronologique)

1.14.2 Modélisation des séries chronologiques avec des méthodes de lissage exponen-
tiel
1. Objectif: Modéliser la tendance et la saisonnalité dans la série chronologique.
2. Méthode: Utilisation de méthodes de lissage exponentiel.
3. Formule: Prédiction = Tendance + Saisonnalité

1.14.3 Modélisation des séries chronologiques avec des modèles de classe ARIMA
1. Objectif: Modéliser une série temporelle stationnaire.
2. Méthode: Utilisation de modèles ARIMA(p, d, q).
3. Formule: Y(t) = C + 1 * Y(t-1) + . . . + p * Y(t-p) + ε(t) - θ1 * ε(t-1) - . . . - θq * ε(t-q)

1.14.4 Recherche du meilleur modèle ARIMA avec auto-ARIMA


1. Objectif: Trouver le meilleur modèle ARIMA pour la série chronologique.
2. Méthode: Utilisation de l’algorithme auto-ARIMA.

23
3. Formule: Sélection automatique des paramètres p, d, q pour minimiser un critère
d’ajustement.

[43]: WINDOW_SIZE = 30

df2["rolling_mean"] = df2["Adj Close"].rolling(window=WINDOW_SIZE).mean()


df2["rolling_std"] = df2["Adj Close"].rolling(window=WINDOW_SIZE).std()
print("""L'analyse nous permet de déduire que les composantes tendancielles et␣
,→saisonnières semblent présenter un schéma presque linéaire annuel.

Par conséquent, on peut utiliser la décomposition additive dans l'étape suivante.


,→""")

[Link](y = ["rolling_mean","Adj Close","rolling_std" ],title="Adj Close")

L'analyse nous permet de déduire que les composantes tendancielles et


saisonnières semblent présenter un schéma presque linéaire annuel.
Par conséquent, on peut utiliser la décomposition additive dans l'étape
suivante.

[43]: <Axes: title={'center': 'Adj Close'}, xlabel='Date'>

output_44_2.png

[44]: df = df_cleaned
df = [Link]([Link])
[Link]

[44]: DatetimeIndex(['2022-12-14', '2022-12-15', '2022-12-16', '2022-12-17',


'2022-12-18', '2022-12-19', '2022-12-20', '2022-12-21',
'2022-12-22', '2022-12-23',
...
'2025-01-13', '2025-01-14', '2025-01-15', '2025-01-16',
'2025-01-17', '2025-01-18', '2025-01-19', '2025-01-20',
'2025-01-21', '2025-01-22'],
dtype='datetime64[ns]', name='Date', length=771, freq='D')

[45]: # Infer the frequency from the existing DatetimeIndex


inferred_freq = pd.infer_freq([Link])

[46]: from [Link] import seasonal_decompose

# Assuming 'Date' is the column containing datetime information

24
df['Adj Close'] = df['Adj Close'].interpolate(method='linear')

decomposition_results = seasonal_decompose(df["Adj Close"],


model="additive")
(
decomposition_results
.plot()
.suptitle("Additive Decomposition")
)

[46]: Text(0.5, 0.98, 'Additive Decomposition')

output_47_1.png

[47]: from [Link] import STL


stl_decomposition = STL(df["Adj Close"]).fit()
stl_decomposition.plot().suptitle("STL Decomposition")

[47]: Text(0.5, 0.98, 'STL Decomposition')

25
1.14.5 Test de stationnarité dans les séries chronologiques

[48]: import pandas as pd


from [Link] import plot_acf, plot_pacf
from [Link] import adfuller, kpss

def adf_test(x):

indices = ["Test Statistic", "p-value",


"# of Lags Used", "# of Observations Used"]

adf_test = adfuller(x, autolag="AIC")


results = [Link](adf_test[0:4], index=indices)

for key, value in adf_test[4].items():


results[f"Critical Value ({key})"] = value

return results

26
[49]: x = df2["Adj Close"]
adf_test(x= x)

[49]: Test Statistic 0.888898


p-value 0.992944
# of Lags Used 2.000000
# of Observations Used 512.000000
Critical Value (1%) -3.443187
Critical Value (5%) -2.867202
Critical Value (10%) -2.569785
dtype: float64

Statistique de Test (0.888898) : Cette valeur est la statistique de test calculée pendant le test ADF.
Plus elle est négative, plus les preuves contre l’hypothèse nulle (H0) sont fortes. Cependant, ici, la
statistique n’est pas inférieure aux valeurs critiques à des niveaux de significativité usuels.
Valeur p (0.992944) : La probabilité d’observer cette statistique de test si l’hypothèse nulle est vraie.
Une valeur p supérieure à 0.05 indique qu’il n’y a pas assez de preuves pour rejeter H0.
Nombre de Retards Utilisés (2) : Ce chiffre représente le nombre de retards pris en compte pour
ajuster le modèle dans le test ADF, souvent déterminé par des critères comme AIC ou BIC.
Nombre d’Observations Utilisées (512) : Cela correspond au nombre total d’observations incluses
dans le calcul après avoir pris en compte les retards.
Valeurs Critiques : Ce sont des seuils de comparaison pour la statistique de test afin d’évaluer la
stationnarité à différents niveaux de significativité statistique.
• Valeur Critique (1%) : -3.443187
• Valeur Critique (5%) : -2.867202
• Valeur Critique (10%) : -2.569785
Interprétation : - Hypothèse Nulle (H0) : La série chronologique n’est pas stationnaire. - Hypothèse
Alternative (H1) : La série chronologique est stationnaire.
Conclusion : Avec une valeur p de 0.992944, qui est supérieure au seuil typique de 0.05, il n’y a pas
suffisamment de preuves pour rejeter l’hypothèse nulle.
Par conséquent, selon le test ADF, la série chronologique peut être considérée comme non station-
naire.

[50]: df2["Adj Close_diff"] = df["Adj Close"].diff().dropna()

[51]: from [Link] import boxcox


df["Adj Close_boxcox"], _ = boxcox(df["Adj Close"])

[52]: from [Link] import adfuller, kpss

def adf_test(x):
indices = ["Test Statistic", "p-value",
"# of Lags Used", "# of Observations Used"]

27
adf_test = adfuller(x, autolag="AIC")
results = [Link](adf_test[0:4], index=indices)

# Check if adf_test[4] is a dictionary before using .items()


if isinstance(adf_test[4], dict):
for key, value in adf_test[4].items():
results[f"Critical Value ({key})"] = value
else:
print("Warning: Critical values not found in adfuller result.")

return results
# Call adf_test and assign the result to a variable
x = df2["Adj Close"]
adf_results = adf_test(x=x) # Call the function and store the result

# Access elements of the results


test_statistic, p_value, lags, observations = adf_results[:4] # Slice the result

# Create a dictionary for the results


results = {
'Test Statistic': test_statistic,
'p-value': p_value,
'# of Lags Used': lags,
'# of Observations Used': observations
}

# Extract critical values if available


if isinstance(adf_results[4], dict):
critical_values = adf_results[4]
for key, value in critical_values.items():
results[f'Critical Value ({key})'] = value
# Now 'results' contains the formatted results of the ADF test
print(results)

{'Test Statistic': 0.8888979746144278, 'p-value': 0.9929437179676649, '# of Lags


Used': 2.0, '# of Observations Used': 512.0}

[53]: N_LAGS = 40
SIGNIFICANCE_LEVEL = 0.05

# Create subplots with 1 row and 2 columns


fig, ax = [Link](1, 2, figsize=(15, 4))

# Plot ACF (left subplot)


plot_acf(df["Adj Close"], ax=ax[0], lags=N_LAGS,
alpha=SIGNIFICANCE_LEVEL,

28
title='Autocorrelation (ACF)')

# Plot PACF (right subplot)


plot_pacf(df["Adj Close"], ax=ax[1], lags=N_LAGS,
alpha=SIGNIFICANCE_LEVEL,
title='Partial Autocorrelation (PACF)')

plt.tight_layout()
[Link]()

output_56_0.png

[54]: # Add log-transformed and rolling features


WINDOW = 30*3
selected_columns = ["Adj Close_log", "rolling_mean_log", "rolling_std_log"]

# Log-transformed price
df["Adj Close_log"] = [Link](df["Adj Close"])

# Rolling mean of log-transformed price


df["rolling_mean_log"] = df["Adj Close_log"].rolling(WINDOW).mean()

# Rolling standard deviation of log-transformed price


df["rolling_std_log"] = df["Adj Close_log"].rolling(WINDOW).std()
colors = ["blue", "green", "orange"]

# Plotting
df[selected_columns].plot(title="AKDITAL Adjusted Close Price (logged + rolling␣
,→mean/std)", subplots=False, color= colors)

[Link]()

29
[55]: from [Link] import ndiffs, nsdiffs
print(f"Suggested # of differences (ADF): {ndiffs(df['Adj Close'], test='adf')}")
print(f"Suggested # of differences (KPSS): {ndiffs(df['Adj Close'],␣
,→test='kpss')}")

print(f"Suggested # of differences (PP): {ndiffs(df['Adj Close'], test='pp')}")

Suggested # of differences (ADF): 1


Suggested # of differences (KPSS): 1
Suggested # of differences (PP): 1

[56]: s_ = df['Adj Close']


print(f"Suggested # of differences (OSCB): {nsdiffs(s_, m=12,test='ocsb')}")
print(f"Suggested # of differences (CH): {nsdiffs(s_, m=12,test='ch')}")

Suggested # of differences (OSCB): 0


Suggested # of differences (CH): 0

1.15 Conclusion
Dans l'interprétation de ces résultats, l'hypothèse nulle (H0) stipule que la série n'est pas st

2 ARIMA
[57]: df = df_cleaned
[Link] , [Link]

TEST_LENGTH = 6
df_train = [Link][:-TEST_LENGTH]
df_test = [Link][-TEST_LENGTH:]

[58]: [Link]()

[58]: Open Close High Low Shares Traded Trading Volume \


Date
2022-12-15 303.0 303.0 303.00 302.0 34731.0 10514971.10
2022-12-16 303.0 300.0 303.90 294.0 54480.0 16404661.80
2022-12-19 300.0 301.4 301.95 300.0 62817.0 18870458.60
2022-12-20 300.0 300.0 301.00 297.0 48741.0 14610350.25
2022-12-21 287.2 290.0 295.00 287.1 10129.0 2928066.75
... ... ... ... ... ... ...
2025-01-16 1164.0 1150.0 1170.00 1150.0 6658.0 7703536.00
2025-01-17 1165.0 1150.0 1165.00 1150.0 18533.0 21403345.00
2025-01-20 1151.0 1144.0 1166.00 1143.0 5012.0 5757666.00
2025-01-21 1140.0 1134.0 1144.00 1125.0 14523.0 16513287.00
2025-01-22 1122.0 1134.0 1134.00 1095.0 11790.0 13099580.00

30
Contracts Market Capitalization Adj Close simple_rtn log_rtn
Date
2022-12-15 190 3.838001e+09 303.0 0.003311 0.003306
2022-12-16 315 3.800001e+09 300.0 -0.009901 -0.009950
2022-12-19 313 3.817734e+09 301.4 0.004667 0.004656
2022-12-20 210 3.800001e+09 300.0 -0.004645 -0.004656
2022-12-21 145 3.673334e+09 290.0 -0.033333 -0.033902
... ... ... ... ... ...
2025-01-16 199 1.628309e+10 1150.0 -0.011178 -0.011241
2025-01-17 188 1.628309e+10 1150.0 0.000000 0.000000
2025-01-20 125 1.619813e+10 1144.0 -0.005217 -0.005231
2025-01-21 185 1.605654e+10 1134.0 -0.008741 -0.008780
2025-01-22 240 1.605654e+10 1134.0 0.000000 0.000000

[521 rows x 11 columns]

[59]: A = [Link]([Link](df_train["Adj Close"]))


df_train["Adj_Close_log"] = A
df_train["first_diff"] = df_train["Adj_Close_log"].diff()

[60]: df_train.plot(subplots=True, title="Original vs transformed series")

[60]: array([<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,


<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,
<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,
<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,
<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,
<Axes: xlabel='Date'>, <Axes: xlabel='Date'>,
<Axes: xlabel='Date'>], dtype=object)

output_65_1.png

[61]: arima_111 = ARIMA(


df_train["Adj_Close_log"], order=(1, 1, 1)
).fit()
arima_111.summary()
[61]:

31
Dep. Variable: Adj_Close_log No. Observations: 516
Model: ARIMA(1, 1, 1) Log Likelihood 1426.336
Date: Mon, 03 Feb 2025 AIC -2846.672
Time: 20:34:02 BIC -2833.939
Sample: 0 HQIC -2841.682
- 516
Covariance Type: opg
coef std err z P> |z| [0.025 0.975]
ar.L1 0.2228 0.248 0.899 0.369 -0.263 0.709
ma.L1 -0.0542 0.256 -0.212 0.832 -0.556 0.448
sigma2 0.0002 7.93e-06 28.980 0.000 0.000 0.000
Ljung-Box (L1) (Q): 0.23 Jarque-Bera (JB): 581.82
Prob(Q): 0.63 Prob(JB): 0.00
Heteroskedasticity (H): 1.74 Skew: 0.38
Prob(H) (two-sided): 0.00 Kurtosis: 8.15

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

[62]: arima_212 = ARIMA(


df_train["Adj_Close_log"], order=(2, 1, 2)
).fit()
arima_212.summary()
[62]:
Dep. Variable: Adj_Close_log No. Observations: 516
Model: ARIMA(2, 1, 2) Log Likelihood 1426.376
Date: Mon, 03 Feb 2025 AIC -2842.753
Time: 20:34:02 BIC -2821.532
Sample: 0 HQIC -2834.436
- 516
Covariance Type: opg
coef std err z P> |z| [0.025 0.975]
ar.L1 0.0728 6.510 0.011 0.991 -12.686 12.832
ar.L2 0.0692 1.497 0.046 0.963 -2.865 3.004
ma.L1 0.0963 6.506 0.015 0.988 -12.654 12.847
ma.L2 -0.0448 0.443 -0.101 0.919 -0.913 0.824
sigma2 0.0002 7.96e-06 28.886 0.000 0.000 0.000
Ljung-Box (L1) (Q): 0.23 Jarque-Bera (JB): 581.80
Prob(Q): 0.63 Prob(JB): 0.00
Heteroskedasticity (H): 1.74 Skew: 0.38
Prob(H) (two-sided): 0.00 Kurtosis: 8.15

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

32
[63]: df["pred_111_log"] = [Link]([arima_111.fittedvalues, arima_111.
,→forecast(TEST_LENGTH)])

df["pred_111"] = [Link](df["pred_111_log"])
df["pred_212_log"] = [Link]([arima_212.fittedvalues, arima_212.
,→forecast(TEST_LENGTH)])

df["pred_212"] = [Link](df["pred_212_log"])
df

[63]: Open Close High Low Shares Traded Trading Volume \


Date
2022-12-14 300.0 302.0 308.00 300.0 82716.0 25118798.75
2022-12-15 303.0 303.0 303.00 302.0 34731.0 10514971.10
2022-12-16 303.0 300.0 303.90 294.0 54480.0 16404661.80
2022-12-19 300.0 301.4 301.95 300.0 62817.0 18870458.60
2022-12-20 300.0 300.0 301.00 297.0 48741.0 14610350.25
... ... ... ... ... ... ...
2025-01-16 1164.0 1150.0 1170.00 1150.0 6658.0 7703536.00
2025-01-17 1165.0 1150.0 1165.00 1150.0 18533.0 21403345.00
2025-01-20 1151.0 1144.0 1166.00 1143.0 5012.0 5757666.00
2025-01-21 1140.0 1134.0 1144.00 1125.0 14523.0 16513287.00
2025-01-22 1122.0 1134.0 1134.00 1095.0 11790.0 13099580.00

Contracts Market Capitalization Adj Close simple_rtn log_rtn \


Date
2022-12-14 249 3.825334e+09 302.0 NaN NaN
2022-12-15 190 3.838001e+09 303.0 0.003311 0.003306
2022-12-16 315 3.800001e+09 300.0 -0.009901 -0.009950
2022-12-19 313 3.817734e+09 301.4 0.004667 0.004656
2022-12-20 210 3.800001e+09 300.0 -0.004645 -0.004656
... ... ... ... ... ...
2025-01-16 199 1.628309e+10 1150.0 -0.011178 -0.011241
2025-01-17 188 1.628309e+10 1150.0 0.000000 0.000000
2025-01-20 125 1.619813e+10 1144.0 -0.005217 -0.005231
2025-01-21 185 1.605654e+10 1134.0 -0.008741 -0.008780
2025-01-22 240 1.605654e+10 1134.0 0.000000 0.000000

pred_111_log pred_111 pred_212_log pred_212


Date
2022-12-14 0.000000 1.000000 0.000000 1.000000
2022-12-15 5.710427 302.000000 5.710427 302.000000
2022-12-16 5.714295 303.170488 5.714297 303.171048
2022-12-19 5.702135 299.506297 5.702130 299.504773
2022-12-20 5.709134 301.609736 5.709167 301.619565
... ... ... ... ...
2025-01-16 NaN NaN NaN NaN
2025-01-17 NaN NaN NaN NaN
2025-01-20 NaN NaN NaN NaN

33
2025-01-21 NaN NaN NaN NaN
2025-01-22 NaN NaN NaN NaN

[522 rows x 15 columns]

[64]: arima_111.forecast(TEST_LENGTH)

[64]: 516 7.055045


517 7.055563
518 7.055678
519 7.055704
520 7.055710
521 7.055711
Name: predicted_mean, dtype: float64

[65]: # df = [Link]([Link])
# Previous code
df["pred_111_log"] = [Link]([arima_111.fittedvalues, arima_111.
,→predict(start=len(df_train), end=len(df_train) + TEST_LENGTH -1,␣

,→dynamic=True)]) # Use numerical index instead of [Link]


df["pred_111"] = [Link](df["pred_111_log"])
df["pred_212_log"] = [Link]([arima_212.fittedvalues, arima_212.
,→predict(start=len(df_train), end=len(df_train) + TEST_LENGTH -1,␣

,→dynamic=True)]) # Use numerical index instead of [Link]


df["pred_212"] = [Link](df["pred_212_log"])
df

[65]: Open Close High Low Shares Traded Trading Volume \


Date
2022-12-14 300.0 302.0 308.00 300.0 82716.0 25118798.75
2022-12-15 303.0 303.0 303.00 302.0 34731.0 10514971.10
2022-12-16 303.0 300.0 303.90 294.0 54480.0 16404661.80
2022-12-19 300.0 301.4 301.95 300.0 62817.0 18870458.60
2022-12-20 300.0 300.0 301.00 297.0 48741.0 14610350.25
... ... ... ... ... ... ...
2025-01-16 1164.0 1150.0 1170.00 1150.0 6658.0 7703536.00
2025-01-17 1165.0 1150.0 1165.00 1150.0 18533.0 21403345.00
2025-01-20 1151.0 1144.0 1166.00 1143.0 5012.0 5757666.00
2025-01-21 1140.0 1134.0 1144.00 1125.0 14523.0 16513287.00
2025-01-22 1122.0 1134.0 1134.00 1095.0 11790.0 13099580.00

Contracts Market Capitalization Adj Close simple_rtn log_rtn \


Date
2022-12-14 249 3.825334e+09 302.0 NaN NaN
2022-12-15 190 3.838001e+09 303.0 0.003311 0.003306
2022-12-16 315 3.800001e+09 300.0 -0.009901 -0.009950
2022-12-19 313 3.817734e+09 301.4 0.004667 0.004656

34
2022-12-20 210 3.800001e+09 300.0 -0.004645 -0.004656
... ... ... ... ... ...
2025-01-16 199 1.628309e+10 1150.0 -0.011178 -0.011241
2025-01-17 188 1.628309e+10 1150.0 0.000000 0.000000
2025-01-20 125 1.619813e+10 1144.0 -0.005217 -0.005231
2025-01-21 185 1.605654e+10 1134.0 -0.008741 -0.008780
2025-01-22 240 1.605654e+10 1134.0 0.000000 0.000000

pred_111_log pred_111 pred_212_log pred_212


Date
2022-12-14 0.000000 1.000000 0.000000 1.000000
2022-12-15 5.710427 302.000000 5.710427 302.000000
2022-12-16 5.714295 303.170488 5.714297 303.171048
2022-12-19 5.702135 299.506297 5.702130 299.504773
2022-12-20 5.709134 301.609736 5.709167 301.619565
... ... ... ... ...
2025-01-16 NaN NaN NaN NaN
2025-01-17 NaN NaN NaN NaN
2025-01-20 NaN NaN NaN NaN
2025-01-21 NaN NaN NaN NaN
2025-01-22 NaN NaN NaN NaN

[522 rows x 15 columns]

[66]: af = df[["Adj Close", "pred_111", "pred_212"]].loc[:'2023-01-12']


af

[66]: Adj Close pred_111 pred_212


Date
2022-12-14 302.00 1.000000 1.000000
2022-12-15 303.00 302.000000 302.000000
2022-12-16 300.00 303.170488 303.171048
2022-12-19 301.40 299.506297 299.504773
2022-12-20 300.00 301.609736 301.619565
2022-12-21 290.00 299.775926 299.754864
2022-12-22 290.00 288.335619 288.341869
2022-12-23 291.00 289.909546 289.909203
2022-12-26 280.20 291.164001 291.103405
2022-12-27 284.00 278.427684 278.424100
2022-12-28 275.00 284.547803 284.563202
2022-12-29 275.10 273.539651 273.466820
2022-12-30 276.00 275.037479 275.072879
2023-01-02 276.00 276.148614 276.088375
2023-01-03 277.00 276.008053 276.012309
2023-01-04 280.00 277.169379 277.172209
2023-01-05 275.00 280.518239 280.518974
2023-01-06 272.15 274.193395 274.194470

35
2023-01-09 270.50 271.629203 271.650708
2023-01-10 270.60 270.194770 270.165497
2023-01-12 271.00 270.600303 270.586669

[67]: [Link](figsize=(12, 6))


[Link]([Link], af["Adj Close"], label="Adj Close")
[Link]([Link], af["pred_111"], label="pred_111")
[Link]([Link], af["pred_212"], label="pred_212")
[Link]("Date")
[Link]("Price")
[Link]("ARIME Adj Close vs. Predictions")
[Link]()
[Link](True)
[Link]()

output_72_0.png

[68]: device = [Link]("cuda:0" if [Link].is_available() else "cpu")


print(device)

cpu

3 APPROACH/MAJOR INNOVATIONS
We have used several approaches and experimented on them in order to find the best approach
and architecture to produce accurate results. ### 1) Implementing a simple LSTM: The initial
approach involved implementing a simple model of LSTM on the time series data which showed
decent fitting of data but failed near some of the troughs and was not very smooth

[69]: class LstmNet([Link]):


def __init__(self,input_size,hidden_size,layers,output_size):
super(LstmNet,self).__init__()
[Link] = layers
self.hidden_size = hidden_size
[Link] = [Link](input_size=input_size, hidden_size=hidden_size,␣
,→num_layers=layers, batch_first=True)

self.fc1 = [Link](hidden_size, output_size)


# self.fc2 = [Link](10,output_size)
def forward(self,x):
# print([Link])

36
h0 = Variable([Link]([Link], [Link](0), self.hidden_size)).
to(device)
,→

c0 = Variable([Link]([Link], [Link](0), self.hidden_size)).


to(device)
,→

out, (h_out, c_out) = [Link](x,(h0,c0))


# print([Link],h_out.shape,c_out.shape)
out = self.fc1(out[:,-1,:])
return [Link](1)

[70]: def prepare_dataset(data,length):


x = []
y = []
for i in range(len(data)-length-1):
[Link](data[i:i+length])
[Link](data[i+length])
return [Link](x),[Link](y)

def train_test_split(X,Y,percent):
per = percent/100
sz = len(X)
xtrain = [Link](X[:int(sz*per)])
ytrain = [Link](Y[:int(sz*per)])
xtest = [Link](X[int(sz*per):])
ytest = [Link](Y[int(sz*per):])
return xtrain,ytrain,xtest,ytest

class DataPrep(Dataset):
def __init__(self, inputs, targets):
[Link] = inputs
[Link] = targets
def __len__(self):
return len([Link])

def __getitem__(self, index):


X = [Link][index]
Y = [Link][index]
return X, Y

[71]: df = df_cleaned.copy()
scaler = StandardScaler()
values = scaler.fit_transform(df['Close'].[Link](-1,1))

[72]: seq_len = 40
data_inp,data_tar = prepare_dataset(values,seq_len)
xtrain,ytrain,xtest,ytest = train_test_split(data_inp,data_tar,80)

37
[73]: traindata = DataPrep(xtrain,ytrain)
testdata = DataPrep(xtest,ytest)
batch_size = 32
trainset = DataLoader(traindata,batch_size = batch_size,shuffle = True)
testset = DataLoader(testdata,batch_size = batch_size,shuffle = True)
for xbatch,ybatch in trainset:
print([Link],[Link])
break

[Link]([32, 40, 1]) [Link]([32, 1])

[111]: train_losses = []
test_losses = []
input_sz = 1
hidden_sz = 200
output_sz = 1
layers = 2
model = LstmNet(input_sz,hidden_sz,layers,output_sz).to(device)
criterion = [Link]()
optimizer = [Link]([Link](), lr=0.00001)
for epoch in range(500):
batch_loss = 0
# correct = 0
for xbatch,ybatch in trainset:
xbatch,ybatch = [Link](device),[Link](device)
out = model(xbatch)
# print([Link],[Link])
loss = criterion(out, [Link](1))
[Link]()
[Link]()
batch_loss += [Link]()
# break

train_loss = batch_loss/len(trainset)
batch_loss = 0
with torch.no_grad():
for xbatch,ybatch in testset:
xbatch,ybatch = [Link](device),[Link](device)
out = model(xbatch)
loss = criterion(out, [Link](1))
batch_loss += [Link]()
test_loss = batch_loss/len(testset)
train_losses.append(train_loss)
test_losses.append(test_loss)
if(epoch%10==9):
print("Epoch: ", epoch+1, "|", "Train Loss : ", "{:.6f}".
,→format(train_loss), "|", "Test Loss : ", "{:.6f}".format(test_loss))

38
Cell In[111], line 36

ˆ
SyntaxError: incomplete input

[75]: train_pred = model([Link](device)).cpu().[Link]()


train_actual = [Link]()
test_pred = model([Link](device)).cpu().[Link]()
test_actual = [Link]()

pred = [Link]((train_pred,test_pred))
# pred = scaler.inverse_transform(pred)
actual = [Link]((train_actual,test_actual)).squeeze()
# actual = scaler.inverse_transform(pred)
print([Link])
print([Link])

(481,)
(481,)

[76]: trace1 = [Link](x = [(i+1) for i in range(len(pred))],y = pred,␣


,→name='Predicted Data')

trace2 = [Link](x = [(i+1) for i in range(len(actual))],y = actual,␣


,→name='Actual data')

datas = [trace1,trace2]

layout = [Link](title='Prediction for 80:20 split Normal LSTM')


fig = [Link](data=datas, layout=layout)
fig.add_vline(x=len(train_pred), line_width=1, line_dash="dash",␣
,→line_color="red")

[Link]()

output_82_0.png

39
3.0.1 2) GADF Image Representations and CNN-based Prediction:
The approach involved generating GADF images from the time series data and training a CNN model
to predict the ‘close price’ of the stock directly. However, this method did not yield satisfactory
results, as CNNs alone were unable to capture the sequential properties of time series data effectively.

[77]: data = df_cleaned.copy()


values = data['Close'].[Link](1,-1)
images = []
labels = []
image_size = 10
data_size = 10
# method = 'difference'
method = 'summation'
sample_range = (0, 1)

for i in range(len(values[0])-data_size-33):
gaf = GramianAngularField(image_size=image_size, method=method,␣
,→sample_range=sample_range)

gadf_image = gaf.fit_transform(values[:,i:i+32])
[Link](gadf_image)
[Link](values[:,i+32+1])
images = [Link](images).astype(np.float32)
labels = [Link](labels)

# scaler= MinMaxScaler()
scaler = StandardScaler()

# labels = scaler.fit_transform(labels).reshape(-1,).astype(np.float32)
fig, ax = [Link](figsize =(10, 7))
[Link](labels, bins = 20)
[Link]()

output_84_0.png

[78]: [Link](images[40].transpose(1,2,0),cmap='hot')

[78]: <[Link] at 0x247b4bf4cb0>

40
output_85_1.png

[79]: class DataPrep(Dataset):


def __init__(self, inputs, targets):
[Link] = inputs
[Link] = targets
def __len__(self):
return len([Link])

def __getitem__(self, index):


X = [Link][index]
Y = [Link][index]
return X, Y
def split_prepare_data(dataset, batch_size=32, test_size=0.2):

num_samples = len(dataset)
num_test_samples = int(test_size * num_samples)
num_train_samples = num_samples - num_test_samples
indices = [Link](num_samples)
train_indices = indices[:num_train_samples]
#print(train_indices)
test_indices = indices[num_train_samples:]
train_sampler = [Link](train_indices)
test_sampler = [Link](test_indices)
train_loader = [Link](dataset, batch_size=batch_size,␣
,→sampler=train_sampler)

test_loader = [Link](dataset, batch_size=batch_size,␣


,→sampler=test_sampler)

return train_loader, test_loader

def prepare_dataset(input,labels,length):
x = []
y = []
for i in range(len(input)-length-1):
[Link](input[i:i+length])
[Link](labels[i+length])
return [Link](x),[Link](y)

41
def prepare_dataset_fromone(input,length):
x = []
y = []
for i in range(len(input)-length-1):
[Link](input[i:i+length])
[Link](input[i+length])
return [Link](x),[Link](y)

[80]: data = df_cleaned.copy()


values = data['Close'].[Link](1,-1)
images = []
labels = []
image_size = 32
data_size = 32
# method = 'difference'
method = 'summation'
sample_range = (0, 1)

for i in range(len(values[0])-data_size-1):
gaf = GramianAngularField(image_size=image_size, method=method,␣
,→sample_range=sample_range)

gadf_image = gaf.fit_transform(values[:,i:i+data_size])
[Link](gadf_image)
[Link](values[:,i+data_size+1])
images = [Link](images).astype(np.float32)
labels = [Link](labels)

scaler = StandardScaler()

labels = scaler.fit_transform(labels).reshape(-1,).astype(np.float32)

dataset = DataPrep(images,labels)
train_loader,test_loader = split_prepare_data(dataset)

[81]: class GAFModel([Link]):


def __init__(self, input_channels, output_size):
super(GAFModel, self).__init__()
self.conv1 = nn.Conv2d(input_channels, 3, kernel_size=3, stride=1,␣
,→padding=1)

# [Link] = nn.MaxPool2d(kernel_size=2, stride=2)


# self.conv2 = nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1)
[Link] = resnet18(weights = True)
# self.fc1 = [Link](32 * 8 * 8, 256)
# self.fc1 = [Link](1000,256)
# self.fc2 = [Link](256,32)
self.fc3 = [Link](1000, output_size)
[Link] = [Link](0.5)

42
def forward(self, x):
x = [Link]()(self.conv1(x))
x = [Link](x)
# x = [Link](-1, 32 * 8 * 8)
x = [Link](x)
x = self.fc3(x)
return x

[82]: model = GAFModel(input_channels=1, output_size=1).to(device)


criterion = [Link]()
optimizer = [Link]([Link](), lr=0.001)
num_epochs = 50
for epoch in range(num_epochs):
train_loss = 0.0
pbar = tqdm(total = len(train_loader))
for xbatch,ybatch in train_loader:
optimizer.zero_grad()
xbatch, ybatch = [Link](device),[Link](device)
output = model(xbatch)
loss = criterion(output,ybatch)+[Link](ybatch)
[Link]()
[Link]()
train_loss += [Link]()
[Link](1)
train_loss /= len(train_loader)
[Link]()

test_loss = 0.0
with torch.no_grad():
for xbatch,ybatch in test_loader:
xbatch, ybatch = [Link](device),[Link](device)
# Check if batch size is 1 and if so, skip batch normalization
if [Link][0] == 1:
# You can skip batch normalization by either:
# 1. Setting the ResNet model to eval mode:
[Link]()
# 2. (More complex) Replacing BatchNorm layers with Identity␣
,→layers.

output = model(xbatch) # Forward pass

# If you set the model to eval mode, set it back to train mode
if [Link][0] == 1:
[Link]()

loss = criterion(output,ybatch)

43
test_loss += [Link]()
test_loss /= len(test_loader)

print(f"Epoch {epoch+1}/{num_epochs} - Training Loss: {train_loss:.4f} -␣


,→Testing Loss: {test_loss:.4f}")

pred = model([Link](images).to(device)).cpu().[Link]().squeeze()

0%| | 0/13 [00:00<?, ?it/s]


Epoch 1/50 - Training Loss: 6.1419 - Testing Loss: 3.0717
0%| | 0/13 [00:00<?, ?it/s]
Epoch 2/50 - Training Loss: 2.3866 - Testing Loss: 2.7210
0%| | 0/13 [00:00<?, ?it/s]
Epoch 3/50 - Training Loss: 2.4615 - Testing Loss: 2.3324
0%| | 0/13 [00:00<?, ?it/s]
Epoch 4/50 - Training Loss: 1.7193 - Testing Loss: 1.4816
0%| | 0/13 [00:00<?, ?it/s]
Epoch 5/50 - Training Loss: 1.4439 - Testing Loss: 1.3197
0%| | 0/13 [00:00<?, ?it/s]
Epoch 6/50 - Training Loss: 1.3778 - Testing Loss: 1.4195
0%| | 0/13 [00:00<?, ?it/s]
Epoch 7/50 - Training Loss: 1.1709 - Testing Loss: 1.7324
0%| | 0/13 [00:00<?, ?it/s]
Epoch 8/50 - Training Loss: 1.1184 - Testing Loss: 1.3312
0%| | 0/13 [00:00<?, ?it/s]
Epoch 9/50 - Training Loss: 1.1452 - Testing Loss: 1.0771
0%| | 0/13 [00:00<?, ?it/s]
Epoch 10/50 - Training Loss: 1.0044 - Testing Loss: 1.1283
0%| | 0/13 [00:00<?, ?it/s]
Epoch 11/50 - Training Loss: 0.8715 - Testing Loss: 1.8442
0%| | 0/13 [00:00<?, ?it/s]
Epoch 12/50 - Training Loss: 0.8801 - Testing Loss: 1.3941
0%| | 0/13 [00:00<?, ?it/s]
Epoch 13/50 - Training Loss: 0.8545 - Testing Loss: 1.6625
0%| | 0/13 [00:00<?, ?it/s]

44
Epoch 14/50 - Training Loss: 0.9512 - Testing Loss: 1.1930
0%| | 0/13 [00:00<?, ?it/s]
Epoch 15/50 - Training Loss: 1.1568 - Testing Loss: 1.7327
0%| | 0/13 [00:00<?, ?it/s]
Epoch 16/50 - Training Loss: 0.9861 - Testing Loss: 1.0624
0%| | 0/13 [00:00<?, ?it/s]
Epoch 17/50 - Training Loss: 0.7993 - Testing Loss: 1.5425
0%| | 0/13 [00:00<?, ?it/s]
Epoch 18/50 - Training Loss: 0.8442 - Testing Loss: 1.3914
0%| | 0/13 [00:00<?, ?it/s]
Epoch 19/50 - Training Loss: 0.8000 - Testing Loss: 1.2805
0%| | 0/13 [00:00<?, ?it/s]
Epoch 20/50 - Training Loss: 0.8243 - Testing Loss: 1.6544
0%| | 0/13 [00:00<?, ?it/s]
Epoch 21/50 - Training Loss: 0.8905 - Testing Loss: 1.2739
0%| | 0/13 [00:00<?, ?it/s]
Epoch 22/50 - Training Loss: 0.8291 - Testing Loss: 1.8790
0%| | 0/13 [00:00<?, ?it/s]
Epoch 23/50 - Training Loss: 0.8030 - Testing Loss: 1.0327
0%| | 0/13 [00:00<?, ?it/s]
Epoch 24/50 - Training Loss: 0.8086 - Testing Loss: 1.1489
0%| | 0/13 [00:00<?, ?it/s]
Epoch 25/50 - Training Loss: 0.7938 - Testing Loss: 1.3265
0%| | 0/13 [00:00<?, ?it/s]
Epoch 26/50 - Training Loss: 0.8582 - Testing Loss: 1.0780
0%| | 0/13 [00:00<?, ?it/s]
Epoch 27/50 - Training Loss: 0.8349 - Testing Loss: 0.9739
0%| | 0/13 [00:00<?, ?it/s]
Epoch 28/50 - Training Loss: 0.7596 - Testing Loss: 1.1610
0%| | 0/13 [00:00<?, ?it/s]
Epoch 29/50 - Training Loss: 0.7884 - Testing Loss: 0.8929
0%| | 0/13 [00:00<?, ?it/s]

45
Epoch 30/50 - Training Loss: 0.7301 - Testing Loss: 1.0581
0%| | 0/13 [00:00<?, ?it/s]
Epoch 31/50 - Training Loss: 0.7616 - Testing Loss: 1.2029
0%| | 0/13 [00:00<?, ?it/s]
Epoch 32/50 - Training Loss: 0.7506 - Testing Loss: 1.1102
0%| | 0/13 [00:00<?, ?it/s]
Epoch 33/50 - Training Loss: 0.7777 - Testing Loss: 1.3477
0%| | 0/13 [00:00<?, ?it/s]
Epoch 34/50 - Training Loss: 0.7132 - Testing Loss: 1.6120
0%| | 0/13 [00:00<?, ?it/s]
Epoch 35/50 - Training Loss: 0.7149 - Testing Loss: 1.3423
0%| | 0/13 [00:00<?, ?it/s]
Epoch 36/50 - Training Loss: 0.7707 - Testing Loss: 1.1672
0%| | 0/13 [00:00<?, ?it/s]
Epoch 37/50 - Training Loss: 0.7074 - Testing Loss: 1.0439
0%| | 0/13 [00:00<?, ?it/s]
Epoch 38/50 - Training Loss: 0.8274 - Testing Loss: 1.0528
0%| | 0/13 [00:00<?, ?it/s]
Epoch 39/50 - Training Loss: 0.7319 - Testing Loss: 1.1594
0%| | 0/13 [00:00<?, ?it/s]
Epoch 40/50 - Training Loss: 0.7606 - Testing Loss: 1.2484
0%| | 0/13 [00:00<?, ?it/s]
Epoch 41/50 - Training Loss: 0.7745 - Testing Loss: 1.3121
0%| | 0/13 [00:00<?, ?it/s]
Epoch 42/50 - Training Loss: 0.7108 - Testing Loss: 1.6118
0%| | 0/13 [00:00<?, ?it/s]
Epoch 43/50 - Training Loss: 0.7283 - Testing Loss: 1.2961
0%| | 0/13 [00:00<?, ?it/s]
Epoch 44/50 - Training Loss: 0.7126 - Testing Loss: 0.8583
0%| | 0/13 [00:00<?, ?it/s]
Epoch 45/50 - Training Loss: 0.6765 - Testing Loss: 1.1658
0%| | 0/13 [00:00<?, ?it/s]

46
Epoch 46/50 - Training Loss: 0.7275 - Testing Loss: 0.8611
0%| | 0/13 [00:00<?, ?it/s]
Epoch 47/50 - Training Loss: 0.7853 - Testing Loss: 0.9544
0%| | 0/13 [00:00<?, ?it/s]
Epoch 48/50 - Training Loss: 0.7740 - Testing Loss: 1.1624
0%| | 0/13 [00:00<?, ?it/s]
Epoch 49/50 - Training Loss: 0.7244 - Testing Loss: 1.3710
0%| | 0/13 [00:00<?, ?it/s]
Epoch 50/50 - Training Loss: 0.6863 - Testing Loss: 1.1623

[83]: actual = labels


print([Link])
print([Link])

(489,)
(489,)

[84]: trace1 = [Link](x = [(i+1) for i in range(len(pred))],y = pred,␣


,→name='Predicted Data')

trace2 = [Link](x = [(i+1) for i in range(len(labels))],y = labels,␣


,→name='Actual data')

plot_data = [trace1,trace2]

layout = [Link](title='Prediction for 80:20 split Using CNN')


fig = [Link](data=plot_data, layout=layout)
# fig.add_vline(x=len(train_pred), line_width=1, line_dash="dash",␣
,→line_color="red")

[Link]()

not yield satisfactory results, as CNNs alone were unable to capture the sequential properties of time
series data effectively

3.0.2 3) LSTM -based Prediction with GADF Image Sequences:


Building upon the limitations of the first approach, the next innovation was to utilize a sequence of
GADF images as input to a recurrent neural network (LSTM) model, with the ‘close_price’ as the
target variable. This approach demonstrated more promising results, as LSTMs are well-suited for
modeling sequential data, and the GADF images provided a suitable representation of the temporal
patterns present in the time series.

[85]: values = df_cleaned['Close'].[Link](1,-1)


images = []
labels = []

47
image_size = 32
data_size = 32
# method = 'difference'
method = 'summation'
sample_range = (0, 1)

for i in range(len(values[0])-data_size-1):
gaf = GramianAngularField(image_size=image_size, method=method,␣
,→sample_range=sample_range)

gadf_image = gaf.fit_transform(values[:,i:i+data_size])
[Link](gadf_image)
[Link](values[:,i+data_size+1])
images = [Link](images).astype(np.float32).reshape(-1,image_size*image_size)

[86]: scaler = StandardScaler()


labels = scaler.fit_transform(labels).reshape(-1,).astype(np.float32)
[Link](images[0].reshape(32,32))

[86]: <[Link] at 0x247b2a5aba0>

output_95_1.png

[87]: print([Link],[Link])

(489, 1024) (489,)

[88]: seq_images,seq_labels = prepare_dataset(images,labels,length = 40)


print(seq_images.shape,[Link])

(448, 40, 1024) (489,)

[89]: dataset = DataPrep(seq_images,seq_labels)


train_loader,test_loader = split_prepare_data(dataset,batch_size = 32)

print(dataset[0][0].shape)

(40, 1024)

[90]: for xbatch,ybatch in train_loader:


print([Link],[Link])
break

[Link]([32, 40, 1024]) [Link]([32])

48
[91]: class ImageLSTMNet([Link]):
def __init__(self,input_size,hidden_size,layers,output_size):
super(ImageLSTMNet,self).__init__()
[Link] = layers
self.hidden_size = hidden_size
[Link] = [Link](input_size=input_size, hidden_size=hidden_size,␣
,→num_layers=layers, batch_first=True)

self.fc1 = [Link](hidden_size, output_size)

def forward(self,x):
# print([Link])
h0 = Variable([Link]([Link], [Link](0), self.hidden_size)).
,→to(device)

c0 = Variable([Link]([Link], [Link](0), self.hidden_size)).


to(device)
,→

out, (h_out, c_out) = [Link](x,(h0,c0))


# print([Link],h_out.shape,c_out.shape)
out = self.fc1(out[:,-1,:])
return [Link](1)

[92]: train_losses = []
test_losses = []
input_sz = 32*32
hidden_sz = 2048
output_sz = 1
layers = 1
LSTMmodel = ImageLSTMNet(input_sz,hidden_sz,layers,output_sz).to(device)

criterion = [Link]()
optimizer = [Link]([Link](), lr=0.0001)
num_epochs = 50
for epoch in range(num_epochs):
train_loss = 0.0
pbar = tqdm(total = len(train_loader))
for xbatch,ybatch in train_loader:
optimizer.zero_grad()
xbatch, ybatch = [Link](device),[Link](device)
output = LSTMmodel(xbatch)
loss = criterion(output,ybatch)
[Link]()
[Link]()
train_loss += [Link]()
[Link](1)
train_loss /= len(train_loader)
[Link]()

49
test_loss = 0.0
with torch.no_grad():
for xbatch,ybatch in test_loader:
xbatch, ybatch = [Link](device),[Link](device)
output = LSTMmodel(xbatch)
loss = criterion(output,ybatch)
test_loss += [Link]()
test_loss /= len(test_loader)

print(f"Epoch {epoch+1}/{num_epochs} - Training Loss: {train_loss:.4f} -␣


,→Testing Loss: {test_loss:.4f}")

pred = LSTMmodel([Link](seq_images).to(device)).cpu().[Link]().
,→squeeze()

0%| | 0/12 [00:00<?, ?it/s]


Epoch 1/50 - Training Loss: 1.0316 - Testing Loss: 0.8926
0%| | 0/12 [00:00<?, ?it/s]
Epoch 2/50 - Training Loss: 0.9153 - Testing Loss: 0.8917
0%| | 0/12 [00:00<?, ?it/s]
Epoch 3/50 - Training Loss: 0.8563 - Testing Loss: 0.8393
0%| | 0/12 [00:00<?, ?it/s]
Epoch 4/50 - Training Loss: 0.8580 - Testing Loss: 0.8244
0%| | 0/12 [00:00<?, ?it/s]
Epoch 5/50 - Training Loss: 0.7783 - Testing Loss: 0.7156
0%| | 0/12 [00:00<?, ?it/s]
Epoch 6/50 - Training Loss: 0.7141 - Testing Loss: 0.7193
0%| | 0/12 [00:00<?, ?it/s]
Epoch 7/50 - Training Loss: 0.6668 - Testing Loss: 0.5928
0%| | 0/12 [00:00<?, ?it/s]
Epoch 8/50 - Training Loss: 0.3352 - Testing Loss: 0.3747
0%| | 0/12 [00:00<?, ?it/s]
Epoch 9/50 - Training Loss: 0.1988 - Testing Loss: 0.1738
0%| | 0/12 [00:00<?, ?it/s]
Epoch 10/50 - Training Loss: 0.1056 - Testing Loss: 0.1090
0%| | 0/12 [00:00<?, ?it/s]

50
Epoch 11/50 - Training Loss: 0.0832 - Testing Loss: 0.0641
0%| | 0/12 [00:00<?, ?it/s]
Epoch 12/50 - Training Loss: 0.0501 - Testing Loss: 0.0591
0%| | 0/12 [00:00<?, ?it/s]
Epoch 13/50 - Training Loss: 0.0373 - Testing Loss: 0.0416
0%| | 0/12 [00:00<?, ?it/s]
Epoch 14/50 - Training Loss: 0.0220 - Testing Loss: 0.0424
0%| | 0/12 [00:00<?, ?it/s]
Epoch 15/50 - Training Loss: 0.0235 - Testing Loss: 0.0317
0%| | 0/12 [00:00<?, ?it/s]
Epoch 16/50 - Training Loss: 0.0205 - Testing Loss: 0.0285
0%| | 0/12 [00:00<?, ?it/s]
Epoch 17/50 - Training Loss: 0.0144 - Testing Loss: 0.0247
0%| | 0/12 [00:00<?, ?it/s]
Epoch 18/50 - Training Loss: 0.0118 - Testing Loss: 0.0225
0%| | 0/12 [00:00<?, ?it/s]
Epoch 19/50 - Training Loss: 0.0121 - Testing Loss: 0.0301
0%| | 0/12 [00:00<?, ?it/s]
Epoch 20/50 - Training Loss: 0.0108 - Testing Loss: 0.0227
0%| | 0/12 [00:00<?, ?it/s]
Epoch 21/50 - Training Loss: 0.0083 - Testing Loss: 0.0187
0%| | 0/12 [00:00<?, ?it/s]
Epoch 22/50 - Training Loss: 0.0077 - Testing Loss: 0.0182
0%| | 0/12 [00:00<?, ?it/s]
Epoch 23/50 - Training Loss: 0.0070 - Testing Loss: 0.0161
0%| | 0/12 [00:00<?, ?it/s]
Epoch 24/50 - Training Loss: 0.0074 - Testing Loss: 0.0152
0%| | 0/12 [00:00<?, ?it/s]
Epoch 25/50 - Training Loss: 0.0059 - Testing Loss: 0.0145
0%| | 0/12 [00:00<?, ?it/s]
Epoch 26/50 - Training Loss: 0.0056 - Testing Loss: 0.0138
0%| | 0/12 [00:00<?, ?it/s]

51
Epoch 27/50 - Training Loss: 0.0040 - Testing Loss: 0.0126
0%| | 0/12 [00:00<?, ?it/s]
Epoch 28/50 - Training Loss: 0.0038 - Testing Loss: 0.0110
0%| | 0/12 [00:00<?, ?it/s]
Epoch 29/50 - Training Loss: 0.0038 - Testing Loss: 0.0102
0%| | 0/12 [00:00<?, ?it/s]
Epoch 30/50 - Training Loss: 0.0040 - Testing Loss: 0.0110
0%| | 0/12 [00:00<?, ?it/s]
Epoch 31/50 - Training Loss: 0.0053 - Testing Loss: 0.0120
0%| | 0/12 [00:00<?, ?it/s]
Epoch 32/50 - Training Loss: 0.0052 - Testing Loss: 0.0148
0%| | 0/12 [00:00<?, ?it/s]
Epoch 33/50 - Training Loss: 0.0046 - Testing Loss: 0.0091
0%| | 0/12 [00:00<?, ?it/s]
Epoch 34/50 - Training Loss: 0.0029 - Testing Loss: 0.0078
0%| | 0/12 [00:00<?, ?it/s]
Epoch 35/50 - Training Loss: 0.0021 - Testing Loss: 0.0065
0%| | 0/12 [00:00<?, ?it/s]
Epoch 36/50 - Training Loss: 0.0024 - Testing Loss: 0.0063
0%| | 0/12 [00:00<?, ?it/s]
Epoch 37/50 - Training Loss: 0.0021 - Testing Loss: 0.0075
0%| | 0/12 [00:00<?, ?it/s]
Epoch 38/50 - Training Loss: 0.0019 - Testing Loss: 0.0058
0%| | 0/12 [00:00<?, ?it/s]
Epoch 39/50 - Training Loss: 0.0023 - Testing Loss: 0.0063
0%| | 0/12 [00:00<?, ?it/s]
Epoch 40/50 - Training Loss: 0.0023 - Testing Loss: 0.0074
0%| | 0/12 [00:00<?, ?it/s]
Epoch 41/50 - Training Loss: 0.0034 - Testing Loss: 0.0082
0%| | 0/12 [00:00<?, ?it/s]
Epoch 42/50 - Training Loss: 0.0034 - Testing Loss: 0.0055
0%| | 0/12 [00:00<?, ?it/s]

52
Epoch 43/50 - Training Loss: 0.0025 - Testing Loss: 0.0054
0%| | 0/12 [00:00<?, ?it/s]
Epoch 44/50 - Training Loss: 0.0019 - Testing Loss: 0.0048
0%| | 0/12 [00:00<?, ?it/s]
Epoch 45/50 - Training Loss: 0.0018 - Testing Loss: 0.0059
0%| | 0/12 [00:00<?, ?it/s]
Epoch 46/50 - Training Loss: 0.0017 - Testing Loss: 0.0056
0%| | 0/12 [00:00<?, ?it/s]
Epoch 47/50 - Training Loss: 0.0015 - Testing Loss: 0.0043
0%| | 0/12 [00:00<?, ?it/s]
Epoch 48/50 - Training Loss: 0.0012 - Testing Loss: 0.0047
0%| | 0/12 [00:00<?, ?it/s]
Epoch 49/50 - Training Loss: 0.0012 - Testing Loss: 0.0039
0%| | 0/12 [00:00<?, ?it/s]
Epoch 50/50 - Training Loss: 0.0012 - Testing Loss: 0.0039

[93]: actual = labels[41:]


print([Link])
print([Link])

(448,)
(448,)

[94]: trace1 = [Link](x = [(i+1) for i in range(len(pred))],y = pred,␣


,→name='Predicted Data')

trace2 = [Link](x = [(i+1) for i in range(len(labels))],y = actual,␣


,→name='Actual data')

plot_data = [trace1,trace2]

layout = [Link](title='Prediction for 80:20 split')


fig = [Link](data=plot_data, layout=layout)
fig.add_vline(x=len(train_pred), line_width=1, line_dash="dash",␣
,→line_color="red")

[Link]()

53
output_103_0.png

demonstrating more promising results, as LSTMs are well-suited for modeling sequential data, and
the GADF images provided a suitable representation of the temporal patterns present in the time
series.

3.0.3 4) CNN Feature Extraction and LSTM Forecasting:


One of the key innovations was the development of a two-stage model, where a pre-trained CNN, such
as ResNet-50, was employed as a feature extractor to obtain embeddings from the GADF-encoded
images. These embeddings were then fed into a Long Short-Term Memory (LSTM) network, which
leveraged the sequential nature of the data to make predictions of future stock prices using a sliding
window approach.

[95]: values = df_cleaned['Close'].[Link](1,-1)


images = []
labels = []
image_size = 32
data_size = 32
LATENT_SIZE = 1000
# method = 'difference'
method = 'summation'
sample_range = (0, 1)

[96]: # ResNet model


resnet = models.resnet18(pretrained=True).float()
[Link]()
preprocess = [Link]([
[Link](),
# [Link](mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

[97]: for i in range(len(values[0])-data_size-1):


gaf = GramianAngularField(image_size=image_size, method=method,␣
,→sample_range=sample_range)

gadf_image = gaf.fit_transform(values[:,i:i+data_size])
gadf_image = [Link]([Link](gadf_image, 3, axis=0), (1, 2, 0))
# print(gadf_image.shape)
image = preprocess(gadf_image).unsqueeze(0)
with torch.no_grad():
encoding = resnet([Link]()).squeeze().numpy()

54
[Link](encoding)
[Link](values[:,i+data_size+1])
images = [Link](images)
print([Link])

(489, 1000)

[98]: scaler = StandardScaler()

labels = scaler.fit_transform(labels).reshape(-1,).astype(np.float32)

[99]: seq_images,seq_labels = prepare_dataset(images,labels,length = 40)


print(seq_images.shape,[Link])

(448, 40, 1000) (489,)

[100]: dataset = DataPrep(seq_images,seq_labels)


train_loader,test_loader = split_prepare_data(dataset,batch_size = 32)

[101]: for xbatch,ybatch in train_loader:


print([Link],[Link])
break

[Link]([32, 40, 1000]) [Link]([32])

[102]: class ImageLSTMNet([Link]):


def __init__(self,input_size,hidden_size,layers,output_size):
super(ImageLSTMNet,self).__init__()
[Link] = layers
self.hidden_size = hidden_size
[Link] = [Link](input_size=input_size, hidden_size=hidden_size,␣
,→num_layers=layers, batch_first=True)

self.fc1 = [Link](hidden_size, output_size)


# self.fc2 = [Link](10,output_size)
def forward(self,x):
# print([Link])
h0 = Variable([Link]([Link], [Link](0), self.hidden_size)).
,→to(device)

c0 = Variable([Link]([Link], [Link](0), self.hidden_size)).


to(device)
,→

out, (h_out, c_out) = [Link](x,(h0,c0))


# print([Link],h_out.shape,c_out.shape)
out = self.fc1(out[:,-1,:])
return [Link](1)

55
[103]: train_losses = []
test_losses = []
# input_sz = 32*32
input_sz = LATENT_SIZE
hidden_sz = 2048
output_sz = 1
layers = 1
LSTMmodel = ImageLSTMNet(input_sz,hidden_sz,layers,output_sz).to(device)

criterion = [Link]()
optimizer = [Link]([Link](), lr=0.0001)
num_epochs = 50
for epoch in range(num_epochs):
train_loss = 0.0
pbar = tqdm(total = len(train_loader))
for xbatch,ybatch in train_loader:
optimizer.zero_grad()
xbatch, ybatch = [Link](device),[Link](device)
output = LSTMmodel(xbatch)
loss = criterion(output,ybatch)
[Link]()
[Link]()
train_loss += [Link]()
[Link](1)
train_loss /= len(train_loader)
[Link]()

test_loss = 0.0
with torch.no_grad():
for xbatch,ybatch in test_loader:
xbatch, ybatch = [Link](device),[Link](device)
output = LSTMmodel(xbatch)
loss = criterion(output,ybatch)
test_loss += [Link]()
test_loss /= len(test_loader)

print(f"Epoch {epoch+1}/{num_epochs} - Training Loss: {train_loss:.4f} -␣


,→Testing Loss: {test_loss:.4f}")

pred = LSTMmodel([Link](seq_images).to(device)).cpu().[Link]().
,→squeeze()

0%| | 0/12 [00:00<?, ?it/s]


Epoch 1/50 - Training Loss: 1.1128 - Testing Loss: 1.0803
0%| | 0/12 [00:00<?, ?it/s]
Epoch 2/50 - Training Loss: 0.6807 - Testing Loss: 0.6393

56
0%| | 0/12 [00:00<?, ?it/s]
Epoch 3/50 - Training Loss: 0.4675 - Testing Loss: 0.2247
0%| | 0/12 [00:00<?, ?it/s]
Epoch 4/50 - Training Loss: 0.1759 - Testing Loss: 0.1003
0%| | 0/12 [00:00<?, ?it/s]
Epoch 5/50 - Training Loss: 0.0603 - Testing Loss: 0.0421
0%| | 0/12 [00:00<?, ?it/s]
Epoch 6/50 - Training Loss: 0.0335 - Testing Loss: 0.0273
0%| | 0/12 [00:00<?, ?it/s]
Epoch 7/50 - Training Loss: 0.0159 - Testing Loss: 0.0199
0%| | 0/12 [00:00<?, ?it/s]
Epoch 8/50 - Training Loss: 0.0088 - Testing Loss: 0.0160
0%| | 0/12 [00:00<?, ?it/s]
Epoch 9/50 - Training Loss: 0.0063 - Testing Loss: 0.0134
0%| | 0/12 [00:00<?, ?it/s]
Epoch 10/50 - Training Loss: 0.0075 - Testing Loss: 0.0101
0%| | 0/12 [00:00<?, ?it/s]
Epoch 11/50 - Training Loss: 0.0043 - Testing Loss: 0.0091
0%| | 0/12 [00:00<?, ?it/s]
Epoch 12/50 - Training Loss: 0.0032 - Testing Loss: 0.0067
0%| | 0/12 [00:00<?, ?it/s]
Epoch 13/50 - Training Loss: 0.0037 - Testing Loss: 0.0073
0%| | 0/12 [00:00<?, ?it/s]
Epoch 14/50 - Training Loss: 0.0034 - Testing Loss: 0.0064
0%| | 0/12 [00:00<?, ?it/s]
Epoch 15/50 - Training Loss: 0.0031 - Testing Loss: 0.0076
0%| | 0/12 [00:00<?, ?it/s]
Epoch 16/50 - Training Loss: 0.0045 - Testing Loss: 0.0063
0%| | 0/12 [00:00<?, ?it/s]
Epoch 17/50 - Training Loss: 0.0041 - Testing Loss: 0.0061
0%| | 0/12 [00:00<?, ?it/s]
Epoch 18/50 - Training Loss: 0.0035 - Testing Loss: 0.0048

57
0%| | 0/12 [00:00<?, ?it/s]
Epoch 19/50 - Training Loss: 0.0025 - Testing Loss: 0.0050
0%| | 0/12 [00:00<?, ?it/s]
Epoch 20/50 - Training Loss: 0.0021 - Testing Loss: 0.0042
0%| | 0/12 [00:00<?, ?it/s]
Epoch 21/50 - Training Loss: 0.0019 - Testing Loss: 0.0056
0%| | 0/12 [00:00<?, ?it/s]
Epoch 22/50 - Training Loss: 0.0016 - Testing Loss: 0.0054
0%| | 0/12 [00:00<?, ?it/s]
Epoch 23/50 - Training Loss: 0.0017 - Testing Loss: 0.0037
0%| | 0/12 [00:00<?, ?it/s]
Epoch 24/50 - Training Loss: 0.0018 - Testing Loss: 0.0042
0%| | 0/12 [00:00<?, ?it/s]
Epoch 25/50 - Training Loss: 0.0018 - Testing Loss: 0.0055
0%| | 0/12 [00:00<?, ?it/s]
Epoch 26/50 - Training Loss: 0.0020 - Testing Loss: 0.0038
0%| | 0/12 [00:00<?, ?it/s]
Epoch 27/50 - Training Loss: 0.0017 - Testing Loss: 0.0037
0%| | 0/12 [00:00<?, ?it/s]
Epoch 28/50 - Training Loss: 0.0024 - Testing Loss: 0.0037
0%| | 0/12 [00:00<?, ?it/s]
Epoch 29/50 - Training Loss: 0.0025 - Testing Loss: 0.0046
0%| | 0/12 [00:00<?, ?it/s]
Epoch 30/50 - Training Loss: 0.0026 - Testing Loss: 0.0080
0%| | 0/12 [00:00<?, ?it/s]
Epoch 31/50 - Training Loss: 0.0027 - Testing Loss: 0.0053
0%| | 0/12 [00:00<?, ?it/s]
Epoch 32/50 - Training Loss: 0.0022 - Testing Loss: 0.0071
0%| | 0/12 [00:00<?, ?it/s]
Epoch 33/50 - Training Loss: 0.0019 - Testing Loss: 0.0033
0%| | 0/12 [00:00<?, ?it/s]
Epoch 34/50 - Training Loss: 0.0014 - Testing Loss: 0.0037

58
0%| | 0/12 [00:00<?, ?it/s]
Epoch 35/50 - Training Loss: 0.0014 - Testing Loss: 0.0047
0%| | 0/12 [00:00<?, ?it/s]
Epoch 36/50 - Training Loss: 0.0012 - Testing Loss: 0.0039
0%| | 0/12 [00:00<?, ?it/s]
Epoch 37/50 - Training Loss: 0.0013 - Testing Loss: 0.0051
0%| | 0/12 [00:00<?, ?it/s]
Epoch 38/50 - Training Loss: 0.0015 - Testing Loss: 0.0066
0%| | 0/12 [00:00<?, ?it/s]
Epoch 39/50 - Training Loss: 0.0015 - Testing Loss: 0.0032
0%| | 0/12 [00:00<?, ?it/s]
Epoch 40/50 - Training Loss: 0.0009 - Testing Loss: 0.0037
0%| | 0/12 [00:00<?, ?it/s]
Epoch 41/50 - Training Loss: 0.0008 - Testing Loss: 0.0036
0%| | 0/12 [00:00<?, ?it/s]
Epoch 42/50 - Training Loss: 0.0009 - Testing Loss: 0.0049
0%| | 0/12 [00:00<?, ?it/s]
Epoch 43/50 - Training Loss: 0.0010 - Testing Loss: 0.0034
0%| | 0/12 [00:00<?, ?it/s]
Epoch 44/50 - Training Loss: 0.0008 - Testing Loss: 0.0029
0%| | 0/12 [00:00<?, ?it/s]
Epoch 45/50 - Training Loss: 0.0009 - Testing Loss: 0.0030
0%| | 0/12 [00:00<?, ?it/s]
Epoch 46/50 - Training Loss: 0.0010 - Testing Loss: 0.0033
0%| | 0/12 [00:00<?, ?it/s]
Epoch 47/50 - Training Loss: 0.0013 - Testing Loss: 0.0039
0%| | 0/12 [00:00<?, ?it/s]
Epoch 48/50 - Training Loss: 0.0015 - Testing Loss: 0.0052
0%| | 0/12 [00:00<?, ?it/s]
Epoch 49/50 - Training Loss: 0.0015 - Testing Loss: 0.0036
0%| | 0/12 [00:00<?, ?it/s]
Epoch 50/50 - Training Loss: 0.0013 - Testing Loss: 0.0039

59
[104]: actual = labels[41:]
print([Link])
print([Link])

(448,)
(448,)

[105]: trace1 = [Link](x = [(i+1) for i in range(len(pred))],y = pred,␣


,→name='Predicted Data')

trace2 = [Link](x = [(i+1) for i in range(len(labels))],y = actual,␣


,→name='Actual data')

plot_data = [trace1,trace2]

layout = [Link](title='Prediction for 80:20 split Using Encoder')


fig = [Link](data=plot_data, layout=layout)

[Link]()

output_116_0.png

[ ]:

60

Vous aimerez peut-être aussi