Spaces:
Sleeping
Sleeping
| import dash | |
| from dash import html, dcc, Input, Output | |
| import yfinance as yf | |
| import os | |
| # ========================= | |
| # PATH | |
| # ========================= | |
| BASE_DIR = os.path.dirname(__file__) | |
| # ========================= | |
| # INIT APP | |
| # ========================= | |
| app = dash.Dash( | |
| __name__, | |
| use_pages=True, | |
| pages_folder=os.path.join(BASE_DIR, "pages"), | |
| suppress_callback_exceptions=True, | |
| external_stylesheets=[ | |
| "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" | |
| ] | |
| ) | |
| server = app.server # 🔥 IMPORTANT pour Hugging Face | |
| app.title = "TradeLux - Plateforme de Trading" | |
| # ========================= | |
| # TICKERS | |
| # ========================= | |
| TICKERS = { | |
| "BTC-USD": "BTC/USD", | |
| "ETH-USD": "ETH/USD", | |
| "^IXIC": "NASDAQ", | |
| "AAPL": "AAPL", | |
| "GOOGL": "GOOGL" | |
| } | |
| # ========================= | |
| # FETCH DATA | |
| # ========================= | |
| def fetch_ticker_data(): | |
| data = [] | |
| for symbol, label in TICKERS.items(): | |
| try: | |
| stock = yf.Ticker(symbol) | |
| hist = stock.history(period="1d", interval="1m") | |
| if len(hist) >= 2: | |
| current = hist['Close'].iloc[-1] | |
| prev = hist['Close'].iloc[-2] | |
| change = (current - prev) / prev * 100 | |
| change_class = "up" if change > 0 else "down" | |
| data.append({ | |
| "label": label, | |
| "value": f"{current:,.2f}", | |
| "change": f"{change:+.2f}%", | |
| "class": change_class | |
| }) | |
| else: | |
| data.append({ | |
| "label": label, | |
| "value": "N/A", | |
| "change": "0.00%", | |
| "class": "down" | |
| }) | |
| except Exception as e: | |
| print("Ticker error:", e) | |
| data.append({ | |
| "label": label, | |
| "value": "ERR", | |
| "change": "0.00%", | |
| "class": "down" | |
| }) | |
| return data | |
| # ========================= | |
| # BACKGROUND SAFE (IMPORTANT) | |
| # ========================= | |
| background = html.Div( | |
| [ | |
| html.Div(className="trade-bg"), | |
| html.Div(className="grid-lines"), | |
| html.Div( | |
| [html.Div(className="particle") for _ in range(40)], | |
| style={ | |
| "position": "fixed", | |
| "zIndex": -10, | |
| "pointerEvents": "none" | |
| } | |
| ) | |
| ] | |
| ) | |
| # ========================= | |
| # NAVBAR | |
| # ========================= | |
| navbar = html.Div( | |
| className="navbar", | |
| children=[ | |
| html.Div( | |
| className="navbar-left", | |
| children=[ | |
| html.Img(src="/assets/logo.png", className="logo") | |
| ] | |
| ), | |
| html.Div( | |
| className="nav-links", | |
| children=[ | |
| dcc.Link("Accueil", href="/", className="nav-link"), | |
| dcc.Link("Marchés", href="/actions", className="nav-link"), | |
| dcc.Link("Analyse", href="/data", className="nav-link"), | |
| ] | |
| ), | |
| ] | |
| ) | |
| # ========================= | |
| # LAYOUT | |
| # ========================= | |
| app.layout = html.Div([ | |
| # Background FIXED (non bloquant) | |
| background, | |
| # Ticker dynamique | |
| html.Div(id="ticker-container"), | |
| # Navbar | |
| navbar, | |
| # Content | |
| html.Div( | |
| className="page-content", | |
| children=[ | |
| dash.page_container | |
| ], | |
| style={"zIndex": 1} # 🔥 FIX CLICK | |
| ), | |
| # Interval | |
| dcc.Interval(id="interval", interval=60000, n_intervals=0) | |
| ]) | |
| # ========================= | |
| # CALLBACK TICKER (FIX STRUCTURE) | |
| # ========================= | |
| def update_ticker(n): | |
| data = fetch_ticker_data() | |
| items = [ | |
| html.Div(className="ticker-item", children=[ | |
| html.Span(d["label"]), | |
| html.Span(d["value"], className="ticker-value"), | |
| html.Span(d["change"], className=f"ticker-change {d['class']}") | |
| ]) | |
| for d in data | |
| ] | |
| return html.Div( | |
| className="ticker-wrap", | |
| children=[ | |
| html.Div( | |
| className="ticker-inner", | |
| children=[ | |
| html.Div(className="ticker-set", children=items), | |
| html.Div(className="ticker-set", children=items) # duplication scroll | |
| ] | |
| ) | |
| ] | |
| ) | |
| # ========================= | |
| # RUN | |
| # ========================= | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860, debug=True) |