curl --request POST \
--url https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"strategyCode": "//@version=5\nstrategy(\"SMA cross\", overlay=true)\nfast = ta.sma(close, 10)\nslow = ta.sma(close, 30)\nif ta.crossover(fast, slow)\n strategy.entry(\"L\", strategy.long)\nif ta.crossunder(fast, slow)\n strategy.close(\"L\")\n",
"marketId": "BTC-USD",
"timeframe": "1m",
"series": "trade",
"fromEpochMs": 1790000000000,
"toEpochMs": 1790086400000,
"horizonTrades": 10,
"seed": 1,
"feeBps": 0,
"slippageBps": 0
}
'import requests
url = "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest"
payload = {
"strategyCode": "//@version=5
strategy(\"SMA cross\", overlay=true)
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
if ta.crossover(fast, slow)
strategy.entry(\"L\", strategy.long)
if ta.crossunder(fast, slow)
strategy.close(\"L\")
",
"marketId": "BTC-USD",
"timeframe": "1m",
"series": "trade",
"fromEpochMs": 1790000000000,
"toEpochMs": 1790086400000,
"horizonTrades": 10,
"seed": 1,
"feeBps": 0,
"slippageBps": 0
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
strategyCode: '//@version=5\nstrategy("SMA cross", overlay=true)\nfast = ta.sma(close, 10)\nslow = ta.sma(close, 30)\nif ta.crossover(fast, slow)\n strategy.entry("L", strategy.long)\nif ta.crossunder(fast, slow)\n strategy.close("L")\n',
marketId: 'BTC-USD',
timeframe: '1m',
series: 'trade',
fromEpochMs: 1790000000000,
toEpochMs: 1790086400000,
horizonTrades: 10,
seed: 1,
feeBps: 0,
slippageBps: 0
})
};
fetch('https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'strategyCode' => '//@version=5
strategy("SMA cross", overlay=true)
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
if ta.crossover(fast, slow)
strategy.entry("L", strategy.long)
if ta.crossunder(fast, slow)
strategy.close("L")
',
'marketId' => 'BTC-USD',
'timeframe' => '1m',
'series' => 'trade',
'fromEpochMs' => 1790000000000,
'toEpochMs' => 1790086400000,
'horizonTrades' => 10,
'seed' => 1,
'feeBps' => 0,
'slippageBps' => 0
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest"
payload := strings.NewReader("{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}"
response = http.request(request)
puts response.read_body{
"marketId": "<string>",
"timeframe": "1m",
"series": "trade",
"seriesLabel": "trade price series",
"window": {
"fromEpochMs": 1,
"toEpochMs": 123
},
"barCount": 1,
"initialCapital": 123,
"feeBps": 1,
"slippageBps": 1,
"summary": {
"tradeCount": 1,
"winningTrades": 1,
"losingTrades": 1,
"pnl": 123,
"maxDrawdown": 1,
"winRate": 123,
"averageTradePnl": 123,
"bestTradePnl": 123,
"worstTradePnl": 123,
"openPosition": true
},
"probability": {
"status": "estimated",
"method": "iid_trade_pnl_bootstrap",
"sampleSize": 1,
"minimumSampleSize": 30,
"window": {
"firstBarTime": 123,
"lastBarTime": 123
},
"seed": 1,
"horizonTrades": 123,
"resamples": 123,
"label": "historical estimate, not a guarantee",
"probabilityProfit": 123,
"probabilityLoss": 123,
"probabilityFlat": 123,
"winRateInterval": {
"lower": 123,
"upper": 123,
"confidence": 0.95
}
}
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}Backtest a Pine strategy on a market's history
Runs a Pine v5 strategy, in the subset agents run, over one market’s closed trade candles between fromEpochMs and toEpochMs, and answers what it would have done: a trades summary, PnL, maximum drawdown, win rate and a probability object. The probability is a bootstrap of the closed trades’ net PnL over horizonTrades trades; it is labelled “historical estimate, not a guarantee” and carries its method, sample size, trade window and seed, so the same request with the same seed answers the same numbers. With fewer than 30 closed trades its status is insufficient_sample and every probability is null. The window holds at most 1440 bars of the timeframe and spans at most 31 days; a wider, empty or reversed window answers 400 with agentRefusal backtest_window_invalid. A simulation that runs past 10 seconds answers 503 backtest_timed_out. Each account has one backtest in progress at a time and at most 6 per minute; past either limit the route answers 429 rate_limited with a Retry-After header, in seconds. Prices are floating point simulation values in quote units with fills at the next bar open; nothing is deployed and no order is placed. Requires the owner’s passkey session or an API key with the agents scope, which the owner grants with a passkey on the agent connect approval page. Available on TestNet only.
Method and path: POST /v1/accounts//agents/backtest
Authentication: DFNS trader bearer required.
Request schema: AgentBacktestRequest.
Response schema: AgentBacktestResult.
Error envelope: ExternalRestError (external-rest-error/v1).
curl --request POST \
--url https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"strategyCode": "//@version=5\nstrategy(\"SMA cross\", overlay=true)\nfast = ta.sma(close, 10)\nslow = ta.sma(close, 30)\nif ta.crossover(fast, slow)\n strategy.entry(\"L\", strategy.long)\nif ta.crossunder(fast, slow)\n strategy.close(\"L\")\n",
"marketId": "BTC-USD",
"timeframe": "1m",
"series": "trade",
"fromEpochMs": 1790000000000,
"toEpochMs": 1790086400000,
"horizonTrades": 10,
"seed": 1,
"feeBps": 0,
"slippageBps": 0
}
'import requests
url = "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest"
payload = {
"strategyCode": "//@version=5
strategy(\"SMA cross\", overlay=true)
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
if ta.crossover(fast, slow)
strategy.entry(\"L\", strategy.long)
if ta.crossunder(fast, slow)
strategy.close(\"L\")
",
"marketId": "BTC-USD",
"timeframe": "1m",
"series": "trade",
"fromEpochMs": 1790000000000,
"toEpochMs": 1790086400000,
"horizonTrades": 10,
"seed": 1,
"feeBps": 0,
"slippageBps": 0
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
strategyCode: '//@version=5\nstrategy("SMA cross", overlay=true)\nfast = ta.sma(close, 10)\nslow = ta.sma(close, 30)\nif ta.crossover(fast, slow)\n strategy.entry("L", strategy.long)\nif ta.crossunder(fast, slow)\n strategy.close("L")\n',
marketId: 'BTC-USD',
timeframe: '1m',
series: 'trade',
fromEpochMs: 1790000000000,
toEpochMs: 1790086400000,
horizonTrades: 10,
seed: 1,
feeBps: 0,
slippageBps: 0
})
};
fetch('https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'strategyCode' => '//@version=5
strategy("SMA cross", overlay=true)
fast = ta.sma(close, 10)
slow = ta.sma(close, 30)
if ta.crossover(fast, slow)
strategy.entry("L", strategy.long)
if ta.crossunder(fast, slow)
strategy.close("L")
',
'marketId' => 'BTC-USD',
'timeframe' => '1m',
'series' => 'trade',
'fromEpochMs' => 1790000000000,
'toEpochMs' => 1790086400000,
'horizonTrades' => 10,
'seed' => 1,
'feeBps' => 0,
'slippageBps' => 0
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest"
payload := strings.NewReader("{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://testnet.edel-api.xyz/v1/accounts/{accountId}/agents/backtest")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"strategyCode\": \"//@version=5\\nstrategy(\\\"SMA cross\\\", overlay=true)\\nfast = ta.sma(close, 10)\\nslow = ta.sma(close, 30)\\nif ta.crossover(fast, slow)\\n strategy.entry(\\\"L\\\", strategy.long)\\nif ta.crossunder(fast, slow)\\n strategy.close(\\\"L\\\")\\n\",\n \"marketId\": \"BTC-USD\",\n \"timeframe\": \"1m\",\n \"series\": \"trade\",\n \"fromEpochMs\": 1790000000000,\n \"toEpochMs\": 1790086400000,\n \"horizonTrades\": 10,\n \"seed\": 1,\n \"feeBps\": 0,\n \"slippageBps\": 0\n}"
response = http.request(request)
puts response.read_body{
"marketId": "<string>",
"timeframe": "1m",
"series": "trade",
"seriesLabel": "trade price series",
"window": {
"fromEpochMs": 1,
"toEpochMs": 123
},
"barCount": 1,
"initialCapital": 123,
"feeBps": 1,
"slippageBps": 1,
"summary": {
"tradeCount": 1,
"winningTrades": 1,
"losingTrades": 1,
"pnl": 123,
"maxDrawdown": 1,
"winRate": 123,
"averageTradePnl": 123,
"bestTradePnl": 123,
"worstTradePnl": 123,
"openPosition": true
},
"probability": {
"status": "estimated",
"method": "iid_trade_pnl_bootstrap",
"sampleSize": 1,
"minimumSampleSize": 30,
"window": {
"firstBarTime": 123,
"lastBarTime": 123
},
"seed": 1,
"horizonTrades": 123,
"resamples": 123,
"label": "historical estimate, not a guarantee",
"probabilityProfit": 123,
"probabilityLoss": 123,
"probabilityFlat": 123,
"winRateInterval": {
"lower": 123,
"upper": 123,
"confidence": 0.95
}
}
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "invalid_request",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "amount_atoms_invalid",
"withdrawalFailure": "requested",
"dependency": "dfns"
}Authorizations
The authorization field takes the session bearer returned by sign-in, or an API key where the route allows keys. Paste the token itself into the authorization field.
Path Parameters
1Body
1 - 200001 - 641m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d, 3d, 1w x >= 0The candle series to backtest on: trade prints, or the index price for a market with no trade history yet.
trade, index How many trades one bootstrap run sums: the probability is over that many trades.
1 <= x <= 100The bootstrap seed. The same request with the same seed answers the same numbers.
0 <= x <= 42949672950 <= x <= 10000 <= x <= 1000Response
Success
11m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1d, 3d, 1w trade, index Which prices the estimate comes from; show it beside the probability.
trade price series, index price series Show child attributes
Show child attributes
x >= 0x >= 0x >= 0Show child attributes
Show child attributes
Show child attributes
Show child attributes