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.
Methods on this page
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_idaddresses only run0000— 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
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.
| File | Description |
|---|---|
| sim_data.parquet | Full simulation output (order book + orders at tick resolution) |
| mid_price_by_min.parquet | Mid-price resampled to 1-minute bars |
| l2_by_second.parquet | Level 2 order book (all 10 levels) sampled per second |
| exec_schedule.parquet | Execution schedule with order times and quantities (if algo present) |
| schedule_by_min.parquet | Algo orders aggregated to 1-minute buckets (if algo present) |
| exec_results.parquet | Market slippage, risk and impact metrics (if algo present) |
| params.json | Simulation configuration and parameters |
| results.json | Summary metrics from the simulation |
mid_price_by_min.parquet
| Column | Type | Description |
|---|---|---|
| time | datetime | Minute timestamp |
| mid_price | float | Mid-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.
| Column | Type | Description |
|---|---|---|
| time | datetime | Second timestamp |
| bid_price_1..10 | float | Bid prices at levels 1-10 |
| bid_size_1..10 | int | Bid sizes at levels 1-10 |
| bid_count_1..10 | int | Number of orders at bid levels 1-10 |
| ask_price_1..10 | float | Ask prices at levels 1-10 |
| ask_size_1..10 | int | Ask sizes at levels 1-10 |
| ask_count_1..10 | int | Number of orders at ask levels 1-10 |
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()orrun().
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_andschedule_flags are false for any run submitted without an execution algorithm.
Examples
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.