ManticDocs
Create forecasts (beta)

Create forecasts in bulk from a template

Validate a CSV of company names, preview every rendered draft, then create the forecasts one at a time with a resumable checkpoint.

Create one forecast per row of a spreadsheet from a single template, using the draft-then-create steps for each row.

Full script: bulk_create_questions.py.

Drafts can't be event, so a yes/no forecast is a categorical draft with Yes and No categories.

Prepare the CSV

Export the spreadsheet as a UTF-8 CSV with a company_name column:

companies.csv
company_name
ASML Holding
Novo Nordisk
Siemens Energy

Define the template

question_for renders one draft body per company. Write the resolution criteria in clarifications; creation fails without them:

return {
    "title": f"Will {company_name} announce a share buyback before 31 December 2027?",
    "type": "categorical",
    "categories": ["Yes", "No"],
    "supplementary_information": (
        f"Use announcements made by {company_name} or its investor-relations team."
    ),
    "clarifications": (
        "Resolve Yes if the company publicly announces a new share-buyback "
        "authorisation on or before 2027-12-31 23:59:59 UTC. Exclude routine "
        "purchases under an authorisation announced before the question was created; "
        "otherwise resolve No."
    ),
}

Validate and preview

The script checks the CSV for a missing column, blank names and duplicates before sending anything. Without --apply, it prints every rendered title and stops:

python bulk_create_questions.py companies.csv

Check them before you continue.

Create the forecasts

With --apply, the script creates a draft for each company that does not have one yet, and saves its ID to question_creation_results.csv straight away. For a draft saved by an earlier run, it replaces the draft with the current template using PUT /beta/question-drafts/{question_draft_id}, so fixes to the template reach the draft:

if not result["draft_id"]:
    response = client.request("POST", "/beta/question-drafts", json=question_for(company))
    result["draft_id"] = str(response.json()["id"])
    results[company] = result
    write_results(results_path, results)
else:
    # A saved draft from an earlier run: replace it with the current template
    draft_path = f"/beta/question-drafts/{result['draft_id']}"
    client.request("PUT", draft_path, json=question_for(company))

It then creates the forecast from that draft with POST /beta/questions and saves the new forecast's question_id:

response = client.request(
    "POST", "/beta/questions", json={"question_draft_id": int(result["draft_id"])}
)
result["question_id"] = str(response.json()["id"])
write_results(results_path, results)
print(f"Created {company}: forecast {result['question_id']}")
python bulk_create_questions.py companies.csv --apply

A rerun skips companies that already have a forecast.

Handle failures

Don't auto-retry POST /beta/question-drafts: after a dropped connection you can't tell whether the draft was saved, and a retry can create a second one. Check GET /beta/question-drafts and rerun.

When POST /beta/questions fails, the script stops with an exception:

  • 422: the draft is not ready, for example it has no clarifications. Fix the template and rerun; the rerun sends the fixed template to the saved draft.
  • 409: the draft has already created a forecast. The detail usually names its question_id; add it to question_creation_results.csv, then rerun.
  • 429: you've used your forecast-creation allowance; wait Retry-After seconds, then rerun.

On this page