Skip to content

Backtest ​

Loads historical candles, runs a script over the window, and returns candles, per-bar chart outputs, and strategy reports when applicable.

Service: dxscript.backtesting.v1.BacktestingService
Method: backtest
Interaction: request / response

HTTP ​

http
POST /dxscript.backtesting.v1.backtestingservice/backtest
Content-Type: application/json

Request ​

FieldTypeRequiredDescription
langstringyesMust be dxscript-js
contentstringyesFull script source
symbolstringyesCandle symbol (e.g. AAPL{=d})
fromTimestringyesRange start as RFC 3339 timestamp
toTimestringyesRange end as RFC 3339 timestamp

fromTime must be before or equal to toTime. Invalid symbols return a validation error without loading candles.

Examples ​

Strategy backtest (success) ​

bash
curl -sS -X POST 'https://dxlink-dxscript-http-demo.dxkube.com/dxscript.backtesting.v1.backtestingservice/backtest' \
  -H 'Content-Type: application/json' \
  -d '{
    "lang": "dxscript-js",
    "content": "strategy({ initialCapital: 10000, title: \"MA Crossover\" });\nconst fast = input.double(\"Fast\", 10);\nconst slow = input.double(\"Slow\", 20);\nfunction onTick() {\n  const f = ta.sma(close, fast);\n  const s = ta.sma(close, slow);\n  if (bar.isConfirmed()) {\n    if (ta.crossover(f, s)) strategy.entry(\"long\", strategy.LONG);\n    if (ta.crossunder(f, s)) strategy.close(\"long\");\n  }\n  spline(f, { title: \"Fast\" });\n  spline(s, { title: \"Slow\" });\n}",
    "symbol": "AAPL{=d}",
    "fromTime": "2024-01-01T00:00:00Z",
    "toTime": "2024-03-01T00:00:00Z"
  }'

Request body:

json
{
  "lang": "dxscript-js",
  "content": "strategy({ initialCapital: 10000, title: \"MA Crossover\" });\nconst fast = input.double(\"Fast\", 10);\nconst slow = input.double(\"Slow\", 20);\nfunction onTick() {\n  const f = ta.sma(close, fast);\n  const s = ta.sma(close, slow);\n  if (bar.isConfirmed()) {\n    if (ta.crossover(f, s)) strategy.entry(\"long\", strategy.LONG);\n    if (ta.crossunder(f, s)) strategy.close(\"long\");\n  }\n  spline(f, { title: \"Fast\" });\n  spline(s, { title: \"Slow\" });\n}",
  "symbol": "AAPL{=d}",
  "fromTime": "2024-01-01T00:00:00Z",
  "toTime": "2024-03-01T00:00:00Z"
}

Response 200 — error is absent (truncated; this run returned 42 daily bars and no fills, so strategyTradeReport and strategyFillReport are omitted):

json
{
  "meta": {
    "title": "MA Crossover"
  },
  "candles": [
    {
      "eventSymbol": "AAPL{=d}",
      "eventTime": "0",
      "eventFlags": 0,
      "index": "7319283979360665600",
      "time": "1704153600000",
      "sequence": 0,
      "count": "1008888",
      "open": 187.15,
      "high": 188.44,
      "low": 183.885,
      "close": 185.64,
      "impVolatility": 0.2491,
      "askVolume": 50986581.0,
      "bidVolume": 31506006.0,
      "openInterest": "NaN",
      "source": "DEFAULT",
      "volume": 82488674.0,
      "vwap": 185.893310807251
    },
    {
      "eventSymbol": "AAPL{=d}",
      "eventTime": "0",
      "eventFlags": 0,
      "index": "7319655064535040000",
      "time": "1704240000000",
      "sequence": 0,
      "count": "656859",
      "open": 184.22,
      "high": 185.88,
      "low": 183.43,
      "close": 184.25,
      "impVolatility": 0.2515,
      "askVolume": 33221869.0,
      "bidVolume": 25193853.0,
      "openInterest": "NaN",
      "source": "DEFAULT",
      "volume": 58414460.0,
      "vwap": 184.3191143024005
    }
  ],
  "calculationResult": {
    "spline": {
      "0": {
        "knots": [
          { "value": "NaN", "style": "LINE", "offset": 0, "title": "Fast", "overlay": true },
          { "value": "NaN" },
          { "value": "NaN" },
          { "value": "NaN" }
        ]
      },
      "1": {
        "knots": [
          { "value": "NaN", "style": "LINE", "offset": 0, "title": "Slow", "overlay": true },
          { "value": "NaN" },
          { "value": "NaN" },
          { "value": "NaN" }
        ]
      }
    }
  },
  "strategyAnalytics": {
    "equitySeries": [10000.0, 10000.0, 10000.0],
    "barCloseSeries": [185.64, 184.25, 181.91],
    "performance": {
      "initialCapital": 10000.0,
      "finalEquity": 10000.0,
      "buyHoldReturnPercent": -3.221288515406162,
      "strategyOutperformancePercent": 3.221288515406162
    }
  }
}

Notes on this response:

  • candles use the full dxFeed candle shape. time is epoch milliseconds as a string; openInterest may serialize as the string "NaN".
  • calculationResult.spline is keyed by layer id ("0", "1", …), not spline title. Each knots array has one entry per loaded bar; static metadata appears on the first knot only.
  • calculationResult.output channels use { "values": [...] } with one entry per loaded bar. Warmup bars use the non-finite string "NaN", not null.
  • Leading "NaN" knots are normal while moving averages warm up.
  • strategyTradeReport and strategyFillReport appear once the strategy fills an order; a position still open at the end of the window is a trade row with "open": true. performance may include only a subset of summary fields when others are not applicable.

Script error after candles are loaded ​

When execution fails after compilation, candles is still returned:

bash
curl -sS -X POST 'https://dxlink-dxscript-http-demo.dxkube.com/dxscript.backtesting.v1.backtestingservice/backtest' \
  -H 'Content-Type: application/json' \
  -d '{
    "lang": "dxscript-js",
    "content": "strategy({ title: \"Broken Strategy\" });\nfunction onTick() {\n  missingHelper();\n}",
    "symbol": "AAPL{=d}",
    "fromTime": "2024-01-01T00:00:00Z",
    "toTime": "2024-01-02T00:00:00Z"
  }'

Response 200:

json
{
  "candles": [
    {
      "eventSymbol": "AAPL{=d}",
      "eventTime": "0",
      "eventFlags": 0,
      "index": "7319283979360665600",
      "time": "1704153600000",
      "sequence": 0,
      "count": "1008888",
      "open": 187.15,
      "high": 188.44,
      "low": 183.885,
      "close": 185.64,
      "impVolatility": 0.2491,
      "askVolume": 50986581.0,
      "bidVolume": 31506006.0,
      "openInterest": "NaN",
      "source": "DEFAULT",
      "volume": 82488674.0,
      "vwap": 185.893310807251
    }
  ],
  "error": {
    "id": "9e5e5647-fc5a-45ce-bc42-fb3f2a2bcd4f",
    "type": "RUNTIME",
    "scriptName": "backtest",
    "startLine": 3,
    "startColumn": 3,
    "endLine": 3,
    "endColumn": 15,
    "message": "missingHelper is not defined",
    "scriptStack": [
      { "functionName": "onTick", "line": 3, "column": 3 }
    ]
  }
}

Candle fetch failure ​

When the candle provider throws (timeout, connectivity, or upstream failure), candles is empty and error carries the provider exception:

json
{
  "candles": [],
  "error": {
    "type": "TimeoutException",
    "scriptName": "",
    "startLine": -1,
    "startColumn": -1,
    "endLine": -1,
    "endColumn": -1,
    "message": "Candle feed fetch timed out after PT2M",
    "scriptStack": []
  }
}

error.type is the Java exception simple name; id is omitted when empty. Some demo feeds return a placeholder candle for unknown symbols instead of raising this error.

Response fields ​

FieldTypeDescription
candlesarrayLoaded candles in full dxFeed market-data format
calculationResultobjectPer-bar chart outputs for the full window; omitted channels are absent
strategyTradeReportobjectClosed/open trades; omitted when there are no trades
strategyFillReportobjectEntry/exit fills; omitted when there are no fills
strategyOrderReportobjectResting and refused orders with their type and status; omitted when no order rested or was refused
strategyAnalyticsobjectEquity curve and performance summary for strategy scripts
metaobjecttitle, shortTitle when declared
errorobjectStructured script or fetch error; present on failure only

calculationResult ​

Unlike validation, every channel contains one value per loaded bar (same length and order as candles):

ChannelMap keyValue shape
outputchannel name{ values: [...] } — one entry per bar; numbers or non-finite strings ("NaN", "Infinity", "-Infinity"), never null
splinelayer id ("0", "1", …){ knots: [...] } — one knot per bar
backgroundColorlayer key{ values: [...] }
barColorlayer key{ values: [...] }
shapeseries key{ points: [...] }

Spline knots follow the same metadata conventions as the Validate response, extended to every bar. Static fields (title, style, offset, overlay) appear on the first knot; later knots usually carry only value.

strategyTradeReport.trades[] ​

FieldDescription
tradeNumberSequential trade id
directionLONG or SHORT
entrySignal, exitSignalSignal names from strategy.entry() / strategy.close()
entryBarIndex, exitBarIndexBar indices in the loaded window
entryTimeMillis, exitTimeMillisFill timestamps
entryPrice, exitPriceFill prices
sizePosition size
netPnl, cumulativePnlTrade and running PnL
maxRunup, maxDrawdownIntrabar excursion while trade was open
opentrue when the trade has no exit yet
entrySide, exitSideBUY or SELL

strategyAnalytics ​

FieldDescription
equitySeriesAccount equity per bar
barCloseSeriesUnderlying close prices for comparison
totalCommissionPaidCumulative commission
performanceSummary metrics (see below)

performance may include initialCapital, finalEquity, netProfit, profitFactor, totalClosedTrades, winningTrades, losingTrades, percentProfitable, maxEquityDrawdown, sharpeRatio, sortinoRatio, buyHoldReturnPercent, strategyReturnPercent, strategyOutperformancePercent, and related fields. Only applicable fields are serialized.

WebSocket RPC ​

Call backtest on service dxscript.backtesting.v1.BacktestingService through the dxLink WebSocket session. Request and response use the BacktestRequest / BacktestResponse protobuf messages.

See also ​