This guide documents how mt5-httpapi runs MetaTrader 5 Strategy Tester optimizations over HTTP, what each MT5 optimization mode actually does, and what artifacts you should expect back from the API.
Optimization requests only run correctly against a terminal configured with mode: backtest in config/config.yaml.
Example:
terminals:
- broker: darwinex
account: tester
port: 6542
utc_offset: "0"
mode: backtest
symbol_suffix: ""If you submit an optimization against a live terminal, MT5 can silently exit without producing a usable Strategy Tester report because the portable directory is already owned by the running terminal process.
The optimization flow is always the same:
- Build or author a tester INI.
- Provide an
.ex5expert. - Provide a
.setfile containing MT5 optimization ranges. - Submit
POST /backtest. - Poll
GET /backtest/<jobId>untilcompletedorfailed. - Inspect
optimizationResults,optimizationCache,/report, and/log.
| Mode | MT5 meaning | Scope | Primary parsed source | Report artifact |
|---|---|---|---|---|
1 |
Slow complete algorithm | Single [Tester].Symbol |
XML report | <report>.xml |
2 |
Fast genetic algorithm | Single [Tester].Symbol |
XML report | <report>.xml |
3 |
All symbols selected in Market Watch | Market Watch symbol set | Tester cache .opt |
<report>.symbols.xml |
Mode 1 enumerates the full search space for the active symbol. Use it when you need exhaustive coverage and the parameter grid is small enough to be practical.
Typical characteristics:
- deterministic pass coverage
- can become very slow as the cartesian product expands
- best when you want confidence that every range combination was evaluated
Example build-ini request:
curl -sS -X POST "$URL/backtest/build-ini" \
-H "Authorization: Bearer $TOK" \
-H "Content-Type: application/json" \
-d '{
"symbol":"GBPCAD",
"timeframe":"M15",
"expert":"EA Studio GBPCAD M15 1615044595.ex5",
"lastYears":1,
"modelling":"open-prices",
"expertParameters":"ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set",
"optimization":1,
"optimizationCriterion":0,
"reportName":"gbpcad-m15-complete-search"
}'Example submit request:
curl -sS -X POST "$URL/backtest" \
-H "Authorization: Bearer $TOK" \
-F "ini=@tester.ini" \
-F "expert_name=EA Studio GBPCAD M15 1615044595.ex5" \
-F "set_name=ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set" \
-F "topPasses=20"Typical completed payload shape:
{
"status": "completed",
"optimizationType": 1,
"reportName": "gbpcad-m15-complete-search.xml",
"optimizationCache": null,
"optimizationResults": [
{
"Pass": 12,
"Result": 1450.22,
"Profit": 450.22,
"Profit Factor": 1.62,
"Expected Payoff": 2.41
}
]
}Mode 2 uses MT5's genetic optimizer for a single symbol. Use it when the search space is too large for a full exhaustive run and you want faster convergence on promising regions.
Typical characteristics:
- much faster than mode
1for large grids - does not guarantee evaluation of every combination
- still returns a normal optimization XML report
Example build-ini request:
curl -sS -X POST "$URL/backtest/build-ini" \
-H "Authorization: Bearer $TOK" \
-H "Content-Type: application/json" \
-d '{
"symbol":"GBPUSD",
"timeframe":"M15",
"expert":"MyEA.ex5",
"lastYears":3,
"modelling":"open-prices",
"expertParameters":"myea-optimizer.set",
"optimization":2,
"optimizationCriterion":5,
"reportName":"gbpusd-m15-sharpe-search"
}'Example completed payload shape:
{
"status": "completed",
"optimizationType": 2,
"reportName": "gbpusd-m15-sharpe-search.xml",
"optimizationCache": null,
"optimizationResults": [
{
"Pass": 184,
"Result": 2.41,
"Profit": 1263.5,
"Profit Factor": 1.48,
"Expected Payoff": 13.02,
"Recovery Factor": 3.11,
"Sharpe Ratio": 2.41,
"FastPeriod": 12,
"SlowPeriod": 34
}
]
}Mode 3 is different from modes 1 and 2 in both scope and artifact handling.
MT5 runs the optimization across the symbols currently selected in Market Watch instead of only the [Tester].Symbol value. That symbol still matters because it participates in cache naming and INI generation, but the pass rows themselves are keyed to the Market Watch symbol set.
Most importantly, MT5 does not put the real optimization rows into the normal report XML for mode 3.
What MT5 writes instead:
<report>.symbols.xmlin the terminalReportsdirectory- one or more
Tester/cache/*.optfiles containing the actual optimization rows - agent logs under
Tester/Agent-*/logs/that identify which symbol belongs to each pass
mt5-httpapi handles this by:
- falling back from
<report>.xmlto<report>.symbols.xml - discovering the matching
.optcache file from the normalized INI - parsing the cache rows and sorting them by
Result - recovering pass-to-symbol mappings from agent logs
- exposing the matched cache artifact through
optimizationCache
Example build-ini request:
curl -sS -X POST "$URL/backtest/build-ini" \
-H "Authorization: Bearer $TOK" \
-H "Content-Type: application/json" \
-d '{
"symbol":"GBPCAD",
"timeframe":"M15",
"expert":"EA Studio GBPCAD M15 1615044595.ex5",
"lastYears":5,
"modelling":"open-prices",
"expertParameters":"ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set",
"optimization":3,
"optimizationCriterion":0,
"reportName":"mode3-gbpcad-m15-last5y-rerun5"
}'Example submit request:
curl -sS -X POST "$URL/backtest" \
-H "Authorization: Bearer $TOK" \
-F "ini=@tester.ini;filename=tester.ini" \
-F "expert_name=EA Studio GBPCAD M15 1615044595.ex5" \
-F "set_name=ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set" \
-F "topPasses=50"Example completed payload shape from a real replay:
{
"jobId": "b05643c6d51c4a4cb0cad8a2b6c5573b",
"status": "completed",
"reportName": "mode3-gbpcad-m15-last5y-rerun5.symbols.xml",
"optimizationType": 3,
"optimizationResults": [
{
"Pass": 21,
"Symbol": "GBPJPY",
"Result": 1657.54,
"Profit": 657.54,
"Profit Factor": 1.9,
"Expected Payoff": 2.57,
"Recovery Factor": 3.89,
"Sharpe Ratio": 0.75,
"Equity DD %": 11.22,
"Trades": 256,
"Custom": ""
}
],
"optimizationCache": {
"name": "EA Studio GBPCAD M15 1615044595.all_symbols.M15.20210525.20260525.22.788ECDD113BA3097A58EF888EBEFF9CA.opt",
"path": "C:\\Users\\Docker\\Desktop\\Shared\\terminals\\darwinex\\live\\a\\Tester\\cache\\EA Studio GBPCAD M15 1615044595.all_symbols.M15.20210525.20260525.22.788ECDD113BA3097A58EF888EBEFF9CA.opt",
"pattern": "EA Studio GBPCAD M15 1615044595.all_symbols.M15.20210525.20260525.*.opt",
"build": "22",
"cacheHash": "788ECDD113BA3097A58EF888EBEFF9CA",
"rowCount": 28,
"sizeBytes": 20013,
"symbolComponent": "all_symbols",
"period": "M15",
"fromDate": "20210525",
"toDate": "20260525",
"expert": "EA Studio GBPCAD M15 1615044595",
"modifiedAt": "2026-05-25T08:19:59.371833"
}
}Optimization only makes sense when the .set file contains MT5 optimization ranges.
MT5 uses this wire format for optimizable fields:
Parameter=CurrentValue||Start||Step||Stop||YExamples:
Take_Profit=92||80||4||92||Y
Stop_Loss=0||0||1||10||NInterpretation:
Ymeans MT5 should optimize this parameter.Nmeans keep it fixed.CurrentValueis the default/base value.Start,Step, andStopdefine the search grid.
You can either:
- save the
.setdirectly from MT5 Strategy Tester Inputs - generate it from
POST /backtest/build-set
Example JSON to generate a .set:
{
"parameters": [
{
"name": "Take_Profit",
"value": 92,
"start": 80,
"step": 4,
"stop": 92,
"optimize": true
},
{
"name": "Stop_Loss",
"value": 0,
"start": 0,
"step": 1,
"stop": 10,
"optimize": false
}
]
}All modes use the same status endpoint:
curl -sS -H "Authorization: Bearer $TOK" "$URL/backtest/$JOB"Important fields:
status:queued,running,completed, orfailedoptimizationType: submitted MT5 modeoptimizationResults: parsed topNrows exposed by the APIoptimizationCache: metadata for the matched.optfile when cache parsing is usedreportName: final report artifact name; mode3usually ends in.symbols.xmlreportUrl: fetch raw MT5 report artifactlogUrl: fetch terminal log for the job
Use these rules of thumb:
- For modes
1and2, start with/reportbecause the XML spreadsheet is the main source of optimization rows. - For mode
3, start withoptimizationCacheandoptimizationResults;/reportexists, but it is the.symbols.xmlheader export rather than the real pass table. - If mode
3symbols look wrong or missing, inspect the agent logs underTester/Agent-*/logs/because pass numbers are recovered from those logs. - If
optimizationResultsis empty for mode1or2, inspect the raw XML report and terminal log first.
This pattern works for any optimization mode. Change the optimization field in the build-ini payload and point to the correct .set file.
export URL=http://127.0.0.1:8888/darwinex/tester
export TOK=changeme-mt5-httpapi-token
tmp_ini=$(mktemp)
job_json=$(mktemp)
trap 'rm -f "$tmp_ini" "$job_json"' EXIT
curl -sS -X POST "$URL/backtest/build-ini" \
-H "Authorization: Bearer $TOK" \
-H "Content-Type: application/json" \
-d '{
"symbol":"GBPCAD",
"timeframe":"M15",
"expert":"EA Studio GBPCAD M15 1615044595.ex5",
"lastYears":1,
"modelling":"open-prices",
"expertParameters":"ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set",
"optimization":2,
"optimizationCriterion":0,
"reportName":"gbpcad-m15-opt"
}' > "$tmp_ini"
curl -sS -X POST "$URL/backtest" \
-H "Authorization: Bearer $TOK" \
-F "ini=@$tmp_ini;filename=tester.ini" \
-F "expert_name=EA Studio GBPCAD M15 1615044595.ex5" \
-F "set_name=ea studio gbpcad m15 1615044595.take-profit-opt-80-92-step4.set" \
-F "topPasses=20" > "$job_json"
JOB=$(jq -r '.jobId' "$job_json")
echo "Submitted job: $JOB"
while :; do
STATUS_JSON=$(curl -sS -H "Authorization: Bearer $TOK" "$URL/backtest/$JOB")
STATUS=$(printf '%s' "$STATUS_JSON" | jq -r '.status')
echo "Status: $STATUS"
[[ "$STATUS" == completed || "$STATUS" == failed ]] && break
sleep 10
done
printf '%s\n' "$STATUS_JSON" | jq '{jobId,status,reportName,optimizationType,optimizationCache,optimizationResults}'