Skip to main content

One post tagged with "parameter sweep"

View All Tags

Parameter Loop - Search the Strategy Space with One Click

· 6 min read
ApudFlow OS
Platform Updates

Strategies are easy to invent and hard to tune. The same trading idea can be profitable with one combination of (stop_loss, take_profit, size) and a money pit with another. Until now ApudFlow forced you to either hand-pick numbers or write one-off Python scripts (sweep_workflow.py, optimize_swing_strategy.py) outside the platform. The new Parameter Loop worker changes that — you describe the parameter grid once, click Run sweep, and watch every iteration stream into a results table inside the workflow editor.

What it does

Parameter Loop runs a single sub-worker multiple times with a different combination of input parameters on every iteration and stores the result of every iteration. The cartesian product of all values lists is executed, so a 3 × 5 grid produces exactly 15 iterations. The result of every iteration is rendered as a row in the right-hand panel of the dialog; the winning iteration (per rankingField / rankingMode) is highlighted in green, failures in red.

It is the worker you reach for when you are trying to answer "which combination of parameters is best?":

  • backtest → find the best (stop_loss, take_profit, size) triple
  • signal_enricher / signal_generator → sweep indicator thresholds
  • python_exec → try many numeric / string configurations of a snippet
  • any other worker → pass a different value of one or more parameters on every iteration

A 2-column dialog, by design

The node has its own custom rendering: a dashed border on the canvas (it is a sink — it has no input handle and no output handle) and a two-column layout inside the dialog. The left column holds the sweep configuration (sub-worker selector, parameter grid, ranking settings); the right column holds the per-iteration results table. There is no "central settings" column and no "output of the previous node" column — neither makes sense for a sweep.

The same rendering mode is now available to any worker that wants it. WorkerDefinition gained a new ui_layout field — set it to "two-column" and the dialog switches to the new layout.

How to set up a sweep

sweepParameters is a list of {name, values} objects. Every entry defines one parameter to vary:

[
{"name": "stop_loss", "values": [1.0, 1.5, 2.0, 2.5, 3.0]},
{"name": "take_profit", "values": [2.0, 3.0, 4.0, 5.0, 6.0]}
]

The total number of iterations is the cartesian product of the values lists (2 × 5 = 10 in the example above). The values field accepts:

Input formExampleResult
JSON list[1, 2, 3, 4, 5][1, 2, 3, 4, 5]
Comma-separated string"1, 2, 3, 4, 5"[1, 2, 3, 4, 5]
Range string"1-5:0.5"[1.0, 1.5, 2.0, 2.5, 3.0]
Python expression"range(5)"[0, 1, 2, 3, 4]

Sub-worker parameters

subWorkerParameters is the base set of parameters passed to the sub-worker on every iteration. The sweep combination is deep-merged on top of it (the sweep value wins when the keys collide). For example, with:

"subWorkerParameters": {
"symbol": "XAUUSD",
"stop_loss": 1.0
},
"sweepParameters": [
{"name": "stop_loss", "values": [2.0, 3.0]}
]

…the sub-worker will see "symbol": "XAUUSD" on every iteration and "stop_loss" alternating between 2.0 and 3.0.

The sweep values are also reachable from the sub-worker's vars context as vars._sweep (a dict of all sweep values for the current iteration) and vars._sweepIndex / vars._sweepTotal, which is handy when the sub-worker wants to do early-exit logic or label its output.

Collecting & ranking results

  • collectField — dotted path of the field to extract from the sub-worker output (e.g. "result.sharpe"). Leave empty to keep the whole output.
  • rankingField — dotted path inside the collected result used to pick the best iteration (e.g. "sharpe_ratio").
  • rankingMode"max" (default) or "min".

The best iteration is exposed as vars.<nodeName>.bestParameters and vars.<nodeName>.bestValue for downstream workers, so you can act on the winner in a follow-up step (run the best parameters through a live trade, log them to a webhook, send a Telegram notification, …).

Safety options

  • continueOnError (default true) — keep going when an iteration throws. Set to false to stop at the first failure.
  • maxIterations (default 0 = no cap) — safety cap on the total number of iterations to run (useful to avoid accidentally running a 10 000-iteration sweep on a slow sub-worker).

Output

{
"subWorkerType": "backtest",
"totalIterations": 10,
"successfulIterations": 10,
"failedIterations": 0,
"iterations": [
{
"iteration": 1,
"parameters": {"stop_loss": 1.0, "take_profit": 2.0},
"result": {"sharpe_ratio": 0.42, "trades": 38},
"fullOutput": {...},
"durationMs": 137
}
],
"bestIteration": 7,
"bestParameters": {"stop_loss": 2.5, "take_profit": 5.0},
"bestValue": 1.87,
"rankingField": "sharpe_ratio",
"rankingMode": "max"
}

Real-World Example: Gold & EURUSD Swing Strategy Optimization

Here's a complete workflow that shows Parameter Loop optimizing a multi-symbol swing trading strategy for Gold (XAUUSD) and EURUSD:

[Trigger] → [Fetch Prices] → [Swing Finder] → [Backtest Strategy] → [Parameter Loop]
WorkerRolePurpose
TriggerStartLaunches the workflow manually or on schedule
Fetch PricesDataDownloads 5-minute OHLC data for XAUUSD and EURUSD
Swing FinderAnalysisDetects swing highs and lows in price action
Backtest StrategyEvaluationTests swing signals with configurable risk parameters
Parameter LoopOptimizationSweeps across stop-loss and take-profit combinations

Setting Up the Sweep

With the Backtest Strategy connected to the Parameter Loop's left (out) handle, configure the sweep grid in the two-column dialog:

Sweep parameters — test 5 stop-loss values × 5 take-profit values:

[
{"name": "stop_loss", "values": [1.0, 1.5, 2.0, 2.5, 3.0]},
{"name": "take_profit", "values": [2.0, 3.0, 4.0, 5.0, 6.0]}
]

25 total iterations. Each combination runs the complete swing detection + backtest pipeline.

Ranking settings:

  • Collect field: result.sharpe_ratio
  • Ranking mode: max (higher Sharpe = better)
  • Session label: gold_eurusd_swing_v1

Live Results

As each iteration completes, the right panel updates in real time:

#p.stop_lossp.take_profitr.sharpe_ratior.total_returnr.max_dd
11.02.00.42+8.3%-12.1%
72.54.01.87+24.3%-6.2%
82.55.01.65+21.1%-7.4%

The best iteration (Sharpe 1.87) is highlighted in green. Clicking its row sets stop_loss: 2.5 and take_profit: 4.0 on the Backtest Strategy worker.

Going Live

Set Production Mode to use_best on the Parameter Loop. Now when your scheduled trigger fires:

  • The sweep does not run (avoiding unnecessary computation)
  • The best parameters from the saved session are loaded automatically
  • The Backtest Strategy executes with the optimized values

Tips & best practices

  • Start with a coarse grid (3 × 3 = 9 iterations) to find the rough optimum, then refine around the winner with a finer grid.
  • When the sub-worker is slow, set maxIterations to a small number during development and lift it for the final run.
  • Use continueOnError: true while exploring so a single bad combination does not abort the whole sweep.
  • Always set a session label — saved sessions let you load results later and enable Production Mode's use_best functionality.
  • The Parameter Loop node does not route execution to a downstream graph — it is a sink. If you want to act on the result, read vars.<nodeName>.bestParameters in the next node.

Available today

Parameter Loop is in the flow category. Drop it onto a canvas, pick the sub-worker, fill in sweepParameters, and the new two-column dialog takes over from there. Happy hunting.