Spaces:
Running
Running
Update hub_dashboard.html
Browse files- hub_dashboard.html +40 -22
hub_dashboard.html
CHANGED
|
@@ -2820,6 +2820,24 @@ const f4 = v => (v==null)?'β':Number(v).toFixed(4); const fPct = v => (v==null
|
|
| 2820 |
const ago = ts => { if(!ts) return 'β'; const s=Math.round(Date.now()/1000-ts); return s<60?s+'s ago':s<3600?Math.floor(s/60)+'m ago':Math.floor(s/3600)+'h ago'; };
|
| 2821 |
const stxt = r => r.signal_confidence>.7 ? '<span style="color:var(--green);font-weight:700;text-shadow:0 0 8px rgba(0,223,138,0.4)">Engaged</span>' : '<span style="color:var(--t3);font-weight:600">Standby</span>';
|
| 2822 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2823 |
function scoreBar(s) {
|
| 2824 |
const v=parseFloat(s)||0; const p=Math.min(Math.abs(v)*100,100).toFixed(1);
|
| 2825 |
const c=v>.05?'#00df8a':v<-.05?'#ff3d5a':'#00c8ff';
|
|
@@ -2840,7 +2858,7 @@ function renderTable(){
|
|
| 2840 |
return `<tr class="${sel} ${rcls}" onclick="onRow('${r.space_name}',this)">
|
| 2841 |
<td class="rc">${rk}</td><td class="nc">${r.space_name}</td><td>${scoreBar(r.score)}</td>
|
| 2842 |
<td class="num">${fPct(r.signal_confidence)}</td><td class="num">${fPct(r.avn_accuracy)}</td>
|
| 2843 |
-
<td><span class="sig ${r
|
| 2844 |
<td class="num">${(r.training_steps||0).toLocaleString()}</td><td class="num">${f4(r.actor_loss)}</td>
|
| 2845 |
<td>${stxt(r)}</td></tr>`;
|
| 2846 |
}).join('');
|
|
@@ -2868,7 +2886,7 @@ function onRow(name,el){
|
|
| 2868 |
<div class="dr"><span class="dk">Model Core</span><span class="dv">QUASAR-X1</span></div>
|
| 2869 |
</div>
|
| 2870 |
<div><div class="dc-title">Ensemble Consensus</div>
|
| 2871 |
-
<div class="dr"><span class="dk">Inference Signal</span><span class="dv ${(r
|
| 2872 |
<div class="dr"><span class="dk">Buy Votes</span><span class="dv g">${r.buy_count}</span></div>
|
| 2873 |
<div class="dr"><span class="dk">Sell Votes</span><span class="dv r">${r.sell_count}</span></div>
|
| 2874 |
</div>`;
|
|
@@ -2878,7 +2896,7 @@ function onRow(name,el){
|
|
| 2878 |
function updateCharts(){
|
| 2879 |
scoreChart.data.labels=_rankings.map(r=>r.space_name);
|
| 2880 |
scoreChart.data.datasets[0].data=_rankings.map(r=>r.score);
|
| 2881 |
-
scoreChart.data.datasets[0].backgroundColor=_rankings.map(r=>{ const sig=r
|
| 2882 |
scoreChart.update();
|
| 2883 |
if(!_rankings.length)return; const top=_rankings[0].space_name; const h=(_hist[top]||[]).slice(-60); if(!h.length)return;
|
| 2884 |
const lb=h.map(p=>new Date(p.ts*1000).toLocaleTimeString('en-GB',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'}));
|
|
@@ -3267,7 +3285,7 @@ const GATE_DEFS = [
|
|
| 3267 |
|
| 3268 |
// Derive per-asset gate status from ranking data
|
| 3269 |
function gateStatus(r) {
|
| 3270 |
-
const A = (r
|
| 3271 |
const B = r.signal_confidence >= 0.55;
|
| 3272 |
const C = Math.abs(r.score) >= 0.10;
|
| 3273 |
const D = r.avn_accuracy >= 0.50;
|
|
@@ -3284,8 +3302,8 @@ function renderTradingTab() {
|
|
| 3284 |
if (!_rankings || !_rankings.length) return;
|
| 3285 |
|
| 3286 |
// ββ KPI Row ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3287 |
-
const buys = _rankings.filter(r => (r
|
| 3288 |
-
const sells = _rankings.filter(r => (r
|
| 3289 |
document.getElementById('t-kpi-buys').textContent = buys.length;
|
| 3290 |
document.getElementById('t-kpi-sells').textContent = sells.length;
|
| 3291 |
|
|
@@ -3314,11 +3332,11 @@ function renderTradingTab() {
|
|
| 3314 |
const allPass = allGatesPass(gates);
|
| 3315 |
const gateIcon = allPass ? '<span style="color:var(--green);font-weight:800">β CLEAR</span>'
|
| 3316 |
: '<span style="color:var(--amber);font-weight:700">β GATED</span>';
|
| 3317 |
-
const sigClass = (r
|
| 3318 |
return `<tr>
|
| 3319 |
<td class="rc ${i===0?'r1':''}">${r.rank}</td>
|
| 3320 |
<td class="nc">${r.space_name}</td>
|
| 3321 |
-
<td><span class="sig ${sigClass}">${r
|
| 3322 |
<td class="num">${fPct(r.signal_confidence)}</td>
|
| 3323 |
<td class="num" style="color:var(--green)">${r.buy_count}</td>
|
| 3324 |
<td class="num" style="color:var(--red)">${r.sell_count}</td>
|
|
@@ -3348,7 +3366,7 @@ function renderTradingTab() {
|
|
| 3348 |
const gLabel = allP
|
| 3349 |
? '<span class="pos-gate-pass">β ALL CLEAR</span>'
|
| 3350 |
: `<span class="pos-gate-warn">A:${gates.A?'β':'β'} B:${gates.B?'β':'β'} C:${gates.C?'β':'β'} D:${gates.D?'β':'β'} E:${gates.E?'β':'β'}</span>`;
|
| 3351 |
-
const side = r
|
| 3352 |
return `<tr>
|
| 3353 |
<td class="nc">${r.space_name}</td>
|
| 3354 |
<td><span class="sig ${side}">${side}</span></td>
|
|
@@ -3366,13 +3384,13 @@ function renderTradingTab() {
|
|
| 3366 |
const total = r.buy_count + r.sell_count || 1;
|
| 3367 |
const buyPct = (r.buy_count / total * 100).toFixed(0);
|
| 3368 |
const sellPct = (r.sell_count / total * 100).toFixed(0);
|
| 3369 |
-
const sigCls = (r
|
| 3370 |
const tagBg = sigCls === 'BUY' ? 'rgba(0,223,138,0.12)' : sigCls === 'SELL' ? 'rgba(255,61,90,0.12)' : 'rgba(255,255,255,0.07)';
|
| 3371 |
const tagClr = sigCls === 'BUY' ? 'var(--green)' : sigCls === 'SELL' ? 'var(--red)' : 'var(--t2)';
|
| 3372 |
return `<div class="vote-row">
|
| 3373 |
<div class="vote-asset-label">
|
| 3374 |
<span class="vote-asset-name">${r.space_name}</span>
|
| 3375 |
-
<span class="vote-signal-tag" style="background:${tagBg};color:${tagClr};border:1px solid ${tagClr}40">${r
|
| 3376 |
</div>
|
| 3377 |
<div class="vote-bar-track">
|
| 3378 |
<div class="vote-bar-buy" style="width:${buyPct}%"></div>
|
|
@@ -3410,8 +3428,8 @@ function renderTradingTab() {
|
|
| 3410 |
const fb = document.getElementById('trade-feed-body');
|
| 3411 |
const now = Date.now();
|
| 3412 |
fb.innerHTML = _rankings.slice(0, 20).map(r => {
|
| 3413 |
-
const dir = r
|
| 3414 |
-
const dirLabel = r
|
| 3415 |
const ts = r.last_updated ? new Date(r.last_updated * 1000).toLocaleTimeString('en-GB', {hour12:false}) : 'β';
|
| 3416 |
return `<div class="feed-cols row">
|
| 3417 |
<div class="f-info">
|
|
@@ -3498,9 +3516,9 @@ function normalise(vals) {
|
|
| 3498 |
// ββ Summary bar βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3499 |
function renderAssetSummary(assets) {
|
| 3500 |
document.getElementById('as-total').textContent = assets.length;
|
| 3501 |
-
document.getElementById('as-bull').textContent = assets.filter(r => (r
|
| 3502 |
-
document.getElementById('as-bear').textContent = assets.filter(r => (r
|
| 3503 |
-
document.getElementById('as-neut').textContent = assets.filter(r => (r
|
| 3504 |
const accs = assets.filter(r => r.avn_accuracy > 0).map(r => r.avn_accuracy);
|
| 3505 |
document.getElementById('as-avgacc').textContent = accs.length
|
| 3506 |
? fPct(accs.reduce((a,b) => a+b, 0) / accs.length) : 'β';
|
|
@@ -3515,7 +3533,7 @@ function renderAssetCards(assets) {
|
|
| 3515 |
return;
|
| 3516 |
}
|
| 3517 |
grid.innerHTML = assets.map(r => {
|
| 3518 |
-
const sigCls = r
|
| 3519 |
const scoreClr = r.score > 0.1 ? 'var(--green)' : r.score < -0.05 ? 'var(--red)' : 'var(--cyan)';
|
| 3520 |
const total = r.buy_count + r.sell_count || 1;
|
| 3521 |
const buyPct = (r.buy_count / total * 100).toFixed(0);
|
|
@@ -3527,7 +3545,7 @@ function renderAssetCards(assets) {
|
|
| 3527 |
<div class="ac-name">${r.space_name}</div>
|
| 3528 |
<div class="ac-rank">RANK #${r.rank}</div>
|
| 3529 |
</div>
|
| 3530 |
-
<span class="sig ${r
|
| 3531 |
</div>
|
| 3532 |
<div class="ac-score-block">
|
| 3533 |
<div class="ac-score-label">AXRVI Score</div>
|
|
@@ -3572,12 +3590,12 @@ function renderAssetTable(assets) {
|
|
| 3572 |
return;
|
| 3573 |
}
|
| 3574 |
body.innerHTML = assets.map(r => {
|
| 3575 |
-
const sigCls = (r
|
| 3576 |
const lossClr = r.actor_loss < 0.3 ? 'var(--green)' : r.actor_loss > 0.7 ? 'var(--red)' : 'var(--amber)';
|
| 3577 |
return `<tr onclick="openDeepDive('${r.space_name}')" style="cursor:pointer">
|
| 3578 |
<td class="rc ${r.rank===1?'r1':''}">${r.rank}</td>
|
| 3579 |
<td class="nc">${r.space_name}</td>
|
| 3580 |
-
<td style="text-align:right"><span class="sig ${sigCls}">${r
|
| 3581 |
<td class="num" style="color:var(--cyan)">${fPct(r.signal_confidence)}</td>
|
| 3582 |
<td class="num">${fPct(r.avn_accuracy)}</td>
|
| 3583 |
<td class="num" style="color:${r.score>0.1?'var(--green)':r.score<0?'var(--red)':'var(--t1)'}">${f4(r.score)}</td>
|
|
@@ -3650,13 +3668,13 @@ function openDeepDive(name) {
|
|
| 3650 |
dive.style.display = '';
|
| 3651 |
dive.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
| 3652 |
|
| 3653 |
-
const sigCls = r
|
| 3654 |
document.getElementById('dive-stats').innerHTML = `
|
| 3655 |
<div class="dive-block">
|
| 3656 |
<div class="dive-block-title">Neural Output</div>
|
| 3657 |
<div class="dive-row"><span class="dive-k">AXRVI Score</span><span class="dive-v ${r.score > 0.1 ? 'g' : r.score < 0 ? 'r' : 'c'}">${f4(r.score)}</span></div>
|
| 3658 |
<div class="dive-row"><span class="dive-k">Signal Confidence</span><span class="dive-v c">${fPct(r.signal_confidence)}</span></div>
|
| 3659 |
-
<div class="dive-row"><span class="dive-k">Dominant Signal</span><span class="dive-v ${sigCls}">${r
|
| 3660 |
<div class="dive-row"><span class="dive-k">Rank</span><span class="dive-v">#${r.rank}</span></div>
|
| 3661 |
<div class="dive-row"><span class="dive-k">Last Updated</span><span class="dive-v">${ago(r.last_updated)}</span></div>
|
| 3662 |
</div>
|
|
|
|
| 2820 |
const ago = ts => { if(!ts) return 'β'; const s=Math.round(Date.now()/1000-ts); return s<60?s+'s ago':s<3600?Math.floor(s/60)+'m ago':Math.floor(s/3600)+'h ago'; };
|
| 2821 |
const stxt = r => r.signal_confidence>.7 ? '<span style="color:var(--green);font-weight:700;text-shadow:0 0 8px rgba(0,223,138,0.4)">Engaged</span>' : '<span style="color:var(--t3);font-weight:600">Standby</span>';
|
| 2822 |
|
| 2823 |
+
/* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2824 |
+
vecOf(r) β normalize the asset's directional signal for display.
|
| 2825 |
+
The hub's /api/state now emits `flip_direction` (BUY|SELL|NONE). Older
|
| 2826 |
+
builds used `latest_signal` or `dominant_signal`. This helper reads the
|
| 2827 |
+
new field first, falls back to the legacy ones, and maps anything
|
| 2828 |
+
non-directional to 'NEUTRAL' so the CSS classes (.sig.BUY, .sig.SELL,
|
| 2829 |
+
.sig.NEUTRAL) and downstream comparisons work uniformly.
|
| 2830 |
+
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 2831 |
+
function vecOf(r){
|
| 2832 |
+
if(!r) return 'NEUTRAL';
|
| 2833 |
+
const fd = r.flip_direction ? String(r.flip_direction).toUpperCase() : '';
|
| 2834 |
+
if(fd === 'BUY' || fd === 'SELL') return fd;
|
| 2835 |
+
const ls = r.latest_signal || r.dominant_signal;
|
| 2836 |
+
const lu = ls ? String(ls).toUpperCase() : '';
|
| 2837 |
+
if(lu === 'BUY' || lu === 'SELL') return lu;
|
| 2838 |
+
return 'NEUTRAL';
|
| 2839 |
+
}
|
| 2840 |
+
|
| 2841 |
function scoreBar(s) {
|
| 2842 |
const v=parseFloat(s)||0; const p=Math.min(Math.abs(v)*100,100).toFixed(1);
|
| 2843 |
const c=v>.05?'#00df8a':v<-.05?'#ff3d5a':'#00c8ff';
|
|
|
|
| 2858 |
return `<tr class="${sel} ${rcls}" onclick="onRow('${r.space_name}',this)">
|
| 2859 |
<td class="rc">${rk}</td><td class="nc">${r.space_name}</td><td>${scoreBar(r.score)}</td>
|
| 2860 |
<td class="num">${fPct(r.signal_confidence)}</td><td class="num">${fPct(r.avn_accuracy)}</td>
|
| 2861 |
+
<td><span class="sig ${vecOf(r)}">${vecOf(r)}</span></td>
|
| 2862 |
<td class="num">${(r.training_steps||0).toLocaleString()}</td><td class="num">${f4(r.actor_loss)}</td>
|
| 2863 |
<td>${stxt(r)}</td></tr>`;
|
| 2864 |
}).join('');
|
|
|
|
| 2886 |
<div class="dr"><span class="dk">Model Core</span><span class="dv">QUASAR-X1</span></div>
|
| 2887 |
</div>
|
| 2888 |
<div><div class="dc-title">Ensemble Consensus</div>
|
| 2889 |
+
<div class="dr"><span class="dk">Inference Signal</span><span class="dv ${(vecOf(r))==='BUY'?'g':(vecOf(r))==='SELL'?'r':''}">${vecOf(r)}</span></div>
|
| 2890 |
<div class="dr"><span class="dk">Buy Votes</span><span class="dv g">${r.buy_count}</span></div>
|
| 2891 |
<div class="dr"><span class="dk">Sell Votes</span><span class="dv r">${r.sell_count}</span></div>
|
| 2892 |
</div>`;
|
|
|
|
| 2896 |
function updateCharts(){
|
| 2897 |
scoreChart.data.labels=_rankings.map(r=>r.space_name);
|
| 2898 |
scoreChart.data.datasets[0].data=_rankings.map(r=>r.score);
|
| 2899 |
+
scoreChart.data.datasets[0].backgroundColor=_rankings.map(r=>{ const sig=vecOf(r); return sig==='BUY'?'rgba(0,223,138,0.7)': sig==='SELL'?'rgba(255,61,90,0.7)':'rgba(0,200,255,0.5)'; });
|
| 2900 |
scoreChart.update();
|
| 2901 |
if(!_rankings.length)return; const top=_rankings[0].space_name; const h=(_hist[top]||[]).slice(-60); if(!h.length)return;
|
| 2902 |
const lb=h.map(p=>new Date(p.ts*1000).toLocaleTimeString('en-GB',{hour12:false,hour:'2-digit',minute:'2-digit',second:'2-digit'}));
|
|
|
|
| 3285 |
|
| 3286 |
// Derive per-asset gate status from ranking data
|
| 3287 |
function gateStatus(r) {
|
| 3288 |
+
const A = (vecOf(r)) === 'BUY' || (vecOf(r)) === 'SELL';
|
| 3289 |
const B = r.signal_confidence >= 0.55;
|
| 3290 |
const C = Math.abs(r.score) >= 0.10;
|
| 3291 |
const D = r.avn_accuracy >= 0.50;
|
|
|
|
| 3302 |
if (!_rankings || !_rankings.length) return;
|
| 3303 |
|
| 3304 |
// ββ KPI Row ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3305 |
+
const buys = _rankings.filter(r => (vecOf(r)) === 'BUY');
|
| 3306 |
+
const sells = _rankings.filter(r => (vecOf(r)) === 'SELL');
|
| 3307 |
document.getElementById('t-kpi-buys').textContent = buys.length;
|
| 3308 |
document.getElementById('t-kpi-sells').textContent = sells.length;
|
| 3309 |
|
|
|
|
| 3332 |
const allPass = allGatesPass(gates);
|
| 3333 |
const gateIcon = allPass ? '<span style="color:var(--green);font-weight:800">β CLEAR</span>'
|
| 3334 |
: '<span style="color:var(--amber);font-weight:700">β GATED</span>';
|
| 3335 |
+
const sigClass = (vecOf(r)) === 'BUY' ? 'BUY' : (vecOf(r)) === 'SELL' ? 'SELL' : 'HOLD';
|
| 3336 |
return `<tr>
|
| 3337 |
<td class="rc ${i===0?'r1':''}">${r.rank}</td>
|
| 3338 |
<td class="nc">${r.space_name}</td>
|
| 3339 |
+
<td><span class="sig ${sigClass}">${vecOf(r)}</span></td>
|
| 3340 |
<td class="num">${fPct(r.signal_confidence)}</td>
|
| 3341 |
<td class="num" style="color:var(--green)">${r.buy_count}</td>
|
| 3342 |
<td class="num" style="color:var(--red)">${r.sell_count}</td>
|
|
|
|
| 3366 |
const gLabel = allP
|
| 3367 |
? '<span class="pos-gate-pass">β ALL CLEAR</span>'
|
| 3368 |
: `<span class="pos-gate-warn">A:${gates.A?'β':'β'} B:${gates.B?'β':'β'} C:${gates.C?'β':'β'} D:${gates.D?'β':'β'} E:${gates.E?'β':'β'}</span>`;
|
| 3369 |
+
const side = vecOf(r) === 'BUY' ? 'BUY' : 'SELL';
|
| 3370 |
return `<tr>
|
| 3371 |
<td class="nc">${r.space_name}</td>
|
| 3372 |
<td><span class="sig ${side}">${side}</span></td>
|
|
|
|
| 3384 |
const total = r.buy_count + r.sell_count || 1;
|
| 3385 |
const buyPct = (r.buy_count / total * 100).toFixed(0);
|
| 3386 |
const sellPct = (r.sell_count / total * 100).toFixed(0);
|
| 3387 |
+
const sigCls = (vecOf(r)) === 'BUY' ? 'BUY' : (vecOf(r)) === 'SELL' ? 'SELL' : 'HOLD';
|
| 3388 |
const tagBg = sigCls === 'BUY' ? 'rgba(0,223,138,0.12)' : sigCls === 'SELL' ? 'rgba(255,61,90,0.12)' : 'rgba(255,255,255,0.07)';
|
| 3389 |
const tagClr = sigCls === 'BUY' ? 'var(--green)' : sigCls === 'SELL' ? 'var(--red)' : 'var(--t2)';
|
| 3390 |
return `<div class="vote-row">
|
| 3391 |
<div class="vote-asset-label">
|
| 3392 |
<span class="vote-asset-name">${r.space_name}</span>
|
| 3393 |
+
<span class="vote-signal-tag" style="background:${tagBg};color:${tagClr};border:1px solid ${tagClr}40">${vecOf(r)}</span>
|
| 3394 |
</div>
|
| 3395 |
<div class="vote-bar-track">
|
| 3396 |
<div class="vote-bar-buy" style="width:${buyPct}%"></div>
|
|
|
|
| 3428 |
const fb = document.getElementById('trade-feed-body');
|
| 3429 |
const now = Date.now();
|
| 3430 |
fb.innerHTML = _rankings.slice(0, 20).map(r => {
|
| 3431 |
+
const dir = vecOf(r) === 'BUY' ? 'L' : vecOf(r) === 'SELL' ? 'S' : 'N';
|
| 3432 |
+
const dirLabel = vecOf(r) === 'BUY' ? 'LONG' : vecOf(r) === 'SELL' ? 'SHORT' : 'HOLD';
|
| 3433 |
const ts = r.last_updated ? new Date(r.last_updated * 1000).toLocaleTimeString('en-GB', {hour12:false}) : 'β';
|
| 3434 |
return `<div class="feed-cols row">
|
| 3435 |
<div class="f-info">
|
|
|
|
| 3516 |
// ββ Summary bar βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 3517 |
function renderAssetSummary(assets) {
|
| 3518 |
document.getElementById('as-total').textContent = assets.length;
|
| 3519 |
+
document.getElementById('as-bull').textContent = assets.filter(r => (vecOf(r)) === 'BUY').length;
|
| 3520 |
+
document.getElementById('as-bear').textContent = assets.filter(r => (vecOf(r)) === 'SELL').length;
|
| 3521 |
+
document.getElementById('as-neut').textContent = assets.filter(r => (vecOf(r)) !== 'BUY' && (vecOf(r)) !== 'SELL').length;
|
| 3522 |
const accs = assets.filter(r => r.avn_accuracy > 0).map(r => r.avn_accuracy);
|
| 3523 |
document.getElementById('as-avgacc').textContent = accs.length
|
| 3524 |
? fPct(accs.reduce((a,b) => a+b, 0) / accs.length) : 'β';
|
|
|
|
| 3533 |
return;
|
| 3534 |
}
|
| 3535 |
grid.innerHTML = assets.map(r => {
|
| 3536 |
+
const sigCls = vecOf(r) === 'BUY' ? 'sig-buy' : vecOf(r) === 'SELL' ? 'sig-sell' : 'sig-hold';
|
| 3537 |
const scoreClr = r.score > 0.1 ? 'var(--green)' : r.score < -0.05 ? 'var(--red)' : 'var(--cyan)';
|
| 3538 |
const total = r.buy_count + r.sell_count || 1;
|
| 3539 |
const buyPct = (r.buy_count / total * 100).toFixed(0);
|
|
|
|
| 3545 |
<div class="ac-name">${r.space_name}</div>
|
| 3546 |
<div class="ac-rank">RANK #${r.rank}</div>
|
| 3547 |
</div>
|
| 3548 |
+
<span class="sig ${vecOf(r) === 'BUY' ? 'BUY' : vecOf(r) === 'SELL' ? 'SELL' : 'HOLD'}">${vecOf(r)}</span>
|
| 3549 |
</div>
|
| 3550 |
<div class="ac-score-block">
|
| 3551 |
<div class="ac-score-label">AXRVI Score</div>
|
|
|
|
| 3590 |
return;
|
| 3591 |
}
|
| 3592 |
body.innerHTML = assets.map(r => {
|
| 3593 |
+
const sigCls = (vecOf(r)) === 'BUY' ? 'BUY' : (vecOf(r)) === 'SELL' ? 'SELL' : 'HOLD';
|
| 3594 |
const lossClr = r.actor_loss < 0.3 ? 'var(--green)' : r.actor_loss > 0.7 ? 'var(--red)' : 'var(--amber)';
|
| 3595 |
return `<tr onclick="openDeepDive('${r.space_name}')" style="cursor:pointer">
|
| 3596 |
<td class="rc ${r.rank===1?'r1':''}">${r.rank}</td>
|
| 3597 |
<td class="nc">${r.space_name}</td>
|
| 3598 |
+
<td style="text-align:right"><span class="sig ${sigCls}">${vecOf(r)}</span></td>
|
| 3599 |
<td class="num" style="color:var(--cyan)">${fPct(r.signal_confidence)}</td>
|
| 3600 |
<td class="num">${fPct(r.avn_accuracy)}</td>
|
| 3601 |
<td class="num" style="color:${r.score>0.1?'var(--green)':r.score<0?'var(--red)':'var(--t1)'}">${f4(r.score)}</td>
|
|
|
|
| 3668 |
dive.style.display = '';
|
| 3669 |
dive.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
| 3670 |
|
| 3671 |
+
const sigCls = vecOf(r) === 'BUY' ? 'g' : vecOf(r) === 'SELL' ? 'r' : '';
|
| 3672 |
document.getElementById('dive-stats').innerHTML = `
|
| 3673 |
<div class="dive-block">
|
| 3674 |
<div class="dive-block-title">Neural Output</div>
|
| 3675 |
<div class="dive-row"><span class="dive-k">AXRVI Score</span><span class="dive-v ${r.score > 0.1 ? 'g' : r.score < 0 ? 'r' : 'c'}">${f4(r.score)}</span></div>
|
| 3676 |
<div class="dive-row"><span class="dive-k">Signal Confidence</span><span class="dive-v c">${fPct(r.signal_confidence)}</span></div>
|
| 3677 |
+
<div class="dive-row"><span class="dive-k">Dominant Signal</span><span class="dive-v ${sigCls}">${vecOf(r)}</span></div>
|
| 3678 |
<div class="dive-row"><span class="dive-k">Rank</span><span class="dive-v">#${r.rank}</span></div>
|
| 3679 |
<div class="dive-row"><span class="dive-k">Last Updated</span><span class="dive-v">${ago(r.last_updated)}</span></div>
|
| 3680 |
</div>
|