Overview
The xAlmanac Upload API lets your own local script or agent send MetaTrader 5 Strategy Tester output to xTriel. The agent reads the files on your machine, then posts the contents to the ingest endpoint.
Your local upload agent reads the MT5 report .htm and the .set preset, then sends those file contents to the API.
Authentication
Uploads use your per-user Ingest Token, the same xti_... token the Reporter EA uses. There is no separate API credential.
Open your profile
Sign in at xtriel.com, open Profile, then open Connect your EA.
Generate or copy the token
The token is shown once, so store it safely. Regenerating the token immediately invalidates the old one for both the Reporter EA and any upload agents.
Send it as a Bearer token
The token resolves to your account server-side. Do not send a user id or email in the upload payload.
Authorization: Bearer xti_xxxxxxxxxxxxxxxxxxxxxxxx
xAlmanac is a paid feature. Free accounts store 0 presets, so uploads on a Free account are rejected with ALMANAC_LIMIT. Pro and Max can store results.
Endpoint
POST https://xtriel.com/api/almanac/ingest
Authorization: Bearer xti_...
Content-Type: application/json
The request body is a batch with one or more preset results:
{
"presets": [
{
"ea": "MyExpert",
"version": "v1",
"name": "EURUSD H1 example",
"kind": "optimization",
"setFile": {
"name": "result.set",
"content": "InpRiskPerSetupPercent=2.0\nInpTakeProfitMode=3\n..."
},
"reportFile": {
"name": "ReportTester.html",
"contentBase64": "PGh0bWw+...",
"encoding": "utf-16le"
},
"tester": {
"expert": "MyExpert.ex5",
"symbol": "EURUSD",
"timeframe": "H1",
"model": 4,
"fromDate": "2026.06.22",
"toDate": "2026.06.28",
"deposit": 10000,
"currency": "USD",
"leverage": "1:100"
},
"results": {
"netProfit": 12450,
"profitFactor": 1.84,
"maxDDpct": 8.7
},
"sourceKey": "sha256-of-set-and-report"
}
]
}
Field reference
| Field | Req | Notes |
|---|---|---|
ea | Yes | EA name, 64 characters or fewer. Results group under this name in xAlmanac. |
setFile.content | Yes | The .set file as UTF-8 text. |
setFile.name | No | Original filename, used for the download name. |
version | No | Defaults to "v1". |
name | No | Display name for the result. |
kind | No | "backtest" or "optimization". Inferred from the .set if omitted. |
reportFile.contentBase64 | No | Base64 of the MT5 .htm report bytes. Parsed in the browser for metrics and balance curve. |
reportFile.encoding | No | For example utf-16le or utf-8. If omitted, xAlmanac sniffs the BOM when parsing the report. |
reportHtmlB64 | No | Shorthand alias for reportFile.contentBase64, with no encoding hint. |
tester | No | Strategy Tester run config shown in the Tester settings panel. Allowed keys include expert, symbol, timeframe/period, model, fromDate, toDate, deposit, currency, leverage, optimization, optimizationCriterion, executionMode, spread, and forwardMode. Unknown keys are dropped. |
results | No | Headline metrics object. Stored as sent; xAlmanac fills missing metrics from the report when you open the result. |
equity | No | Balance curve as [[time, balance], ...], up to 5000 points. Usually unnecessary because it is taken from the report. |
tags | No | Array of strings. |
sourceKey | No | Stable dedupe key. Same key, or same EA + version + .set + report, updates the existing result instead of creating a duplicate. |
Limits
- Report size: 2 MB or less after decoding.
- Request body: 25 MB or less.
- Batch size: up to 50 presets per request.
results: up to 16 KB.equity: up to 5000 points.
Response
A successful request returns 200 OK with created, updated, and skipped arrays.
{
"ok": true,
"created": [
{ "artifactId": "...", "ea": "MyExpert", "version": "v1", "name": "..." }
],
"updated": [],
"skipped": []
}
created: new results stored.updated: existing results updated because the dedupe key matched.skipped: rejected items, each with a reason such asMISSING_EA,MISSING_SET,BAD_REPORT,REPORT_TOO_LARGE, orALMANAC_LIMIT.- A bad item does not fail the entire batch.
- If any item hits the plan cap, the response also carries
code: "ALMANAC_LIMIT".
| Status | Meaning |
|---|---|
401 | Missing or invalid token. |
400 | Malformed JSON, no presets array, or more than 50 presets in a batch. |
413 | Body too large. |
429 | Too many bad-token attempts. Back off for about 15 minutes. |
What appears in xAlmanac
Open xAlmanac. Uploaded results are auto-imported on load, or by using the Agent uploads button, and render like a manual import.
- Full Detail view: net profit, profit factor, drawdown, Sharpe, and balance curve.
- Tester settings panel: Expert, Symbol, Period, Model, date range, Deposit, Currency, and Leverage.
- Blue Download .set button for the uploaded preset.
- Source filter and source column for Manual vs Agent API uploads.
- API badge on uploaded results.
- Download report action for retrieving the original
.htm.
To reproduce a run in MT5, download the .set, open View -> Strategy Tester, load the EA, choose Inputs -> Load, then match Symbol, Period, Modelling, date range, and Deposit to the Tester settings panel.
Python upload agent
This example reads the preset as text, reads the report as raw bytes, base64-encodes the report, and posts one result to the ingest endpoint.
import base64
import os
import pathlib
import requests
GATEWAY = "https://xtriel.com/api/almanac/ingest"
TOKEN = os.environ["XTRIEL_INGEST_TOKEN"]
def upload_result(set_path, report_path, ea, version="v1", name="", tester=None):
set_path = pathlib.Path(set_path)
report_path = pathlib.Path(report_path)
set_text = set_path.read_text(encoding="utf-8", errors="replace")
report_raw = report_path.read_bytes()
preset = {
"ea": ea,
"version": version,
"name": name or report_path.stem,
"setFile": {
"name": set_path.name,
"content": set_text,
},
"reportFile": {
"name": report_path.name,
"contentBase64": base64.b64encode(report_raw).decode("ascii"),
"encoding": "utf-16le",
},
}
if tester:
preset["tester"] = tester
response = requests.post(
GATEWAY,
headers={"Authorization": f"Bearer {TOKEN}"},
json={"presets": [preset]},
timeout=30,
)
response.raise_for_status()
return response.json()
print(upload_result(
set_path="myexpert.set",
report_path="report.htm",
ea="MyExpert",
version="v1",
name="EURUSD H1 example",
tester={
"expert": "MyExpert.ex5",
"symbol": "EURUSD",
"timeframe": "H1",
"model": 4,
"fromDate": "2026.06.22",
"toDate": "2026.06.28",
"deposit": 10000,
"currency": "USD",
"leverage": "1:100",
},
))
curl upload
Use curl when you already have a prepared batch.json payload.
curl -sS -X POST https://xtriel.com/api/almanac/ingest \
-H "Authorization: Bearer $XTRIEL_INGEST_TOKEN" \
-H "Content-Type: application/json" \
--data @batch.json
Deploy the upload agent
The upload agent can run on the same machine that can read your MT5 files, or on a VPS where you sync Strategy Tester exports. Keep the ingest token out of code and pass it as an environment variable.
Create a small Python environment
python3 -m venv .venv
. .venv/bin/activate
pip install requests
Store the token outside the script
export XTRIEL_INGEST_TOKEN="xti_xxxxxxxxxxxxxxxxxxxxxxxx"
Run the uploader against your exported files
python upload_xalmanac.py
To upload your 10 good results, send 10 objects in the presets array in one batch, or call the Python helper once per result. Re-running with the same .set and report updates the existing entry instead of duplicating it.
Troubleshooting
401: copy a fresh Ingest Token from Profile -> Connect your EA and update your environment variable.ALMANAC_LIMIT: upgrade to Pro or Max, or remove old results before uploading again.BAD_REPORT: re-export the report from MT5 Strategy Tester and preserve its original bytes before base64 encoding.REPORT_TOO_LARGE: keep the decoded report at 2 MB or less.- Missing metrics: include the MT5 report. xAlmanac can fill many gaps from the report when you open the result.