Optimization
QuantCraft can run many backtests in a row with different values for your input parameters and let you compare the results. Configure runs from Test (Running code); summarized outcomes appear in Test results. Two search strategies are available:
- Brute force — try every combination of values you list.
- Genetic — use a genetic algorithm to explore large search spaces with far fewer runs.
Both modes are configured in the Run backtest modal (the Test button) and only appear once your script declares parameters via ide.inputs_module.
Where to find it
- Open the Test modal with the Test button in the IDE toolbar.
- Expand “Script Parameters & Optimization (ide.inputs_module)”. This section appears only when your script imports
paramsfromide.inputs_module. - Tick “Optimize script parameters (brute grid or genetic search)”.
- A tab strip appears with two modes — Brute force and Genetic.
Optimization runs use the same symbols, dates, fundamentals, account settings, and risk options as a normal backtest. You only configure how the parameters vary.
Parameter table behaviour with optimization on
The Parameter values table grows two extra columns when optimization is enabled:
| Column | Purpose |
|---|---|
| Parameter | The name from your script (read-only). |
| Type | int, float, bool, or str. |
| Value | For numeric types, this is the start of the grid. For bool/str it is the fixed value. |
| Step | Numeric only. Step between consecutive values (positive or negative). |
| Max | Numeric only. Upper bound for ascending grids, or lower bound for descending grids. |
Behaviour by type:
int/float— values are generated asstart, start+step, …up to and including Max (rounded forint).bool— automatically expanded totrueandfalse(two values).str— left fixed; a string parameter is not varied.
Each row shows a small placeholder (true / false or fixed) when the value can’t be expanded, so you can see at a glance which parameters actually vary.
Brute force (parameter grid)
Brute force evaluates every combination of the values you defined. If you list 5 values for length and 4 values for threshold, that’s 5 × 4 = 20 backtests.
Limits and validation
- The total number of combinations is capped at 256 runs. Above that the modal blocks submission with an explicit message asking you to widen steps or remove parameters from the grid.
- Each numeric range needs all three of Value (start), Step, and Max. Step cannot be 0.
- If Max isn’t reachable from Value with the chosen sign of Step, the modal explains the problem (e.g. start ≤ max for positive steps).
What you see while it runs
When you submit a brute-force optimization:
- A header banner appears in the Test results view:
Optimization — <file> (current/total). - The Output Panel logs each run with its parameter combo, plus a header line:
Optimization (brute force): N backtest run(s). - The Output Panel’s progress bar shows
Run X / N. - A Stop button is available — already-completed runs are kept and shown in the results.
What you see when it finishes
The Test Results pane displays an Optimization view:
- A header summarizing the script and how many runs completed.
- A tab strip of all runs, each tab labeled with the parameter combination (e.g.
length=20, threshold=1.5). When labels get long, the full combo is shown above the chart of the active run. - Selecting a tab shows that run’s normal performance view (equity chart, metrics, trades), so you can dig into any combination.
- If the run was stopped before all combinations completed, a banner notes that the results are partial.
Genetic search
Genetic mode is the right choice when the brute-force grid would be too large. Instead of trying every combination, it evolves a small population of candidates over several generations, keeping the best fits and recombining them.
Hyperparameters
Switch the inner tab to Genetic and you’ll see these fields:
| Field | Range | Notes |
|---|---|---|
| Population size | 4 – 64 | The number of candidates evaluated per generation. Typical values are 8–32. Must be greater than Elitism. |
| Generations | 1 – 40 | Each generation runs one full population of backtests. |
| Elitism | ≥ 1, < population | Top candidates copied unchanged into the next generation. |
| Crossover rate | 0 – 1 | Chance two parents produce a mixed child (otherwise the first parent is copied). |
| Mutation rate | 0 – 1 | Per‑gene chance to randomly jump to another discrete value. |
| Fitness metric | dropdown | What the search maximizes (see below). |
| Random seed (optional) | text | Use the same seed to reproduce a search exactly. |
The total number of backtests run is population × generations. This product is capped at 400 evaluations — if you exceed it, the modal asks you to lower one of the two values.
The alleles (the discrete values each parameter can take) are exactly the same grids brute force would build:
- Numeric parameters use Value / Step / Max to create a discrete list.
- Bool parameters become
true/false. - String parameters are fixed (and don’t affect the search).
Genetic search needs at least one parameter that can vary (more than one allele). The modal blocks submission and asks you to widen a numeric grid or include a
boolif every parameter is fixed.
Fitness metrics
The dropdown offers (higher is better):
- Sharpe ratio (default)
- Sortino ratio
- Total return
- Total P/L ($)
- Profit factor
- Calmar ratio
The metric is read from each backtest’s account performance — runs that errored out get the lowest possible fitness so they’re always sorted last.
What you see while it runs
- Header banner:
Genetic optimization — <file>withGeneration g of N, individual i of P. - The Output Panel logs the search:
Genetic optimization: population P, N generation(s), fitness = …, then── Gen g/N, indiv i/P: <combo> ──for each backtest. - The progress bar shows
Gen g/N · i/P. - A partial results panel appears as soon as the first runs complete — already-evaluated candidates are sorted by fitness and listed with
G<g>·…prefixes so you can see what’s leading without waiting for the whole search.
What you see when it finishes
- The Test Results pane shows Genetic optimization — <file> along with the fitness metric used and the number of runs.
- The final view contains the last generation sorted by fitness, with the best at the top. Each tab opens the full performance view of that run.
- If you press Stop, completed evaluations from across all generations so far are shown sorted by fitness, and a banner notes the search was stopped early.
Practical tips
- Start small. A 3-parameter brute-force grid with 10 values each is already 1000 runs — well over the 256 cap. Cut steps down before turning optimization on.
- Prefer Genetic for large spaces. Anything beyond ~3 numeric parameters is usually faster and more useful with a few generations of genetic search than with a tiny brute-force grid.
- Pin a seed when you want a Genetic run to be reproducible (e.g. comparing two strategies with the same search).
- Use Stop liberally. Both modes preserve the runs they’ve already completed, so you can interrupt as soon as a clear winner appears.
- Pick fitness intentionally. Sharpe and Sortino reward consistency, Total return / Total P/L chase raw growth, Profit factor rewards win/loss balance, Calmar penalizes deep drawdowns.
- Inspect every run. The result tabs each contain the standard equity chart, metrics, and trade history — you can export any individual run’s HTML report just like a single backtest.