Signal Analysis, Models & ValidationGuide

Tunable parameters

A tunable parameter is a value in a signal or strategy that you expose as a named knob with a min, a max, and a default, instead of hard-coding it. Once a value is a parameter, it renders as a labelled control, and you change it and re-run without editing the program. On a backtest, you can go one step further: the platform sweeps the whole range for you, runs one backtest per value, and overlays every result on the same chart. There is no objective-function optimizer that picks the "best" value. You read the overlay and decide.

Updated Jul 20264 min read

What a tunable parameter is

A tunable parameter turns one buried number into a control you can move. You pick a value inside a signal or a strategy, give it a name, and set three things: the lowest value it can take (min), the highest (max), and the value it starts at (default). The platform then draws it as a labelled slider wherever the declaring signal is referenced.

The control follows the signal. If the parameter is declared on a signal, you see its slider on the Signal tab. Reference that signal from a model and the same slider appears on the Model tab; add it to a backtest and it appears on the Backtest tab. On the Model and Backtest tabs the control shows both the originating signal's name and the parameter name, so you always know which knob on which signal you are moving.

That window is a count of observations on the input's current spine, not a span of days, and that count shapes the signal the slider redraws. See the observation-count rule.

What can be tuned

The values you would expose are the inputs you already set. Each is a real knob in the engine, and exposing it as a named, labelled parameter turns it into a control.

What you tune
Where it lives
Default
Contract dte, delta, moneyness
none (you set them)
dte_threshold, delta_threshold, moneyness_threshold, strike_threshold
Contract selection
5, 0.05, 0.05, 1.0
Rolling-window periods
none (you set it)
Sizing fraction and the sizing mode's value
none (you set it)
max_concurrent_positions, max_positions_per_day
Position limits
1, no limit
Entry and exit thresholds
none (you set them)
Walk-forward settings
set per model family

The two numbers in a stock entry signal are a clean example. The trailing-window length and the gate both sit right there in the program:

// 1-year IV rank on 30-DTE IV; the 252-observation window and the
// 0.50 gate are the two values you would expose as parameters
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
rolling_rank(iv_daily, 252) > 0.50

Turn 252 into a window parameter (min 60, max 504, default 252) and 0.50 into a gate parameter (min 0.30, max 0.90, default 0.50), and both become sliders instead of edits.

Adjusting and re-running

The base loop is move-and-re-run. On the Signal or Model tab you move the parameter's slider to a new value, then save the signal or retrain the model, and read the updated result. The signal's time-series chart and its EDA charts recompute against the new value; a model's outputs recompute against it.

On a backtest, change the value and run the backtest again to get one fresh result set. One value, one run, one result. Sweeping (below) is how you compare many values at once instead of doing this by hand.

Sweeping a range and overlaying results

On the Backtest tab you do not have to re-run by hand. Point the platform at a parameter's range and it sweeps it: one backtest per value across the min-to-max span, with every result overlaid on the same chart. You pick the parameter, set the range and the step across its min and max, and run the sweep. The platform runs one backtest per value and overlays the equity curves, drawing one line per swept value so you see the whole family of outcomes together.

This is a manual range sweep for eyeballing sensitivity. You are looking at how the result moves as the knob moves, not asking the platform to hand you a winner.

Every line in that overlay is a full backtest and inherits the run's fill mode and mid marks. A sweep changes one parameter and holds the rest fixed, so read the spread against the backtest assumptions that shaped all of the runs.

What is deliberately not here: an optimizer

There is no objective-function optimizer. No grid search that returns a single best parameter, no auto-tuning, no solver that maximizes a metric for you. The sweep runs the range and overlays the results; it does not rank them or choose one. You read the overlay and make the call yourself. The platform gives you the picture, not the pick.

A tuned window is still observation-count

When the tunable is a rolling periods, the parameter is a count of observations at the series' effective sample rate, not a span of days. The same value means minutes, days, or weeks depending on whether the input was sampled or bucketed. This is the observation-count rule, and a slider does not change it.

Sweeping periods from 20 to 60 on a raw 1-minute series sweeps 20 to 60 minutes, not days.

// WRONG if you meant "20 to 60 days": option.iv(30) is a 1-minute series,
// so a window swept from 20 to 60 here lands in MINUTES, not days
rolling_rank(option.iv(30), 60)

Sample to daily first, then the same window numbers mean trading days:

// on a daily series, a window swept from 20 to 60 means 20 to 60 trading days
iv_daily = option.iv(30, sampling=sampling(period="day", time="close"))
rolling_rank(iv_daily, 60)

Overfitting

A slider and a sweep make it faster to find the single value that looked best over your history. That is the trap, not the goal. Hand-tuning to the best in-sample number, or eyeballing a sweep and locking in the top line, fits the parameter to the past. The overlay makes that easier to do, not harder, because the winning line is right there and it is tempting to keep it.

The guard is at the model stage. Walk-forward validation is the default discipline on every model type: it fits on one window and tests on the next, so a parameter that only worked by memorizing history has nowhere to hide. Before you trust a tuned value, put it through model outputs and validation rather than trusting the best line in a sweep.

Was this page helpful?