Documentation

Input Parameters

Input parameters let you turn values inside your strategy code (for example a lookback length or a maximum number of open longs) into knobs you set from run modals — without editing the file every time you want to try a new number. The same params wiring applies to Run backtest (Backtests), Bulk forward (Quant Cloud), and Chart forward test algo parameters. Test is described in Running code; multi-run sweeps are covered under Optimization. Params are not populated for plain Run.

This page covers the two halves of that flow:

  • Declaring params in your Python file
  • Wiring those params to run modals so you can set, save, and optimize them (see also Run presets (.qcs))

How it works at a glance

  1. In your strategy file, you import a special params object and read attributes off it (e.g. params.PERIOD).
  2. The IDE scans your code to discover those names and lists them in the run modal (Test, bulk forward, or chart forward).
  3. You enter a type and value for each name in the dialog (or define a sweep when you optimize).
  4. When you start the run, the engine fills params with your values before your script runs, so the values are available the moment your file is imported.

Inputs are populated for Run backtest, Bulk forward, and Chart forward test modals. They are not populated for plain Run (/ws/run) — use hasattr defaults if you want the same file to run both ways. See Strategy lifecycle for when params are visible to callbacks.


Declaring params in your strategy

Add a single import and reference each parameter name as an attribute on params:

from quantcraft.inputs import params PERIOD = params.PERIOD if hasattr(params, "PERIOD") else 14 MAX_OPEN_LONGS = params.MAX_OPEN_LONGS if hasattr(params, "MAX_OPEN_LONGS") else 3 USE_FILTER = params.USE_FILTER if hasattr(params, "USE_FILTER") else True LABEL = params.LABEL if hasattr(params, "LABEL") else "default"

A few rules apply:

  • Recommended import: from quantcraft.inputs import params. The discovery regex also accepts from quantcraft.inputs_module import params and legacy from ide.inputs_module import params. Prefer quantcraft.* for new code.
  • The runner applies modal values before your entry script loads. In the normal IDE path that is enough for from quantcraft.inputs import params in the strategy file.
  • Caveat: if quantcraft.inputs was already imported earlier in the process, its params alias may stay empty after apply (it does not follow the rebind). In that rare case, read ide.inputs_module.params inside the callback, or ensure the strategy import runs after apply (normal IDE load order).
  • Each parameter name must be a valid Python identifier (A–Z, a–z, 0–9, _, not starting with a digit).
  • Each input must include a literal params.NAME token in source so the IDE can discover it. The client regex-matches params.<identifier>; names referenced only via getattr(params, "NAME", default) are not discovered and will not appear on the Script tab.
  • You can use params.NAME anywhere in the file — top‑level, inside on_init, inside on_bar, etc. Each unique reference becomes one input the dialog will ask you about.
  • There is no schema in the module itself — the names you reference are the schema.

Avoid (form will not list the parameter):

MA_PERIOD = getattr(params, "MA_PERIOD", 24) # no params.MA_PERIOD token PERIOD = 14 # no params reference

Common idioms:

def on_init(): global lookback lookback = int(params.PERIOD) def on_bar(bar_index, bar, fundamentals=None, symbol=None): if bar.bars_back < lookback: return ...

Tip: read the value once into a local/global so you don’t pay attribute lookups on every bar.


Wiring the Test / backtest modal

Open the Test dialog from the editor toolbar (the Test button). When your file declares params.NAME inputs, a Script tab appears with parameter rows and Optimize. The IDE has already scanned your active file for params.NAME references, so each parameter you used appears as a row.

For each row you set:

ColumnWhat you enter
ParameterThe name (read-only — comes from your code).
TypeOne of int, float, str, bool (aliases integer, number, boolean are also accepted on the wire).
ValueThe value to use for the run.

Notes on Type choices:

  • int / integer — whole numbers (e.g. 14).
  • float / number — decimals (e.g. 0.5, 1e-3); commas in numbers are stripped.
  • bool / boolean — the backend accepts true / false, yes / no, y / n, on / off, and 1 / 0. The modal UI typically offers True/False.
  • str — free text, kept as-is (trimmed of surrounding whitespace).

When you start the backtest, the dialog packages your rows and sends them to the engine. The engine sets the values on params before your on_init() runs, so:

from quantcraft.inputs import params PERIOD = params.PERIOD # <- already filled by the time this line executes

If a parameter name has no row in the dialog (you removed the dialog entry, or your file references a brand‑new name you haven’t set yet), params won’t have that attribute and a bare params.NAME read will raise AttributeError. Add the row in the dialog and re‑run — or use the hasattr + else default pattern shown above (which still includes the params.NAME token for discovery).

Your dialog values are remembered between runs, so you don’t need to retype them every time. Save and reload full parameter sets with Run presets (.qcs).


Optimizing parameters

On the Script tab, enable Optimize to run your strategy many times with different parameter combinations. Pick Brute force or Genetic:

Brute force

For each parameter row, the dialog adds Step and Max columns where it makes sense:

  • int / float — define a discrete grid: start value → step → max.
  • bool — both true and false are tried.
  • str — kept fixed (string parameters don’t form a grid).

The engine runs every combination of those discrete values and reports each run separately in the Test Results view.

Useful for small grids (a handful of parameters with a few values each). Combinations explode quickly — keep your search space modest.

Genetic

For larger search spaces, switch to Genetic to let an evolutionary search find good combinations without trying every cell:

  • Population size — number of parameter combinations evaluated each generation.
  • Generations — how many generations to run (each is one full population of backtests).
  • Elitism — top individuals copied unchanged into the next generation.
  • Crossover rate (01) — chance two parents produce a mixed child.
  • Mutation rate (01) — chance a gene jumps to another value.
  • Fitness metric — which metric to optimize (Sharpe, Sortino, total return, etc.).
  • Random seed (optional) — reproducibility of the population and operators.

Both modes show live progress in the Output Panel (a progress bar in the header) and a Stop button so you can end a long run.

When optimization finishes, the Test Results view shows the runs sorted by your fitness metric, and you can drill into each one.


Tips

  • Names are discovered from your file: if a row doesn’t appear, double‑check your params.NAME references and that you import params from quantcraft.inputs, quantcraft.inputs_module, or ide.inputs_module.

  • Keep names UPPER_SNAKE_CASE — by convention they read as constants in your code.

  • Provide sensible types in the dialog so coercion does what you expect (e.g. don’t set int for a value like 0.5).

  • Booleans aren’t case-sensitiveTrue, true, 1, yes all evaluate to true.

  • Use Optimize for parameter searches, not editor scripts: brute force is exhaustive, genetic is for larger spaces.

  • Plain Run does not populate params: when you click Run, params is not filled. Use hasattr defaults (with a literal params.NAME token) if you want the same file to work for both Run and Test:

    from quantcraft.inputs import params PERIOD = params.PERIOD if hasattr(params, "PERIOD") else 14
  • Forward runs use the same discovery rules — bulk forward and chart forward modals scan for params.NAME the same way as the backtest dialog.