Documentation

Backtests

Use the Test button (Running code) in the QuantCraft IDE to open Run backtest. There you set symbols, price data, optional fundamentals, the simulation window, starting balance and fill costs, and (if your script uses them) input parameters for a single run. The engine invokes your strategy lifecycle callbacks. Results appear in Test results; OHLCV explains bar shapes.

At the top of the modal body you can Load or Save to reuse a run preset (.qcs). Everything else is grouped into up to three tabs so you can work through the form before you start the backtest.

Run backtest modal tabs

TabWhat you set
ScriptShown only when the script declares params.NAME inputs. Parameter values, plus Optimize (brute-force grid or genetic search). See Input parameters and Optimization.
Symbol & dataPrice feed (connected broker feed or QuantCraft B2), Symbol source, Asset class, Bar timeframe (broker feed) / Timeframe (B2), Additional timeframes (optional) for rt.get_bar, Min bars (optional) (broker feed), Uses fundamentals (select if your strategy requires fundamental data), Include Fama-French factors (global), and Include calendar data (dividends, splits, earnings dates) (Premium / free-data override; on by default).
SimulationStart / end date (default: the last month), warmup bars (default 45), starting balance (default 100,000), commission and slippage models. See Account model.

Clock / primary symbol: There is no primary-symbol control in the modal. With Selected symbols, the bar clock uses a SYMBOL constant in your script (if that ticker is in the loaded list), otherwise the first loaded symbol. With Screener there is no clock symbol at all — the symbol set changes as the run goes, so read each ticker's history from its own bar.close[i] offsets. See Strategy lifecycle.

Timer / on_timer: The Run backtest modal does not set a timer interval. on_timer is for forward / bulk / chart-forward runs that expose a timer control — not IDE backtests.


Price source (broker feed vs. B2)

On the Symbol & data tab, choose the price feed:

  • Connected broker feed — intraday and daily-style timeframes are available (requires a connected broker account with credentials configured in the app).
  • QuantCraft B2 — on a free plan, only daily (d), weekly (w), and monthly (m) OHLCV timeframes are available; Premium unlocks intraday and extended B2 timeframes. You must be signed in to QuantCraft so a session token can be sent for B2 OHLCV (and for fundamentals when enabled).

The choice is saved in run presets (.qcs) as ohlcvSource ("alpaca" or "b2").


Bar timeframe and asset class (broker feed only)

When the price source is the connected broker feed, you can set:

FieldPurpose
Bar timeframeCandle resolution for the run — e.g. 1 Day, 1 Hour, 4 Hour, 1 Week, and several minute bars (1 / 5 / 15 / 30 min).
Asset classUS equity or Crypto — drives which symbol list you search and how the broker fetches data.
Min bars (optional)If set, after the date range is applied the engine keeps only the N most recent bars. Leave empty to use the full range implied by your start and end dates.

When the price source is B2, you only choose Timeframe: Daily, Weekly, or Monthly on a free plan; Premium adds intraday and extended B2 timeframes (see the B2 codes in OHLCV and bar data).


Additional timeframes (optional)

Use Additional timeframes on Symbol & data to load extra bar sizes alongside your primary timeframe. In strategy code, read them with rt.get_bar(Timeframe.X, shift) — see OHLCV and bar data and Strategy lifecycle.

  • Multi-select any extra timeframes your strategy needs (e.g. daily when your primary bar is 5-minute).
  • The primary timeframe is also available via rt.get_bar without listing it under Additional timeframes.
  • Returns None if that timeframe has no buffer for the run, on a Screener source, or when data is unavailable.
  • In a backtest get_bar only ever sees bars up to the current step, and always reads the clock symbol's bars — for another ticker's history use that ticker's bar.close[i] offsets.
  • Settings can be saved in run presets (.qcs) under secondaryTimeframes.

Symbols

Symbol source

Symbol source on the Symbol & data tab decides which tickers the backtest trades:

SourceWhat it does
Selected symbolsTrades the fixed list you pick, plus anything you add with Paste tickers.
ScreenerRe-runs a screen as the backtest goes and trades whatever matched at that point in the simulation.

With Screener selected, you configure the screen itself:

SettingWhat it does
Screener sourceScreener preset or Python script.
Sort column / DirectionHow preset matches are ranked before the limit is applied.
Rank directionFor scripts — which end of your rank() score to take first.
LimitHow many symbols to trade per rebalance. 0 takes every match.
Refresh interval (days)How often the screen re-runs, in simulated calendar days. Scripts require 7 days or more.
Keep tickers with an open positionKeeps a symbol tradable after it stops matching, so an open position can still be managed.

Limits for a screener-driven backtest: it needs explicit start and end dates; additional timeframes are not supported; a Python script screen needs a Refresh interval of 7 days or more; and Debug Test doesn't support a Screener source (it stops with a message asking you to switch to Selected symbols). With fundamentals on, every screened symbol gets its fundamentals automatically. Results include a Screener Output tab beside Logs, showing every rebalance as a collapsible date with the symbols traded for that window. See Screener for building screens and for point-in-time and survivorship details.

Selected symbols

On Symbol & data, choose one or more tickers for the run:

  • With the connected broker feed, the list comes from your broker's asset universe (equity vs. crypto follows Asset class). You can search and multi-select.
  • With B2, the list comes from the same catalog as fundamentals paths on the server.

Rules:

  • At least one symbol is required.
  • Symbols are normalized to uppercase for the run.
  • Valid characters: letters, digits, . and - (typical for exchange suffixes like BRK.B or RHM.XETRA).

The first symbol in your selection order is the default clock symbol unless your strategy sets a top-level SYMBOL = "..." that matches a loaded ticker. The modal has no separate primary-symbol control.


Fundamentals (optional)

On Symbol & data, check Uses fundamentals (select if your strategy requires fundamental data) when your strategy reads the fundamentals argument in on_bar / on_tick (backtest), or in on_timer on a forward run, or uses fundamentals-aware logic.

The checkbox is shown when fundamentals features are turned on for your build, but is disabled (with a Premium hint) unless you have Premium or free-data access is enabled. When fundamentals are on:

  • You must be signed in (QuantCraft token is sent to load B2 fundamentals files).
  • Pick one or more fundamentals bundles from the multi-select (paths like fundamentals/AAPL.json.gz). The dialog shows the normalized path under each selection.

Important: OHLCV symbols and fundamentals symbols are independent. You can backtest RNMBY via the broker feed while loading RHM.XETRA fundamentals — the UI reminds you that in that case you must pass the correct ticker into fundamentals.current(..., symbol="RHM.XETRA") (or equivalent) in code.

When multiple fundamentals bundles are selected, the engine builds a per-path map. The primary fundamentals bundle used for bar-aligned snapshots defaults to the traded symbol (or the alphabetically first bundle); in multi-symbol and Screener runs always pass symbol= on fundamentals.current / get_data_with_offset, since the primary may not be the ticker you're on.


Fama-French factors (optional)

On Symbol & data, tick Include Fama-French factors (global) when your strategy reads global monthly factor returns via fama_french.qc. See Fama-French factors for the full API.

When factors are on:

  • You need Premium (or free-data access when enabled) and must be signed in.
  • The checkbox is on by default when the data gate allows it. Uncheck to skip factor data for that run.
  • OHLCV symbols, fundamentals bundles, and Fama-French factors are independent — enable any combination you need.
  • There is no factors= callback argument — import quantcraft.fama_french and read fama_french.qc inside callbacks (on_init, on_bar, on_tick, …).

Calendar data and corporate actions (optional)

On Symbol & data, Include calendar data adds dividend history, split history, earnings dates, and SEC filings to your backtest. It is on by default, and your choice is remembered for the next run.

Why this matters

Backtest price bars are unadjusted. Without calendar data, a 4-for-1 split appears as a 75 % overnight price collapse and your simulated position collapses with it. Dividends also never arrive, which quietly understates the total return of any long-held position.

Sub-options

Two sub-options control how actions are applied to open positions during the simulation:

Sub-optionWhat it does
Adjust positions for stock splitsOn the split's effective date, share count is multiplied by the ratio and cost basis divided by it. Stop and target prices are rescaled too. Because qty × entry_price is unchanged, your equity curve stays continuous across the split.
Credit dividends to cashOn the ex-date, qty × amount is added to cash for longs and subtracted for shorts (shorts owe the dividend to the lender). This flows into equity and every return metric but not into realized_pnl.

Both sub-options are on by default when calendar data is enabled.

Note on ex-date estimation: EDGAR does not publish ex-dividend dates. Unless exchange data was available, the ex-date is estimated from the declaration date (accurate to about ±7 days) and is marked est. in Test Results. Estimated dividends are credited by default; to skip them, set dividendDatePolicy to exact_only (or off) in a .qcs preset — the modal has no control for it. Filter on d.estimated in code if your strategy is sensitive to the exact date.

Screener universes

Calendar data works with a screener symbol source. Calendars load once the screen has resolved the full ticker list; symbols matched but with no price bars cost no calendar request. A wide screen may include symbols with no calendar on the server — the run log reports the count rather than failing.

Where you see it

  • Output panel — one [calendar] line per corporate action applied during the run, plus a summary line at the end.
  • Test Results — Split and Dividend rows appear inline in the closed-trades table (then titled Closed trades & events), showing the ratio or per-share amount and cash impact.

See Calendar data for the full BacktestCalendar API and the calendar callback argument.


Simulation — dates, balance, and fill costs

All of the following live on the Simulation tab:

FieldPurpose
Start dateRequired (defaults to about a month ago). First UTC calendar day included in the execution window (YYYY-MM-DD).
End dateRequired. Last UTC calendar day included. Must be on or after the start date.
Warmup bars (indicators)Extra bars loaded before the start date (same timeframe as your bars) so moving averages and similar indicators have history. Callbacks still run only between start and end dates. Default is 45; enter 0 to disable extra warmup.
Starting balanceRequired. Initial cash for the simulated PaperAccount (default 100,000).
Commission / slippageHow fills are adjusted for trading costs — see below and Account model.

The Run backtest modal does not configure a timer interval. on_timer is only available on forward runs (chart forward test, bulk forward / QuantCloud) when timerIntervalMs is set. See Strategy lifecycle.

Optional default stop-loss / take-profit / risk sizing can still appear in .qcs presets when present, but the Run backtest modal does not currently expose SL/TP / risk inputs. Configure those in code or via presets if your helpers support them — see Account model and Run presets (.qcs).

Commission and slippage

SettingPurpose
Commission modelPer share (default), per trade, or zero
Commission costCost per share or flat fee per order ($) — default $0.001 per share
Minimum trade costFloor per order when using per-share commission ($) — default $0
Slippage modelVolume share (default), fixed spread, or zero
Volume limit / Price impactVolume-share slippage parameters (volume_limit also shrinks the quantity on opens)
SpreadTotal spread per share for fixed-spread slippage ($)

Simulation settings can be saved in run presets (.qcs).


Script parameters (single run)

If your strategy imports from quantcraft.inputs import params (the older quantcraft.inputs_module / ide.inputs_module paths are still matched for existing scripts) and references params.NAME, the dialog shows a Script tab with one row per discovered name: Parameter, Type (int / float / str / bool), and Value.

Fill every row before you start — values are required for each declared parameter. They are sent on the wire as strings and coerced by the engine before your file loads, so params.PERIOD (for example) is already the correct Python type when your module executes.

On the same tab you can enable Optimize for grid or genetic search — see Optimization. Leave Optimize unchecked for a normal single backtest.


What happens when you start the run

The app validates the form (dates, symbols, balance, fundamentals if enabled, and script parameter types). On success it starts one backtest with the assembled configuration; progress and logs appear in the Output panel, and structured results open in Test Results.


Quick reference

TopicWhere it’s set
Broker feed vs. B2 pricesSymbol & data → price feed (ohlcvSource in .qcs).
TimeframeBar timeframe (broker feed) or Timeframe (B2: d / w / m on a free plan; more with Premium).
Additional timeframesAdditional timeframes multi-select — for rt.get_bar(Timeframe.X, shift).
Asset classUS equity / Crypto (broker feed only).
Truncate to last N barsMin bars (broker feed only, optional).
What to tradeSymbol source: Selected symbols (multi-select) or Screener (fundamentals-driven screen — see Screener).
Clock symbolScript SYMBOL if loaded; else first symbol (no modal control); none on a Screener source.
Financial statementsUses fundamentals + fundamentals multi-select (automatic on a Screener source); requires sign-in.
Global factor returnsInclude Fama-French factors (global) — see Fama-French factors.
Dividends, splits, earnings datesInclude calendar data — on by default; see Calendar data.
Calendar windowStart date / End date on Simulation (required).
Indicator history before startWarmup bars on Simulation.
Starting cashStarting balance on Simulation.
Commission / slippageSimulation tab — see Account model.
on_timerNot in Run backtest — forward runs with timerIntervalMs (Strategy lifecycle).
on_tickBacktest: four synthetic OHLC ticks per bar (no real tick history). Forward runs: every broker price update — see Strategy lifecycle.
Tunables in codeScript tab (params.*), single values per run (or Optimize).

Tips

  • If broker feed symbols never load, open Trade once or confirm your broker connection; for B2, confirm you’re signed in and the symbol catalog loaded.
  • Warmup only extends loaded history — it does not add trading days outside your date range.
  • Do not expect on_timer in the IDE backtest — there is no timer control on that modal. Use on_bar / on_tick instead. Timers apply on forward runs when timerIntervalMs is configured.
  • on_tick in backtest uses synthetic OHLC from each bar (four calls per bar) — not real tick data. Test live tick logic on forward runs (Strategy lifecycle).
  • Match fundamentals bundles to the tickers you actually read in code when OHLCV and fundamentals tickers differ.
  • Use Min bars sparingly — it is easy to accidentally shorten a run more than you intended.