mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 05:24:43 +02:00
feat(finlab): console embarquée sous le graphique + vue chaîne de valeur IA
- Dashboard : la console Claude Code (/console/) est désormais embarquée en iframe sous le graphique (boutons reconnecter / plein écran), en plus du lien topbar - Nouvelle vue pleine largeur « Chaîne de valeur IA — par couche » : regroupe les watchlists par couche de l'électron au logiciel (⚡ énergie → 🔧 puces → 🏢 datacenter → 🔌 câbles/optique → ☁️ software), tickers cliquables colorés par biais (haussier/baissier), tooltip cours/RSI/MACD - Backend : endpoint /api/layers (scan groupé par thème, ordre des couches) Vérifié au navigateur (Playwright, instance locale) : layout complet OK — portefeuille + graphique chandeliers MM50/200 + console embarquée + alertes + 5 cartes de couches colorées. Endpoints et JS validés. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
4c61758899
commit
b704860227
2 changed files with 99 additions and 1 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -116,6 +133,16 @@
|
|||
<button class="btn" onclick="loadAlerts()">🔔 Recharger alertes</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Console IA embarquée, sous le graphique -->
|
||||
<div class="panel">
|
||||
<h3>Console IA
|
||||
<span>
|
||||
<button class="btn perbtn" onclick="document.getElementById('consoleFrame').src='/console/'">↻ reconnecter</button>
|
||||
<a class="btn perbtn" href="/console/" target="_blank">plein écran ↗</a>
|
||||
</span>
|
||||
</h3>
|
||||
<iframe id="consoleFrame" src="/console/" title="Console Claude Code"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Colonne droite : alertes -->
|
||||
|
|
@ -132,6 +159,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chaîne de valeur IA — pleine largeur -->
|
||||
<div class="layers-wrap">
|
||||
<div class="panel">
|
||||
<h3>Chaîne de valeur IA — par couche
|
||||
<span class="muted" style="text-transform:none;letter-spacing:0">de l'électron au logiciel ⚡→🔧→🏢→🔌→☁️ ·
|
||||
<span class="pos">haussier</span> / <span class="neg">baissier</span> · clic = graphique</span>
|
||||
</h3>
|
||||
<div class="layers" id="layers"><div class="loading">Analyse des couches…</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal plan de trade -->
|
||||
<div class="modal" id="planModal" onclick="if(event.target===this)this.classList.remove('on')">
|
||||
<div class="box">
|
||||
|
|
@ -318,12 +356,54 @@ async function runPlan(){
|
|||
}catch(e){ out.textContent='Erreur: '+e.message; }
|
||||
}
|
||||
|
||||
// ── Chaîne de valeur IA (couches) ──────────────────────────
|
||||
const LAYER_META = {
|
||||
energy_power: {ic:'⚡', lb:'Énergie', ds:'alimente les datacenters'},
|
||||
chips: {ic:'🔧', lb:'Puces / Semi', ds:'le calcul'},
|
||||
datacenter_infra: {ic:'🏢', lb:'Datacenter / Infra', ds:'serveurs · refroidissement'},
|
||||
cables_optical_network: {ic:'🔌', lb:'Câbles / Optique', ds:'interconnexion'},
|
||||
software_cloud: {ic:'☁️', lb:'Software / Cloud', ds:'la couche logicielle'},
|
||||
};
|
||||
const LAYER_ORDER = ['energy_power','chips','datacenter_infra','cables_optical_network','software_cloud'];
|
||||
function biasCls(b){
|
||||
if(!b) return '';
|
||||
if(b.includes('haussier')||b.includes('survendu')) return 'up';
|
||||
if(b.includes('baissier')||b.includes('surachat')) return 'down';
|
||||
return '';
|
||||
}
|
||||
async function loadLayers(){
|
||||
const wrap=document.getElementById('layers');
|
||||
// 1) structure instantanée depuis /api/themes
|
||||
let t;
|
||||
try{ t=(await api('/api/themes')).themes; }
|
||||
catch(e){ wrap.innerHTML='<div class="err">'+e.message+'</div>'; return; }
|
||||
const order=LAYER_ORDER.filter(k=>t[k]).concat(Object.keys(t).filter(k=>!LAYER_ORDER.includes(k)));
|
||||
wrap.innerHTML=order.map(k=>{
|
||||
const m=LAYER_META[k]||{ic:'•',lb:k,ds:''};
|
||||
const chips=(t[k]||[]).map(tk=>`<span class="lchip" data-tk="${tk}" onclick="loadChart('${tk}')">${tk}</span>`).join('');
|
||||
return `<div class="layer-card" data-layer="${k}">
|
||||
<div class="layer-h"><span class="layer-ic">${m.ic}</span>
|
||||
<div><b>${m.lb}</b><div class="muted" style="font-size:11px">${m.ds}</div></div></div>
|
||||
<div class="lchips">${chips}</div></div>`;
|
||||
}).join('');
|
||||
// 2) enrichissement biais (scan de toutes les couches — peut être un peu long, caché ensuite)
|
||||
try{
|
||||
const d=await api('/api/layers');
|
||||
d.layers.forEach(L=>(L.tickers||[]).forEach(r=>{
|
||||
const el=document.querySelector(`.layer-card[data-layer="${L.key}"] .lchip[data-tk="${r.ticker}"]`);
|
||||
if(el){ const c=biasCls(r.biais); if(c) el.classList.add(c);
|
||||
el.title=`${r.ticker} · ${r.cours??'—'} · RSI ${r.RSI??'—'} · MACD ${r.MACD??''} · ${r.biais??''}`; }
|
||||
}));
|
||||
}catch(e){ /* on garde la structure non colorée */ }
|
||||
}
|
||||
|
||||
// ── Init ───────────────────────────────────────────────────
|
||||
function refreshAll(){ loadPortfolio(); loadScan(); loadAlerts(); if(SEL) loadChart(SEL); }
|
||||
function refreshAll(){ loadPortfolio(); loadScan(); loadAlerts(); loadLayers(); if(SEL) loadChart(SEL); }
|
||||
initChart();
|
||||
loadPortfolio();
|
||||
loadThemes();
|
||||
loadAlerts();
|
||||
loadLayers();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue