Spaces:
Build error
Build error
| import streamlit as st | |
| import pandas as pd | |
| import yfinance as yf | |
| import plotly.express as px | |
| import os | |
| from datetime import datetime, timedelta | |
| # ✅ Ceci doit être le premier appel Streamlit | |
| st.set_page_config(layout="wide", page_title="📈 Euronext Growth - Analyse Interactive") | |
| # --- Constantes | |
| #CACHE_FILE = "cac40_data.json" | |
| CACHE_FILE = os.path.join("/tmp", "euronext_growth_data.json") | |
| CACHE_DURATION_HOURS = 1 | |
| #TICKERS = [ | |
| # "ALNEV.PA", "ALSRS.PA", "ALNOV.PA", "ALTD.PA", "ALTBG.PA", "ALBOO.PA", "ALNRG.PA", "ALHG.PA", | |
| # "ALSPW.PA", "ALCBI.PA", "ALTAO.PA", "ALARF.PA", "ALADO.PA", "ALAFY.PA", "ALAGP.PA", "ALGR.PA", | |
| # "ALCHI.PA" | |
| #] | |
| TICKERS = [ | |
| "ALNEV.PA", "ALSRS.PA", "ALNOV.PA", "ALTD.PA", "ALTBG.PA", "ALBOO.PA", "ALNRG.PA", "ALHG.PA", | |
| "ALSPW.PA", "ALCBI.PA", "ALTAO.PA", "ALARF.PA", "ALADO.PA" | |
| ] | |
| # --- Fonctions cache / data | |
| def is_cache_valid(path, duration_hours): | |
| if not os.path.exists(path): | |
| return False | |
| mtime = datetime.fromtimestamp(os.path.getmtime(path)) | |
| return datetime.now() - mtime < timedelta(hours=duration_hours) | |
| def load_cached_data(path): | |
| return pd.read_json(path) | |
| def save_data_to_cache(df, path): | |
| df.to_json(path, orient="records", indent=2) | |
| def fetch_cac40_data(): | |
| data = [] | |
| for ticker in TICKERS: | |
| stock = yf.Ticker(ticker) | |
| info = stock.info | |
| try: | |
| data.append({ | |
| "Name": info.get("shortName", ticker), | |
| "Price Change (%)": round(info.get("regularMarketChangePercent", 0), 2), | |
| "Volume": info.get("regularMarketVolume", 0), | |
| "Nb shares": info.get("sharesOutstanding", 0), | |
| "Price": round(info.get("regularMarketPrice", 0), 2), | |
| "Sector": info.get("industry", "N/A"), | |
| "Effectif": info.get("fullTimeEmployees", 0) | |
| }) | |
| except: | |
| continue | |
| return pd.DataFrame(data) | |
| def get_data(): | |
| if is_cache_valid(CACHE_FILE, CACHE_DURATION_HOURS): | |
| return load_cached_data(CACHE_FILE) | |
| else: | |
| df = fetch_cac40_data() | |
| save_data_to_cache(df, CACHE_FILE) | |
| return df | |
| # --- Streamlit UI | |
| st.title("📈 Euronext Growth : Capitalisations, secteurs et variations en un coup d'œil") | |
| st.markdown("Affichage des sociétés du CAC 40 avec variation de prix et capitalisation boursière.") | |
| df = get_data() | |
| df["Market Cap (B eur)"] = round(df["Nb shares"] * df["Price"] / 1e9, 2) | |
| df["return_ratio_text_info"] = df["Price Change (%)"].apply(lambda x: f"{x:+.2f}") | |
| df["Root"] = "📊 Euronext Growth" | |
| fig = px.treemap( | |
| df, | |
| path=["Root", "Sector", "Name"], | |
| values="Market Cap (B eur)", | |
| color="Price Change (%)", | |
| color_continuous_scale=px.colors.diverging.RdYlGn, | |
| color_continuous_midpoint=0, | |
| custom_data=[ | |
| "Price", "return_ratio_text_info", "Volume", "Nb shares", | |
| "Market Cap (B eur)", "Sector", "Effectif" | |
| ], | |
| width=1200, | |
| height=700 | |
| ) | |
| fig.update_traces( | |
| root_color="#f0f0f0", | |
| textposition="middle center", | |
| texttemplate="<b>%{label}</b><br>%{customdata[1]}%", | |
| hovertemplate="<b>%{label}</b><br>" + | |
| "Secteur : %{customdata[5]}<br>" + | |
| "Cours : %{customdata[0]:.2f} €<br>" + | |
| "Variation : %{customdata[1]}%<br>" + | |
| "Volume : %{customdata[2]:,}<br>" + | |
| "Capitalisation : %{customdata[4]:.2f} Mds €<br>" + | |
| "Effectif : %{customdata[6]:,}" | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) |