Strategy

Check that a registered strategy can actually run

Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any engine notices — is recorded and served from GET /strategy/{strategyId}.

Idempotent. If a verdict already exists for the current compilation it comes straight back with 200 and nothing is queued. Otherwise the check is queued and this returns 202; poll GET /strategy/{strategyId} until validation is passed or failed.

Recompiling supersedes a verdict, which makes this callable again — the old answer described bytecode that is no longer what would run.

post/strategy/{strategyId}/validate

Request

  • Base URL: https://api.qtsurfer.net/v1
  • URL: https://api.qtsurfer.net/v1/strategy/{strategyId}/validate
  • Auth: HTTP bearer

Path parameters

strategyIdstring required

Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller. How much formatting the id ignores depends on the language — see POST /strategy for exactly which rewrites preserve it and which do not.

Example:6bsh31ikwkuivhtgcoa6s4

The id returned by POST /strategy

Response

Already validated; the recorded verdict, unchanged

strategyIdstring required

Unique identifier for a compiled strategy, derived from the source itself: the same code always yields the same id, for every caller. How much formatting the id ignores depends on the language — see POST /strategy for exactly which rewrites preserve it and which do not.

validation'not_validated' | 'pending' | 'passed' | 'failed' required
  • not_validated — registered, never checked. POST /strategy/{strategyId}/validate checks it.
  • pending — a check was asked for and has not answered yet.
  • passed — the class loaded and survived its first event.
  • failed — it did not; detail says how.
compiledAtstring date-time

When the live compilation was produced.

requiredSourcesstring[]

The market data a strategy needs, read off the compiled class rather than off anything you sent — TickerStrategy, KlineStrategy and FundingRateStrategy each declare one, and a MultiSourceStrategy declares a set.

Absent is not "needs nothing". A strategy always needs market data, so an absent field never means an empty requirement — it means the platform could not establish the answer without constructing your strategy, which it will not do to fill in a field. That happens for a MultiSourceStrategy, for a class that overrides getMarketDataSource(), and for anything registered before this field existed; re-registering the source fills it in.

validatedAtstring date-time

When the verdict was recorded. Absent until there is one.

detailstring

Why validation failed, or why a queued check has not reported. Present on failed, and alongside validationStalled.

noticesTruncatedinteger

How many notices were dropped past the cap. Absent when none were.

dryRunIncompleteboolean

The check did not finish its budget — it ran out of time, was refused because the platform was already holding too many unfinishable runs, or hit a failure attributable to the synthetic instrument rather than to your strategy. The verdict stands as far as it went; it simply reached less than a full run would.

validationStalledboolean

A queued check has not reported for far longer than one takes. Nothing is disproved about the strategy — the check has not run. Stop waiting and re-request it later.

Example response

{
  "strategyId": "6bsh31ikwkuivhtgcoa6s4",
  "validation": "passed",
  "compiledAt": "2026-08-04T16:23:04Z",
  "requiredSources": [
    "Ticker"
  ],
  "validatedAt": "2026-08-04T16:24:11Z",
  "notices": [
    {
      "level": "WARN",
      "code": "indicator.bar-data-on-ticker-path",
      "message": "Indicator requires bar data but is on the ticker path",
      "provenance": "compile-dry-run"
    }
  ],
  "_links": {
    "code": {
      "href": "/v1/strategy/6bsh31ikwkuivhtgcoa6s4/code"
    }
  }
}

Changes

No recorded changes to this operation across all 50 revisions of this API.