add: 30d/90d/180d/365d forward returns in all backtest views
- Bracket table now shows Avg 30d, 90d, 180d, and 1yr columns - Signal events show all 4 timeframes - Current context shows all 4 average returns - Comparable examples show all available timeframes - Updated backtest screenshot
This commit is contained in:
+12
-5
@@ -1121,7 +1121,7 @@ tr:hover td{background:var(--card-hover)}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Score Range</th><th>Label</th><th>Days</th>
|
||||
<th>Avg 30d</th><th>Avg 90d</th><th>Avg 1yr</th>
|
||||
<th>Avg 30d</th><th>Avg 90d</th><th>Avg 180d</th><th>Avg 1yr</th>
|
||||
<th>Win Rate (1yr)</th><th>Max Gain (1yr)</th><th>Max Loss (1yr)</th>
|
||||
<th>Avg Max DD</th>
|
||||
</tr>
|
||||
@@ -1228,7 +1228,7 @@ function renderAll() {
|
||||
document.getElementById('mainContent').innerHTML = `
|
||||
<div class="section"><div class="context-box" id="contextBox"><h2>Current Signal Context</h2><div class="context-score" id="ctxScore">--</div><div class="context-percentile" id="ctxPercentile"></div><div class="context-return" id="ctxReturn"></div><div class="comparable-list" id="ctxComparables"></div></div></div>
|
||||
<div class="section"><div class="card"><h2>Historical Score vs BTC Price</h2><div class="chart-dual"><canvas id="dualChart"></canvas></div></div></div>
|
||||
<div class="section"><div class="card"><h2>Score Bracket Performance</h2><div style="overflow-x:auto"><table><thead><tr><th>Score Range</th><th>Label</th><th>Days</th><th>Avg 30d</th><th>Avg 90d</th><th>Avg 1yr</th><th>Win Rate (1yr)</th><th>Max Gain</th><th>Max Loss</th><th>Avg Max DD</th></tr></thead><tbody id="bracketBody"></tbody></table></div></div></div>
|
||||
<div class="section"><div class="card"><h2>Score Bracket Performance</h2><div style="overflow-x:auto"><table><thead><tr><th>Score Range</th><th>Label</th><th>Days</th><th>Avg 30d</th><th>Avg 90d</th><th>Avg 180d</th><th>Avg 1yr</th><th>Win Rate (1yr)</th><th>Max Gain</th><th>Max Loss</th><th>Avg Max DD</th></tr></thead><tbody id="bracketBody"></tbody></table></div></div></div>
|
||||
<div class="section"><div class="card"><h2>Major Signal Events (Score Crossed 70/80/90+)</h2><div id="signalEvents"></div></div></div>
|
||||
`;
|
||||
renderContext();
|
||||
@@ -1246,9 +1246,12 @@ function renderContext() {
|
||||
document.getElementById('ctxPercentile').textContent =
|
||||
'Historical percentile: top ' + (100 - ctx.percentile).toFixed(1) + '% of all days (' + ctx.comparable_days + ' comparable days found)';
|
||||
if (ctx.avg_1yr_return != null) {
|
||||
document.getElementById('ctxReturn').textContent =
|
||||
'Average 1-year return from this score level: ' + fmtPct(ctx.avg_1yr_return);
|
||||
document.getElementById('ctxReturn').className = 'context-return ' + retClass(ctx.avg_1yr_return);
|
||||
let retHtml = 'Average returns from this score level: ';
|
||||
if (ctx.avg_30d_return != null) retHtml += '<span class="' + retClass(ctx.avg_30d_return) + '">30d: ' + fmtPct(ctx.avg_30d_return) + '</span> · ';
|
||||
if (ctx.avg_90d_return != null) retHtml += '<span class="' + retClass(ctx.avg_90d_return) + '">90d: ' + fmtPct(ctx.avg_90d_return) + '</span> · ';
|
||||
if (ctx.avg_180d_return != null) retHtml += '<span class="' + retClass(ctx.avg_180d_return) + '">180d: ' + fmtPct(ctx.avg_180d_return) + '</span> · ';
|
||||
retHtml += '<span class="' + retClass(ctx.avg_1yr_return) + '"><strong>1yr: ' + fmtPct(ctx.avg_1yr_return) + '</strong></span>';
|
||||
document.getElementById('ctxReturn').innerHTML = retHtml;
|
||||
}
|
||||
// Examples
|
||||
const list = document.getElementById('ctxComparables');
|
||||
@@ -1259,6 +1262,8 @@ function renderContext() {
|
||||
html += '<div class="comparable-item"><span>' + ex.date + ' — Score ' + ex.score + ' — ' + fmtPrice(ex.price) + '</span>';
|
||||
html += '<span>';
|
||||
if (fr['30d'] != null) html += '<span class="' + retClass(fr['30d']) + '">30d: ' + fmtPct(fr['30d']) + '</span> ';
|
||||
if (fr['90d'] != null) html += '<span class="' + retClass(fr['90d']) + '">90d: ' + fmtPct(fr['90d']) + '</span> ';
|
||||
if (fr['180d'] != null) html += '<span class="' + retClass(fr['180d']) + '">180d: ' + fmtPct(fr['180d']) + '</span> ';
|
||||
if (fr['365d'] != null) html += '<span class="' + retClass(fr['365d']) + '">1yr: ' + fmtPct(fr['365d']) + '</span>';
|
||||
html += '</span></div>';
|
||||
}
|
||||
@@ -1362,6 +1367,7 @@ function renderBracketTable() {
|
||||
html += '<td>' + b.days + '</td>';
|
||||
html += '<td class="' + retClass(b.avg_30d) + '">' + fmtPct(b.avg_30d) + '</td>';
|
||||
html += '<td class="' + retClass(b.avg_90d) + '">' + fmtPct(b.avg_90d) + '</td>';
|
||||
html += '<td class="' + retClass(b.avg_180d) + '">' + fmtPct(b.avg_180d) + '</td>';
|
||||
html += '<td class="' + retClass(b.avg_365d) + '">' + fmtPct(b.avg_365d) + '</td>';
|
||||
html += '<td>' + (b.win_rate_365d != null ? b.win_rate_365d + '%' : '--') + '</td>';
|
||||
html += '<td class="t-green">' + fmtPct(b.max_gain_365d) + '</td>';
|
||||
@@ -1390,6 +1396,7 @@ function renderSignalEvents() {
|
||||
const fr = ev.forward_returns || {};
|
||||
if (fr['30d'] != null) html += '<span class="' + retClass(fr['30d']) + '">30d: ' + fmtPct(fr['30d']) + '</span>';
|
||||
if (fr['90d'] != null) html += '<span class="' + retClass(fr['90d']) + '">90d: ' + fmtPct(fr['90d']) + '</span>';
|
||||
if (fr['180d'] != null) html += '<span class="' + retClass(fr['180d']) + '">180d: ' + fmtPct(fr['180d']) + '</span>';
|
||||
if (fr['365d'] != null) html += '<span class="' + retClass(fr['365d']) + '">1yr: ' + fmtPct(fr['365d']) + '</span>';
|
||||
if (ev.price_365d) html += '<span style="color:var(--text-dim)">Price 1yr: ' + fmtPrice(ev.price_365d) + '</span>';
|
||||
html += '</div></div>';
|
||||
|
||||
Reference in New Issue
Block a user