diff --git a/tools/finlab/dashboard/server.py b/tools/finlab/dashboard/server.py index 1fd6f29..17ab647 100644 --- a/tools/finlab/dashboard/server.py +++ b/tools/finlab/dashboard/server.py @@ -94,6 +94,24 @@ def api_scan(theme: str = "all", target: float = 5.0, bullish: bool = False): return {"theme": theme, "target": target, "rows": _records(df)} +# Couches de la chaîne de valeur IA/datacenter, de l'électron au logiciel. +LAYER_ORDER = ["energy_power", "chips", "datacenter_infra", "cables_optical_network", "software_cloud"] + + +@app.get("/api/layers") +def api_layers(target: float = 5.0): + """Watchlists regroupées par couche de la chaîne IA, avec biais/signal par titre.""" + themes = yaml.safe_load(open(scanner.WATCHLISTS_FILE, encoding="utf-8"))["themes"] + order = LAYER_ORDER + [k for k in themes if k not in LAYER_ORDER] + layers = [] + for key in order: + if key not in themes: + continue + df = scanner.scan(themes[key], target) + layers.append({"key": key, "tickers": _records(df)}) + return {"layers": layers} + + @app.get("/api/ohlc") def api_ohlc(ticker: str, period: str = "6mo"): df = data.history(ticker, period=period) diff --git a/tools/finlab/dashboard/static/index.html b/tools/finlab/dashboard/static/index.html index 2bc716c..3e12fd3 100644 --- a/tools/finlab/dashboard/static/index.html +++ b/tools/finlab/dashboard/static/index.html @@ -60,6 +60,23 @@ .modal pre{background:var(--bg);padding:12px;border-radius:8px;overflow:auto;white-space:pre-wrap;font-size:13px;color:var(--txt)} .err{color:var(--down);font-size:12px} .loading{color:var(--mut);font-size:12px;padding:8px} + /* Console embarquée */ + #consoleFrame{width:100%;height:340px;border:0;border-radius:8px;background:#0b0e14} + /* Chaîne de valeur IA */ + .layers-wrap{padding:0 12px 16px} + .layers{display:grid;grid-template-columns:repeat(5,1fr);gap:10px} + .layer-card{background:var(--panel2);border:1px solid var(--line);border-radius:10px;padding:10px;position:relative} + .layer-card:not(:last-child)::after{content:"→";position:absolute;right:-9px;top:50%;transform:translateY(-50%);color:var(--mut);font-size:14px;z-index:1} + .layer-h{display:flex;gap:8px;align-items:center;margin-bottom:9px} + .layer-ic{font-size:20px;line-height:1} + .layer-h b{color:#fff} + .lchips{display:flex;flex-wrap:wrap;gap:5px} + .lchip{background:#0e131d;border:1px solid var(--line);border-radius:6px;padding:3px 7px;font-size:12px;cursor:pointer;font-weight:600} + .lchip:hover{border-color:var(--acc);color:#fff} + .lchip.up{border-color:var(--up);color:#7fdfd0} + .lchip.down{border-color:var(--down);color:#f0a08c} + .lchip.sel{outline:1px solid var(--acc)} + @media(max-width:1100px){.layers{grid-template-columns:repeat(2,1fr)} .layer-card::after{display:none}}
@@ -116,6 +133,16 @@ + +