Update status for using KubuntuPC
This commit is contained in:
@@ -795,7 +795,10 @@ function renderResultsTable(data) {
|
||||
<td>${Number(r.oos_total_return_pct).toFixed(2)}%</td>
|
||||
<td>${Number(r.oos_max_dd_worst_pct).toFixed(2)}%</td>
|
||||
<td>${Number(r.oos_final_equity).toFixed(2)}</td>
|
||||
<td class="text-secondary">${r.message || ""}</td>
|
||||
<td class="text-secondary">
|
||||
${r.message || ""}
|
||||
${Array.isArray(r.warnings) && r.warnings.length ? `<div class="mt-1"><small>${r.warnings.map(escapeHtml).join(" · ")}</small></div>` : ""}
|
||||
</td>
|
||||
</tr>
|
||||
`);
|
||||
});
|
||||
@@ -827,7 +830,10 @@ function populatePlotSelector(data) {
|
||||
if (!sel) return;
|
||||
|
||||
sel.innerHTML = "";
|
||||
const ids = Object.keys((data.series && data.series.strategies) ? data.series.strategies : {});
|
||||
|
||||
// ✅ usar results, no series (para que aparezcan también warning/fail)
|
||||
const ids = (data.results || []).map(r => r.strategy_id);
|
||||
|
||||
ids.forEach((sid) => {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = sid;
|
||||
@@ -835,43 +841,74 @@ function populatePlotSelector(data) {
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
|
||||
sel.onchange = () => renderPlotsForSelected(data);
|
||||
sel.onchange = () => {
|
||||
const sid = sel.value;
|
||||
selectStrategy(sid, data);
|
||||
};
|
||||
|
||||
if (ids.length > 0) {
|
||||
sel.value = ids[0];
|
||||
sel.value = selectedStrategyId || ids[0];
|
||||
}
|
||||
}
|
||||
|
||||
function renderPlotsForSelected(data) {
|
||||
const sel = document.getElementById("plot_strategy_select");
|
||||
const sid = sel ? sel.value : null;
|
||||
if (!sid) return;
|
||||
function selectStrategy(strategyId, data) {
|
||||
if (!strategyId || !data) return;
|
||||
|
||||
const s = data.series?.strategies?.[sid];
|
||||
if (!s) return;
|
||||
selectedStrategyId = strategyId;
|
||||
|
||||
const equity = s.window_equity || [];
|
||||
const returns = s.window_returns_pct || [];
|
||||
const xEq = [...Array(equity.length).keys()];
|
||||
const xRet = [...Array(returns.length).keys()].map((i) => i + 1);
|
||||
const row = (data.results || []).find(r => r.strategy_id === strategyId);
|
||||
|
||||
Plotly.newPlot("plot_equity", [
|
||||
{ x: xEq, y: equity, type: "scatter", mode: "lines", name: "Equity (OOS)" },
|
||||
], {
|
||||
title: `WF OOS equity · ${sid}`,
|
||||
margin: { t: 40, l: 50, r: 20, b: 40 },
|
||||
xaxis: { title: "Window index" },
|
||||
yaxis: { title: "Equity" },
|
||||
}, { displayModeBar: false });
|
||||
// 1) Alerts por status (siempre explícito)
|
||||
if (row?.status === "warning") {
|
||||
showPlotAlert("warning", `WARNING — ${strategyId}`, row.message || "Strategy warning.", row.warnings);
|
||||
} else if (row?.status === "fail") {
|
||||
showPlotAlert("danger", `FAIL — ${strategyId}`, row.message || "Strategy failed.", row.warnings);
|
||||
} else {
|
||||
clearPlotAlert();
|
||||
}
|
||||
|
||||
Plotly.newPlot("plot_returns", [
|
||||
{ x: xRet, y: returns, type: "bar", name: "Return % (per window)" },
|
||||
], {
|
||||
title: `WF returns per window · ${sid}`,
|
||||
margin: { t: 40, l: 50, r: 20, b: 40 },
|
||||
xaxis: { title: "Window" },
|
||||
yaxis: { title: "Return (%)" },
|
||||
}, { displayModeBar: false });
|
||||
// 2) Si el backend indica que NO hay serie, no intentamos renderizar
|
||||
if (row && row.series_available === false) {
|
||||
showPlotAlert(
|
||||
row.status === "fail" ? "danger" : "warning",
|
||||
`${(row.status || "warning").toUpperCase()} — ${strategyId}`,
|
||||
row.series_error || row.message || "No chart series available for this strategy.",
|
||||
row.warnings
|
||||
);
|
||||
clearPlots();
|
||||
highlightSelectedRow(strategyId);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) Renderizar serie si existe
|
||||
const s = data?.series?.strategies?.[strategyId];
|
||||
if (!s) {
|
||||
// fallback explícito (por si backend antiguo no manda series_available)
|
||||
showPlotAlert(
|
||||
row?.status === "fail" ? "danger" : "warning",
|
||||
`${(row?.status || "warning").toUpperCase()} — ${strategyId}`,
|
||||
row?.message || "No chart series available for this strategy.",
|
||||
row?.warnings
|
||||
);
|
||||
clearPlots();
|
||||
highlightSelectedRow(strategyId);
|
||||
return;
|
||||
}
|
||||
|
||||
renderStrategyCharts(strategyId, s, data);
|
||||
highlightSelectedRow(strategyId);
|
||||
|
||||
// 4) Caso “serie vacía” (opción B) -> warning explícito (aunque ya lo tengas)
|
||||
const trd = s.window_trades || [];
|
||||
const hasTrades = Array.isArray(trd) && trd.some(v => (v ?? 0) > 0);
|
||||
if (!hasTrades && row?.status !== "fail") {
|
||||
showPlotAlert(
|
||||
"warning",
|
||||
`NO TRADES — ${strategyId}`,
|
||||
"Walk-forward produced no closed trades in OOS. Charts may be flat/empty.",
|
||||
row?.warnings
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderValidateResponse(data) {
|
||||
@@ -902,26 +939,13 @@ function renderValidateResponse(data) {
|
||||
// -------------------------------
|
||||
// 3️⃣ Plots (primera estrategia por ahora)
|
||||
// -------------------------------
|
||||
if (data.series && data.series.strategies) {
|
||||
|
||||
const strategies = data.series.strategies;
|
||||
const keys = Object.keys(strategies);
|
||||
|
||||
if (!selectedStrategyId && keys.length > 0) {
|
||||
selectedStrategyId = keys[0];
|
||||
}
|
||||
|
||||
if (selectedStrategyId && strategies[selectedStrategyId]) {
|
||||
renderStrategyCharts(
|
||||
selectedStrategyId,
|
||||
strategies[selectedStrategyId],
|
||||
data
|
||||
);
|
||||
highlightSelectedRow(selectedStrategyId);
|
||||
if (data.results && data.results.length > 0) {
|
||||
if (!selectedStrategyId) {
|
||||
selectedStrategyId = data.results[0].strategy_id;
|
||||
}
|
||||
selectStrategy(selectedStrategyId, data);
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------
|
||||
// 4️⃣ Table
|
||||
// -------------------------------
|
||||
@@ -981,18 +1005,7 @@ function renderValidateResponse(data) {
|
||||
console.log("Clicked:", selectedStrategyId);
|
||||
|
||||
selectedStrategyId = this.dataset.strategy;
|
||||
|
||||
if (!lastValidationResult?.series?.strategies[selectedStrategyId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderStrategyCharts(
|
||||
selectedStrategyId,
|
||||
lastValidationResult.series.strategies[selectedStrategyId],
|
||||
lastValidationResult
|
||||
);
|
||||
|
||||
highlightSelectedRow(selectedStrategyId);
|
||||
selectStrategy(selectedStrategyId, lastValidationResult);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1307,6 +1320,65 @@ async function init() {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// PLOT ALERTS (Tabler) + SAFE PLOT CLEAR
|
||||
// =================================================
|
||||
|
||||
function ensurePlotAlertContainer() {
|
||||
// Lo colocamos antes del primer plot si existe
|
||||
let el = document.getElementById("plot_alert");
|
||||
if (el) return el;
|
||||
|
||||
const anchor = document.getElementById("plot_equity");
|
||||
if (!anchor || !anchor.parentElement) return null;
|
||||
|
||||
el = document.createElement("div");
|
||||
el.id = "plot_alert";
|
||||
el.className = "mb-3";
|
||||
anchor.parentElement.insertBefore(el, anchor);
|
||||
return el;
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str ?? "")
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function showPlotAlert(type, title, message, warnings) {
|
||||
const el = ensurePlotAlertContainer();
|
||||
if (!el) return;
|
||||
|
||||
const warnHtml = Array.isArray(warnings) && warnings.length
|
||||
? `<ul class="mb-0">${warnings.map(w => `<li>${escapeHtml(w)}</li>`).join("")}</ul>`
|
||||
: "";
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="alert alert-${type}" role="alert">
|
||||
<div>
|
||||
<h4 class="alert-title">${escapeHtml(title)}</h4>
|
||||
<div class="text-secondary">${escapeHtml(message || "")}</div>
|
||||
${warnHtml}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function clearPlotAlert() {
|
||||
const el = document.getElementById("plot_alert");
|
||||
if (el) el.innerHTML = "";
|
||||
}
|
||||
|
||||
function clearPlots() {
|
||||
const eq = document.getElementById("plot_equity");
|
||||
const ret = document.getElementById("plot_returns");
|
||||
if (eq) eq.innerHTML = "";
|
||||
if (ret) ret.innerHTML = "";
|
||||
}
|
||||
|
||||
document.getElementById("lock_inherited")
|
||||
.addEventListener("change", applyInheritedLock);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user