Build a forward curve from several forecasts
Line up numerical forecasts for successive dates into one curve, with its median and ranges at each date.
A forward curve shows one quantity, such as the Brent crude price, at a series of future dates. Each Mantic forecast covers a single date, so you build the curve from several forecasts.
Full script: forward_curve.py.
Find the forecasts with search or a tag filter, and take each one's date from its title or clarifications.
Fetch the forecasts together
Pass their IDs to GET /questions as a JSON array in question_ids:
curl --get "https://api.mantic.com/questions" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
--data-urlencode 'question_ids=[201,202,203]' \
--data-urlencode "show_forecasts=true"Each forecast's latest_forecast gives percentiles:
{
"id": 202,
"title": "What will the settlement price of Brent crude oil be on 30 November 2026?",
"latest_forecast": {
"id": 5120,
"date": "2026-11-04T09:10:00Z",
"type": "numerical",
"percentiles": { "10": 84.12, "25": 92.59, "50": 101.73, "75": 111.44, "90": 123.29 },
"thresholds": null,
"intervals": null
}
}question_ids takes up to 100 values but a page holds 25 forecasts, so read every page with paginate(), keeping numerical runs:
questions = paginate(
client,
"/questions",
"questions",
{"question_ids": json.dumps(list(target_dates)), "show_forecasts": "true"},
)
return {
question["id"]: question["latest_forecast"]
for question in questions
if question["latest_forecast"] is not None
and question["latest_forecast"]["type"] == "numerical"
}Order the points by date
Sort the forecasts by the date each one is for, and read the same percentiles from each run:
rows = []
for question_id, target in sorted(target_dates.items(), key=lambda item: item[1]):
run = runs.get(question_id)
if run is None:
continue
percentiles = run["percentiles"]
rows.append(
{"date": target, "run": run["date"][:10]}
| {level: percentiles.get(level) for level in LEVELS}
)
return rowsThe 25th and 75th percentiles bound a 50% range, and the 10th and 90th an 80% range. Older runs can lack the 10th and 90th percentiles, so read them with .get().
Run it with a QUESTION_ID=DATE pair for each point:
python forward_curve.py 201=2026-11-16 202=2026-11-30 203=2026-12-15| Date | 10th | 25th | Median | 75th | 90th | Run |
| --- | --- | --- | --- | --- | --- | --- |
| 2026-11-16 | 88.34 | 95.8 | 102.8 | 110.5 | 120.8 | 2026-11-04 |
| 2026-11-30 | 84.12 | 92.59 | 101.7 | 111.4 | 123.3 | 2026-11-04 |
| 2026-12-15 | 82.94 | 91.59 | 103.3 | 118.3 | 136.5 | 2026-10-26 |Read the curve
Each point is a separate forecast, so neighbouring points can jump, and their runs can be from different days: show each run's date beside the curve. If you or your organisation have run a forecast with your own context, latest_forecast is that run for you; see Forecast with your own context.
To compare the curve with futures prices for the same dates, note that a futures price is closer to an expected value than to a median, so expect some gap where the forecast is skewed.