From e311d00fe4ded3664a197da73d9bd7b98ba9837d Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Sat, 13 Jun 2026 21:35:15 +0000 Subject: [PATCH] fix(dashboard): tooltip equity allineato al mouse (responsive canvas) Forzare l'altezza del canvas via CSS !important con maintainAspectRatio default sfalsava la risoluzione interna vs quella mostrata -> hit-test tooltip offset dal mouse. Fix canonico: canvas in contenitore .chartbox ad altezza fissa + chart responsive:true/maintainAspectRatio:false + interaction index su tutti e 3 i grafici (equity, modal, paper). Co-Authored-By: Claude Fable 5 --- src/live/dashboard.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/live/dashboard.py b/src/live/dashboard.py index d11ff8d..1b01367 100644 --- a/src/live/dashboard.py +++ b/src/live/dashboard.py @@ -386,7 +386,10 @@ HTML = r""" .ov.on{display:flex}.modal{background:var(--card);border:1px solid var(--bd);border-radius:12px;max-width:820px;width:94%;margin:40px 0;padding:20px} .modal h3{margin:0 0 4px}.x{float:right;cursor:pointer;color:var(--mut);font-size:20px;line-height:1} .btn{display:inline-block;background:#1f6feb;color:#fff;padding:7px 12px;border-radius:7px;text-decoration:none;font-size:13px;margin-top:10px} - canvas{width:100%!important;height:220px!important}#mchart{height:240px!important}#eq{height:300px!important}#peq{height:200px!important} + /* contenitore ad altezza fissa + canvas responsive: l'hit-test del tooltip combacia + col mouse (forzare l'altezza del canvas via CSS sfalsa la risoluzione interna) */ + .chartbox{position:relative;width:100%}.chartbox canvas{width:100%!important;height:100%!important} + .chartbox.eq{height:300px}.chartbox.peq{height:200px}.chartbox.m{height:240px} .paperzone{margin-top:22px;border:1px dashed #6e552233;border-radius:12px;padding:14px;background:linear-gradient(180deg,rgba(210,153,34,.05),transparent)} #eqcard{background:linear-gradient(180deg,#11161d,#0d1117)} .eqhead{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:6px} @@ -397,7 +400,7 @@ HTML = r"""

Equity

-
+

PnL per strategia REALE (realizzato, netto fee)

Trade attivi — stato in tempo reale

Trade chiusi (ultimi 50)

@@ -406,7 +409,7 @@ HTML = r"""

📄 Area PAPER — multi-asset (solo statistica, fuori dal pool reale)

TR01 / ROT02 / TSM01 / XS01 girano con capitale nozionale fisso per valutarne l'edge in vista di un'esecuzione reale futura (bloccata dal capitale a €2k). Equity e PnL separati dal portafoglio reale.
-
+
@@ -419,7 +422,7 @@ HTML = r"""

PnL cumulato (reale vs sim)

-
+
📊 Scheda dettagliata con grafici della strategia

Trade di questa strategia

@@ -451,7 +454,7 @@ function renderChart(curve,init){const ctx=document.getElementById('eq'); chart=new Chart(ctx,{type:'line',data:{labels,datasets:[{data,borderColor:`rgb(${col})`, backgroundColor:grad,fill:true,pointRadius:0,pointHoverRadius:5,pointHoverBackgroundColor:'#fff', borderWidth:2.5,tension:.25}]}, - options:{interaction:{mode:'index',intersect:false},plugins:{legend:{display:false}, + options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},plugins:{legend:{display:false}, tooltip:{backgroundColor:'#161b22',borderColor:'#30363d',borderWidth:1,titleColor:'#8b949e', bodyColor:'#c9d1d9',padding:10,displayColors:false, callbacks:{label:c=>'€'+c.parsed.y.toFixed(2)+' ('+(c.parsed.y-init>=0?'+':'')+(c.parsed.y-init).toFixed(2)+'€)'}}}, @@ -492,7 +495,7 @@ async function openModal(wid){const ov=document.getElementById('ov');ov.classLis data:{labels:lab,datasets:[ {label:'reale',data:c.map(x=>x.real),borderColor:'#3fb950',backgroundColor:'rgba(63,185,80,.08)',fill:true,pointRadius:0,borderWidth:2,tension:.15}, {label:'sim',data:c.map(x=>x.sim),borderColor:'#8b949e',borderDash:[5,4],pointRadius:0,borderWidth:1.5,tension:.15}]}, - options:{plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#8b949e',maxTicksLimit:7},grid:{display:false}},y:{ticks:{color:'#8b949e'},grid:{color:'#21262d'}}}}});} + options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},plugins:{legend:{labels:{color:'#c9d1d9'}}},scales:{x:{ticks:{color:'#8b949e',maxTicksLimit:7},grid:{display:false}},y:{ticks:{color:'#8b949e'},grid:{color:'#21262d'}}}}});} // trade const mt=document.getElementById('mtrades'); if(!d.trades.length){mt.innerHTML='
nessun trade
';} @@ -530,7 +533,7 @@ function renderPaper(p){ peqchart=new Chart(ctx,{type:'line',data:{labels:lab.length?lab:['inizio'],datasets:[{ data:data.length?data:[p.init],borderColor:'#d29922',backgroundColor:'rgba(210,153,34,.12)', fill:true,pointRadius:c.length<30?3:0,borderWidth:2,tension:.2}]}, - options:{plugins:{legend:{display:false},tooltip:{callbacks:{label:x=>'€'+x.parsed.y.toFixed(2)}}}, + options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},plugins:{legend:{display:false},tooltip:{callbacks:{label:x=>'€'+x.parsed.y.toFixed(2)}}}, scales:{x:{ticks:{color:'#6e7681',maxTicksLimit:7,font:{size:10}},grid:{display:false}}, y:{ticks:{color:'#6e7681',callback:v=>'€'+v},grid:{color:'rgba(48,54,61,.5)'}}}}});} function renderDescs(d){const wrap=document.getElementById('descs');if(!wrap)return;wrap.innerHTML='';