Research & records
Notebooks
Not every research question deserves a full strategy. Notebooks let you run free-form Python — parameter sweeps, feature studies, quick hypothesis checks — right inside your workspace, with a research backtest call and the full scientific stack available.
How notebook runs work
Create a .ipynb or .py file in your workspace, open it,
and press Run Notebook. Three things to know about the execution
model:
- Runs are top-to-bottom. The saved file executes from the first cell to the last in one pass — there is no long-lived interactive kernel or per-cell execute. This keeps every notebook result reproducible: what ran is exactly what was saved, in order.
- It runs in the same isolated environment as strategies — no network, the same import rules, with time and memory limits.
- Results land in the panels. Printed output appears in the Outputs panel as plain text, cell by cell, with a summary line like “12 of 12 cell(s) ran · 3 output file(s)”. Files your notebook writes are listed under Artifacts. The run itself appears in the run library like any other.
-
Plot with matplotlib, to files. Cells may
import matplotlib(it is the one package available to notebooks but not tostrategy.py); there is no display, sofig.savefig("fig.png")— cells run inside the run'soutputs/folder, and the file shows under Artifacts. Strategies draw withplot(self, …)/plot_level(self, …)fromplatform_sdk.plotinstead. Cells run on the same image as every backtest, withpandas3.0.3,numpy2.4.6 and scikit-learn 1.9.0 (since 2026-09-23; notebooks ran on pandas 2.2.3 before, so a cell written against a 2.2.3-only idiom may need a touch).
The research backtest call
The headline tool inside notebooks is platform_sdk.research.backtest —
a fast, idealized backtest you can call in a loop:
from platform_sdk import research
class Breakout(StrategyPlugin): # a strategy defined right here, in the notebook
...
result = research.backtest(
Breakout, # a strategy class defined in this notebook
{"start": "2026-01-05", "end": "2026-02-27"},
params={"stop_ticks": 10, "target_r": 2.5}, # optional overrides
symbol="ES", # optional; defaults sensibly
)
print(result["sharpe"], result["expectancy_R"], result["n_trades"])
-
What you can run: a strategy class defined right in the notebook.
A built-in template's key, or a
{"template": ..., "params": ...}spec, is refused by name since 2026-10-22: the built-in templates run on the realistic exchange model, as a preview or a backtest of a strategy version (create one from the template on the Strategies page). The workspace's ownstrategy.pyis not an input here: passing"workspace"is refused by name, because the notebook tool runs only on the idealized research pass — run your real strategy as a preview or a backtest from the IDE (the workspace’s Preview / Backtest buttons), where it meets the honest simulator. - What you get back: a plain dictionary of summary statistics (Sharpe, expectancy, trade counts and friends) plus warnings — data, not live objects, so it's easy to collect into a DataFrame and plot.
- Parameters are validated against the strategy's declared parameter schema; anything you don't override keeps its default.
Research calls are idealized — on purpose
Every result is labeled with its fill model: idealized. Notebook backtests are for exploring — mapping a parameter landscape, checking whether an idea is worth engineering. The graded, conservative verdict still only comes from a real Backtest run. Treat notebook numbers as a compass, not a scorecard. Two guardrails are built in: windows must lie inside the data access your run declared, and reserved holdout windows are refused outright — held-out data stays untouched until a real evaluation spends it.
The limits, and what hitting them looks like
| Limit | Behavior when you hit it |
|---|---|
| Wall-clock time (generous — tens of minutes) | The run ends with status timed out — never a silent hang. Split long sweeps into smaller runs. |
| Memory (several GB) | The run is stopped and the run page shows the reason. |
| Output files (200 files / 100 MB per run) | Extra files are skipped, with a visible count in the Outputs summary — e.g. “· 4 skipped by the size cap”. |
| Research invocations (very high, but bounded) | A hard ceiling on research.backtest calls per run keeps
runaway loops from consuming your compute allowance. |
| Network | None, ever. Network-touching imports are flagged in the editor and refused at run time. |
If a cell fails, the Outputs panel names the failing cell and its error type, and the run page carries the full error detail.
Patterns that work well
-
Parameter sweeps: loop
research.backtestover a grid, collect the stat dicts, and print (or write to a CSV artifact) a ranked table. Look for plateaus of good parameters, not single sharp peaks — peaks are usually overfitting. - Feature studies: pull features through the same SDK your strategy uses and test whether a relationship exists at all before building entry logic around it.
- Pre-flight checks: before an expensive graded backtest, sanity- check trade counts and expectancy over the window so the credit spend is informed.