feat(dashboard): trade attivi con data/ora ingresso + tempo in posizione
Nuove colonne 'Ingresso (UTC)' (entry_time) e 'In posizione' (durata dall'ingresso, formattata m/h/g) con flag stantio. Sostituisce la colonna Eta (staleness) col tempo di holding effettivo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+17
-3
@@ -213,6 +213,14 @@ def build_state() -> dict:
|
||||
# SEMPRE prezzi REALI (entry reale di esecuzione vs mark reale USDC): il feed
|
||||
# di decisione SIM (testnet inverse) è dislocato e non va mostrato come prezzo.
|
||||
executed = st.get("real_in_position")
|
||||
et = st.get("entry_time", "")
|
||||
held = None
|
||||
if et:
|
||||
try:
|
||||
t0 = datetime.fromisoformat(et.replace("Z", "+00:00"))
|
||||
held = round((now - t0).total_seconds() / 60.0, 1)
|
||||
except Exception:
|
||||
held = None
|
||||
row = {
|
||||
"id": _short(wid), "family": _family_of(wid),
|
||||
"dir": "LONG" if st.get("direction", 0) > 0 else "SHORT",
|
||||
@@ -220,6 +228,7 @@ def build_state() -> dict:
|
||||
"tp": st.get("tp", 0.0), "age_min": round(_age_min(sp), 1),
|
||||
"stale": _age_min(sp) > 15, "pair": is_pair,
|
||||
"real": bool(executed),
|
||||
"entry_time": et, "held_min": held,
|
||||
}
|
||||
if is_pair:
|
||||
a_, b_ = wid.split("__")[1].split("_") # es. ETH_SOL
|
||||
@@ -605,16 +614,21 @@ function renderDescs(d){const wrap=document.getElementById('descs');if(!wrap)ret
|
||||
wrap.append(box);}
|
||||
function renderActive(a){const wrap=document.getElementById('active');wrap.innerHTML='';
|
||||
if(!a.length){wrap.innerHTML='<div class=mut>nessuna posizione aperta</div>';return;}
|
||||
const t=E('table');t.innerHTML='<tr><th>Strategia</th><th>Lato</th><th>Entry reale</th><th>Mercato reale</th><th>PnL non realizz.</th><th>Barre</th><th>→TP%</th><th>Età</th></tr>';
|
||||
const t=E('table');t.innerHTML='<tr><th>Strategia</th><th>Lato</th><th>Ingresso (UTC)</th><th>In posizione</th><th>Entry reale</th><th>Mercato reale</th><th>PnL non realizz.</th><th>Barre</th><th>→TP%</th></tr>';
|
||||
a.forEach(p=>{const tr=E('tr');
|
||||
const side=`<span class="pill ${p.dir=='LONG'?'long':'short'}">${p.dir}${p.pair?' ratio':''}</span>`;
|
||||
const ue=p.unreal_eur!=null?`<span class="${cls(p.unreal_eur)}">${eur(p.unreal_eur)}${p.unreal_pct!=null?' ('+p.unreal_pct+'%)':''}</span>`:'<span class=mut>—</span>';
|
||||
const mkt=p.pair?(p.mark!=null?`${p.mark} / ${p.mark_b}`:'—'):(p.mark||'—');
|
||||
const ing=p.entry_time?p.entry_time.slice(5,16).replace('T',' '):'—';
|
||||
const held=fmtDur(p.held_min);
|
||||
tr.innerHTML=`<td>${p.id}${p.real?'':' <span class=tag>sim</span>'}${p.pair?' <span class=tag>'+p.leg_a+'/'+p.leg_b+'</span>':''}</td><td>${side}</td>
|
||||
<td class=mut>${ing}</td><td class="${p.stale?'stale':''}">${held}${p.stale?' ⚠':''}</td>
|
||||
<td>${p.entry}</td><td>${mkt}</td><td>${ue}</td>
|
||||
<td>${p.bars}/${p.max_bars||'∞'}</td><td class=mut>${p.to_tp_pct!=null?p.to_tp_pct:'—'}</td>
|
||||
<td class="${p.stale?'stale':'mut'}">${p.age_min}m${p.stale?' ⚠':''}</td>`;
|
||||
<td>${p.bars}/${p.max_bars||'∞'}</td><td class=mut>${p.to_tp_pct!=null?p.to_tp_pct:'—'}</td>`;
|
||||
t.append(tr);});wrap.append(t);}
|
||||
function fmtDur(m){if(m==null)return '—';m=Math.round(m);if(m<60)return m+'m';
|
||||
const h=Math.floor(m/60),mm=m%60;if(h<24)return h+'h '+mm+'m';
|
||||
const d=Math.floor(h/24);return d+'g '+(h%24)+'h';}
|
||||
function renderClosed(c){const wrap=document.getElementById('closed');wrap.innerHTML='';
|
||||
if(!c.length){wrap.innerHTML='<div class=mut>nessun trade chiuso</div>';return;}
|
||||
const t=E('table');t.innerHTML='<tr><th>Ora (UTC)</th><th>Strategia</th><th>Motivo</th><th>PnL</th><th>sim</th><th>esito</th></tr>';
|
||||
|
||||
Reference in New Issue
Block a user