"""HTML pieces for the page. Every dynamic string goes through esc().""" from __future__ import annotations import html import math import pandas as pd from bbca.data import TZ from bbca.backtest import Backtest from bbca.model import Forecast from bbca.service import Prediction def esc(value) -> str: return html.escape(str(value), quote=True) def rp(x: float) -> str: return f"Rp {x:,.0f}" def pct(x: float, signed: bool = False) -> str: if x is None or (isinstance(x, float) and math.isnan(x)): return "n/a" return f"{x * 100:+.2f}%" if signed else f"{x * 100:.2f}%" def day(ts: pd.Timestamp) -> str: return pd.Timestamp(ts).strftime("%a %d %b %Y") def placeholder() -> str: return ( '
Waiting for a run
' "Press Get fresh forecast. The app pulls the latest BBCA prices, " "refits the model and shows where the next closes are likely to land.
" "No forecast
{esc(message)}
{esc(base_label)}
{esc(rp(base_value))}
{esc(base_date)}
Forecast · {esc(day(fc.dates[0]))}
{esc(rp(nxt))}
{esc(pct(move, signed=True))} vs {esc(base_label.lower())}
95% range
{esc(rp(fc.lower[0]))} – {esc(rp(fc.upper[0]))}
±{esc(pct(width / 2))} around the forecast
Forecast by session
' '| Session | Close | vs start | 95% range | " "
|---|
Weekdays only. IDX holidays are not skipped, so a date can land on a day the market is closed. ' "The range widens fast: past a few days it says more about uncertainty than about price.
How it did over the last {len(b.actual)} sessions
{esc(verdict)}
| MAPE | MAE | |
|---|---|---|
| SARIMA, next-day | {esc(pct(b.model_mape))} | {esc(rp(b.model_mae))} |
| Naive: same as last close | {esc(pct(b.naive_mape))} | {esc(rp(b.naive_mae))} |
Up/down called right: {esc(hit)}. The model is fitted before this window and only sees each real close after forecasting it. A low MAPE on daily prices is easy, since the naive guess already gets {esc(pct(b.naive_mape))}. Beating that is the real test.
Data: {esc(src)}, fetched {esc(p.fetched_at.strftime("%d %b %Y %H:%M"))} WIB. ' f"Model: SARIMA(0,1,0)(2,1,0)[5] on the last {pred.forecast.fit_points} sessions. " "A statistics exercise, not investment advice.
" )