Methodiek & Validatie

De canonical productlijn voorspelt eerst D1/D2/S1/S2/S3, rekent daarna analytische tightness uit en vertaalt die pas dan naar prijsdruk of sold price. Direct-price ARDL/GRDL materiaal blijft zichtbaar als baseline en historische vergelijking.

Canonical chained run

2026-06-07_H9_ORIGIN_REGIME_POLICY

— h2 rows · MAPE —

Canonical volgorde

Van inputdata naar forecastvertrouwen

Deze volgorde is leidend voor de niet-hoofdpagina's en hun datacontracten.

Inputdata Ruwe bronnen en curated factor panels met as-of grenzen.
D1/D2/S1/S2/S3 Demand/supply factor forecasts zonder directe prijsfeatures.
Analytische tightness Preregistered transforms vertalen factorstanden naar marktstrakheid.
Prijsdruk / sold price De prijsstap volgt pas na de tightness-berekening.
Validatie en onzekerheid Horizon-specifieke fout, richting, leakage audit en caveats blijven zichtbaar.
Paginataxonomie

Waar haalt elke pagina zijn data vandaan?

Alle niet-hoofdpagina's tonen hun echte feed of een expliciete ontbrekende-artifact status.

Vraag D1 verwerkingsvraag en D2 contract/vrije-markt split in dezelfde tightness-keten.
D1/D2 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus D1_processing_factory_demand_volume, D2_contract_free_demand_split
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Verwerkingsvraag Processor/factory demand business lever mapped to model factor D1.
D1_demand -> D1 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus business_component_forecast_inputs.csv:D1_demand, D1_processing_factory_demand_volume
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Contract/vrije markt Contract/free-market demand split business lever mapped to model factor D2.
D2_free_market_share -> D2 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus business_component_forecast_inputs.csv:D2_free_market_share, D2_contract_free_demand_split
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Aanbod S1 vrije voorraad, S2 oogsttonnage en S3 bruikbare kwaliteit als aanbodkant van tightness.
S1/S2/S3 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus S1_free_stock_inventory, S2_harvest_area_yield_tonnage, S3_quality_usable_tonnage_modifier
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Marktbalans Vraag en aanbod samen: D1/D2/S1/S2/S3 -> analytische tightness -> prijsdruk.
D1/D2/S1/S2/S3 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus D1_processing_factory_demand_volume, D2_contract_free_demand_split, S1_free_stock_inventory, S2_harvest_area_yield_tonnage, S3_quality_usable_tonnage_modifier
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Oogst Yield and harvest-tonnage business lever mapped to the combined S2 model factor.
S2_yield_tonnage -> S2 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus business_component_forecast_inputs.csv:S2_yield_tonnage, S2_harvest_area_yield_tonnage
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Areaal Post-season areaalmonitor en promoted variants als zichtbare S2-bewijslaag.
area evidence -> S2 -> tightness -> prijsdruk Missing required artifacts
Data source NW-Europe satellite potato area monitor -
Focus potato_area_monitor_nwe_patch4x4_v1.csv, robust_candidate_ranking.csv
Validation 0 monitor rows; holdout/ranking artifacts drive route trust fields. post_season_satellite_patch_monitor · diagnostic_post_season_monitor_not_ex_ante_forecast post_season_diagnostic_monitor pred_area_m2 is a post-season level estimate; predicted_change_pct is a delta versus previous area; absolute_relative_error_pct is historical-only. After the March-October satellite season closes; required inputs: Published 10x10 km regional satellite store for the hotspot-year, Parcel masks and crop labels for the same hotspot-year, Previous available potato area for the hotspot. Available only after the same-year March-October satellite path and parcel-mask/label inputs are present; canonical issue date is October 31 of the target harvest year. Area evidence is an S2 input layer only: area -> S2_harvest_area_yield_tonnage -> s2_harvest_scarcity / analytical tightness -> price pressure / sold price. Area is only one side of S2_harvest_area_yield_tonnage; S2 then maps through -log(S2) into tightness, and price pressure is downstream of tightness.
Opslag Voorraadcheckpoints, forecast horizons en vrije-voorraad context als S1-inputlaag.
inventory drivers -> S1 -> tightness -> prijsdruk Missing required artifacts
Data source Inventory workspace / S1 movement benchmark artifacts 2026-04-24_13-12-01Z; PM-S1-CHANGE-UNDER40-20260615
Focus inventory_workspace, S1_free_stock_inventory_source_inventory.csv
Validation Inventory route uses the annual training/evaluation artifacts plus the S1 H1-H9 movement benchmark and raises when a complete run is absent. source_state_plus_inventory_weather_change_rule · percentage_deviation_change_forecast S1 source-state/history blend with inventory/weather movement rule predicted_change_pct scored against actual_change_pct; predicted level is reference only target source-state is used only when available at origin; otherwise inventory/weather/history rule rows are used S1_free_stock_inventory -> -log(S1) tightness -> price pressure/sold price S1_free_stock_inventory -> -log(S1) tightness -> price pressure/sold price
Kwaliteit Quality/usable-tonnage business lever mapped to model factor S3.
S3_usable_quality -> S3 -> tightness -> prijsdruk Missing required artifacts
Data source S3 usable-quality tonnage artifacts -
Focus business_component_forecast_inputs.csv:S3_usable_quality, S3_quality_usable_tonnage_modifier, usable_quality_change_sources.json
Validation S3 quality/usable-tonnage modifier, metric effects and stability backtest feed the canonical S3 explanation.
Groei Groeigerelateerde tweede-laag inputs die S2 en S3 voeden.
growth/quality signals -> S2/S3 -> tightness -> prijsdruk Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus S2_harvest_area_yield_tonnage, S3_quality_usable_tonnage_modifier
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Methodiek Uitleg van de canonical factorketen plus baseline/history context.
page taxonomy and model/data contract Artifact-backed
Data source Chained five-factor price workspace 2026-06-07T18:55:40Z
Focus docs/canonical_entrypoint.md, preregistration.json, model_report.md
Validation h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
Validatie Horizon-specifieke validation views, with the h2 chained-factor anchor called out separately.
validation/uncertainty/caveat Artifact-backed
Data source Direct-price rolling-origin validation exports plus h2 chained-factor audits 2026-06-08 08:01
Focus rolling-origin CV exports, h2 chained-factor audits
Validation 28 direct-price CV export files detected; h2 anchor shown as separate canonical caveat.
Ruwe bronnen User-facing provenance catalog for raw variables and source timelines.
input provenance Source-backed
Data source Raw source catalog and time-series loaders 2026-06-17 14:45
Focus raw variable catalog, source loaders
Validation Each detail page loads the real series and exposes row count, range, min/max and latest value.
Outputcontract

Welke training/testing outputs voeden de UI?

Deze lijst is data-backed: bestanden die ontbreken worden niet stil vervangen door businesslogica.

Feed Status Artifact root Validation/provenance
Chained five-factor price workspace
2026-06-07T18:55:40Z
Artifact-backed /data/chained_factor_forecast_price_workspace h2 rows 40; MAPE 12.77509549523312; model rolling_factor_model_selector_s3_quality_shock; not product-ready.
selected_factor_forecast_inputs.csv plus leakage/input provenance audits
NW-Europe satellite potato area monitor
-
Missing required artifacts /app/data/analysis_outputs/potato_area_monitor_optimization_v1 0 monitor rows; holdout/ranking artifacts drive route trust fields.
Historical rows are honest out-of-sample estimates: grouped out-of-fold predictions on train hotspot-years plus temporal holdout predictions on the latest hotspot-years. No row uses future-year labels, and the source remains post-season because the full same-year March-October satellite path is required.
Inventory workspace / S1 movement benchmark artifacts
2026-04-24_13-12-01Z; PM-S1-CHANGE-UNDER40-20260615
Missing required artifacts /data/inventory_workspace; /app/service_outputs/factor_forecast_benchmarks/PM-S1-CHANGE-UNDER40-20260615 Inventory route uses the annual training/evaluation artifacts plus the S1 H1-H9 movement benchmark and raises when a complete run is absent.
inventory_prediction_sources.json, S1_free_stock_inventory_source_inventory.csv, s1_free_stock_inventory_v1.csv, validation JSON, checkpoint/source CSVs, s1_change_h1_h9_summary.csv and s1_change_h1_h9_predictions.csv
S3 usable-quality tonnage artifacts
-
Missing required artifacts /app/data/analysis_outputs/usable_quality_tonnage_v1 S3 quality/usable-tonnage modifier, metric effects and stability backtest feed the canonical S3 explanation.
data/analysis_outputs/usable_quality_tonnage_v1, usable_quality_change_sources.json and nweu_fry_potato_quality source files
Direct-price rolling-origin validation exports plus h2 chained-factor audits
2026-06-08 08:01
Artifact-backed /data 28 direct-price CV export files detected; h2 anchor shown as separate canonical caveat.
presentation validation bundle, h2 leakage audit, product readiness verdict
Baseline / historie

Hoe trainden we de oude direct-price modellen?

  • Gegevensopbouw · We laden canonieke weekprijzen uit de gecombineerde Landbouw + Boerderij-feed, synchroniseren ze via `_canonicalize_weekly_prices` en koppelen externe bronnen (CBS diesel, Open-Meteo, Boerderij-sentiment en Gemini-nieuwsfeatures).
  • Feature-engineering · Per horizon gebruiken we de samengestelde ‘mixed v4’-sets (diesel MA's, kwaliteit/bewaarsentiment, verwerkingsbenutting, cumulatieve groeigraden, week-of-year en feestdag-afstanden) met prijs-lags (0,1,8) en exogene lags (0,1,8,12).
  • Modeltraining · `src/forecasting/production_training.py` bouwt één consistent ARDL-datasetpakket, draait rolling-origin CV per horizon en traint vervolgens `GradientBoostingRegressor` op alle complete rijen met dezelfde scaler/pipeline als in productie.
  • Export & Controle · Elke horizon levert `model.joblib`, `scaler.joblib`, CV-predictions, live voorspellingen, feature-insights en een manifest zodat de dashboardservice exact dezelfde artefacten kan renderen.

Bronnen in één oogopslag: Landbouw + Boerderij weekprijzen, CBS pompprijzen voor diesel, Open-Meteo weerarchieven, Boerderij-sentiment over verwerking en bewaarbaarheid, en wekelijkse nieuwsfeatures die we met Gemini samenvatten. Alles wordt via src/pipeline/datasets.py naar één kalender gebracht voordat er ook maar één model wordt getraind.

Van ruwe data naar modelklare dataset

  1. Ontsluiten: APIs en scrapers leveren dagelijkse of wekelijkse reeksen. We bewaren elke bron in zijn eigen map onder src/sources zodat updates traceerbaar blijven.
  2. Kalenderen: _canonicalize_weekly_prices zet álle weekreeksen op dezelfde ISO-week (maandag) en dwingt unieke sleutel week_start af. Hierdoor missen we nooit een horizon doordat er een RVO-week “ontbreekt”.
  3. Lags en gaps: Via _calendar_lag_merge voegen we prijs- en exogene lags toe zónder shift-trucs. Korte gaten (≤16 weken) worden forward-filled, langere gaten blijven NaN en worden uitgesloten zodat we geen verzonnen data creëren.
  4. CV-pakketten: create_experiment_datasets levert meteen AR- én ARDL-matrices plus metadata (laglijsten, aantal weeks). Deze bestanden gaan rechtstreeks de training, de productie-run en het dashboard in.

Het gevolg: dezelfde dataset voedt zowel onderzoek, productie als visualisatie. Geen “it worked on my notebook”-effecten.

Hoe ziet de modellering er exact uit?

  • Modelbibliotheek: Naast GradientBoosting trainen we tijdens onderzoek ook XGBoost, RandomForest, Ridge en LinearRegression varianten (de code staat nu in src/forecasting/ardl_feature_training.py). In productie houden we het bij GradientBoosting omdat dat op deze branch nog steeds de beste gevalideerde ARDL-score laat zien; XGBoost blijft volledig ondersteund voor reproduceerbare trainingsruns.
  • Train/test scheiding: Rolling-origin CV (expanderend venster, 4-weekse stappen) simuleert een realistische live omgeving. Elke fold levert één voorspelling per horizon, zodat we statistische toetsen (Diebold-Mariano, Harvey-Leybourne-Newbold) kunnen uitvoeren.
  • Bewaren van modellen: De recepten voor de laatste fold (scalers, pipelines, modelobjecten) worden opgeslagen in production_models/<timestamp>. Het dashboard gebruikt exact dezelfde bestanden voor zowel historische als actuele voorspellingen.
  • Uitlegbaarheid: feature_insights.json bevat importances, correlaties en de laatste featurewaarden. Deze bestanden voeden direct de driver-tabellen en toelichtingen op het dashboard.

Omdat alles (datasets, CV-resultaten, feature-insights) als Parquet/JSON wordt weggeschreven, kunnen we elke run achteraf volledig reproduceren of inspecteren. Geen verborgen “handmatige fixes”.

Belangrijkste databronnen en wat ze betekenen

Bron Wat meten we? Waarom relevant?
Landbouw & Boerderij Werkelijke weekprijzen (bovenkant/onderkant). Doelvariabele en momentum-indicator (lags 0/1/8).
CBS pompprijzen Dagelijkse dieselprijzen → rollende 4w/8w gemiddelden. Transport- en bewaarkosten drukken door in spotprijzen.
Open-Meteo Cumulatieve groeigraden, hitte-/stressdagen. Verklaart vroeg in het seizoen aanboddruk of vertraagde oogst.
Gemini LLM-features Tekstanalyse van vakpers → “processing_utilization”, “quality_storability”. Snel sentiment-signaal over fabrieksuitval, bewaarbaarheid en contractdruk.
Seizoenskalender Week-of-year, afstand tot feestdagen, ag-season labels. Vangt terugkerende patronen (kerstgraan, frietvraag, opslagstress).

Wat doen we vóór een productie-run?

  1. Nieuwsfeatures bijwerken: ensure_news_features_up_to_date checkt of de geconsolideerde JSON alle ISO-weken dekt. Ontbrekende weken triggeren automatisch fetch → clean → dedup → sample → extract → convert.
  2. Dataset sanity: create_experiment_datasets wordt voor elke horizon één keer gedraaid zodat alle metingen gelijk “vers” zijn (zelfde window, zelfde lagregels).
  3. Training + evaluatie: src/forecasting/production_training.py start CV, fit het volledige model, exporteert predictions + rapporten en logt alles in production_models/<timestamp>.
  4. Weekly predictions service: services/weekly_predictions kan hetzelfde proces in Docker draaien en de outputs rechtstreeks naar service_outputs dumpen voor het dashboard.

Zo houden we research, productie en visualisatie in één lijn en kunnen we exact zien welke data en modellen een dashboard-run bevatten.

Kruisvalideringsopzet

Initieel trainvenster
104 weken (expanderend)
Stapgrootte
4 weken
CV-folds
70–95 (afhankelijk van horizon)
Doel
Prijsniveau (EUR/100 kg) op h weken vooruit
Baseline
GradientBoosting met alleen prijs-lags (vergelijkingsbaseline)
Naïeve check
Vandaag = morgen (referentie voor sanity checks)
Trendscore
Correcte richting t.o.v. actuele prijs, alleen wanneer |Δ| ≥ €2

De trendscore die je hieronder ziet is afgeleid van de CV-voorspellingen: we vergelijken de voorspelde prijsverandering versus de laatste bekende prijs en tellen alleen weken mee waarin de gerealiseerde beweging groter is dan €2. Dat sluit de ruis rond vlakke periodes uit.

Horizonresultaten (laatste productie-run)

Horizon MAE (GB + features) MAPE Trendscore (Δ ≥ €2) MAE baseline (GB + prijs) Trendscore baseline MAE naïef
H04W €2.23 15.2% 88.6% €3.07 75.0% €3.18
H08W €2.84 23.9% 90.7% €4.17 65.8% €4.87
H12W €4.14 45.0% 89.5% €5.28 48.8% €6.82
H16W €4.27 45.6% 82.5% €5.71 57.5% €9.12
H20W €4.27 41.9% 93.4% €6.63 61.5% €10.34
H24W €5.51 55.0% 82.0% €6.8 74.3% €11.33
H28W €3.87 25.1% 89.2% €6.0 63.6% €11.7

Merk vooral het verschil tussen 24 w en 28 w op: dezelfde featurefamilie, maar 28 w ontwijkt de drukke decemberperiode en blijft daardoor op MAE ≈ €3,9 met een trendscore van 89%. Voor 24 w werken we aan extra vraag-/feestdagfeatures om de staartfouten te verkleinen.

Hoe interpreteer je de cijfers?

  • MAE vertelt hoeveel euro de voorspelling gemiddeld naast de realisatie zat. Onder €4 betekent praktisch bruikbare signalen voor onderhandelingen.
  • MAPE stijgt bij lange horizons omdat het prijsbereik enorm is (van €5 tot €60). Kijk daarom altijd ook naar de absolute fout.
  • Trendscore is dé beslismetric: het aandeel weken waarin we de richting correct hebben zodra de markt écht beweegt (> €2). Waarden boven 80% geven vertrouwen voor alerts en scenario’s.
  • Baseline en naïef dienen als sanity check. Als de productie-run ooit slechter presteert dan “vandaag = morgen”, stoppen we de deployment automatisch.

Short vs. mid vs. long:

  • 4–12 weken: momentum + diesel + actuele marktverhalen. Ze signaleren bij >88% van de grote bewegingen de juiste richting.
  • 16–20 weken: fundamentals (verwerking, energie, bewaring) nemen het stokje over; absolute fout blijft rond €4 terwijl de trendscore naar 93% klimt.
  • 24–28 weken: dezelfde features, maar 28 w valt na de feestdagen en kent dus veel minder uitschieters dan 24 w. De tabel hierboven laat het verschil zien.

Featurefamilies per horizon

Horizon Belangrijkste signalen
4–8 weken Prijs-lags (0/1/8), diesel 4w/8w, week-of-year, holiday proximity, LLM-sentiment (“quality_storability”, “processing_utilization”).
12–20 weken Idem + cumulatieve groeigraden (GDD) met lags 8/12 en transportkosten-index die oogstdruk representeert.
24–28 weken Fundamentals lags (12 weken terug) en lange-termijn GDD domineren; directe prijs-lag is vooral een ankerpunt.

De exacte kolomnamen vind je in src/pipeline/feature_sets.py en in de feature_insights.json per horizon. Alles wat we tonen in het dashboard komt rechtstreeks uit die bestanden.

Wat betekenen de extra metrics?

  • Trendscore (Δ ≥ €2): alleen weken waarin het verschil tussen actuele prijs en doelprijs groter is dan €2 tellen mee. Een vlakke markt wordt zo niet overschat.
  • Naïeve benchmark: “Vandaag = morgen”. Geeft direct aan hoeveel absolute fout er puur door ruis ontstaat. In onze CV-run blijft deze regel steevast boven €11 voor de lange horizons.
  • Baseline (GB + prijs): laat zien wat er gebeurt als we alle exogene signalen weglaten. Als de ARDL-variant ooit slechter scoort dan deze baseline, gaat er iets mis in de feature-pipeline.

Tip: combineer de trendscore met MAE voor beslissingen. Een horizon met MAE ±€4 maar 90% richtingsnauwkeurigheid is bruikbaarder dan een horizon met MAE ±€3 maar slechts 60% juiste richting.

Verder lezen

Wil je de volledige beschrijving inclusief testscripts, exact dezelfde tabel en een samenvatting van de 24 w vs. 28 w-analyse meekrijgen? Bekijk dan docs/model_training_report.md in de repo. Daar linken we ook naar tmp_model_cv_summary.csv en tmp_model_cv_direction.csv, zodat je onze tabel makkelijk kunt importeren in eigen rapportages.

Daarnaast vind je in docs/model_performance_summary.md een compacte versie van de tabellen en in docs/methodology-secties een Engelstalige breakdown. Alles draait rond dezelfde artefacten die dit dashboard ook gebruikt – er is geen verborgen logica buiten de repo.