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 to strategy.py); there is no display, so fig.savefig("fig.png") — cells run inside the run's outputs/ folder, and the file shows under Artifacts. Strategies draw with plot(self, …) / plot_level(self, …) from platform_sdk.plot instead. Cells run on the same image as every backtest, with pandas 3.0.3, numpy 2.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 own strategy.py is 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

LimitBehavior 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.backtest over 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.