Skip to main content

2 posts tagged with "grid search"

View All Tags

Multi-Symbol Parameter Optimization - Test Your Strategy Across All Markets at Once

· 3 min read
ApudFlow OS
Platform Updates

Your strategy's stop loss works great on Gold — but how does it hold up on Bitcoin? Will the same take-profit target that nails EURUSD also work for GBPJPY? Until now, you had to guess, or run separate workflows for every symbol and stitch the results together manually.

One Sweep, Multiple Symbols

The Parameter Loop worker doesn't care what symbols your strategy trades. Connect a multi-symbol data fetcher and your strategy runs exactly the same way for every asset — but the Parameter Loop adds a second dimension: it also sweeps the parameters.

The result is a 3D optimization grid:

Symbol × Stop Loss × Take Profit = Total Iterations
3 × 5 × 5 = 75

Every combination runs automatically. The best parameter set for XAUUSD might be different from the best set for BTCUSD — and the results table shows you both.

Workflow: Multi-Symbol Risk Optimization

[Trigger] → [Fetch Prices (XAUUSD, BTCUSD, EURUSD)] → [Swing Finder] → [Backtest] → [Parameter Loop]

Step 1: Fetch Multi-Symbol Data

Your Fetch Prices worker pulls OHLC data for all three symbols:

{
"symbols": ["XAUUSD", "BTCUSD", "EURUSD"],
"timeframe": "1h",
"limit": 2000
}

The Swing Finder detects swing points across all three independently. The Backtest evaluates each symbol's signals separately and returns combined statistics.

Step 2: Configure the Sweep

In the Parameter Loop's two-column dialog, set up the grid:

Sweep parameters:

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

Ranking: Collect result.sharpe_ratio, rank by max.

Step 3: Read the Results

The results table shows every combination for every symbol. You can instantly see:

Iterationp.stop_lossp.take_profitr.sharpe_ratior.total_returnr.max_drawdown
123.04.01.92+28.4%-5.8%
372.03.01.45+18.2%-8.3%
554.05.01.21+15.1%-11.2%

The best combination works across all three symbols — not just one. This is portfolio-level optimization in a single click.

When to Use Multi-Symbol Sweeps

ScenarioWhy It Works
Portfolio strategyFind parameters that balance risk across all assets
Cross-market validationIf parameters only work on one symbol, they're overfitted
Volatility regime testingSee which symbol needs tighter SL vs wider TP
Strategy generalizationBuild strategies that adapt to any market, not just one

Live Production: Auto-Deploy Per-Symbol Parameters

Save the sweep results as a session, then set the Backtest's Production Mode to use_best. On every scheduled run, the optimized parameters load automatically — no re-sweep needed.


The takeaway: One workflow, one sweep, unlimited symbols. Stop guessing which parameters work where — let the Parameter Loop find out.

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.