Add real historical backtesting with Hyperliquid mainnet candle data

backtests/historical_runner.py: Fetches real 1h candles from Hyperliquid
mainnet API (candleSnapshot endpoint). Runs all 7 strategies against
actual BTC price history (721 candles, 30 days, $63,024→$63,605).
Each strategy's signal logic operates on real OHLCV data with
configurable fee tiers. Saves to backtests/results/historical/.

Results on 30d BTC data at VIP0:
  Mean Reversion: +93.87% net (Sharpe 0.94)
  Order Book Imbalance: +54.31% net (Sharpe 1.03)
  Avellaneda-Stoikov: -1.02% net (Sharpe -0.13)
  Iceberg Detection: -33.20% net
  Momentum Breakout: -54.72% net

Server: Added /api/backtests/historical (list) and
/api/backtest/historical/{name} (full data) endpoints.

Dashboard: Added "Historical" tab with "Real Data" badge. Cards show
coin + mainnet source. Click opens the same detail panel with fee
tier dropdown and equity chart.
This commit is contained in:
ramseshk
2026-08-04 07:29:51 +00:00
parent 0c0d2124ad
commit 1bf54b4c00
19 changed files with 44522 additions and 11 deletions
+64
View File
@@ -76,6 +76,19 @@ body{background:var(--bg);color:var(--hi);font-family:var(--f);min-height:100vh;
footer{text-align:center;padding:30px;font-size:10px;color:#2a2a35}
footer a{color:#3f3f4a;text-decoration:none}footer a:hover{color:var(--tx)}
/* Risk Analytics panel — collapsible */
.risk-wrap{max-width:1440px;margin:0 auto 20px;padding:0 24px}
.risk-toggle{display:flex;align-items:center;gap:8px;cursor:pointer;background:none;border:1px solid var(--ln);border-radius:var(--ra);color:var(--tx);font-family:var(--f);font-size:11px;font-weight:600;padding:10px 16px;text-transform:uppercase;letter-spacing:.5px;transition:all .15s}
.risk-toggle:hover{color:var(--hi);border-color:var(--hr)}
.risk-toggle .arrow{display:inline-block;transition:transform .2s;font-size:10px}
.risk-toggle.open .arrow{transform:rotate(90deg)}
.risk-panel{display:none;background:var(--srf);border:1px solid var(--ln);border-radius:var(--ra);padding:16px;margin-top:8px}
.risk-panel.show{display:block}
.risk-corr{font-family:var(--m);font-size:10px;color:var(--tx);line-height:1.8;margin-top:12px;padding:10px;background:rgba(0,0,0,.2);border-radius:6px;max-height:200px;overflow-y:auto}
.risk-corr .corr-high{color:var(--rd)}
.risk-corr .corr-med{color:var(--am)}
.risk-corr .corr-low{color:var(--tx)}
@media(max-width:768px){
.topbar{padding:10px 14px;flex-direction:column;align-items:flex-start}
.tabs{padding:0 14px;top:88px;overflow-x:auto;white-space:nowrap}
@@ -104,6 +117,7 @@ footer a{color:#3f3f4a;text-decoration:none}footer a:hover{color:var(--tx)}
<button class="tab on" id="tl-live" onclick="switchTab('live')">Live<span class="badge test">Testnet</span></button>
<button class="tab" id="tl-paper" onclick="switchTab('paper')">Paper<span class="badge main">$100K Mainnet</span></button>
<button class="tab" id="tl-backtest" onclick="switchTab('backtest')">Backtest</button>
<button class="tab" id="tl-historical" onclick="switchTab('historical')">Historical<span class="badge main">Real Data</span></button>
</div>
<!-- Main -->
<div class="main-wrap">
@@ -118,7 +132,19 @@ footer a{color:#3f3f4a;text-decoration:none}footer a:hover{color:var(--tx)}
<div class="panel" id="pnl-backtest">
<div class="sgrid" id="bt-sgrid"></div>
</div>
<div class="panel" id="pnl-historical">
<div class="sgrid" id="hist-sgrid"></div>
</div>
</div>
<!-- Risk Analytics -->
<div class="risk-wrap">
<button class="risk-toggle" onclick="toggleRisk()" id="risk-btn"><span class="arrow"></span> Risk Analytics</button>
<div class="risk-panel" id="risk-panel">
<div class="stats-row" id="risk-stats" style="margin-bottom:12px"></div>
<div class="risk-corr" id="risk-corr"></div>
</div>
</div>
<footer><a href="https://git.ftdt.io/rams/ftdt-quant-lab" target="_blank">rams/ftdt-quant-lab</a> &middot; 12 strategies &middot; $100K paper &middot; Hyperliquid</footer>
<!-- Detail Overlay -->
@@ -193,6 +219,7 @@ function switchTab(t){
if(t==='live'&&lastData)renLive(lastData);
if(t==='paper'&&lastPaper)renPaper(lastPaper);
if(t==='backtest')loadBT();
if(t==='historical')loadHistBT();
}
// ═══════════ Render strategy cards ═══════════
@@ -375,6 +402,43 @@ function loadBT(){
// ═══════════ Init ═══════════
initDetChart();connect();loadBT();
// ═══════════ Risk Analytics ═══════════
function toggleRisk(){
var p=document.getElementById('risk-panel'),b=document.getElementById('risk-btn');
p.classList.toggle('show');b.classList.toggle('open');
if(p.classList.contains('show')&&!p.dataset.loaded){loadRisk();p.dataset.loaded='1'}
}
function loadRisk(){
fetch('/cv/api/risk').then(function(r){return r.json()}).then(function(d){
if(d.error){document.getElementById('risk-stats').innerHTML='<div style="color:var(--tx);padding:8px">'+d.error+'</div>';return}
var pf=d.portfolio||{};
document.getElementById('risk-stats').innerHTML=
'<div class="stat"><div class="lbl">VaR 95%</div><div class="val dn">'+(pf.var_95*100).toFixed(2)+'%</div></div>'+
'<div class="stat"><div class="lbl">CVaR 95%</div><div class="val dn">'+(pf.cvar_95*100).toFixed(2)+'%</div></div>'+
'<div class="stat"><div class="lbl">Max DD</div><div class="val dn">'+(pf.max_drawdown*100).toFixed(2)+'%</div></div>'+
'<div class="stat"><div class="lbl">Calmar</div><div class="val '+(pf.calmar_ratio>=0?'up':'dn')+'">'+pf.calmar_ratio.toFixed(2)+'</div></div>'+
'<div class="stat"><div class="lbl">Sharpe</div><div class="val '+(pf.sharpe>=0?'up':'dn')+'">'+pf.sharpe.toFixed(2)+'</div></div>'+
'<div class="stat"><div class="lbl">Sortino</div><div class="val">'+pf.sortino.toFixed(2)+'</div></div>';
// Correlation summary
var cs=d.correlation_summary||[];
var ch='<div style="font-size:10px;color:var(--tx);text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px">Strategy Correlations (|ρ| &gt; 0.3)</div>';
if(cs.length===0){ch+='<span style="color:var(--tx)">No significant correlations found — strategies are well-diversified.</span>'}
else{for(var i=0;i<cs.length;i++){var c=cs[i],cls=c.level==='high'?'corr-high':'corr-med';ch+='<div><span class="'+cls+'">ρ='+(c.correlation>=0?'+':'')+c.correlation.toFixed(3)+'</span> '+c.pair+'</div>'}}
document.getElementById('risk-corr').innerHTML=ch;
// Mark loaded + store timestamp
window._riskLoaded=Date.now();
}).catch(function(e){document.getElementById('risk-stats').innerHTML='<div style="color:var(--rd);padding:8px">Failed: '+e.message+'</div>'})
}
// Auto-refresh risk panel when paper data updates (throttled to every 30s)
var _origRenPaper=renPaper;
renPaper=function(d){
_origRenPaper(d);
var p=document.getElementById('risk-panel');
if(p&&p.classList.contains('show')&&(!window._riskLoaded||Date.now()-window._riskLoaded>30000)){
loadRisk();
}
};
</script>
</body>
</html>