"""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