Parameter Loop - Search the Strategy Space with One Click
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)triplesignal_enricher/signal_generator→ sweep indicator thresholdspython_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 form | Example | Result |
|---|---|---|
| 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(defaulttrue) — keep going when an iteration throws. Set tofalseto stop at the first failure.maxIterations(default0= 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]
| Worker | Role | Purpose |
|---|---|---|
| Trigger | Start | Launches the workflow manually or on schedule |
| Fetch Prices | Data | Downloads 5-minute OHLC data for XAUUSD and EURUSD |
| Swing Finder | Analysis | Detects swing highs and lows in price action |
| Backtest Strategy | Evaluation | Tests swing signals with configurable risk parameters |
| Parameter Loop | Optimization | Sweeps 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_loss | p.take_profit | r.sharpe_ratio | r.total_return | r.max_dd |
|---|---|---|---|---|---|
| 1 | 1.0 | 2.0 | 0.42 | +8.3% | -12.1% |
| 7 | 2.5 | 4.0 | 1.87 | +24.3% | -6.2% |
| 8 | 2.5 | 5.0 | 1.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
maxIterationsto a small number during development and lift it for the final run. - Use
continueOnError: truewhile 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_bestfunctionality. - 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>.bestParametersin 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.
