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.
Every simulation run writes a set of output files — the full tick-level book, minute and second resamples, and (for runs with an execution algorithm) the schedule and its results. You download them by sim_id: one file at a time as a Polars DataFrame with get_sim_data(), or many runs at once as a ZIP with get_bulk_data().
Methods — full reference in the API reference
simulation.get_sim_dataOne output file of one run as a DataFrame.Any tiersimulation.get_bulk_dataSeveral runs or file types at once, as one ZIP.Any tiersimulation.list_sim_filesWhich output files a run actually produced.Any tiersimulation.get_sim_paramsThe parameters one run was submitted with.Any tiersimulation.get_sim_metricsOne run's summary metrics.Any tier
Getting simulation IDs
Free tier — cached simulations
list_cached() lists pre-run baseline groups, each with an example_sim_id. That id addresses run 0000 only; swap its final field (…:0001, …:0002, …) to reach the other runs. See Simulations.
Pro tier — running a simulation
run() returns sim_ids immediately on submission; get_job_results() lists them again for a finished job, with each run's available_files.
Downloading and metering
Single and bulk downloads are metered the same way on the free tier: per simulation group, never per call, run or file format, and a group you already own is free to re-fetch forever. Your own job runs and pro/demo keys are never charged. See Download limits and profile.downloads(). get_bulk_data() takes up to 100 sim_ids per call; files that fail to read are skipped rather than failing the ZIP.
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())Output files
See Output format for 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 |
Execution files
The exec_* and schedule_* files exist only when the run was submitted with exec_algos (Pro). Rather than guessing, ask which files a run actually produced: list_sim_files() returns the file list plus one has_* flag per file.
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() — see Available symbols.
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.