Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f69b8d2
Added Lightgbm, LightGBM Linear Trees and Hybrid Stacking Forecasters
Lars800 Nov 7, 2025
6fcd632
Fixed small issues
Lars800 Nov 7, 2025
7523987
Ruff compliance
Lars800 Nov 10, 2025
c680aa1
fixed quality checks
Lars800 Nov 10, 2025
9c1e3d3
Fixed last issues, Signed-off-by: Lars van Someren <lars.vansomeren@s…
Lars800 Nov 10, 2025
759c82f
initial_commit
Lars800 Nov 10, 2025
a24e995
WIP
Lars800 Nov 10, 2025
65dcdd2
Working version
Lars800 Nov 10, 2025
3d31474
Updated Greedy Backtester
Lars800 Nov 11, 2025
a7a34cd
updates
Lars800 Nov 11, 2025
2cb1cab
updates
Lars800 Nov 12, 2025
8fc7810
Updated example
Lars800 Nov 12, 2025
36fcdcd
Fixed multiprocessing
Lars800 Nov 12, 2025
755007b
Squashed commit of the following:
Lars800 Nov 12, 2025
df309a9
updates
Lars800 Nov 13, 2025
1ff6a58
Merge remote-tracking branch 'origin/release/v4.0.0' into ParameterOp…
Lars800 Dec 16, 2025
c759ac9
Improvements
Lars800 Dec 17, 2025
ec38077
Update Opestef Models to research branch
Lars800 Dec 17, 2025
6f12929
Removed references to OpenSTEF Meta
Lars800 Dec 17, 2025
83de9fd
Removed ALL references to openSTEF Meta
Lars800 Dec 17, 2025
53f9c57
Cleaned up optimizer code
Lars800 Dec 17, 2025
09d8d3d
Partial testing
Lars800 Dec 17, 2025
0574d6a
Made (Almost) PR compliant
Lars800 Dec 17, 2025
25a5cf5
Merge remote-tracking branch 'origin/research/v4.1.0' into ParameterO…
Lars800 Dec 17, 2025
44de3b4
Made mostly PR compliant
Lars800 Dec 17, 2025
1469e50
Merge remote-tracking branch 'origin/release/v4.0.0' into ParameterOp…
Lars800 Dec 17, 2025
9f4b5c3
Merge branch 'research/v4.1.0' into ParameterOptimization
Lars800 Dec 18, 2025
24e6593
made PR compliant
Lars800 Dec 18, 2025
eeca5c9
Really PR Complaint
Lars800 Dec 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions examples/benchmarks/liander2024_optimization_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <[email protected]>
#
# SPDX-License-Identifier: MPL-2.0

"""Backtesting pipeline for evaluating energy forecasting models.

Simulates realistic forecasting scenarios by replaying historical data with
proper temporal constraints. Executes prediction and retraining schedules
that mirror operational deployment conditions, ensuring evaluation results
accurately reflect real-world model performance.
"""

import logging
import sys
from datetime import timedelta
from pathlib import Path

from openstef_beam.backtesting.backtest_forecaster.mixins import BacktestForecasterConfig
from openstef_beam.backtesting.backtest_pipeline import BacktestConfig
from openstef_beam.benchmarking.benchmarks.liander2024 import Liander2024TargetProvider
from openstef_beam.evaluation.metric_providers import RCRPSSampleWeightedProvider
from openstef_beam.parameter_tuning.models import (
FloatDistribution,
IntDistribution,
LGBMParameterSpace,
OptimizationMetric,
)
from openstef_beam.parameter_tuning.optimizer.benchmark_optimizer import BenchmarkOptimizer
from openstef_beam.parameter_tuning.optimizer.optimizer import (
OptimizerConfig,
)
from openstef_core.types import LeadTime, Quantile
from openstef_models.presets import ForecastingWorkflowConfig

logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)


single_target = False

horizons = [LeadTime.from_string("PT12H")]
quantiles = [Quantile(0.1), Quantile(0.3), Quantile(0.5), Quantile(0.7), Quantile(0.9)]
forecaster_name = "lgbm" # Choose model type: "lgbm", "xgboost", "gblinear", "lgbmlinear", "hybrid", "flatliner"


# Base Forecasting Workflow Config
base_config = ForecastingWorkflowConfig(
model_id="common_model_",
run_name=None,
model=forecaster_name,
horizons=horizons,
quantiles=quantiles,
model_reuse_enable=True,
mlflow_storage=None,
radiation_column="shortwave_radiation",
rolling_aggregate_features=["mean", "median", "max", "min"],
wind_speed_column="wind_speed_80m",
pressure_column="surface_pressure",
temperature_column="temperature_2m",
relative_humidity_column="relative_humidity_2m",
energy_price_column="EPEX_NL",
)


# Define hyperparameter search space
params = LGBMParameterSpace(
learning_rate=FloatDistribution(low=0.01, high=0.5, log=True),
num_leaves=IntDistribution(low=5, high=120),
max_depth=IntDistribution(low=1, high=3),
reg_lambda=0.2,
)

# Set optimization goal
optimization_metric = OptimizationMetric(
metric=RCRPSSampleWeightedProvider(lower_quantile=0.01, upper_quantile=0.99),
direction_minimize=True,
)

# Load target provider with historical data
target_provider = Liander2024TargetProvider(
data_dir=Path("../data/liander2024-energy-forecasting-benchmark"),
)

# Create the backtest configuration
backtest_config = BacktestConfig(
prediction_sample_interval=timedelta(minutes=15),
predict_interval=timedelta(hours=6),
train_interval=timedelta(days=7),
)

# Configure the backtest forecaster
backtest_forecaster_config = BacktestForecasterConfig(
requires_training=True,
predict_length=timedelta(hours=13),
predict_min_length=timedelta(hours=11),
predict_context_length=timedelta(days=14), # Context needed for lag features
predict_context_min_coverage=0.5,
training_context_length=timedelta(days=90), # Three months of training data
training_context_min_coverage=0.5,
predict_sample_interval=timedelta(minutes=15),
)


# Create the optimizer configuration
optimizer_config = OptimizerConfig(
base_config=base_config,
parameter_space=params,
backtest_config=backtest_config,
backtest_forecaster_config=backtest_forecaster_config,
optimization_metric=optimization_metric,
n_jobs=4,
n_trials=20,
)

optimizer = BenchmarkOptimizer(config=optimizer_config)

best_hyperparams = optimizer.optimize(experiment_name="Liander2024 Benchmark", target_provider=target_provider)

msg = f"{forecaster_name} - Best hyperparameters found: {best_hyperparams}"
logger.info(msg)
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
BENCHMARK_RESULTS_PATH_GBLINEAR = OUTPUT_PATH / "GBLinear"
N_PROCESSES = multiprocessing.cpu_count() # Amount of parallel processes to use for the benchmark


# Model configuration
FORECAST_HORIZONS = [LeadTime.from_string("P3D")] # Forecast horizon(s)
PREDICTION_QUANTILES = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,26 @@
from pydantic import Field, PrivateAttr
from pydantic_extra_types.coordinate import Coordinate

from openstef_beam.backtesting.backtest_forecaster.mixins import BacktestForecasterConfig, BacktestForecasterMixin
from openstef_beam.backtesting.restricted_horizon_timeseries import RestrictedHorizonVersionedTimeSeries
from openstef_beam.benchmarking.benchmark_pipeline import BenchmarkContext, BenchmarkTarget, ForecasterFactory
from openstef_beam.backtesting.backtest_forecaster.mixins import (
BacktestForecasterConfig,
BacktestForecasterMixin,
)
from openstef_beam.backtesting.restricted_horizon_timeseries import (
RestrictedHorizonVersionedTimeSeries,
)
from openstef_beam.benchmarking.benchmark_pipeline import (
BenchmarkContext,
BenchmarkTarget,
ForecasterFactory,
)
from openstef_core.base_model import BaseConfig, BaseModel
from openstef_core.datasets import TimeSeriesDataset
from openstef_core.exceptions import FlatlinerDetectedError, NotFittedError
from openstef_core.types import Q
from openstef_models.presets import ForecastingWorkflowConfig
from openstef_models.workflows.custom_forecasting_workflow import CustomForecastingWorkflow
from openstef_models.workflows.custom_forecasting_workflow import (
CustomForecastingWorkflow,
)


class WorkflowCreationContext(BaseConfig):
Expand Down Expand Up @@ -54,6 +65,10 @@ class OpenSTEF4BacktestForecaster(BaseModel, BacktestForecasterMixin):
default=False,
description="When True, saves intermediate input data for debugging",
)
contributions: bool = Field(
default=False,
description="When True, saves base Forecaster prediction contributions for ensemble models in cache_dir",
)

_workflow: CustomForecastingWorkflow | None = PrivateAttr(default=None)
_is_flatliner_detected: bool = PrivateAttr(default=False)
Expand All @@ -62,7 +77,7 @@ class OpenSTEF4BacktestForecaster(BaseModel, BacktestForecasterMixin):

@override
def model_post_init(self, context: Any) -> None:
if self.debug:
if self.debug or self.contributions:
self.cache_dir.mkdir(parents=True, exist_ok=True)

@property
Expand All @@ -82,7 +97,9 @@ def fit(self, data: RestrictedHorizonVersionedTimeSeries) -> None:

# Extract the dataset for training
training_data = data.get_window(
start=data.horizon - self.config.training_context_length, end=data.horizon, available_before=data.horizon
start=data.horizon - self.config.training_context_length,
end=data.horizon,
available_before=data.horizon,
)

if self.debug:
Expand Down Expand Up @@ -225,4 +242,8 @@ def create_openstef4_preset_backtest_forecaster(
)


__all__ = ["OpenSTEF4BacktestForecaster", "WorkflowCreationContext", "create_openstef4_preset_backtest_forecaster"]
__all__ = [
"OpenSTEF4BacktestForecaster",
"WorkflowCreationContext",
"create_openstef4_preset_backtest_forecaster",
]
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ def _iterate_subsets(
for lead_time in self.config.lead_times:
predictions_filtered = predictions.filter_by_lead_time(lead_time=lead_time).select_version()
if evaluation_mask is not None:
predictions_filtered = predictions_filtered.filter_index(evaluation_mask)
predictions_filtered = predictions_filtered.filter_index(evaluation_mask) # type: ignore

# Remove target column from predictions to avoid duplication
if target_column in predictions_filtered.data.columns:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2025 Contributors to the OpenSTEF project <[email protected]>
#
# SPDX-License-Identifier: MPL-2.0

"""Parameter tuning module for OpenSTEF Beam."""
Loading