Ahora el Step 3 esta parcialmente acabado, y vamos a pasar a realizar el Step 3.5 donde añadimos el regimen de mercado
This commit is contained in:
0
src/core/market_regime.py
Normal file
0
src/core/market_regime.py
Normal file
@@ -734,98 +734,342 @@ async function pollStatus(jobId) {
|
|||||||
// =================================================
|
// =================================================
|
||||||
|
|
||||||
function renderEquityAndReturns(strategyId, s, data) {
|
function renderEquityAndReturns(strategyId, s, data) {
|
||||||
|
const equity = s.window_equity || [];
|
||||||
|
const ret = s.window_returns_pct || [];
|
||||||
|
const trd = s.window_trades || [];
|
||||||
|
|
||||||
console.log("Plotly object:", Plotly); // Esto debería mostrarte el objeto Plotly completo
|
// X común (windows)
|
||||||
|
const n = Math.max(equity.length, ret.length, trd.length);
|
||||||
|
const x = Array.from({ length: n }, (_, i) => i);
|
||||||
|
|
||||||
if (Plotly && Plotly.subplots) {
|
// ---- Escalado para alinear visualmente el 0 (returns) con el 0 (trades) ----
|
||||||
console.log("make_subplots is available");
|
const retMax = Math.max(0, ...ret);
|
||||||
} else {
|
const retMin = Math.min(0, ...ret);
|
||||||
console.error("make_subplots is NOT available");
|
|
||||||
|
const minTrades = data.config?.wf?.min_trades_test ?? 10;
|
||||||
|
|
||||||
|
const trdMaxRaw = Math.max(0, ...trd);
|
||||||
|
const trdMax = Math.max(trdMaxRaw, minTrades);
|
||||||
|
|
||||||
|
const retPosSpan = Math.max(1e-9, retMax);
|
||||||
|
const retNegSpan = Math.abs(retMin);
|
||||||
|
|
||||||
|
const trdNegSpan = (retNegSpan / retPosSpan) * trdMax;
|
||||||
|
|
||||||
|
const y1Range = [retMin, retMax];
|
||||||
|
const y2Range = [-trdNegSpan, trdMax];
|
||||||
|
|
||||||
|
// ---- Trazas ----
|
||||||
|
const equityTrace = {
|
||||||
|
x,
|
||||||
|
y: equity,
|
||||||
|
type: "scatter",
|
||||||
|
mode: "lines",
|
||||||
|
name: "Equity",
|
||||||
|
xaxis: "x",
|
||||||
|
yaxis: "y"
|
||||||
|
};
|
||||||
|
|
||||||
|
const returnsTrace = {
|
||||||
|
x,
|
||||||
|
y: ret,
|
||||||
|
type: "bar",
|
||||||
|
name: "Return %",
|
||||||
|
marker: { color: "#3b82f6" },
|
||||||
|
xaxis: "x2",
|
||||||
|
yaxis: "y2",
|
||||||
|
offsetgroup: "returns",
|
||||||
|
alignmentgroup: "bottom"
|
||||||
|
};
|
||||||
|
|
||||||
|
const tradesTrace = {
|
||||||
|
x,
|
||||||
|
y: trd,
|
||||||
|
type: "bar",
|
||||||
|
name: "Trades",
|
||||||
|
marker: { color: "#f59e0b" },
|
||||||
|
xaxis: "x2",
|
||||||
|
yaxis: "y3",
|
||||||
|
offsetgroup: "trades",
|
||||||
|
alignmentgroup: "bottom"
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- Layout ----
|
||||||
|
const layout = {
|
||||||
|
grid: { rows: 2, columns: 1, pattern: "independent" },
|
||||||
|
|
||||||
|
// IMPORTANTE: share X de verdad (pan/zoom sincronizado)
|
||||||
|
xaxis: { matches: "x2", showgrid: true },
|
||||||
|
xaxis2: { matches: "x", showgrid: true },
|
||||||
|
|
||||||
|
// Dominios (más altura para ver mejor)
|
||||||
|
// Ajusta estos números a gusto:
|
||||||
|
yaxis: {
|
||||||
|
domain: [0.60, 1.00],
|
||||||
|
title: "Equity",
|
||||||
|
showgrid: true,
|
||||||
|
rangemode: "tozero"
|
||||||
|
},
|
||||||
|
xaxis: {
|
||||||
|
domain: [0.00, 1.00],
|
||||||
|
anchor: "y"
|
||||||
|
},
|
||||||
|
|
||||||
|
yaxis2: {
|
||||||
|
domain: [0.00, 0.25],
|
||||||
|
title: "Return %",
|
||||||
|
range: y1Range,
|
||||||
|
zeroline: true,
|
||||||
|
zerolinewidth: 2,
|
||||||
|
showgrid: true
|
||||||
|
},
|
||||||
|
xaxis2: {
|
||||||
|
domain: [0.00, 1.00],
|
||||||
|
anchor: "y2",
|
||||||
|
title: "Windows"
|
||||||
|
},
|
||||||
|
|
||||||
|
yaxis3: {
|
||||||
|
title: "Trades",
|
||||||
|
overlaying: "y2",
|
||||||
|
side: "right",
|
||||||
|
range: y2Range,
|
||||||
|
zeroline: true,
|
||||||
|
zerolinewidth: 2,
|
||||||
|
showgrid: false
|
||||||
|
},
|
||||||
|
|
||||||
|
// Barras lado a lado
|
||||||
|
barmode: "group",
|
||||||
|
bargap: 0.2,
|
||||||
|
|
||||||
|
shapes: [
|
||||||
|
{
|
||||||
|
type: "line",
|
||||||
|
x0: -0.5,
|
||||||
|
x1: n - 0.5,
|
||||||
|
y0: minTrades,
|
||||||
|
y1: minTrades,
|
||||||
|
xref: "x2",
|
||||||
|
yref: "y3",
|
||||||
|
line: { color: "red", width: 2, dash: "dash" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
legend: { orientation: "h" },
|
||||||
|
margin: { t: 50, r: 70, l: 70, b: 50 }
|
||||||
|
};
|
||||||
|
|
||||||
|
const topDomain = layout.yaxis.domain; // [start,end]
|
||||||
|
const botDomain = layout.yaxis2.domain; // [start,end]
|
||||||
|
|
||||||
|
layout.annotations = [
|
||||||
|
{
|
||||||
|
text: `Equity — ${strategyId}`,
|
||||||
|
x: 0.5, xref: "paper",
|
||||||
|
y: topDomain[1] + 0.05, yref: "paper",
|
||||||
|
showarrow: false,
|
||||||
|
font: { size: 16 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: `Returns & Trades — ${strategyId}`,
|
||||||
|
x: 0.5, xref: "paper",
|
||||||
|
y: botDomain[1] + 0.05, yref: "paper",
|
||||||
|
showarrow: false,
|
||||||
|
font: { size: 16 }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
Plotly.newPlot("plot_strategy", [equityTrace, returnsTrace, tradesTrace], layout, {
|
||||||
|
displayModeBar: true,
|
||||||
|
responsive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const gd = document.getElementById("plot_strategy");
|
||||||
|
|
||||||
|
// Limpia listeners anteriores (si re-renderizas)
|
||||||
|
gd.removeAllListeners?.("plotly_relayout");
|
||||||
|
|
||||||
|
// Flag para evitar bucles cuando hacemos relayout desde el listener
|
||||||
|
gd.__syncing = false;
|
||||||
|
|
||||||
|
function clamp01(v) {
|
||||||
|
return Math.max(0, Math.min(1, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crea el subplot con dos filas y una columna (1x2)
|
function zeroFrac(min, max) {
|
||||||
var fig = Plotly.subplots.make_subplots({
|
const span = max - min;
|
||||||
rows: 2, // 2 filas
|
if (!isFinite(span) || span <= 1e-12) return 0;
|
||||||
cols: 1, // 1 columna
|
return clamp01((0 - min) / span);
|
||||||
shared_xaxes: true, // Compartir el eje X entre ambos gráficos
|
}
|
||||||
vertical_spacing: 0.1, // Espacio entre los gráficos
|
|
||||||
subplot_titles: [`Equity — ${strategyId}`, `Returns & Trades — ${strategyId}`], // Títulos para cada subgráfico
|
// Dado un max fijo y una fracción f (posición del 0), calcula el min
|
||||||
column_widths: [0.7] // Ajustar el ancho de las columnas si es necesario
|
function minFromMaxAndFrac(max, f) {
|
||||||
|
const denom = Math.max(1e-9, (1 - f));
|
||||||
|
return -(f / denom) * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
gd.on("plotly_relayout", (ev) => {
|
||||||
|
if (gd.__syncing) return;
|
||||||
|
|
||||||
|
const update = {};
|
||||||
|
|
||||||
|
// ===== 0) DETECTAR AUTOSCALE / RESET (prioridad máxima) =====
|
||||||
|
// Plotly puede indicar autoscale de varias maneras.
|
||||||
|
const autoscaleTriggered =
|
||||||
|
ev["yaxis2.autorange"] === true ||
|
||||||
|
ev["yaxis3.autorange"] === true ||
|
||||||
|
ev["yaxis.autorange"] === true ||
|
||||||
|
ev["xaxis.autorange"] === true ||
|
||||||
|
ev["xaxis2.autorange"] === true ||
|
||||||
|
ev["autosize"] === true ||
|
||||||
|
ev["resetScale2d"] === true ||
|
||||||
|
ev["xaxis.range[0]"] === undefined && ev["xaxis.range[1]"] === undefined && ev["yaxis.range[0]"] === undefined && ev["yaxis.range[1]"] === undefined
|
||||||
|
? false
|
||||||
|
: false;
|
||||||
|
|
||||||
|
// Si el usuario pulsa Autoscale/Reset, queremos SIEMPRE:
|
||||||
|
// - Reforzar yaxis2/yaxis3 con tus rangos calculados (0 alineado)
|
||||||
|
// - Volver a dibujar la línea minTrades
|
||||||
|
// - (Opcional) dejar X en autorange en ambos
|
||||||
|
if (
|
||||||
|
ev["yaxis2.autorange"] === true ||
|
||||||
|
ev["yaxis3.autorange"] === true ||
|
||||||
|
ev["yaxis.autorange"] === true ||
|
||||||
|
ev["xaxis.autorange"] === true ||
|
||||||
|
ev["xaxis2.autorange"] === true ||
|
||||||
|
ev["resetScale2d"] === true
|
||||||
|
) {
|
||||||
|
// update["yaxis2.autorange"] = false;
|
||||||
|
// update["yaxis3.autorange"] = false;
|
||||||
|
update["yaxis2.range"] = y1Range;
|
||||||
|
update["yaxis3.range"] = y2Range;
|
||||||
|
|
||||||
|
// Asegura que la línea de Min Trades se vea SIEMPRE
|
||||||
|
update["shapes[0].type"] = "line";
|
||||||
|
update["shapes[0].xref"] = "x2";
|
||||||
|
update["shapes[0].yref"] = "y3";
|
||||||
|
update["shapes[0].x0"] = -0.5;
|
||||||
|
update["shapes[0].x1"] = n - 0.5;
|
||||||
|
update["shapes[0].y0"] = minTrades;
|
||||||
|
update["shapes[0].y1"] = minTrades;
|
||||||
|
update["shapes[0].line.color"] = "red";
|
||||||
|
update["shapes[0].line.width"] = 2;
|
||||||
|
update["shapes[0].line.dash"] = "dash";
|
||||||
|
|
||||||
|
// Mantén X sincronizado también tras autoscale
|
||||||
|
// if (ev["xaxis.autorange"] === true || ev["xaxis2.autorange"] === true || ev["resetScale2d"] === true) {
|
||||||
|
// update["xaxis.autorange"] = true;
|
||||||
|
// update["xaxis2.autorange"] = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
gd.__syncing = true;
|
||||||
|
Plotly.relayout(gd, update).finally(() => {
|
||||||
|
gd.__syncing = false;
|
||||||
|
});
|
||||||
|
return; // IMPORTANTÍSIMO: no seguimos con el resto de sincronizaciones
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 1) Sync X arriba/abajo (pan/zoom normal) =====
|
||||||
|
if (ev["xaxis2.range[0]"] !== undefined && ev["xaxis2.range[1]"] !== undefined) {
|
||||||
|
update["xaxis.range"] = [ev["xaxis2.range[0]"], ev["xaxis2.range[1]"]];
|
||||||
|
update["xaxis.autorange"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev["xaxis.range[0]"] !== undefined && ev["xaxis.range[1]"] !== undefined) {
|
||||||
|
update["xaxis2.range"] = [ev["xaxis.range[0]"], ev["xaxis.range[1]"]];
|
||||||
|
update["xaxis2.autorange"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 2) Mantener 0 alineado cuando pan/zoom en Y2 o Y3 =====
|
||||||
|
const y2Changed = ev["yaxis2.range[0]"] !== undefined && ev["yaxis2.range[1]"] !== undefined;
|
||||||
|
const y3Changed = ev["yaxis3.range[0]"] !== undefined && ev["yaxis3.range[1]"] !== undefined;
|
||||||
|
|
||||||
|
const full = gd._fullLayout || {};
|
||||||
|
const curY2 = full.yaxis2?.range || y1Range;
|
||||||
|
const curY3 = full.yaxis3?.range || y2Range;
|
||||||
|
|
||||||
|
if (y2Changed && !y3Changed) {
|
||||||
|
const newY2 = [ev["yaxis2.range[0]"], ev["yaxis2.range[1]"]];
|
||||||
|
const f = zeroFrac(newY2[0], newY2[1]);
|
||||||
|
|
||||||
|
const y3MaxKeep = (curY3 && curY3.length === 2) ? curY3[1] : y2Range[1];
|
||||||
|
const y3Min = minFromMaxAndFrac(y3MaxKeep, f);
|
||||||
|
|
||||||
|
update["yaxis2.autorange"] = false;
|
||||||
|
update["yaxis3.autorange"] = false;
|
||||||
|
update["yaxis2.range"] = newY2;
|
||||||
|
update["yaxis3.range"] = [y3Min, y3MaxKeep];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y3Changed && !y2Changed) {
|
||||||
|
const newY3 = [ev["yaxis3.range[0]"], ev["yaxis3.range[1]"]];
|
||||||
|
const f = zeroFrac(newY3[0], newY3[1]);
|
||||||
|
|
||||||
|
const y2MaxKeep = (curY2 && curY2.length === 2) ? curY2[1] : y1Range[1];
|
||||||
|
const y2Min = minFromMaxAndFrac(y2MaxKeep, f);
|
||||||
|
|
||||||
|
update["yaxis2.autorange"] = false;
|
||||||
|
update["yaxis3.autorange"] = false;
|
||||||
|
update["yaxis3.range"] = newY3;
|
||||||
|
update["yaxis2.range"] = [y2Min, y2MaxKeep];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si ambos cambian (zoom box), prioriza el zeroFrac de y2
|
||||||
|
if (y2Changed && y3Changed) {
|
||||||
|
const newY2 = [ev["yaxis2.range[0]"], ev["yaxis2.range[1]"]];
|
||||||
|
const f = zeroFrac(newY2[0], newY2[1]);
|
||||||
|
|
||||||
|
const newY3 = [ev["yaxis3.range[0]"], ev["yaxis3.range[1]"]];
|
||||||
|
const y3MaxKeep = newY3[1];
|
||||||
|
const y3Min = minFromMaxAndFrac(y3MaxKeep, f);
|
||||||
|
|
||||||
|
update["yaxis2.autorange"] = false;
|
||||||
|
update["yaxis3.autorange"] = false;
|
||||||
|
update["yaxis2.range"] = newY2;
|
||||||
|
update["yaxis3.range"] = [y3Min, y3MaxKeep];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 3) Re-asegurar la línea minTrades (por si Plotly la toca en relayouts) =====
|
||||||
|
// (No molesta y evita que desaparezca en algunos reset)
|
||||||
|
update["shapes[0].y0"] = minTrades;
|
||||||
|
update["shapes[0].y1"] = minTrades;
|
||||||
|
|
||||||
|
if (Object.keys(update).length === 0) return;
|
||||||
|
|
||||||
|
gd.__syncing = true;
|
||||||
|
Plotly.relayout(gd, update).finally(() => {
|
||||||
|
gd.__syncing = false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Datos de Equity
|
|
||||||
const equityTrace = {
|
|
||||||
y: s.window_equity, // Datos de la equity
|
|
||||||
type: "scatter", // Tipo de gráfico: línea
|
|
||||||
mode: "lines", // Modo: línea
|
|
||||||
name: "Equity"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Datos de Return %
|
|
||||||
const returnsTrace = {
|
|
||||||
y: s.window_returns_pct || [], // Datos de returns
|
|
||||||
type: "bar", // Tipo de gráfico: barra
|
|
||||||
name: "Return %",
|
|
||||||
marker: { color: "#3b82f6" }, // Color de la barra
|
|
||||||
yaxis: "y1", // Asociar al primer eje Y
|
|
||||||
};
|
|
||||||
|
|
||||||
// Datos de Trades
|
|
||||||
const tradesTrace = {
|
|
||||||
y: s.window_trades || [], // Datos de trades
|
|
||||||
type: "bar", // Tipo de gráfico: barra
|
|
||||||
name: "Trades",
|
|
||||||
marker: { color: "#f59e0b" }, // Color de la barra
|
|
||||||
yaxis: "y2", // Asociar al segundo eje Y
|
|
||||||
};
|
|
||||||
|
|
||||||
// Agregar la traza de "Equity" al subplot (fila 1, columna 1)
|
|
||||||
fig.addTrace(equityTrace, 1, 1);
|
|
||||||
|
|
||||||
// Agregar las trazas de "Returns" y "Trades" al subplot (fila 2, columna 1)
|
|
||||||
fig.addTrace(returnsTrace, 2, 1);
|
|
||||||
fig.addTrace(tradesTrace, 2, 1);
|
|
||||||
|
|
||||||
// Configurar los ejes y los márgenes
|
|
||||||
fig.update_layout({
|
|
||||||
title: `Strategy Overview — ${strategyId}`,
|
|
||||||
yaxis: {
|
|
||||||
title: "Equity",
|
|
||||||
showgrid: true
|
|
||||||
},
|
|
||||||
yaxis2: {
|
|
||||||
title: "Return % / Trades",
|
|
||||||
overlaying: "y",
|
|
||||||
side: "right",
|
|
||||||
showgrid: true
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
title: "Windows",
|
|
||||||
showgrid: true
|
|
||||||
},
|
|
||||||
showlegend: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Renderizar en el contenedor del gráfico
|
|
||||||
Plotly.newPlot("plot_strategy", fig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderRollingSharpe(strategyId, s, data) {
|
function renderRollingSharpe(strategyId, s, data) {
|
||||||
const roll = s.diagnostics?.rolling?.rolling_sharpe_like || [];
|
const roll = s.diagnostics?.rolling?.rolling_sharpe_like || [];
|
||||||
const x = roll.map((_, i) => i + 1);
|
const x = roll.map((_, i) => i + 1);
|
||||||
|
|
||||||
Plotly.newPlot("plot_strategy", [{
|
const k = s.diagnostics?.rolling?.rolling_window ?? "?";
|
||||||
x,
|
|
||||||
y: roll,
|
Plotly.newPlot(
|
||||||
type: "scatter",
|
"plot_strategy",
|
||||||
mode: "lines+markers",
|
[
|
||||||
name: `Rolling Sharpe-like (k=${s.diagnostics?.rolling?.rolling_window ?? "?"})`
|
{
|
||||||
}], {
|
x,
|
||||||
margin: { t: 40 },
|
y: roll,
|
||||||
title: `Rolling Sharpe-like — ${strategyId}`,
|
type: "scatter",
|
||||||
xaxis: { title: "Window" },
|
mode: "lines+markers",
|
||||||
yaxis: { title: "Sharpe-like" }
|
name: `Rolling Sharpe-like (k=${k})`
|
||||||
});
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
margin: { t: 60, r: 40, l: 60, b: 50 },
|
||||||
|
title: { text: `Rolling Sharpe-like — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
|
||||||
|
xaxis: { title: "Window", showgrid: true, zeroline: false },
|
||||||
|
yaxis: { title: "Sharpe-like", showgrid: true, zeroline: true, zerolinewidth: 2 },
|
||||||
|
legend: { orientation: "h" }
|
||||||
|
},
|
||||||
|
{ responsive: true, displayModeBar: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderOOSReturnsDistribution(strategyId, s, data) {
|
function renderOOSReturnsDistribution(strategyId, s, data) {
|
||||||
@@ -841,7 +1085,7 @@ function renderOOSReturnsDistribution(strategyId, s, data) {
|
|||||||
name: "OOS Return% (bins)"
|
name: "OOS Return% (bins)"
|
||||||
}], {
|
}], {
|
||||||
margin: { t: 40 },
|
margin: { t: 40 },
|
||||||
title: `OOS Returns Distribution — ${strategyId}`,
|
title: { text: `OOS Returns Distribution — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
|
||||||
xaxis: { title: "Return % (window)" },
|
xaxis: { title: "Return % (window)" },
|
||||||
yaxis: { title: "Count" }
|
yaxis: { title: "Count" }
|
||||||
});
|
});
|
||||||
@@ -859,7 +1103,7 @@ function renderDrawdownEvolution(strategyId, s, data) {
|
|||||||
name: "Drawdown %"
|
name: "Drawdown %"
|
||||||
}], {
|
}], {
|
||||||
margin: { t: 40 },
|
margin: { t: 40 },
|
||||||
title: `Drawdown Evolution — ${strategyId}`,
|
title: { text: `Drawdown Evolution — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
|
||||||
xaxis: { title: "Point (initial + windows)" },
|
xaxis: { title: "Point (initial + windows)" },
|
||||||
yaxis: { title: "Drawdown %", zeroline: true }
|
yaxis: { title: "Drawdown %", zeroline: true }
|
||||||
});
|
});
|
||||||
@@ -888,7 +1132,7 @@ function renderTradeDensity(strategyId, s, data) {
|
|||||||
}
|
}
|
||||||
], {
|
], {
|
||||||
margin: { t: 40 },
|
margin: { t: 40 },
|
||||||
title: `Trade Density — ${strategyId}`,
|
title: { text: `Trade Density — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico,
|
||||||
barmode: "group",
|
barmode: "group",
|
||||||
xaxis: { title: "Window" },
|
xaxis: { title: "Window" },
|
||||||
yaxis: { title: "Trades / window" },
|
yaxis: { title: "Trades / window" },
|
||||||
|
|||||||
@@ -27,9 +27,9 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta19/dist/js/tabler.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta19/dist/js/tabler.min.js"></script>
|
||||||
|
|
||||||
<!-- UI v2 core JS -->
|
<!-- UI v2 core JS -->
|
||||||
<script src="/static/js/app.js"></script>
|
<!-- <script src="/static/js/app.js"></script>
|
||||||
<script src="/static/js/api.js"></script>
|
<script src="/static/js/api.js"></script>
|
||||||
<script src="/static/js/router.js"></script>
|
<script src="/static/js/router.js"></script> -->
|
||||||
|
|
||||||
{% block extra_js %}{% endblock %}
|
{% block extra_js %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -317,7 +317,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
<div id="plot_strategy" style="height: 320px;"></div>
|
<div id="plot_strategy" style="height: 700px; width: 100%;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
@@ -348,6 +348,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/js/plotly.js"></script>
|
<script src="https://cdn.plot.ly/plotly-3.3.1.min.js"></script>
|
||||||
<script src="/static/js/pages/calibration_strategies.js"></script>
|
<script src="/static/js/pages/calibration_strategies.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user