Skip to main content

peltwtcn

A faithful Python implementation of the PELT → wavelet → deep-learning carbon price forecasting framework, with the replication audit the paper needs.

Python License: MIT Tests

Implements

Ren, R., Li, J., Li, Y., Huang, S., Shen, J., Li, W., Le, J. and Wang, S. (2025) "A Hybrid Deep Learning based Carbon Price Forecasting Framework with Structural Breakpoints Detection and Signal Denoising", arXiv:2511.04988.

Author of this package: Dr Merwan Roudane — merwanroudane920@gmail.comgithub.com/merwanroudane

Everything is built on the real EU ETS dataset — 6,113 daily EUA observations from 2007-09-10 to 2024-06-04, matching the paper exactly. Nothing here is simulated.


Table of contents

  1. What this package does
  2. Installation
  3. Sixty-second quick start
  4. The three protocols
  5. Results
  6. What the replication found
  7. The real dataset
  8. Module map
  9. Documentation
  10. Testing
  11. Citation
  12. Author

1. What this package does

The paper's pipeline, end to end, in one call — plus the diagnostics it omits.

  • Three structural break detectors — PELT (Killick et al., 2012), ICSS (Inclán & Tiao, 1994), Bai–Perron (2003), and the combined BP&ICSS baseline of Lin & Zhang (2022).
  • Wavelet denoising in the paper's two-sided form and a leakage-free rolling form.
  • LSTM, GRU and TCN in Keras, with the paper's exact hyper-parameters. The TCN's dilated causal convolution is written from scratch so it runs on a CPU and is verified for strict causality.
  • Live data loaders for the EUA price and its drivers, with a bundled cache.
  • Publication-quality tables and figures — LaTeX/booktabs, Markdown, HTML, CSV, Excel; every figure of the paper at 300 dpi.
  • Diebold–Mariano and Model Confidence Set tests, plus a random-walk benchmark and Theil's U. The paper reports none of these.
  • The published Table 1 as a checkable constant, so any run can be diffed against it.

2. Installation

git clone https://github.com/merwanroudane/peltwtcn.git
cd peltwtcn
pip install -e ".[all]"

Extras: [deep] TensorFlow · [data] yfinance + openpyxl · [plots] seaborn · [test] pytest. Without TensorFlow everything except the three networks still works.

Requires Python ≥ 3.9.


3. Sixty-second quick start

import peltwtcn as pw

# 1. the real EUA carbon price plus 13 exogenous drivers
df = pw.load_paper_dataset()
print(df.shape)                      # (6113, 14)

# 2. the paper's best model, in a configuration that works out of sample
pipe = pw.PELTWTPipeline(model="tcn", stationary=True).fit(df)
print(pipe.summary())

# 3. all five specifications of Table 1
res = pw.run_experiment(df, stationary=True)
print(pw.results_table(res.table, fmt="markdown"))

# 4. how does it compare with the published numbers?
print(pw.compare_with_paper(res.table))

# 5. every figure, at 300 dpi
pw.set_journal_style()
pw.save_all_figures(res, df, outdir="assets")

The full replication, all three protocols, tables and figures:

python examples/run_full_replication.py

4. The three protocols

The paper's specification does not survive an honest chronological split. Rather than quietly fix it, this package implements all three variants and reports them side by side.

Protocol Call What it is
A level mode="paper", stationary=False The faithful replication — the paper's specification, literally. Does not work out of sample.
B stationary mode="paper", stationary=True Same models, same hyper-parameters, applied to the first difference. Levels rebuilt as last value + predicted change, so metrics stay in EUR.
C causal mode="causal", stationary=True Adds a causal wavelet, a training-only scaler and a raw target. The only protocol whose numbers are an honest out-of-sample claim.

mode="causal" forces the leak-free settings, so you cannot leave one in by accident.


5. Results

Protocol B on the real dataset, the paper's own settings (50 epochs, early stopping), sorted by RMSE:

Model MAE RMSE MAPE (%) Theil U Train (s)
Random walk 0.8457 1.2230 1.1571 0.9934 1.0000 0.0
PELT-WT-GRU 0.8484 1.2361 1.1611 0.9933 1.0107 79.1
PELT-WT-LSTM (multi) 0.8930 1.2615 1.2292 0.9930 1.0315 64.9
BP&ICSS-WT-LSTM 0.9210 1.2847 1.2583 0.9928 1.0505 76.7
PELT-WT-LSTM (uni) 1.0078 1.3459 1.4313 0.9920 1.1005 271.1
PELT-WT-TCN 1.6752 2.1012 2.2983 0.9806 1.7180 46.8

Two results worth stating plainly. Every one of the five models has Theil's U above 1, so not one of them beats a random walk. And the paper's ranking does not survive: it puts PELT-WT-TCN first, whereas here the TCN comes last by a clear margin. The rest of the order does reproduce — GRU, then multivariate LSTM, then BP&ICSS, then univariate LSTM.

As published in the paper (Table 1, p. 22):

Model MAE RMSE MAPE (%)
BP&ICSS-WT-LSTM 4.6345 5.3878 5.8731 0.8712
PELT-WT-LSTM (uni) 2.3627 2.7488 3.0582 0.9664
PELT-WT-LSTM (multi) 1.8192 2.2967 2.3267 0.9765
PELT-WT-GRU 1.3308 1.6987 1.7401 0.9872
PELT-WT-TCN 1.1855 1.5866 1.6451 0.9888

Available in code as pw.PAPER_TABLE1.

RMSE under all three protocols, so the effect of each change is visible:

Model Paper A level B stationary C causal
BP&ICSS-WT-LSTM 5.3878 48.8677 1.2847 1.7253
PELT-WT-LSTM (uni) 2.7488 43.3046 1.3459 1.7221
PELT-WT-LSTM (multi) 2.2967 38.2507 1.2615 1.7215
PELT-WT-GRU 1.6987 55.1775 1.2361 1.7269
PELT-WT-TCN 1.5866 37.2711 2.1012 2.2677
Random walk 1.2230 1.2230 1.7217

Under protocol A, the paper's specification applied literally, every model lands between RMSE 37 and 55 with R² between −5.1 and −12.4, and the forecast collapses to a flat band around EUR 20 against a truth of EUR 33–98. That is not a bug in this implementation — §6 explains why, and the reasoning is reproducible.

Under protocol C, with every look-ahead removed, all four recurrent models land within 0.3 % of the random walk and of each other. Theil's U is 1.000 to three decimals. The formal tests then settle it:

  • Diebold–Mariano, best model vs the random walk: DM = −0.122, p = 0.9027. Not significant. No pairwise difference among the four recurrent models is significant either.
  • Model Confidence Set at α = 0.10: five of six models survive, including the random walk. The only specification rejected is the paper's own preferred one, PELT-WT-TCN — rejected for being significantly worse (p = 0.0000).

Once the leak is removed there is no evidence that any of the paper's five architectures forecasts the EUA price better than assuming tomorrow's price equals today's.

Full numbers, and the 34 figures, land in results/ and assets/.

A few of the figures

Structural breaks in the EUA price, matched to the policy chronology (the paper's Figure 7):

Detected structural breaks

Every model's forecast against the realised price on the test window (Figure 14):

All forecasts

Pairwise Diebold–Mariano p-values — the test the paper does not run. Pale cells are pairs that cannot be distinguished:

Diebold-Mariano p-values


6. What the replication found

Four findings, all reproducible. Details and derivations in docs/REPLICATION_NOTES.md.

The models cannot extrapolate past the training range

The EUA price trends hard: the 80 % training window tops out at EUR 35.14 while the test window reaches EUR 98.01 — 2.79× higher. An LSTM or GRU squashes its state through tanh, so once inputs leave the range seen in training the state saturates and the forecast flattens. Measured, 25 epochs:

Configuration RMSE Prediction range
one-hot regimes + exogenous (the paper's spec) 55.18 −12.37 EUR 15.4 – 25.9
exogenous only 24.01 −1.53 EUR 32.2 – 69.2
denoised price only 4.42 0.914 EUR 34.3 – 90.5

The more faithful the configuration, the worse it does. stationary=True fixes it by modelling the change instead of the level.

One-hot regime dummies cannot describe a future regime

PELT finds 11 breaks, so 12 regimes. After an 80/20 split, regimes 7–11 occur only in the test window: five of the twelve one-hot columns are identically zero for every training row, so no network can learn a weight for them. This is structural to the paper's e_t encoding — the last regime always begins after the last training observation.

The wavelet filter looks ahead

wavedec/waverec over the whole series is two-sided. Perturbing the price from t = 3000 onwards changes the denoised value as early as t = 2994 — a six-observation look-ahead. For a one-step-ahead forecast, one would already be too many. Use denoise_mode="causal".

The paper never benchmarks against a random walk

On this data the no-change forecast gives RMSE 1.223, R² 0.9934 — better than every model in the paper's Table 1, including the winner (1.5866). Plain OLS on the same 30-step windows reaches RMSE 0.8189. A high R² on a near-unit-root series in levels is not evidence of skill.

Neither Diebold–Mariano nor a Model Confidence Set appears in the paper, so none of its reported differences is shown to be significant. Both are provided here.

Two errata

  • The abstract's headline "22.35 % RMSE / 18.63 % MAE" improvement cannot be recovered from the paper's own Table 1. The true figures are 70.55 % / 74.42 % against BP&ICSS-WT-LSTM and 6.60 % / 10.92 % against PELT-WT-GRU; no pair of rows gives 22.35 % / 18.63 %. Check it with pw.improvement_table.
  • Training times are reported twice and disagree (Figure 16 vs the Section 4.3 text). Both are kept in pw.PAPER_TRAIN_TIMES.

7. The real dataset

load_paper_dataset() returns 6,113 rows × 14 columns, exactly the paper's sample size, cached in data/.

Block Columns
Target Carbon_Price (EUA spot, EUR/tCO₂)
Energy Europe_Coal, TTF_Natural_Gas, Henry_Hub_Gas, Brent_Crude
Equity / FX Euro_Stoxx_50, DAX, VIX, EURUSD
Uncertainty EPU_US, EPU_UK, GPR, EU_10Y_Yield
Policy Policy (reconstructed — see below)

Three features named in the paper have no free feed and are listed in pw.UNAVAILABLE_FEATURES: Epex Spot Germany (commercial licence), the Citi Economic Surprise Index (Bloomberg/Citi), and 1-week Euribor (EMMI licence). Epex is the paper's second-ranked driver, so its absence is material.

The Policy feature is never defined in the paper, despite being its most important predictor (Extra-Trees importance > 0.5). build_policy_features reconstructs it transparently from the twelve dated events the paper itself lists in Section 4.1, as a signed exponentially-decaying impulse plus the EU ETS phase number. It is documented as a reconstruction, not the original series.

Your own data works too — any dated frame with a target column:

pipe = pw.PELTWTPipeline(stationary=True).fit(my_df, price_col="Price")

8. Module map

Module Contents
peltwtcn/datasets.py live loaders, caching, the policy reconstruction, POLICY_EVENTS
peltwtcn/breaks.py PELT, ICSS, Bai–Perron, BP&ICSS, regime encoding
peltwtcn/wavelet.py decomposition, denoising, causal denoising, thresholds
peltwtcn/features.py z_t design matrix, sliding windows, scalers
peltwtcn/models.py LSTM, GRU, TCN, weight-normalised dilated causal conv
peltwtcn/pipeline.py PipelineConfig, PELTWTPipeline, run_experiment
peltwtcn/metrics.py MAE/RMSE/MAPE/R²/Theil U, Diebold–Mariano, MCS
peltwtcn/tables.py Table 1, descriptives, break inventory, paper comparison
peltwtcn/plots.py every figure of the paper, journal styling

9. Documentation

Start with the tutorial. It is a single script you can run today, and the guide is its narrative twin.

Document What it covers
examples/tutorial_step_by_step.py Start here. A runnable 13-step tutorial, ~5 minutes, printing what every stage produced
docs/TUTORIAL_OUTPUT.md The verbatim output of that script, so you can check your own run against it
docs/STEP_BY_STEP_GUIDE.md How to write the code, stage by stage — 15 steps, every block runnable, with the reasoning
docs/SYNTAX.md Complete API reference: every function, argument and return value
docs/REPLICATION_NOTES.md What matches the paper, what cannot, and the errata
examples/run_full_replication.py All three protocols end to end at the paper's own 50-epoch settings
python examples/tutorial_step_by_step.py     # learn it   (~5 min)
python examples/run_full_replication.py      # replicate it (~30 min)

10. Testing

pytest                          # everything
pytest -m "not slow"            # skip the network fits
pytest -m "not network"         # skip the live downloads

200 fast tests, 13 more that fit networks. Highlights of what is actually verified, rather than merely asserted:

  • the TCN's dilated convolution is strictly causal — perturbing x_t for t ≥ 20 leaves every output before t = 20 bit-identical — and matches a hand-rolled reference convolution to float32 precision;
  • the wavelet reconstruction identity A + ΣD = f;
  • the paper's filter does leak and the causal one does not;
  • PELT recovers known break locations to within 5 observations, and ICSS finds a variance shift while ignoring a mean shift;
  • Theil's U of a random walk is exactly 1;
  • the bundled dataset is 6,113 rows spanning the paper's exact dates;
  • the 70.55 % and 6.60 % improvement figures, pinned as regression tests.

11. Citation

If you use this software, please cite both the implementation and the original paper. A CITATION.cff is included.

@software{roudane2026peltwtcn,
  author  = {Roudane, Merwan},
  title   = {peltwtcn: Hybrid deep-learning carbon price forecasting with
             PELT structural-break detection and wavelet denoising},
  year    = {2026},
  version = {1.0.0},
  url     = {https://github.com/merwanroudane/peltwtcn},
  license = {MIT}
}

@article{ren2025hybrid,
  author  = {Ren, Runsheng and Li, Jing and Li, Yanxiu and Huang, Shixun and
             Shen, Jun and Li, Wanqing and Le, John and Wang, Sheng},
  title   = {A Hybrid Deep Learning based Carbon Price Forecasting Framework
             with Structural Breakpoints Detection and Signal Denoising},
  journal = {arXiv preprint arXiv:2511.04988},
  year    = {2025},
  url     = {https://arxiv.org/abs/2511.04988}
}

Key methodological references

Reference DOI
Bai & Perron (2003), J. Applied Econometrics 10.1002/jae.659
Inclán & Tiao (1994), JASA 10.2307/2290916
Killick, Fearnhead & Eckley (2012), JRSS-B 10.1111/j.1467-9868.2011.01004.x
Mallat (1989), IEEE TPAMI 10.1109/34.192463
Lin & Zhang (2022), Process Safety and Env. Protection 10.1016/j.psep.2022.08.011
Liu et al. (2012), Energy Economics 10.1016/j.eneco.2011.09.002
Geurts, Ernst & Wehenkel (2006), Machine Learning 10.1007/s10994-006-6226-1
Cho et al. (2014), EMNLP 10.3115/v1/D14-1179
Bai, Kolter & Koltun (2018) arXiv:1803.01271
Hochreiter & Schmidhuber (1997), Neural Computation 10.1162/neco.1997.9.8.1735

12. Author

Dr Merwan Roudane

Other packages: QuantileOnQuantile, mqqr, qqkrls, mqqcause (CRAN).


Licence

MIT — see LICENSE.

The implementation is original work. The methodology is due to Ren et al. (2025); please cite them. Redistribution of the underlying price series is subject to each provider's own terms.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

peltwtcn-1.0.0.tar.gz (116.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

peltwtcn-1.0.0-py3-none-any.whl (63.8 kB view details)

Uploaded Python 3

File details

Details for the file peltwtcn-1.0.0.tar.gz.

File metadata

  • Download URL: peltwtcn-1.0.0.tar.gz
  • Upload date:
  • Size: 116.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for peltwtcn-1.0.0.tar.gz
Algorithm Hash digest
SHA256 bfca119686dcbc691850949fb11f00248244c1aca3e93724a6d7c6e45d1faab9
MD5 389baa048604b0b9268f5fb03c2cc02b
BLAKE2b-256 fc427895c9e50e8e010cf68f9e75add374b9619dfd30a39673b1f8287d538c75

See more details on using hashes here.

File details

Details for the file peltwtcn-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: peltwtcn-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 63.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for peltwtcn-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f4a9ecabd45a5a878365fff78d4200df74e66a423a4ec08273854065a3252b9
MD5 34864dd34391dcd9365e6f9d91a432ed
BLAKE2b-256 a7b6633ee6d231e6d01114cf9534d24e9ae8262c59fa353d9c7c5fef0c6f41b6

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page