← Notes
June 10, 2026·9 min readai-agentscase-studyforecasting

I built an AI agent that predicts the next fuel-price hike

I built an AI agent that predicts the next fuel-price hike

Every Indonesian knows the feeling: you pull up to the pump and Pertamax is suddenly Rp800 more than last month. It feels random. It isn't.

Background, why I built this

Sudden fuel-price jumps are stressful precisely because they feel arbitrary. But market-priced fuel (Pertamax, Turbo, Dex) is re-priced on a published formula tied to world oil and the rupiah. If the inputs are public, the surprise is optional, you just have to do the math before Pertamina does. So I turned that math into an agent.

What

BBM Price Predictor, an agent that forecasts the next adjustment for each market-priced fuel: direction (↑/↓/→), the likely date, and an estimated price, with the drivers and a confidence number.

⛽ BBM forecast - effective Jul 2026

Pertamax Turbo (RON 98)
  Verdict:   ↓ TURUN   est ~Rp20,000  (range Rp18,900-21,050)
  Now:       Rp20,750   →  Δ Rp-762
  Drivers:   world oil + USD/IDR over the pricing window
  Confidence:90%  (verified Apr-Jun 2026 data)

Here's every market-priced fuel through 2026, the agent tracks them all, and you can see the hikes it has to explain:

Every market-priced BBM, 2026

World oil spiked from ~28 Feb 2026 (Israel-Iran war). Turbo and Dexlite jumped +48% and +66% on 18 April. Pertamax and Pertamax Green were held through March-May, then leapt +32% on 10 June. Same shock, four different timings, exactly what the model has to untangle.

Why it matters

Market fuels reprice monthly, effective ~the 1st, via a regulated formula: a Singapore fuel benchmark (MOPS) plus margin, converted at USD/IDR, plus tax. That means the next move isn't a coin flip, it's mostly determined by oil and the rupiah over a known window before the announcement. The hike is knowable in advance.

Who it's for

Anyone tired of being ambushed at the pump, and developers who want a clean template for a forecasting agent that turns public signals into a dated, directional call.

When & where

It runs from the command line on free data, any time. Its forecasts target the monthly adjustment date (the 1st), so you get a heads-up roughly a week before Pertamina announces.

How, the method, in detail

The agent is deliberately transparent. Six steps, no black box:

1. Pull the public signal. Two oil futures via Yahoo Finance, RBOB gasoline (RB=F) for the Pertamax grades, Brent (BZ=F) for diesel, plus USD/IDR (IDR=X). Roughly 500 daily closes each, cached locally.

2. Average over the pricing window. Pertamina's formula doesn't react to today's oil, it reflects a trailing window (≈ the 25th of two months ago to the 24th of last month). For each target month the agent averages the oil proxy and the rupiah over that exact window, not the spot price.

3. Convert oil to rupiah-per-litre. oil_idr_per_litre = (proxy_avg_usd_per_barrel / 159) · USD­IDR. 159 litres per barrel. This single number is the model's one driver.

4. Fit a transparent line per fuel. price ≈ A · oil_idr_per_litre + B, with A and B found by least-squares on each fuel's verified price history. A captures pass-through + tax; B captures fixed margins. You can read off exactly why a forecast moves.

5. Forecast next month. Run step 2-4 for the upcoming window, compare the estimate to the current price → direction (↑/↓/→), magnitude rounded to Rp50, date = the next adjustment. A Google-Trends + headline overlay ("harga bbm naik") nudges the confidence when the public is already bracing.

6. Backtest, honestly. Replay the model month-by-month vs verified 2026 prices:

FuelDirection hitMean error
Pertamax Turbo (98)80%~Rp770
Pertamax (92)60%~Rp800
Pertamax Green (95)60%~Rp830
Dexlite (51)40%~Rp1,540

The honest finding: the model is strongest where price tracks oil (Turbo caught the +48% April jump), and weakest where Pertamina holds price below the formula for political reasons, Pertamax sat flat at Rp12,300 from March to May while oil was spiking, then snapped +32% on 10 June. A pure-formula model can't see a subsidy decision coming; it flags lower confidence instead of pretending.

Each fuel, its own chart

Model estimate (dashed) vs actual price (solid), with next month's forecast (★ with an error bar):

Pertamax (RON 92), model vs actual + forecast

Pertamax Green (RON 95), model vs actual + forecast

Pertamax Turbo (RON 98), model vs actual + forecast

Dexlite (CN 51), model vs actual + forecast

The data-science, in theory

There's no deep learning here, and that's a deliberate choice, not a shortcut. Here's the reasoning:

  • Functional form: affine, by design. Pertamina's regulated price is an affine function of the benchmark, roughly price = (MOPS + margin)·(1+tax)·kurs. Collapse oil and kurs into one term and the true relationship is a straight line. So the right model is ordinary least-squares (OLS) simple linear regression, y = A·x + B, not because it's the simplest, but because it matches the actual generating process.
  • Feature engineering. The two raw signals (oil in USD/barrel, USD/IDR) are folded into a single physically-meaningful feature, x = oil_idr_per_litre = (oil_usd_per_bbl / 159) · kurs. One feature, one coefficient, no multicollinearity, fully interpretable.
  • Calibration = least squares. A and B are fit per fuel by minimising squared error over the price history (numpy.polyfit, degree 1). A = pass-through × tax (the slope of how oil reaches the pump); B = fixed distribution + margin (the intercept).
  • Time-series care: the pricing window. Fuel prices lag oil. Instead of spot oil, each month uses the trailing averaging window the regulator actually uses (~25th of two months prior → 24th of last month). This is the single most important modelling choice, get the window wrong and the whole thing is noise.
  • Validation = walk-forward backtest. Score the model month-by-month against verified prices, reporting direction accuracy (did it call ↑/↓ right?) and MAE (rupiah error). Direction matters more than the exact number for a "should I fill up now?" decision.
  • Sentiment overlay = z-score. Google-Trends interest in "harga bbm naik" is standardised to a z-score ((recent − mean) / std); a z above ~1 means the public is bracing, which nudges the confidence on upward calls.
  • Confidence. Derived from the model's relative error: 100 − 100·MAE/price, clamped to [35, 95], then nudged by the sentiment z-score. It's a self-honesty number, not a guarantee.

Why not a neural net or XGBoost? With n = 6 months per fuel and an affine ground truth, a heavy model would overfit noise and hide why it predicts. Linear regression here is more accurate and fully auditable, you can trace every rupiah of a forecast back to oil, kurs, and two fitted constants. The honest limit is the same one any model hits: when Pertamina holds price below the formula for political reasons (Pertamax flat Mar-May while oil spiked), no formula model sees the subsidy decision coming, so it lowers confidence instead of pretending.

Every parameter, explained

ParameterValue / sourceRole
RB=F (RBOB gasoline)Yahoo Finance, dailyOil proxy for Pertamax/Green/Turbo
BZ=F (Brent crude)Yahoo Finance, dailyOil proxy for Dexlite (diesel)
IDR=X (USD/IDR)Yahoo Finance, dailyRupiah conversion
Pricing window~25th (M-2) → 24th (M-1)Which days of oil/kurs feed month M
159litres per barrelConvert USD/barrel → per-litre
A, Bfit per fuel (OLS)Slope (pass-through×tax) + intercept (margin)
Direction threshold±Rp150Below this Δ = "→ flat"
RoundingRp50Forecast rounded to the nearest Rp50
MAEfrom backtestMean rupiah error → sets the ± range + confidence
Trends z-score(recent−mean)/stdPublic-anticipation overlay
Confidence100−100·MAE/price (+z nudge), 35-95Self-reported certainty

And here's the agent running live, end to end, fetch → backtest → forecast:

BBM Price Predictor running end-to-end

And the subsidised ones, how long can it hold?

Pertalite and Biosolar don't follow the formula, their price is a political-fiscal decision, frozen at Rp10,000 and Rp6,800. So the agent doesn't forecast a price; it gauges strain: how expensive the subsidy is to keep, from the gap to the real market price plus the state's fiscal load.

Subsidy strain gauge, Pertalite & Biosolar

  • Pertalite (RON 90) at Rp10,000, a Rp6,250/L gap to Pertamax, 38% of the real price. Strain: HIGH.
  • Biosolar (CN 48) at Rp6,800, a Rp16,200/L gap to Dexlite, 70% of the real price. Strain: EXTREME.

The fiscal backdrop is the real story. Energy subsidy + compensation already hit Rp118.7T, 26.6% of the APBN allocation, in Q1 alone, against a 2.9% deficit, with crude above $100 (every +$1 of oil adds ~Rp6.7T to the bill).

Verdict, how long? Through 2026, it holds: the government has committed to no increase and is absorbing the strain with quotas and engine-capacity limits, not price (a 50 L/day cap ran Apr-May). But the pressure is structural, the gap is wide and rising oil keeps the bill climbing. The hike risk concentrates in 2027, or a sharper oil break, when it's the APBN gap, not the pump, that forces the decision.

The takeaway

The formula is public, so the surprise is optional. Swap "fuel" for any regulated or derived price, electricity tariffs, toll fees, commodity-linked goods, and it's the same agent: public signal in, a dated directional call out.


Honest limits: prices here are verified from news on Pertamina's Apr-Jun 2026 adjustments, but the deeper back-history is thin, only Turbo and Dexlite had enough sourced months to calibrate; Pertamax and Pertamax Green need a fuller table. It uses a free oil proxy, not the paid MOPS/Argus data Pertamina actually uses, so estimates are directional. Adjustments aren't always on the 1st, 2026 saw mid-month moves (10 Jun, 18 Apr). Subsidized fuels (Pertalite, Biosolar) are political, not formula-driven, so the agent gauges strain (pressure), never a price or a date. Not financial advice, a thinking tool, not an oracle.

Price sources: Kompas, CNBC Indonesia, InvestorTrust, Kompas (18 Apr). Subsidy & fiscal: Kompas/ESDM, Kontan (quota).

Building an AI agent?

I'm packaging how I ship them into one kit. Early access:

AI Agent Starter Kit →