Backtesting API
Full reference for the Backtesting API — endpoints, request schema, response schema, metrics, and error codes.
Overview
The Backtesting API exposes two endpoints. You submit a strategy execution request and then poll for the result using the returned backtest ID.
Base URL: https://api.emidlabs.com/api/public/v1
x-api-key header with a valid API key. Keys are generated in the Console.#POST /backtest — Submit
/backtestSubmit a new backtest for execution.
The body must be application/json. The strategy is embedded as a serialized JSON string inside strategySnapshotJson.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| strategySnapshotJson | string | Yes | Strategy object serialized as a JSON string. |
| assetPair | string | Yes | Trading pair. E.g. "BTC-USDC", "ETH-USDT". |
| initialDate | string | Yes | Start date in ISO format: YYYY-MM-DD. |
| finalDate | string | Yes | End date in ISO format: YYYY-MM-DD. |
Supported asset pairs
Asset pairs correspond to pairs available on Coinbase. Current examples:
BTC-USDCETH-USDCETH-USDTSOL-USDCBTC-USDTThe full list of supported pairs matches the assets available in the Console. Date ranges must fall within the available historical data for the selected asset.
Submit response
| Field | Type | Description |
|---|---|---|
| id | string (UUID) | Unique identifier for the backtest. |
| status | string | "Running" immediately after submission. |
| assetPair | string | The asset pair used. |
| initialDate | string | Start date of the backtest period. |
| finalDate | string | End date of the backtest period. |
| createdAtUtc | string | Timestamp of creation in UTC. |
| canViewResult | boolean | Whether the result is accessible (depends on credits). |
#GET /backtest/:id — Fetch Results
/backtest/{id}Fetch the status and results of a submitted backtest.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| includeTrades | boolean | false | When true, includes the full tradesDetail array in result. |
includeTrades or set it to false when you only need aggregate metrics. The trade list can be large for long date ranges, so only request it when you need per-trade analysis.Response fields
| Field | Type | Description |
|---|---|---|
| id | string | UUID of the backtest. |
| status | string | "Running", "Completed", or "Failed". |
| strategySnapshotJson | string | The strategy used, serialized. |
| assetPair | string | Asset pair. |
| initialDate / finalDate | string | Date range. |
| createdAtUtc | string | Creation timestamp (UTC). |
| errorMessage | string | null | Error details if status is "Failed". |
| canViewResult | boolean | Whether the result is accessible. |
| logsJson | string | null | Execution logs serialized as JSON string. |
| result | object | null | Full performance metrics when Completed. |
#Metrics Reference
When status is Completed, the result object contains:
Profitability metrics
| Field | Type | Description |
|---|---|---|
| pnlR | number | Net profit/loss in R-units. Sum of all trade PnLs. |
| grossProfitR | number | Sum of all winning trades in R-units. |
| grossLossR | number | Sum of all losing trades in R-units (negative). |
| profitFactor | number | grossProfitR / abs(grossLossR). >1 is profitable. |
| expectancyR | number | Average expected R per trade. (winRate × avgWinR) + (lossRate × avgLossR). |
| avgWinR | number | Average R on winning trades. |
| avgLossR | number | Average R on losing trades (always -1.0 with fixed model). |
Trade statistics
| Field | Type | Description |
|---|---|---|
| trades | number | Total number of trades executed. |
| wins | number | Number of winning trades. |
| losses | number | Number of losing trades. |
| winRate | number | wins / trades. Range: 0–1. |
| bothHit | number | Trades where both SL and TP were hit in the same candle (resolved as SL). |
Diagnostics
| Field | Type | Description |
|---|---|---|
| conditionsDistributionPct | object | For each condition: fraction of candles where it was true. |
| scoreDistributionPct | object | For each possible score value: fraction of candles with that score. |
conditionsDistributionPct and scoreDistributionPct fields are key for strategy quality analysis. A condition that is almost always true adds no discriminating power. Use these to diagnose and refine your strategy structure.Trade detail fields (with includeTrades=true)
| Field | Type | Description |
|---|---|---|
| number | number | Sequential trade number. |
| entryPrice | number | Price at which the trade was entered. |
| exitPrice | number | Price at which the trade was closed. |
| pnlR | number | +3.0 for a win, -1.0 for a loss (fixed model). |
| pnlPct | number | Percentage gain/loss on the trade. |
| totalScoreAtEntry | number | Total score that triggered the entry. |
| conditionsAtEntry | object | Which conditions were true at entry. |
| scoreBreakdownAtEntry | object | Score contributed by each condition at entry. |
#Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | invalid_payload | The request body is malformed or missing required fields. |
| 400 | invalid_strategy | The strategySnapshotJson is invalid or references undefined variables. |
| 400 | invalid_date_range | The date range is outside available historical data for the asset. |
| 400 | unsupported_asset | The assetPair is not supported. |
| 401 | unauthorized | Missing or invalid API key. |
| 402 | insufficient_credits | Not enough credits to execute this backtest. |
| 429 | rate_limited | Too many requests. Back off and retry. |
| 500 | execution_failed | Server-side execution error. Check errorMessage field. |
When an error occurs at the execution level (after submission), the backtest status is set to Failed and the errorMessage field contains a description of what went wrong.