Update app.py
Browse files
app.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
import datetime
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
import pandas as pd
|
| 4 |
import plotly.graph_objects as go
|
|
@@ -7,90 +8,42 @@ import tensorflow as tf
|
|
| 7 |
import yfinance as yf
|
| 8 |
from sklearn.preprocessing import MinMaxScaler
|
| 9 |
from prophet import Prophet
|
| 10 |
-
import os
|
| 11 |
|
| 12 |
# Configuration de la page
|
| 13 |
-
st.set_page_config(
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
layout="wide",
|
| 17 |
-
initial_sidebar_state="expanded"
|
| 18 |
-
)
|
| 19 |
-
|
| 20 |
-
# CSS personnalisé pour un design moderne
|
| 21 |
st.markdown("""
|
| 22 |
<style>
|
| 23 |
.main-title {
|
| 24 |
font-size: 2.5rem;
|
| 25 |
-
font-weight:
|
| 26 |
-
color: #
|
| 27 |
-
margin-bottom: 1rem;
|
| 28 |
-
text-align: center;
|
| 29 |
-
}
|
| 30 |
-
.section-header {
|
| 31 |
-
font-size: 1.8rem;
|
| 32 |
-
font-weight: 600;
|
| 33 |
-
color: #2563EB;
|
| 34 |
-
margin-top: 2rem;
|
| 35 |
-
margin-bottom: 1rem;
|
| 36 |
-
}
|
| 37 |
-
.info-box {
|
| 38 |
-
background-color: #F8FAFC;
|
| 39 |
-
padding: 1rem;
|
| 40 |
-
border-radius: 8px;
|
| 41 |
-
border-left: 4px solid #2563EB;
|
| 42 |
-
margin-bottom: 1rem;
|
| 43 |
-
}
|
| 44 |
-
.stButton>button {
|
| 45 |
-
background-color: #2563EB;
|
| 46 |
-
color: white;
|
| 47 |
-
border-radius: 8px;
|
| 48 |
-
padding: 0.5rem 1rem;
|
| 49 |
-
}
|
| 50 |
-
.stButton>button:hover {
|
| 51 |
-
background-color: #1E40AF;
|
| 52 |
-
color: white;
|
| 53 |
-
}
|
| 54 |
-
.sidebar .sidebar-content {
|
| 55 |
-
background-color: #F1F5F9;
|
| 56 |
}
|
| 57 |
</style>
|
|
|
|
| 58 |
""", unsafe_allow_html=True)
|
| 59 |
|
| 60 |
-
# En-tête
|
| 61 |
-
st.markdown("<div class='main-title'>📈 Prédiction des Cours de l'Action Microsoft (MSFT)</div>", unsafe_allow_html=True)
|
| 62 |
-
st.markdown("<div class='info-box'>Analyse et prévisions des cours de Microsoft (MSFT) basées sur des modèles de Deep Learning (RNN) et Neural Prophet. Personnalisez les paramètres dans la barre latérale pour explorer les données et les prédictions.</div>", unsafe_allow_html=True)
|
| 63 |
-
|
| 64 |
# Barre latérale
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
# Fonction pour charger les données
|
| 78 |
@st.cache_data(ttl=3600)
|
| 79 |
-
def load_data(
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
start_date = (datetime.datetime.now() - pd.Timedelta(days=365*int(period[0]))).strftime('%Y-%m-%d')
|
| 83 |
-
data = yf.download("MSFT", start=start_date, end=end_date)[['Close']]
|
| 84 |
-
if data.empty:
|
| 85 |
-
raise ValueError("Aucune donnée disponible pour MSFT.")
|
| 86 |
-
return data
|
| 87 |
-
except Exception as e:
|
| 88 |
-
st.error(f"❌ Erreur lors du chargement des données : {str(e)}")
|
| 89 |
-
st.stop()
|
| 90 |
|
| 91 |
# Fonction pour calculer les indicateurs techniques
|
| 92 |
def compute_indicators(df):
|
| 93 |
-
df = df.copy()
|
| 94 |
df['SMA20'] = df['Close'].rolling(window=20).mean()
|
| 95 |
df['SMA50'] = df['Close'].rolling(window=50).mean()
|
| 96 |
df['SMA200'] = df['Close'].rolling(window=200).mean()
|
|
@@ -108,175 +61,200 @@ def compute_indicators(df):
|
|
| 108 |
|
| 109 |
df['Buy_RSI'] = df['RSI'] < 30
|
| 110 |
df['Sell_RSI'] = df['RSI'] > 70
|
|
|
|
| 111 |
df['Buy_MACD'] = (df['MACD'] > df['Signal']) & (df['MACD'].shift(1) <= df['Signal'].shift(1))
|
| 112 |
df['Sell_MACD'] = (df['MACD'] < df['Signal']) & (df['MACD'].shift(1) >= df['Signal'].shift(1))
|
|
|
|
| 113 |
df['Buy_Signal'] = df['Buy_RSI'] | df['Buy_MACD']
|
| 114 |
df['Sell_Signal'] = df['Sell_RSI'] | df['Sell_MACD']
|
| 115 |
|
| 116 |
return df
|
| 117 |
|
| 118 |
-
|
| 119 |
def predict_with_prophet(df, steps):
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
for _ in range(steps):
|
| 145 |
-
pred = model.predict(input_seq.reshape(1, -1, 1), verbose=0)[0]
|
| 146 |
-
prediction_list.append(pred)
|
| 147 |
-
pred = pred.reshape(1, 1)
|
| 148 |
-
input_seq = np.append(input_seq[1:], pred, axis=0)
|
| 149 |
-
predictions = scaler.inverse_transform(np.array(prediction_list))
|
| 150 |
-
future_dates = pd.date_range(start=df.index[-1] + pd.Timedelta(days=1), periods=steps, freq='B')
|
| 151 |
-
return pd.DataFrame(predictions, columns=["Prix Prédit"], index=future_dates)
|
| 152 |
-
except Exception as e:
|
| 153 |
-
st.error(f"❌ Erreur lors de la prédiction avec RNN : {str(e)}")
|
| 154 |
-
st.stop()
|
| 155 |
-
|
| 156 |
-
# Chargement des données
|
| 157 |
-
data = load_data(period)
|
| 158 |
data = compute_indicators(data)
|
| 159 |
|
| 160 |
-
|
| 161 |
-
st.
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
)
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
#
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
if model_choice == "Neural Prophet":
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
fig_components = prophet_model.plot_components(forecast_full)
|
| 186 |
st.pyplot(fig_components)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
else:
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
| 194 |
buy_signals = data[data['Buy_Signal']]
|
| 195 |
sell_signals = data[data['Sell_Signal']]
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
))
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
| 205 |
))
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
xaxis_title="Date",
|
| 209 |
-
yaxis_title="Prix ($)",
|
| 210 |
-
template="plotly_white",
|
| 211 |
-
hovermode="x unified"
|
| 212 |
-
)
|
| 213 |
-
st.plotly_chart(fig_combined, use_container_width=True)
|
| 214 |
-
|
| 215 |
-
# Indicateurs techniques
|
| 216 |
-
st.markdown("<div class='section-header'>📉 Indicateurs Techniques</div>", unsafe_allow_html=True)
|
| 217 |
-
st.markdown("<div class='info-box'>"
|
| 218 |
-
"Les indicateurs techniques aident à identifier les tendances et opportunités : <br>"
|
| 219 |
-
"- <b>SMA</b> : Moyennes mobiles (20, 50, 200 jours).<br>"
|
| 220 |
-
"- <b>RSI</b> : Surachat (>70) ou survente (<30).<br>"
|
| 221 |
-
"- <b>MACD</b> : Croisements pour détecter des signaux d'achat/vente."
|
| 222 |
-
"</div>", unsafe_allow_html=True)
|
| 223 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
if show_sma:
|
| 225 |
fig_sma = go.Figure()
|
| 226 |
-
fig_sma.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close'
|
| 227 |
-
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA20'], mode='lines', name='SMA20'
|
| 228 |
-
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA50'], mode='lines', name='SMA50'
|
| 229 |
-
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA200'], mode='lines', name='SMA200'
|
| 230 |
-
fig_sma.update_layout(title="Moyennes Mobiles",
|
| 231 |
st.plotly_chart(fig_sma, use_container_width=True)
|
| 232 |
|
|
|
|
| 233 |
if show_rsi:
|
| 234 |
fig_rsi = go.Figure()
|
| 235 |
-
fig_rsi.add_trace(go.Scatter(x=data.index, y=data['RSI'], mode='lines', name='RSI', line=dict(color='
|
| 236 |
-
fig_rsi.add_hline(y=70, line_dash='dash', line_color='red'
|
| 237 |
-
fig_rsi.add_hline(y=30, line_dash='dash', line_color='green'
|
| 238 |
-
fig_rsi.update_layout(title="RSI (Relative Strength Index)",
|
| 239 |
st.plotly_chart(fig_rsi, use_container_width=True)
|
| 240 |
|
|
|
|
| 241 |
if show_macd:
|
| 242 |
fig_macd = go.Figure()
|
| 243 |
-
fig_macd.add_trace(go.Scatter(x=data.index, y=data['MACD'], mode='lines', name='MACD'
|
| 244 |
-
fig_macd.add_trace(go.Scatter(x=data.index, y=data['Signal'], mode='lines', name='Signal'
|
| 245 |
-
fig_macd.update_layout(title="MACD",
|
| 246 |
st.plotly_chart(fig_macd, use_container_width=True)
|
| 247 |
|
| 248 |
-
#
|
| 249 |
if show_table:
|
| 250 |
-
st.
|
| 251 |
-
st.dataframe(pred_df.style.format("{:.2f}")
|
| 252 |
|
| 253 |
# Statistiques
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
| 265 |
if download_csv:
|
| 266 |
-
csv = pred_df.to_csv(index=True).encode('utf-8')
|
| 267 |
st.download_button(
|
| 268 |
-
label="📥 Télécharger les prédictions
|
| 269 |
-
data=
|
| 270 |
-
file_name=
|
| 271 |
-
mime=
|
| 272 |
-
use_container_width=True
|
| 273 |
)
|
| 274 |
|
| 275 |
-
#
|
| 276 |
st.markdown("""
|
| 277 |
-
<hr style='margin-top:
|
| 278 |
-
<div style='text-align: center;
|
| 279 |
-
Développé par <strong>Ntonga Babong Lewis</strong>
|
| 280 |
-
Propulsé par Streamlit, Prophet, et TensorFlow
|
| 281 |
</div>
|
| 282 |
""", unsafe_allow_html=True)
|
|
|
|
| 1 |
import datetime
|
| 2 |
+
|
| 3 |
import numpy as np
|
| 4 |
import pandas as pd
|
| 5 |
import plotly.graph_objects as go
|
|
|
|
| 8 |
import yfinance as yf
|
| 9 |
from sklearn.preprocessing import MinMaxScaler
|
| 10 |
from prophet import Prophet
|
|
|
|
| 11 |
|
| 12 |
# Configuration de la page
|
| 13 |
+
st.set_page_config(page_title="Prédiction MSFT", layout="wide")
|
| 14 |
+
|
| 15 |
+
# En-tête
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
st.markdown("""
|
| 17 |
<style>
|
| 18 |
.main-title {
|
| 19 |
font-size: 2.5rem;
|
| 20 |
+
font-weight: bold;
|
| 21 |
+
color: #2E86C1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
| 23 |
</style>
|
| 24 |
+
<div class='main-title'>📈 Prédiction des Cours de l'Action Microsoft (MSFT)</div>
|
| 25 |
""", unsafe_allow_html=True)
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
# Barre latérale
|
| 28 |
+
st.sidebar.title("🔧 Paramètres de Prédiction")
|
| 29 |
+
st.sidebar.markdown("## 📈 Indicateurs techniques")
|
| 30 |
+
show_sma = st.sidebar.checkbox("Afficher SMA (20/50/200)", value=True)
|
| 31 |
+
show_rsi = st.sidebar.checkbox("Afficher RSI", value=True)
|
| 32 |
+
show_macd = st.sidebar.checkbox("Afficher MACD", value=True)
|
| 33 |
+
model_choice = st.sidebar.radio("Modèle utilisé", ["RNN", "Neural Prophet"])
|
| 34 |
+
steps = st.sidebar.slider("Jours à prédire", 1, 30, 15)
|
| 35 |
+
show_table = st.sidebar.checkbox("Afficher les valeurs prédictives", True)
|
| 36 |
+
download_csv = st.sidebar.checkbox("Téléchargement CSV", True)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
# Données historiques
|
|
|
|
| 40 |
@st.cache_data(ttl=3600)
|
| 41 |
+
def load_data():
|
| 42 |
+
return yf.download("MSFT", start="2020-01-01", end=datetime.datetime.now().strftime('%Y-%m-%d'))[['Close']]
|
| 43 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
# Fonction pour calculer les indicateurs techniques
|
| 46 |
def compute_indicators(df):
|
|
|
|
| 47 |
df['SMA20'] = df['Close'].rolling(window=20).mean()
|
| 48 |
df['SMA50'] = df['Close'].rolling(window=50).mean()
|
| 49 |
df['SMA200'] = df['Close'].rolling(window=200).mean()
|
|
|
|
| 61 |
|
| 62 |
df['Buy_RSI'] = df['RSI'] < 30
|
| 63 |
df['Sell_RSI'] = df['RSI'] > 70
|
| 64 |
+
|
| 65 |
df['Buy_MACD'] = (df['MACD'] > df['Signal']) & (df['MACD'].shift(1) <= df['Signal'].shift(1))
|
| 66 |
df['Sell_MACD'] = (df['MACD'] < df['Signal']) & (df['MACD'].shift(1) >= df['Signal'].shift(1))
|
| 67 |
+
|
| 68 |
df['Buy_Signal'] = df['Buy_RSI'] | df['Buy_MACD']
|
| 69 |
df['Sell_Signal'] = df['Sell_RSI'] | df['Sell_MACD']
|
| 70 |
|
| 71 |
return df
|
| 72 |
|
| 73 |
+
|
| 74 |
def predict_with_prophet(df, steps):
|
| 75 |
+
df = df.copy()
|
| 76 |
+
if 'Date' not in df.columns or 'Close' not in df.columns:
|
| 77 |
+
df = df.rename(columns={'ds': 'Date', 'y': 'Close'})
|
| 78 |
+
|
| 79 |
+
df_prophet = df.reset_index()[['Date', 'Close']]
|
| 80 |
+
df_prophet.columns = ['ds', 'y']
|
| 81 |
+
|
| 82 |
+
model = Prophet(daily_seasonality=True)
|
| 83 |
+
model.fit(df_prophet)
|
| 84 |
+
|
| 85 |
+
future = model.make_future_dataframe(periods=steps)
|
| 86 |
+
forecast = model.predict(future)
|
| 87 |
+
|
| 88 |
+
forecast_filtered = forecast[['ds', 'yhat']].set_index('ds').tail(steps)
|
| 89 |
+
forecast_filtered.columns = ['Prix Prédit']
|
| 90 |
+
return forecast_filtered, model, forecast
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
data = load_data()
|
| 94 |
+
|
| 95 |
+
if data.empty:
|
| 96 |
+
st.error("❌ Échec du chargement des données. Aucune donnée disponible pour MSFT.")
|
| 97 |
+
st.stop()
|
| 98 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
data = compute_indicators(data)
|
| 100 |
|
| 101 |
+
st.subheader("📊 Évolution Historique du Cours")
|
| 102 |
+
st.line_chart(data['Close'])
|
| 103 |
+
|
| 104 |
+
# Normalisation
|
| 105 |
+
scaler = MinMaxScaler()
|
| 106 |
+
scaled_data = scaler.fit_transform(data[['Close']])
|
| 107 |
+
last_60_days = scaled_data[-60:]
|
| 108 |
+
|
| 109 |
+
# Chargement du modèle
|
| 110 |
+
model_path = "model_lstm_MSFT.h5" if model_choice == "LSTM" else "model_rnn_MSFT.h5"
|
| 111 |
+
model = tf.keras.models.load_model(model_path)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
# Fonction de prédiction
|
| 115 |
+
def predict_next_days(model, last_sequence, steps=15):
|
| 116 |
+
prediction_list = []
|
| 117 |
+
input_seq = last_sequence.copy()
|
| 118 |
+
for _ in range(steps):
|
| 119 |
+
pred = model.predict(input_seq.reshape(1, -1, 1), verbose=0)[0]
|
| 120 |
+
prediction_list.append(pred)
|
| 121 |
+
pred = pred.reshape(1, 1)
|
| 122 |
+
input_seq = np.append(input_seq[1:], pred, axis=0)
|
| 123 |
+
return scaler.inverse_transform(np.array(prediction_list))
|
| 124 |
+
|
| 125 |
+
|
| 126 |
if model_choice == "Neural Prophet":
|
| 127 |
+
data_prophet = data.copy()
|
| 128 |
+
pred_df, prophet_model, forecast_full = predict_with_prophet(data_prophet, steps)
|
| 129 |
+
|
| 130 |
+
st.subheader("📆 Composants Prophet (Tendance, Saison, etc.)")
|
| 131 |
+
|
| 132 |
+
# Explication des graphiques Prophet
|
| 133 |
+
st.markdown("""
|
| 134 |
+
<div style='border: 1px solid #ccc; padding: 1rem; border-radius: 10px; background-color: #f9f9f9'>
|
| 135 |
+
<h4>🧠 <u>Explication des Graphiques Prophet</u></h4>
|
| 136 |
+
- Le **graphique principal** montre les valeurs réelles (points noirs) et les prévisions effectuées par Prophet (ligne bleue), accompagnées d'une **zone d'incertitude** (en bleu clair).
|
| 137 |
+
- Le **graphique des composantes** détaille la **tendance globale**, les **saisonnalités hebdomadaires et annuelles**, et d'autres patterns identifiés par le modèle.
|
| 138 |
+
Ces éléments vous permettent de comprendre non seulement où le prix est supposé aller, mais aussi *pourquoi* Prophet fait cette prédiction.
|
| 139 |
+
</div>
|
| 140 |
+
""", unsafe_allow_html=True)
|
| 141 |
+
|
| 142 |
fig_components = prophet_model.plot_components(forecast_full)
|
| 143 |
st.pyplot(fig_components)
|
| 144 |
+
|
| 145 |
+
fig_forecast = prophet_model.plot(forecast_full)
|
| 146 |
+
st.pyplot(fig_forecast)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
|
| 150 |
else:
|
| 151 |
+
predictions = predict_next_days(model, last_60_days, steps)
|
| 152 |
+
future_dates = pd.date_range(start=data.index[-1] + pd.Timedelta(days=1), periods=steps)
|
| 153 |
+
pred_df = pd.DataFrame(predictions, columns=["Prix Prédit"], index=future_dates)
|
| 154 |
+
|
| 155 |
+
# Visualisation interactive
|
| 156 |
+
st.subheader(f"🔮 Prédiction sur {steps} jours avec {model_choice}")
|
| 157 |
|
| 158 |
+
st.markdown(f"""
|
| 159 |
+
ℹ️ <u><b>Prédiction sur {steps} jours :</b></u>
|
| 160 |
+
Ce graphique montre les cours historiques ainsi que les prédictions du modèle {model_choice}.
|
| 161 |
+
Les triangles verts 🔺 représentent des **opportunités d'achat** possibles, et les triangles rouges 🔻 des **ventes potentielles**.
|
| 162 |
+
""", unsafe_allow_html=True)
|
| 163 |
+
|
| 164 |
+
pred_df.index = pd.to_datetime(pred_df.index)
|
| 165 |
buy_signals = data[data['Buy_Signal']]
|
| 166 |
sell_signals = data[data['Sell_Signal']]
|
| 167 |
+
fig = go.Figure()
|
| 168 |
+
fig.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Historique'))
|
| 169 |
+
fig.add_trace(go.Scatter(x=pred_df.index, y=pred_df['Prix Prédit'], mode='lines+markers', name='Prédictions',
|
| 170 |
+
line=dict(color='red')))
|
| 171 |
+
fig.add_trace(go.Scatter(
|
| 172 |
+
x=buy_signals.index,
|
| 173 |
+
y=buy_signals['Close'],
|
| 174 |
+
mode='markers',
|
| 175 |
+
marker=dict(color='green', size=10, symbol='triangle-up'),
|
| 176 |
+
name='Signal Achat'
|
| 177 |
))
|
| 178 |
+
|
| 179 |
+
fig.add_trace(go.Scatter(
|
| 180 |
+
x=sell_signals.index,
|
| 181 |
+
y=sell_signals['Close'],
|
| 182 |
+
mode='markers',
|
| 183 |
+
marker=dict(color='red', size=10, symbol='triangle-down'),
|
| 184 |
+
name='Signal Vente'
|
| 185 |
))
|
| 186 |
+
fig.update_layout(xaxis_title='Date', yaxis_title='Prix ($)', template='plotly_white')
|
| 187 |
+
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
|
| 189 |
+
# Graphique des indicateurs techniques
|
| 190 |
+
st.subheader("📉 Indicateurs Techniques")
|
| 191 |
+
|
| 192 |
+
st.markdown("""
|
| 193 |
+
ℹ️ <u><b>Explication :</b></u>
|
| 194 |
+
Les indicateurs techniques vous aident à identifier les tendances du marché.
|
| 195 |
+
- **SMA** : Moyennes mobiles à court, moyen et long terme.
|
| 196 |
+
- **RSI** : Indique si une action est surachetée (>70) ou survendue (<30).
|
| 197 |
+
- **MACD** : Compare deux moyennes exponentielles et détecte des croisements pour signaler des opportunités.
|
| 198 |
+
""", unsafe_allow_html=True)
|
| 199 |
+
|
| 200 |
+
# Affichage des moyennes mobiles
|
| 201 |
if show_sma:
|
| 202 |
fig_sma = go.Figure()
|
| 203 |
+
fig_sma.add_trace(go.Scatter(x=data.index, y=data['Close'], mode='lines', name='Close'))
|
| 204 |
+
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA20'], mode='lines', name='SMA20'))
|
| 205 |
+
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA50'], mode='lines', name='SMA50'))
|
| 206 |
+
fig_sma.add_trace(go.Scatter(x=data.index, y=data['SMA200'], mode='lines', name='SMA200'))
|
| 207 |
+
fig_sma.update_layout(title="Moyennes Mobiles", template="plotly_white")
|
| 208 |
st.plotly_chart(fig_sma, use_container_width=True)
|
| 209 |
|
| 210 |
+
# RSI
|
| 211 |
if show_rsi:
|
| 212 |
fig_rsi = go.Figure()
|
| 213 |
+
fig_rsi.add_trace(go.Scatter(x=data.index, y=data['RSI'], mode='lines', name='RSI', line=dict(color='orange')))
|
| 214 |
+
fig_rsi.add_hline(y=70, line_dash='dash', line_color='red')
|
| 215 |
+
fig_rsi.add_hline(y=30, line_dash='dash', line_color='green')
|
| 216 |
+
fig_rsi.update_layout(title="RSI (Relative Strength Index)", yaxis_title="RSI", template="plotly_white")
|
| 217 |
st.plotly_chart(fig_rsi, use_container_width=True)
|
| 218 |
|
| 219 |
+
# MACD
|
| 220 |
if show_macd:
|
| 221 |
fig_macd = go.Figure()
|
| 222 |
+
fig_macd.add_trace(go.Scatter(x=data.index, y=data['MACD'], mode='lines', name='MACD'))
|
| 223 |
+
fig_macd.add_trace(go.Scatter(x=data.index, y=data['Signal'], mode='lines', name='Signal'))
|
| 224 |
+
fig_macd.update_layout(title="MACD", yaxis_title="Valeur", template="plotly_white")
|
| 225 |
st.plotly_chart(fig_macd, use_container_width=True)
|
| 226 |
|
| 227 |
+
# Affichage des données
|
| 228 |
if show_table:
|
| 229 |
+
st.subheader("🧾 Valeurs Prédites")
|
| 230 |
+
st.dataframe(pred_df.style.format("{:.2f}"))
|
| 231 |
|
| 232 |
# Statistiques
|
| 233 |
+
st.sidebar.markdown("## 📊 Statistiques")
|
| 234 |
+
last_close = float(data['Close'].iloc[-1])
|
| 235 |
+
last_pred = float(pred_df.iloc[-1, 0])
|
| 236 |
+
if isinstance(last_close, pd.Series):
|
| 237 |
+
last_close = last_close.values[0]
|
| 238 |
+
st.sidebar.metric("Prix actuel", f"{float(last_close):.2f} $")
|
| 239 |
+
|
| 240 |
+
st.sidebar.metric("Prix prédit (dernier)", f"{pred_df.iloc[-1, 0]:.2f} $")
|
| 241 |
+
variation = last_pred - last_close
|
| 242 |
+
percent = (variation / last_close) * 100
|
| 243 |
+
st.sidebar.metric("Variation", f"{variation:.2f} $", delta=f"{percent:.2f} %")
|
| 244 |
+
|
| 245 |
+
# Bouton de téléchargement
|
| 246 |
if download_csv:
|
|
|
|
| 247 |
st.download_button(
|
| 248 |
+
label="📥 Télécharger les prédictions CSV",
|
| 249 |
+
data=pred_df.to_csv().encode('utf-8'),
|
| 250 |
+
file_name='predictions_msft.csv',
|
| 251 |
+
mime='text/csv',
|
|
|
|
| 252 |
)
|
| 253 |
|
| 254 |
+
# Footer
|
| 255 |
st.markdown("""
|
| 256 |
+
<hr style='margin-top: 51px;'>
|
| 257 |
+
<div style='text-align: center;'>
|
| 258 |
+
Développé par <strong>Ntonga Babong Lewis</strong> - 2025 ⓒ
|
|
|
|
| 259 |
</div>
|
| 260 |
""", unsafe_allow_html=True)
|