Skip to content

Preparing a Session Forecast

After downloading the raw measurements data, the next step for Forecasters is to prepare their forecasts for an open session and respective challenges (with clear start and end datetime boundaries) This involves creating forecast submissions based on the retrieved data and any additional variables or models the Forecaster might utilize.

In this example, we'll demonstrate how to prepare a simple 24-hour submission using randomly generated data. In a real-world scenario, you would replace this with your forecasting model based on the dataset retrieved in the previous step.

Note

  • Example Purpose: This example demonstrates preparing a 24-hour forecast submission with 15-minute interval samples using random data. Replace this with your forecasting model for actual use.
  • Quantiles: The example submission includes values for three variables, specifically 'quantiles' 10, 50, and 90.

Prerequisites

  • Python Environment: Ensure you have Python installed with the necessary libraries (pandas, numpy, requests).
  • Access Token: A valid access token is required for authentication. Refer to the Authentication section if needed.
  • Challenges List: A list of challenges retrieved from the Listing Challenges section.

How does it work?

Forecasters can submit forecasts for open session Challenges, competing for the prize money available. They may commence or stop contributing to the market at any time.

Before you submit

  • Submit all three quantiles — Q10, Q50, Q90. Partial submissions are excluded from scoring and payment.
  • Quantiles should satisfy Q10 ≤ Q50 ≤ Q90 at every timestamp — crossing quantiles generally hurt the MWI score.
  • Match the challenge's 15-minute resolution and cover the full window (96 values per day, or 92 / 100 on DST-transition days).
  • Datetimes in UTC; check each challenge's gate_closure — late submissions are rejected.
  • One variable per HTTP request (one request for Q10, one for Q50, one for Q90).

What's needed for a day to qualify

Submitting one challenge is the start, not the finish. Intraday needs every slot of every timestamp filled — typically a cadence of intraday submissions plus a D+1 / D+N commitment for the same target day. D+1 and D+N require submitting your full Q10/Q50/Q90 to every opened session of that target day (how many sessions open, and at which gate hours, is program-specific: see Programs). See Evaluation.

Selecting a Challenge

First, retrieve the information about the challenge you want to submit a forecast for. In the example below, we will select a challenge for a specific market session (open_market_session_id), target resource name (resource_name) and horizon (horizon). A session can hold several challenges for the same resource, one per horizon, so always select by horizon.

submit_session_forecast.py
# Get the session id from `/market/session/` endpoint
open_market_session_id = "your_open_market_session_id"

# Resource name, as listed on the Programs pages of the documentation:
resource_name = "germany_solar_50hertz_ca"
# Horizon to submit for: "Intraday", "D+1", "D+2", ... A session can hold
# several challenges for the same resource, one per horizon.
horizon = "D+1"

# Request the challenges for the open market session:
response = requests.get(
    url=f"{API_URL}/market/challenge",
    params={'market_session': int(open_market_session_id),
            'resource_name': resource_name},
    headers=headers,
    timeout=30
)

# Check if the request was successful
if response.status_code != 200:
    print("Failed to retrieve challenges.")
    print(f"Status code: {response.status_code}")
    print(response.text)
    exit()

# Pick the challenge for the chosen horizon:
challenges = [c for c in response.json()["data"] if c["horizon"] == horizon]
if not challenges:
    print(f"No {horizon} challenge for {resource_name} in this session.")
    exit()
selected_challenge = challenges[0]

# Unpack selected challenge information
resource_id = selected_challenge["resource"]  # challenge resource UUID
challenge_id = selected_challenge["id"]  # challenge UUID
start_datetime = selected_challenge["start_datetime"]  # first forecast leadtime
end_datetime = selected_challenge["end_datetime"]  # last forecast leadtime
print(f"""
Challenge info:
ID: {challenge_id}
Horizon: {selected_challenge["horizon"]}
Resource ID: {resource_id}
First forecast leadtime: {start_datetime}
Last forecast leadtime: {end_datetime}
""")

Preparing Forecast Submissions Time-series Data

Next, prepare the forecast data for submission.

Use your own model

The example uses random samples for illustration. Replace it with your model, fed from the raw measurements (Downloading Raw Data) and any external signals you have.

Info

After each submission you'll receive a confirmation email.

submit_session_forecast.py
# Create a random submission for the challenge period (random values):
# Generate datetime values for the challenge period:
datetime_range = pd.date_range(start=start_datetime,
                               end=end_datetime,
                               freq='15min')
datetime_range = [x.strftime("%Y-%m-%dT%H:%M:%SZ") for x in datetime_range]
# Random Q50 (median) values, with Q10/Q90 placed around it so that
# Q10 <= Q50 <= Q90 at every timestamp. Values are in the resource's own
# unit (MW, MWh or EUR/MWh) and are not clipped: signed targets such as
# spain_mfrr_energy take negative values. Replace this with your model.
q50 = np.random.uniform(low=0.1, high=0.9, size=len(datetime_range))
quantile_values = {
    "q10": [round(float(x), 3) for x in q50 - 0.1],
    "q50": [round(float(x), 3) for x in q50],
    "q90": [round(float(x), 3) for x in q50 + 0.1],
}

# Prepare submissions for the three quantiles Q10, Q50, Q90:
submission_list = []
for qt in ["q50", "q10", "q90"]:
    qt_forec = pd.DataFrame({
        'datetime': datetime_range,
        'value': quantile_values[qt],
    })
    submission_list.append({
        "variable": qt,
        "forecasts": qt_forec.to_dict(orient="records")
    })

# Your submissions:
print("Submission List:")
for i, submission in enumerate(submission_list):
    print("-"*79)
    print(f"Submission #{i+1}")
    print(json.dumps(submission, indent=3))

Download Full Example