Files
ramseshk 140b0cc360 feat: full-information live terminal — all 10 modules with coefficients
live/monitor_service.py — rewritten state() includes all 10:
  - Hawkes: 3x3 alpha excitation matrix, mu baseline per type, beta decay
  - Dealer GEX: net GEX (), pin levels, direction signal
  - Tick Regime: current/next tick size, boundary price, bars-to-cross
  - Triangular Arb: venue count, opportunities, best spread bps
  - Sequencer: stale-state detection, P50/P99 latency, event count
  - Full: prices, microstructure, HLP, whipsaw, liq, term, spoof

live.html — complete rewrite: Bloomberg-terminal 4-column grid
  - Panel 1: Prices (mid, mark, oracle, premium, spread, funding)
  - Panel 2: Microstructure (OBI, best bid/ask, toxicity bar, branching ratio, sequencer latency P50/P99)
  - Panel 3: Composite Signals (HLP, Whipsaw w/countdown, Term Structure w/z-score, GEX w/signal, Tick Regime, Spoof probability bar)
  - Panel 4: Dealer GEX (net GEX bar, pin levels, direction)
  - Full-width: HLP Vault (total delta, assets, toxicity, per-coin signals)
  - Wide: Hawkes Coefficients (3x3 alpha matrix, mu per type, beta)
  - Liq Waterfall + Tri Arb + Sequencer + Tick Regime
  - Term Structure + Funding Whipsaw with countdown
2026-08-10 10:54:54 +08:00

259 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FTDT Quant Lab — Live Terminal</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'SF Mono','Cascadia Code','Ubuntu Mono',monospace;background:#080a0d;color:#789;min-height:100vh;overflow-y:auto}
.pos{color:#00e676}.neg{color:#ff1744}.warn{color:#ff9100}.neu{color:#789}.blue{color:#00b0ff}.dim{color:#445}.white{color:#ddd}.cyan{color:#00e5ff}
.topbar{background:#0a0d12;border-bottom:1px solid #171a22;padding:4px 16px;display:flex;justify-content:space-between;align-items:center;height:30px;position:sticky;top:0;z-index:100}
.topbar h1{font-size:10px;font-weight:700;color:#ddd;letter-spacing:1px}
.topbar .dot{display:inline-block;width:5px;height:5px;background:#00e676;border-radius:50%;margin-right:5px;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{box-shadow:0 0 3px #00e676}50%{box-shadow:0 0 10px #00e676}}
.topbar .st{font-size:8px;color:#555}
.topbar .st .coin-pill{display:inline-block;background:#0f1118;border:1px solid #1a1c24;border-radius:2px;padding:1px 5px;margin:0 1px;color:#00b0ff;font-size:9px;cursor:pointer;transition:.15s}
.topbar .st .coin-pill.active{border-color:#00b0ff;background:#0a1620;color:#fff}
.grid{display:grid;grid-template-columns:repeat(4,1fr);gap:4px;padding:4px;max-width:1920px;margin:0 auto}
.panel{background:#0a0d12;border:1px solid #14171f;border-radius:3px}
.panel.full{grid-column:1/-1}
.panel.wide{grid-column:span 2}
.panel-header{display:flex;justify-content:space-between;align-items:center;padding:4px 8px;background:#0d1018;border-bottom:1px solid #14171f}
.panel-header h3{font-size:8px;text-transform:uppercase;letter-spacing:1.5px;color:#445;font-weight:600}
.panel-body{padding:5px 8px;font-size:9px}
.stat-row{display:flex;justify-content:space-between;align-items:center;padding:2px 0;border-bottom:1px solid #0c0e14}
.stat-row:last-child{border-bottom:none}
.stat-label{color:#445;font-size:8px;text-transform:uppercase;letter-spacing:0.5px}
.stat-value{font-size:10px;font-family:monospace;font-weight:600}
.metric-grid{display:grid;grid-template-columns:1fr 1fr;gap:3px}
.metric-item{background:#080a0d;border:1px solid #0f1118;border-radius:2px;padding:4px 6px}
.metric-item .ml{font-size:7px;color:#445;text-transform:uppercase;margin-bottom:1px}
.metric-item .mv{font-size:11px;font-family:monospace;font-weight:700}
.bar-bg{height:3px;background:#0f1118;border-radius:2px;margin-top:2px;overflow:hidden}
.bar-fill{height:100%;border-radius:2px;transition:width .3s}
.bar-fill.ok{background:#00e676}.bar-fill.warn{background:#ff9100}.bar-fill.bad{background:#ff1744}
.signal-row{display:flex;align-items:center;gap:4px;margin:3px 0;padding:4px 6px;border-radius:2px;font-size:9px}
.signal-row.buy{background:#001a10;border:1px solid #002a18;color:#00e676}
.signal-row.sell{background:#1a0000;border:1px solid #2a0000;color:#ff1744}
.signal-row.neutral{background:#0f1118;border:1px solid #14171f;color:#555}
.signal-row .sig{font-weight:700;text-transform:uppercase;font-size:9px}
.signal-row .reason{font-size:8px;color:#555;margin-left:2px}
.coef-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px}
.coef{font-size:9px;font-family:monospace;color:#789;padding:2px}
.coef .key{color:#445;font-size:7px;text-transform:uppercase}
.alpha-row{display:flex;gap:4px;margin:2px 0;font-family:monospace;font-size:8px}
.alpha-row span{background:#080a0d;padding:2px 4px;border-radius:1px;min-width:40px;text-align:center}
.alpha-row span.p{color:#00e676}.alpha-row span.n{color:#ff1744}.alpha-row span.z{color:#333}
.gex-bar{height:6px;background:#0f1118;border-radius:3px;overflow:hidden;margin:3px 0}
.gex-fill{height:100%;border-radius:3px;transition:width .5s}
.gex-fill.long{background:linear-gradient(90deg,#003311,#00e676)}.gex-fill.short{background:linear-gradient(270deg,#330000,#ff1744)}
.countdown{font-size:16px;font-family:monospace;font-weight:700;text-align:center;margin:4px 0}
.countdown.near{color:#ff9100}.countdown.far{color:#445}
</style>
</head>
<body>
<div class="topbar">
<h1><span class="dot"></span>FTDT QUANT LAB — LIVE TERMINAL</h1>
<div class="st">
<span class="coin-pill active" onclick="selectCoin('BTC')" id="pill-BTC">BTC</span>
<span class="coin-pill" onclick="selectCoin('ETH')" id="pill-ETH">ETH</span>
<span style="margin-left:6px;color:#445">HL Testnet</span>
<span style="margin-left:4px;font-size:8px" id="update-age"></span>
</div>
</div>
<div class="grid" id="root"></div>
<script>
const API=''; let currentCoin='BTC'; let lastUpdate=0;
async function loadState(){
try{
const r=await fetch(API+'/api/monitors/status?coin='+currentCoin);
if(!r.ok)throw new Error('HTTP '+r.status);
const d=await r.json();
lastUpdate=Date.now();
render(d);
document.getElementById('update-age').textContent='● LIVE';
document.getElementById('update-age').style.color='#00e676';
}catch(e){
document.getElementById('update-age').textContent='○ OFFLINE';
document.getElementById('update-age').style.color='#ff1744';
}
}
function render(d){
const p=d.prices||{},ms=d.microstructure||{},hlp=d.hlp||{};
const seq=d.sequencer||{},fw=d.whipsaw||{},lw=d.liq_waterfall||{};
const ts=d.term_structure||{},gex=d.gex||{},tr=d.tick_regime||{};
const tri=d.triangular||{},sp=d.spoof||{},hk=d.hawkes||{};
const tox=hlp.toxicity_score||0,tp=Math.min(tox*100,100).toFixed(1);
const spoofP=(sp.spoof_probability||0)*100;
const seqLag=seq.avg_latency||{};
const gexM=(gex.net_gex_m||0).toFixed(1);
const gexCls=gexM>0?'long':'short';
const cf=ts.quarterly_perp_basis||{};
const fq=ts.fair_quarterly||{};
document.getElementById('root').innerHTML=`
<div class="panel" id="px">
<div class="panel-header"><h3>${currentCoin}/USD PRICES</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">Mid</span><span class="stat-value white">$${(+(p.mid||0)).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Mark</span><span class="stat-value ${p.mark>p.oracle?'pos':'neg'}">$${(+(p.mark||0)).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Oracle</span><span class="stat-value dim">$${(+(p.oracle||0)).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Premium</span><span class="stat-value ${p.premium_bps>0?'pos':'neg'}">${(p.premium_bps||0).toFixed(1)} bps</span></div>
<div class="stat-row"><span class="stat-label">Spread</span><span class="stat-value dim">${(p.spread_bps||0).toFixed(1)} bps</span></div>
<div class="stat-row"><span class="stat-label">Funding</span><span class="stat-value ${Math.abs(p.funding_apr)>10?'warn':'neu'}">${(p.funding_apr||0).toFixed(1)}% APR</span></div>
</div>
</div>
<div class="panel" id="ms">
<div class="panel-header"><h3>MICROSTRUCTURE</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">OBI</span><span class="stat-value ${ms.obi>0.1?'pos':(ms.obi<-0.1?'neg':'neu')}">${(ms.obi||0).toFixed(4)}</span></div>
<div class="stat-row"><span class="stat-label">Best Bid</span><span class="stat-value pos">$${(+(ms.bid||0)).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Best Ask</span><span class="stat-value neg">$${(+(ms.ask||0)).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Toxicity</span><span class="stat-value ${tox>0.2?'neg':'neu'}">${tp}%</span></div>
<div class="bar-bg"><div class="bar-fill ${tox>0.3?'bad':(tox>0.1?'warn':'ok')}" style="width:${tp}%"></div></div>
<div class="stat-row"><span class="stat-label">Branching</span><span class="stat-value ${hk.branching_ratio>0.5?'warn':'neu'}">${((hk.branching_ratio||0)*100).toFixed(1)}%</span></div>
<div class="stat-row"><span class="stat-label">Seq Lag P50</span><span class="stat-value dim">${(seqLag.p50_ms||0).toFixed(0)}ms</span></div>
<div class="stat-row"><span class="stat-label">Seq Lag P99</span><span class="stat-value ${(seqLag.p99_ms||0)>100?'warn':'neu'}">${(seqLag.p99_ms||0).toFixed(0)}ms</span></div>
</div>
</div>
<div class="panel" id="sig">
<div class="panel-header"><h3>COMPOSITE SIGNALS</h3></div>
<div class="panel-body">
<div class="signal-row ${(hlp.signals||{})[currentCoin]?.direction>0?'buy':((hlp.signals||{})[currentCoin]?.direction<0?'sell':'neutral')}">
<span class="sig">HLP ${((hlp.signals||{})[currentCoin]||{}).signal||'idle'}</span>
<span class="reason">${((hlp.signals||{})[currentCoin]||{}).reason||''}</span>
</div>
<div class="signal-row ${fw.action?.includes('long')?'buy':(fw.action?.includes('short')?'sell':'neutral')}">
<span class="sig">WHIPSAW ${fw.action||'idle'}</span>
<span class="reason">${fw.reason||''} ${fw.premium_bps?.toFixed(1)||''}bps</span>
</div>
<div class="stat-row"><span class="stat-label">To Funding</span><span class="stat-value countdown ${(fw.seconds_to_funding||0)<120?'near':'far'}">${(fw.seconds_to_funding||0).toFixed(0)}s</span></div>
<div class="signal-row ${ts.primary_signal?.includes('buy')?'buy':(ts.primary_signal?.includes('sell')?'sell':'neutral')}">
<span class="sig">TERM ${ts.primary_signal||'none'}</span>
<span class="reason">Q-perp ${cf.current_bps||'?'}bps z=${cf.z_score||'?'}</span>
</div>
<div class="signal-row ${gex.signal?.signal==='fade_breakout'?'buy':(gex.signal?.signal==='ride_momentum'?'sell':'neutral')}">
<span class="sig">GEX ${gex.signal?.signal||'none'}</span>
<span class="reason">$${gexM}M ${gexCls}</span>
</div>
<div class="signal-row ${tr.action==='widen_quotes'?'sell':(tr.action==='tighten_quotes'?'buy':'neutral')}">
<span class="sig">TICK ${tr.action||'none'}</span>
<span class="reason">${tr.reason||''}</span>
</div>
<div style="margin-top:4px">
<span class="stat-label" style="font-size:7px">Spoof Probability</span>
<div class="bar-bg"><div class="bar-fill ${spoofP>30?'bad':(spoofP>10?'warn':'ok')}" style="width:${spoofP}%"></div></div>
<span style="font-size:8px;color:#555;float:right">${spoofP.toFixed(1)}% · ${sp.spoof_count||0} detected</span>
</div>
</div>
</div>
<div class="panel" id="gex2">
<div class="panel-header"><h3>DEALER GEX</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">Net GEX</span><span class="stat-value ${gexM>=0?'pos':'neg'}">$${gexM}M</span></div>
<div class="gex-bar"><div class="gex-fill ${gexM>=0?'long':'short'}" style="width:${Math.min(Math.abs(gexM||0)*10,100)}%"></div></div>
<div class="stat-row"><span class="stat-label">Strikes</span><span class="stat-value dim">${gex.strikes_tracked||0}</span></div>
<div class="stat-row"><span class="stat-label">Pin</span><span class="stat-value ${gex.nearest_pin?'blue':'dim'}">${gex.nearest_pin?'$$'+(gex.nearest_pin.strike||0).toLocaleString()+' ('+gex.nearest_pin.distance_pct+'%)':'—'}</span></div>
<div class="stat-row"><span class="stat-label">Direction</span><span class="stat-value white">${gex.signal?.direction||'—'}</span></div>
</div>
</div>
<div class="panel full" id="hlp2">
<div class="panel-header"><h3>HLP VAULT · 0xfefe...fefe</h3><span style="font-size:7px;color:#333">Protocol Counterparty</span></div>
<div class="panel-body">
<div class="metric-grid">
<div class="metric-item"><div class="ml">Total Delta</div><div class="mv ${(hlp.total_delta_m||0)>5?'warn':'neu'}">$${(hlp.total_delta_m||0).toFixed(1)}M</div></div>
<div class="metric-item"><div class="ml">Assets</div><div class="mv neu">${hlp.assets_tracked||0}</div></div>
<div class="metric-item"><div class="ml">Toxicity</div><div class="mv ${tox>0.3?'neg':'neu'}">${tp}%</div></div>
<div class="metric-item"><div class="ml">Overextended</div><div class="mv ${(hlp.overextended_assets||[]).length?'warn':'neu'}">${(hlp.overextended_assets||[]).length||0}</div></div>
</div>
${hlp.signals?Object.entries(hlp.signals).map(([c,s])=>'<div class="signal-row '+(s.direction>0?'buy':(s.direction<0?'sell':'neutral'))+'"><span class="sig">'+c+'</span><span class="sig">'+s.signal+'</span><span class="reason">'+s.reason+'</span></div>').join(''):'<div class="dim" style="font-size:8px;text-align:center;padding:4px">No HLP signals</div>'}
</div>
</div>
<div class="panel wide" id="coef">
<div class="panel-header"><h3>HAWKES COEFFICIENTS</h3><span style="font-size:7px;color:#333">μ baseline | α excitation | β decay</span></div>
<div class="panel-body">
<div class="coef-grid">
<div class="coef"><div class="key">μ trade</div>${(hk.mu||[0])[0].toFixed(4)}</div>
<div class="coef"><div class="key">μ cancel</div>${(hk.mu||[0,0])[1].toFixed(4)}</div>
<div class="coef"><div class="key">μ spread</div>${(hk.mu||[0,0,0])[2].toFixed(4)}</div>
</div>
<div style="font-size:7px;color:#445;margin-top:6px;text-transform:uppercase">Excitation α (row=target, col=source)</div>
<div class="alpha-row"><span style="color:#445;min-width:60px">Trade→</span>${(hk.alpha||[[0]])[0].map(v=>'<span class="'+(v>0?'p':'z')+'">'+v.toFixed(4)+'</span>').join('')}</div>
<div class="alpha-row"><span style="color:#445;min-width:60px">Cancel→</span>${(hk.alpha||[[0],[0]])[1].map(v=>'<span class="'+(v>0?'p':'z')+'">'+v.toFixed(4)+'</span>').join('')}</div>
<div class="alpha-row"><span style="color:#445;min-width:60px">Spread→</span>${(hk.alpha||[[0],[0],[0]])[2].map(v=>'<span class="'+(v>0?'p':'z')+'">'+v.toFixed(4)+'</span>').join('')}</div>
<div style="margin-top:4px"><span class="stat-label">β decay</span><span class="stat-value dim" style="font-size:10px">${(hk.beta||0).toFixed(2)}</span></div>
</div>
</div>
<div class="panel" id="liq">
<div class="panel-header"><h3>LIQ WATERFALL</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">At Risk</span><span class="stat-value ${(lw.at_risk_accounts||[]).length?'warn':'neu'}">${(lw.at_risk_accounts||[]).length}</span></div>
<div class="stat-row"><span class="stat-label">Tracked</span><span class="stat-value dim">${lw.tracked_accounts||0}</span></div>
${(lw.at_risk_accounts||[]).slice(0,2).map(a=>'<div class="signal-row '+(a.level==='critical'?'sell':'neutral')+'"><span class="sig">'+a.address+'</span><span class="sig">MR '+a.margin_ratio.toFixed(2)+'</span></div>').join('')||'<div class="dim" style="font-size:8px;text-align:center;padding:4px">No at-risk accounts</div>'}
</div>
</div>
<div class="panel wide" id="arb">
<div class="panel-header"><h3>TRIANGULAR ARB · SEQUENCER · TICK REGIME</h3></div>
<div class="panel-body">
<div class="coef-grid">
<div class="coef"><div class="key">Tri Opps</div><span class="${tri.opportunities_count>0?'warn':'neu'}">${tri.opportunities_count||0}</span></div>
<div class="coef"><div class="key">Venues</div>${tri.venues?.length||0}</div>
<div class="coef"><div class="key">Best BPS</div><span class="${Math.abs(tri.best_opportunity?.spread_bps||0)>1?'warn':'neu'}">${tri.best_opportunity?.spread_bps?.toFixed(1)||'—'}bps</span></div>
</div>
<div style="margin-top:4px"><span class="stat-label">Stale State</span><span class="stat-value ${seq.stale_state?.stale_window_active?'warn':'neu'}">${seq.stale_state?.stale_window_active?'ACTIVE':'idle'}</span></div>
<div class="stat-row"><span class="stat-label">Seq P50</span><span class="stat-value dim">${(seqLag.p50_ms||0).toFixed(0)}ms</span></div>
<div class="stat-row"><span class="stat-label">Seq P99</span><span class="stat-value ${(seqLag.p99_ms||0)>100?'warn':'neu'}">${(seqLag.p99_ms||0).toFixed(0)}ms</span></div>
<div class="stat-row"><span class="stat-label">Sequence Count</span><span class="stat-value dim">${seqLag.count||0}</span></div>
<div style="margin-top:4px"><span class="stat-label">Tick Regime</span><span class="stat-value ${tr.urgent?'warn':'dim'}">${tr.action||'none'} ${tr.reason||''}</span></div>
<div class="stat-row"><span class="stat-label">Current Tick</span><span class="stat-value dim">${tr.current_tick||'—'}</span></div>
<div class="stat-row"><span class="stat-label">Next Tick</span><span class="stat-value ${tr.tick_delta?'warn':'dim'}">${tr.next_tick||'—'} ${tr.tick_delta>0?'↑':'↓' if tr.tick_delta else ''}</span></div>
<div class="stat-row"><span class="stat-label">Boundary</span><span class="stat-value dim">$${(+tr.boundary_price||0).toLocaleString()}</span></div>
</div>
</div>
<div class="panel" id="geo">
<div class="panel-header"><h3>TERM STRUCTURE</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">Qtr-Perp</span><span class="stat-value dim">${cf.current_bps||'—'} bps</span></div>
<div class="stat-row"><span class="stat-label">Z-score</span><span class="stat-value ${Math.abs(cf.z_score||0)>2?'warn':'neu'}">${(cf.z_score||0).toFixed(2)}σ</span></div>
${fq?`<div class="stat-row"><span class="stat-label">Fair Qtrly</span><span class="stat-value dim">$${(+fq.fair_quarterly).toLocaleString()}</span></div>
<div class="stat-row"><span class="stat-label">Carry Rate</span><span class="stat-value dim">${(fq.carry_rate_annual_pct||0).toFixed(1)}%</span></div>`:''}
<div class="stat-row"><span class="stat-label">Signal</span><span class="stat-value ${ts.primary_signal?.includes('buy')?'pos':(ts.primary_signal?.includes('sell')?'neg':'neu')}">${ts.primary_signal||'none'}</span></div>
</div>
</div>
<div class="panel" id="fw">
<div class="panel-header"><h3>FUNDING WHIPSAW</h3></div>
<div class="panel-body">
<div class="stat-row"><span class="stat-label">Action</span><span class="stat-value ${fw.action?.includes('long')?'pos':(fw.action?.includes('short')?'neg':'neu')}">${fw.action||'idle'}</span></div>
<div class="stat-row"><span class="stat-label">Premium</span><span class="stat-value ${fw.premium_bps>0?'pos':'neg'}">${(fw.premium_bps||0).toFixed(1)} bps</span></div>
<div class="countdown ${(fw.seconds_to_funding||0)<60?'near':'far'}">${(fw.seconds_to_funding||0).toFixed(0)}s</div>
<div style="font-size:7px;color:#445;text-align:center">to funding settlement</div>
<div class="stat-row"><span class="stat-label">Confidence</span><span class="stat-value dim">${((fw.confidence||0)*100).toFixed(0)}%</span></div>
<div class="stat-row"><span class="stat-label">Size</span><span class="stat-value dim">${(fw.size||0).toFixed(6)}</span></div>
</div>
</div>
`;
}
function selectCoin(c){currentCoin=c;document.querySelectorAll('.coin-pill').forEach(el=>el.classList.remove('active'));document.getElementById('pill-'+c)?.classList.add('active');loadState();}
loadState();
const interval=setInterval(()=>{if(Date.now()-lastUpdate>2500)loadState();},3000);
</script>
</body>
</html>