From 90bbb9955cac4362e43b8a32c1c32cbec101536b Mon Sep 17 00:00:00 2001 From: Lucifer Date: Sat, 15 Aug 2026 22:23:49 +0530 Subject: [PATCH] fix(dashboard): anchor forecast series and correct fan chart trace layering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Shifted future business day generation by +1 BDay and prepended to p50, p10, and p90 using np.insert(), so it elminated duplicate timestamps and visual step discontinuity at the boundary. 2. Replaced deprecated DatetimeIndex.append() with .union() to prevent runtime Future Warning. 3. and I reordered Plotly trace definitions to draw the semi-transparent 10%–90% CI polygon first so making sure the historical and median forecast lines render sharply on top. --- dashboard/app.py | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/dashboard/app.py b/dashboard/app.py index dde43b6..8bfb559 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -25,11 +25,16 @@ def load_predictions() -> dict: """Load latest model outputs from DB. Replace with real DB query.""" n = 60 t_hist = pd.date_range("2024-01-01", periods=120, freq="B") - t_fut = pd.date_range(t_hist[-1], periods=n, freq="B") price_hist = np.cumprod(1 + np.random.randn(120) * 0.01) * 100 - p50 = price_hist[-1] * np.cumprod(1 + np.random.randn(n) * 0.008) - p10 = p50 * 0.92 - p90 = p50 * 1.08 + t_fut_future = pd.date_range(t_hist[-1] + pd.offsets.BDay(1), periods=n, freq="B") + raw_forecast = np.cumprod(1 + np.random.randn(n) * 0.008) + p50_future = price_hist[-1] * raw_forecast + + # Last historical point + t_fut = t_hist[-1:].union(t_fut_future) + p50 = np.insert(p50_future, 0, price_hist[-1]) + p10 = np.insert(p50_future * 0.92, 0, price_hist[-1]) + p90 = np.insert(p50_future * 1.08, 0, price_hist[-1]) return { "sharpe": 1.72, "sharpe_delta": 0.08, "mfg_residual": 0.0073, @@ -67,17 +72,23 @@ def load_predictions() -> dict: # ── Price fan chart ─────────────────────────────────────────────────────────── fig_price = go.Figure() +# 1. CI Band first +fig_price.add_trace(go.Scatter( + x=list(preds["t_fut"]) + list(reversed(preds["t_fut"])), + y=list(preds["p10"]) + list(reversed(preds["p90"])), + fill="toself", fillcolor="rgba(93,173,226,0.18)", + line=dict(color="rgba(255,255,255,0)"), hoverinfo="skip", name="10%–90% CI")) + +# 2. Historical line fig_price.add_trace(go.Scatter( x=preds["t_hist"], y=preds["price_hist"], name="Historical", line=dict(color="white", width=2))) + +# 3. Median line fig_price.add_trace(go.Scatter( x=preds["t_fut"], y=preds["p50"], name="MFG Median", line=dict(color="orange", width=2))) -fig_price.add_trace(go.Scatter( - x=list(preds["t_fut"]) + list(reversed(preds["t_fut"])), - y=list(preds["p10"]) + list(reversed(preds["p90"])), - fill="toself", fillcolor="rgba(93,173,226,0.15)", - line=dict(color="rgba(255,255,255,0)"), name="10%–90% CI")) + fig_price.update_layout(template="plotly_dark", title="60-Day Price Forecast (MFG equilibrium paths)") st.plotly_chart(fig_price, use_container_width=True)