API v1

xAlmanac Upload API

Push MT5 backtest and optimization results into your xTriel profile from an external agent. Each upload appears in xAlmanac as a fully parsed preset with metrics, balance curve, inputs, tester settings, and a downloadable .set.

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.

xTriel cannot read files directly from your computer.

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
Plan note:

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

FieldReqNotes
eaYesEA name, 64 characters or fewer. Results group under this name in xAlmanac.
setFile.contentYesThe .set file as UTF-8 text.
setFile.nameNoOriginal filename, used for the download name.
versionNoDefaults to "v1".
nameNoDisplay name for the result.
kindNo"backtest" or "optimization". Inferred from the .set if omitted.
reportFile.contentBase64NoBase64 of the MT5 .htm report bytes. Parsed in the browser for metrics and balance curve.
reportFile.encodingNoFor example utf-16le or utf-8. If omitted, xAlmanac sniffs the BOM when parsing the report.
reportHtmlB64NoShorthand alias for reportFile.contentBase64, with no encoding hint.
testerNoStrategy 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.
resultsNoHeadline metrics object. Stored as sent; xAlmanac fills missing metrics from the report when you open the result.
equityNoBalance curve as [[time, balance], ...], up to 5000 points. Usually unnecessary because it is taken from the report.
tagsNoArray of strings.
sourceKeyNoStable 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 as MISSING_EA, MISSING_SET, BAD_REPORT, REPORT_TOO_LARGE, or ALMANAC_LIMIT.
  • A bad item does not fail the entire batch.
  • If any item hits the plan cap, the response also carries code: "ALMANAC_LIMIT".
StatusMeaning
401Missing or invalid token.
400Malformed JSON, no presets array, or more than 50 presets in a batch.
413Body too large.
429Too 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.