thainowcast: nowcasting models for Thai GDP and inflation
No weights here, and no results about Thailand. These models keep no fitted state. At every forecast origin each one is re-estimated from scratch on the data released by that date, so a frozen set of weights would be stale the day after it was saved. This repository records the models instead: the classes, hyperparameters, feature sets and the model × feature-set grid. Every number quoted below comes from synthetic test data. It shows that the pipeline works, not how well these models nowcast the Thai economy.
These are the models of the working paper ThaiNowcast: Pseudo-Real-Time Nowcasting of Thai GDP and Inflation
with Official and Alternative Data by Thanatchon Chongmankhong
(read online ·
PDF ·
DOI 10.5281/zenodo.23995547).
Code: https://github.com/thanatchon36/thainowcast. Data and full results:
thanatchon36/thainowcast (dataset).
Independent personal research. Does not represent the views of any employer or institution.
Targets
| target | frequency | forecast origins |
|---|---|---|
gdp_yoy: real GDP growth, y-o-y (%), NESDC |
quarterly | M1, M2, M3 (end of each month of the quarter), PR (the day before the official release) |
cpi_yoy: headline CPI inflation, y-o-y (%), TPSO |
monthly | M0 (mid-month), PR |
Models
| code | class | hyperparameters |
|---|---|---|
rw |
RandomWalk |
last released value |
ar |
AR |
AR(p), p ≤ 4 chosen by AIC |
bridge |
Bridge |
indicators completed to the end of the target period with AR(1) forecasts, averaged to the target frequency, 2 principal components, OLS |
midas |
MIDAS |
one regressor (mean of standardized indicators), 12 monthly lags, exponential Almon weights re-estimated for each number of missing months. GDP only |
dfm |
DFM |
mixed-frequency dynamic factor model (statsmodels DynamicFactorMQ), 1 factor, AR(1) factor, EM up to 50 iterations, re-estimated once a year; also gives the news decomposition |
enet |
ElasticNetNowcast |
ElasticNetCV, l1 ratio in {0.1, 0.5, 0.9}, 40 alphas, time-series CV with up to 4 folds |
lgbm |
LightGBMNowcast |
300 trees, learning rate 0.03, 4 leaves, min 5 samples per leaf, row and column subsampling 0.8, L2 1.0, deterministic |
Feature sets: hard_only (14 official monthly statistics), hard_trends, hard_lights, hard_no2 and
hard_prices (hard data plus one family of alternative data: Google Trends, nighttime lights, Sentinel-5P NO2,
online prices), all (hard data plus all five families, mobility included) and none for the univariate
benchmarks. GDP runs 20 model × feature-set pairs and CPI 22. Each model's 80% interval is the conformal quantile
of its absolute backtest errors at the same origin.
Files
| path | content |
|---|---|
model_specs.json |
every model's class and hyperparameters, the indicators in each feature set, the backtest settings and the full grid per target, pinned to code commit ce9dd21 |
config/indicators.yaml |
the indicator registry: agency, frequency, start, publication lag, transform and the source of each series |
live_nowcast.json |
synthetic. The live nowcast as of 2026-11-05: every model's value and interval, the headline model and the GDP news decomposition. Rerunning the snippet under Use reproduces the GDP headline, 2.622 |
LICENSE |
MIT, as for the code |
Accuracy on synthetic data
RMSFE at the last origin (PR) over the full backtest, relative to the AR benchmark (below 1 beats AR). GDP covers 2015Q1 to 2026Q2 (46 quarters), CPI 2015-01 to 2026-09 (141 months).
| model | GDP hard_only |
GDP all |
CPI hard_only |
CPI all |
|---|---|---|---|---|
enet |
0.46 | 0.78 | 0.68 | 0.54 |
bridge |
0.55 | 0.66 | 1.45 | 1.29 |
dfm |
0.64 | 0.89 | 0.92 | 0.92 |
midas |
0.64 | 0.66 | – | – |
lgbm |
0.73 | 0.72 | 0.72 | 0.60 |
rw |
1.04 | 1.02 |
The synthetic generator sets how much each data family helps, so this ranking tells you nothing about Thailand. The full tables, Diebold-Mariano tests, regime splits and ablations are in the dataset.
Use
The models run inside the pipeline, so you use the code rather than a download from this repository:
git clone https://github.com/thanatchon36/thainowcast && cd thainowcast
make setup # uv venv + pip install -e ".[dev,app]"
make all # data -> eval (backtest) -> live -> dashboard -> paper
To nowcast for real, put the official CSVs named in
docs/data_sources.md into
data/raw/ (the synthetic_* files are used only when a real one is missing, with a warning), then run
make all ASOF=YYYY-MM-DD. One model on its own:
from thainowcast.config import load_config
from thainowcast.data.loaders import load_panel
from thainowcast.features import transform_wide
from thainowcast.models import MODELS
from thainowcast.config import RAW_DIR
import pandas as pd
cfg, asof = load_config(), pd.Timestamp("2026-11-05")
panel = load_panel(cfg, RAW_DIR)
y = panel.series_available(asof, "gdp_yoy") # GDP released by asof
X = transform_wide(panel.wide_available(asof, list(cfg.indicators), "M"), cfg.indicators)
X = X[cfg.set_columns("hard_only")]
print(MODELS["enet"]().nowcast(y, X, pd.Period("2026Q3", "Q"), asof))
Limitations
- Trained and evaluated on synthetic data only. Real Thai series have revisions, breaks and gaps the generator does not reproduce, and the backtest uses first releases, not real-time vintages.
- Publication lags in
config/indicators.yamlare approximations of each agency's usual timing. - The panel is short: 46 GDP quarters in the backtest, so differences between models are noisy.
Citation
@misc{chongmankhong2026thainowcast,
author = {Chongmankhong, Thanatchon},
title = {ThaiNowcast: Pseudo-Real-Time Nowcasting of Thai GDP and Inflation with Official and Alternative Data},
year = {2026},
doi = {10.5281/zenodo.23995547},
url = {https://thanatchon36.github.io/thainowcast/paper/},
note = {Working paper; results are from synthetic test data}
}