Download data

How to get simulation IDs and download output data.

HTTP endpoints on this page

  • GET/simulation/results/{sim_id}/files
  • GET/simulation/results/{sim_id}/data/{filename}
  • POST/simulation/results/bulk

Parameters and response shapes for each are in the API reference.

All simulation data is downloaded via get_sim_data(sim_id). To use it you first need a sim_id — where you get one depends on your tier.

get_sim_data() itself is documented on Simulations; this page covers the bulk path and what is inside each file.

Getting simulation IDs

Free tier — cached simulations

Use list_cached() to browse pre-run simulations and pick a sim_id. See Simulations for the full list of available cached data and filtering options.

Pro tier — running a simulation

run() returns sim_ids immediately on submission. You can also retrieve sim_ids from a completed job via get_job_results(). See Simulations for the full submission and job tracking API.

Downloading data

Once you have a sim_id, use get_sim_data() to download any of the output files as a Polars DataFrame.

Single simulation

One file of one run, as a Polars DataFrame — get_sim_data() documents the call and every filename it takes.

simulation.get_bulk_data(sim_ids, include_sim_data=True, include_mid_price=False, include_l2_by_second=False)Download several simulations at once as a single ZIP of parquet files.Any tier

Parameters

sim_ids : list of str
The runs to fetch, up to 100 per call. An example_sim_id addresses only run 0000 — swap the final field to reach the others.
include_sim_data : bool, default True
Include each run's sim_data.parquet.
include_mid_price : bool, default False
Include each run's mid_price_by_min.parquet.
include_l2_by_second : bool, default False
Include each run's l2_by_second.parquet.

Returns

bytes

ZIP content holding the requested parquet files. Write it to disk, or read it straight out of memory.

Notes

Bulk downloads are metered exactly like single ones: per group, never per call, run or file format. A group you already own is free to re-fetch forever. See your download allowance.

Examples

python
cached = client.simulation.list_cached(symbol="700.HK", scenario="normal")
sim = cached["simulations"][0]

# example_sim_id is only run 0000 — expand it to the first 5 Monte Carlo
# runs. The run index is the final field: swap it for 0000, 0001, …
base = sim["example_sim_id"].rsplit(":", 1)[0]
sim_ids = [f"{base}:{i:04d}" for i in range(min(sim["n_runs"], 5))]

# get_bulk_data accepts up to 100 sim_ids per call
zip_bytes = client.simulation.get_bulk_data(
    sim_ids=sim_ids,
    include_sim_data=True,
    include_mid_price=True
)

with open("simulation_data.zip", "wb") as f:
    f.write(zip_bytes)

# Or load directly without saving
import zipfile, io, polars as pl

with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf:
    for name in zf.namelist():
        if name.endswith(".parquet"):
            df = pl.read_parquet(io.BytesIO(zf.read(name)))
            print(f"{name}: {df.shape}")

Output files

See Output format for a full explanation of the book and event columns, message types, and side convention inside sim_data.parquet.

FileDescription
sim_data.parquetFull simulation output (order book + orders at tick resolution)
mid_price_by_min.parquetMid-price resampled to 1-minute bars
l2_by_second.parquetLevel 2 order book (all 10 levels) sampled per second
exec_schedule.parquetExecution schedule with order times and quantities (if algo present)
schedule_by_min.parquetAlgo orders aggregated to 1-minute buckets (if algo present)
exec_results.parquetMarket slippage, risk and impact metrics (if algo present)
params.jsonSimulation configuration and parameters
results.jsonSummary metrics from the simulation

mid_price_by_min.parquet

ColumnTypeDescription
timedatetimeMinute timestamp
mid_pricefloatMid-price at end of minute

l2_by_second.parquet

The full Level 2 order book (10 price levels on each side) sampled at 1-second intervals. Contains the last snapshot within each second.

ColumnTypeDescription
timedatetimeSecond timestamp
bid_price_1..10floatBid prices at levels 1-10
bid_size_1..10intBid sizes at levels 1-10
bid_count_1..10intNumber of orders at bid levels 1-10
ask_price_1..10floatAsk prices at levels 1-10
ask_size_1..10intAsk sizes at levels 1-10
ask_count_1..10intNumber of orders at ask levels 1-10
python
sim_id = client.simulation.list_cached()["simulations"][0]["example_sim_id"]

mid_df = client.simulation.get_sim_data(sim_id, "mid_price_by_min.parquet")
l2_df = client.simulation.get_sim_data(sim_id, "l2_by_second.parquet")

print(mid_df.columns)            # time, mid_price
print(len(l2_df.columns), "L2 columns")  # time + 60

# Bid-ask spread per second
spread = l2_df["ask_price_1"] - l2_df["bid_price_1"]
print(spread.head())

Execution files

Execution files are only present when the simulation was submitted with an exec_algos parameter (Pro tier only). Rather than guessing, ask which files a run actually produced.

simulation.list_sim_files(sim_id)Report which output files a given run produced.Any tier

Parameters

sim_id : str
The run to inspect, from list_cached() or run().

Returns

dict

sim_id : str
The run identifier, echoed back.
files : list of str
Filenames actually present for this run.
has_sim_data, has_params, has_results, has_mid_price, has_l2_by_second, has_exec_schedule, has_schedule_by_min, has_exec_results : bool
One flag per output file. The exec_ and schedule_ flags are false for any run submitted without an execution algorithm.

Examples

python
sim_id = client.simulation.list_cached()["simulations"][0]["example_sim_id"]
files = client.simulation.list_sim_files(sim_id)
print({k: v for k, v in files.items() if k.startswith("has_")})

if files["has_exec_schedule"]:
    exec_df = client.simulation.get_sim_data(sim_id, "exec_schedule.parquet")

Available symbols (Pro tier)

Pro tier users can run simulations on any calibrated instrument-day across HKEX and LSE. The calibrated catalog grows over time, so rather than a static list, call client.data.calibrated_data() to retrieve what is currently available — see Available symbols for the full response schema.

Calibration is what turns a raw vendor day into a simulatable one, and the raw universe is far larger than the calibrated catalog. If the instrument or date you need is not in the catalog, email support@simudyne.com and we can calibrate it for you.