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:
@@ -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> · 12 strategies · $100K paper · 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 (|ρ| > 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>
|
||||
|
||||
Reference in New Issue
Block a user