"""Plotly chart: recent closes, the forecast and its 95% band.""" from __future__ import annotations import plotly.graph_objects as go from bbca.service import Prediction INK = "#E8EEF4" MUTED = "#7F8FA0" GRID = "#1C2631" ACCENT = "#C8F53C" BAND = "rgba(200, 245, 60, 0.14)" LIVE = "#FFB547" SHOW_SESSIONS = 120 def _money(x: float) -> str: return f"Rp {x:,.0f}" def build(pred: Prediction) -> go.Figure: closes = pred.prices.closes.iloc[-SHOW_SESSIONS:] fc = pred.forecast base_x = [fc.base_date] base_y = [fc.base_price] fx = list(fc.dates) fig = go.Figure() fig.add_trace(go.Scatter( x=base_x + fx + fx[::-1] + base_x, y=base_y + list(fc.upper) + list(fc.lower[::-1]) + base_y, mode="lines", fill="toself", fillcolor=BAND, line=dict(width=0), hoverinfo="skip", name="95% range", )) fig.add_trace(go.Scatter( x=closes.index, y=closes.values, mode="lines", name="Close", line=dict(color=INK, width=1.6), hovertemplate="%{x|%d %b %Y}
%{y:,.0f}close", )) fig.add_trace(go.Scatter( x=base_x + fx, y=base_y + list(fc.mean), mode="lines+markers", name="Forecast", line=dict(color=ACCENT, width=2, dash="dot"), marker=dict(size=[0] + [7] * len(fx), color=ACCENT), hovertemplate="%{x|%d %b %Y}
%{y:,.0f}forecast", )) if fc.base_is_live: fig.add_trace(go.Scatter( x=base_x, y=base_y, mode="markers", name="Running price", marker=dict(size=10, color=LIVE, line=dict(color="#0A0E13", width=2)), hovertemplate=f"today, intraday
{_money(fc.base_price)}", )) fig.update_layout( height=400, margin=dict(l=8, r=8, t=8, b=8), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", font=dict(family="IBM Plex Mono, ui-monospace, monospace", color=MUTED, size=12), hovermode="x unified", hoverlabel=dict(bgcolor="#111821", bordercolor=GRID, font=dict(color=INK)), legend=dict(orientation="h", y=1.02, x=0, bgcolor="rgba(0,0,0,0)"), xaxis=dict( gridcolor=GRID, zeroline=False, showline=False, rangebreaks=[dict(bounds=["sat", "mon"])], ), yaxis=dict(gridcolor=GRID, zeroline=False, tickformat=",.0f", side="right"), ) return fig def empty() -> go.Figure: fig = go.Figure() fig.update_layout( height=400, margin=dict(l=8, r=8, t=8, b=8), paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)", xaxis=dict(visible=False), yaxis=dict(visible=False), annotations=[dict(text="chart appears after the first run", showarrow=False, font=dict(family="IBM Plex Mono, monospace", color=MUTED, size=13))], ) return fig