Hyperparameter Combinations
On this page
Whale‑E runs one backtest for every possible combination of a strategy’s hyperparameters. The number of combinations can become very large, but you can filter them to keep only the relevant configurations.
Let us start with an indicator configured as follows:
[[moving_average]]
id = "slow"
source = "ohlc4"
type = "sma"
length.start = 10
length.stop = 50
# length.step = 1In this example:
sourceandtypeeach have only one value, so there is one choice for each.lengthranges from 10 to 50 with a step of 1. Whale‑E assumeslength.step = 1when it is omitted, so there are 41 possible values.
Combining these choices gives 1 (source) * 1 (type) * 41 (length) = 41 combinations. Whale‑E will therefore run 41 different backtests for this indicator.
If you add a second choice for source to test the strategy with both the standard close and the ohlc4 average:
source = ["close", "ohlc4"]the number of combinations doubles and becomes 2 (source) * 1 (type) * 41 (length) = 82.
Now, if you want to test several moving-average types:
type = ["sma", "ema", "wma"]the number of choices for type goes from 1 to 3, which triples the number of combinations: 2 (source) * 3 (type) * 41 (length) = 246. Whale‑E will therefore run 246 backtests for this indicator.
Let us now add a second [[moving_average]] indicator configured in the same way to build a crossover strategy. The two blocks differ only by their id:
[[moving_average]]
id = "fast"
source = ["close", "ohlc4"]
type = ["sma", "ema", "wma"]
length.start = 10
length.stop = 50
[[moving_average]]
id = "slow"
source = ["close", "ohlc4"]
type = ["sma", "ema", "wma"]
length.start = 10
length.stop = 50Each indicator generates 246 combinations, which brings the total to 246 (fast) * 246 (slow) = 60,516 combinations for both indicators together.
Creating grids
A grid is a specific version of the strategy where all qualitative choices are fixed: source, timeframe, type for moving averages, and, when relevant, symbols. Whale‑E creates one grid for each combination of these choices, including when several symbols or timeframes are defined in the [backtest] block.
Numeric hyperparameters do not create new grids. They are explored inside each grid. Typical examples are length and offset.
Let us reuse the configuration above with two moving averages, fast and slow:
[[moving_average]]
id = "fast"
source = ["close", "ohlc4"]
type = ["sma", "ema", "wma"]
length.start = 10
length.stop = 50
[[moving_average]]
id = "slow"
source = ["close", "ohlc4"]
type = ["sma", "ema", "wma"]
length.start = 10
length.stop = 50In this configuration, the fast block offers two sources and three moving-average types, which gives six qualitative configurations. The slow block offers the same six possibilities. Whale‑E builds one grid for each (fast, slow) pair, which gives 6 × 6 = 36 grids. Inside a grid, the source and type of fast and slow are fixed.
Inside each grid, numeric hyperparameters are evaluated. The lengths of fast and slow each take 41 values, from 10 to 50 with an implicit step of 1. All pairs of lengths are tested, which gives 41 × 41 = 1,681 numeric combinations per grid.
The total number of backtests is the number of grids multiplied by the number of combinations inside each grid: 36 × 1,681 = 60,516.
Validation of combinations
To require that the fast moving-average length (fast) is always smaller than the slow one (slow):
[constraints]
condition = "fast.length < slow.length"This constraint removes every configuration where fast.length is not strictly smaller than slow.length. For n = 41 possible lengths, this gives n (n ‑ 1) / 2 = 820 valid (fast.length, slow.length) pairs.
When also taking into account source (2 choices: close and ohlc4) and type (3 choices: sma, ema, wma) for each of the two indicators, we get 820 (fast and slow lengths) * 2 × 3 (fast source & type) * 2 × 3 (slow source & type) = 29,520 combinations. Whale‑E will therefore run 29,520 backtests instead of 60,516.
If you also want both indicators to use the same source and the same type, the rule becomes:
[constraints]
condition = "fast.length < slow.length and fast.source = slow.source and fast.type = slow.type"This additional constraint requires that fast and slow share the same source and type. For each choice of source (2) and type (3), 820 coherent length pairs remain. In total, that gives 820 * 2 (source) * 3 (type) = 4,920 combinations. Thanks to these restrictions, the number of backtests drops from 60,516 to 4,920.