Interface BacktestOutcome

All Known Implementing Classes:
BacktestOutcome.Aborted, BacktestOutcome.Completed, BacktestOutcome.Failed, BacktestOutcome.InProgress

What the platform has to say about a backtest run right now — read by QTSurfer.backtestResult(String, String) for a job the calling process did not necessarily start.

Four answers, and they are not invented here: they are the four cases the SDK's own poll loop already reasons in when it decides whether a run is still moving. Reading a run without running it has to keep them apart, so they are the variants of this type:

Why a sealed type rather than the results alone. The workflow path — Backtest.await() and the backtest(...) shortcut — hands back a bare ResultMap because it reports the two bad endings out of band, completing exceptionally with QTSExecutionError or QTSCanceledError. A standalone read cannot borrow that channel: asking "what happened to this job?" and being told "it failed" is the question being answered, not the read going wrong. With the exception channel gone, the status has to travel in the return value or it does not travel at all — and the platform always sends it, so dropping it would be discarding the answer.

Matching on the variants is the intended use:


 BacktestOutcome outcome = qts.backtestResult("binance", jobId);

 if (outcome instanceof BacktestOutcome.Completed c) {
     report(c.results().getPnlTotal());
 } else if (outcome instanceof BacktestOutcome.Failed f) {
     report("failed: " + f.state().getStatusDetail());
 } else if (outcome instanceof BacktestOutcome.Aborted) {
     report("cancelled");
 } else {
     report("still running; ask again later");
 }
 

This library targets Java 17, where a switch over the variants is still a preview feature — hence the instanceof chain. On a newer runtime the sealing makes that switch exhaustive without a default.

A job id the platform does not recognise is not one of these. That is the one answer that stays an exception — see QTSurfer.backtestResult(String, String).

The links above point at QTSurfer for concreteness; AuthenticatedClient carries the same method with the same semantics, and this type is what both return.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Interface
    Description
    static final record 
    The run was cancelled.
    static final record 
    The run finished and produced its numbers.
    static final record 
    The run finished badly.
    static final record 
    The platform knows this job but has nothing final to report yet.
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    Whether the run has stopped moving.
    com.qtsurfer.api.client.model.ResultMap
    The run's numbers, exactly as the platform sent them.
    com.qtsurfer.api.client.model.JobState
    The platform's job record, carrying the raw status, the progress counts, and — on a run that ended badly — JobState.getStatusDetail().
  • Method Details

    • state

      com.qtsurfer.api.client.model.JobState state()
      The platform's job record, carrying the raw status, the progress counts, and — on a run that ended badly — JobState.getStatusDetail().

      Non-null on every terminal variant — a run cannot be reported as finished, failed or cancelled without the platform having said so. BacktestOutcome.InProgress is the only variant where it may be absent, and it is absent for one reason: the platform answered without a body, which is how it reports a job it knows but cannot yet describe. An BacktestOutcome.InProgress that came from an unrecognised or missing status on a response that did carry a body has one. Either way, read it defensively on that variant.

      Returns:
      the job record; may be null on BacktestOutcome.InProgress
    • results

      com.qtsurfer.api.client.model.ResultMap results()
      The run's numbers, exactly as the platform sent them.

      Only final on BacktestOutcome.Completed. The field is carried on every variant rather than dropped, because the platform always sends it and this SDK is not the right place to decide it is uninteresting — but a run that has not finished, or that ended badly, has at best a partial account of itself here, and it can be absent entirely.

      Returns:
      the result map, or null when the response carried none
    • finished

      boolean finished()
      Whether the run has stopped moving. true for BacktestOutcome.Completed, BacktestOutcome.Failed and BacktestOutcome.Aborted alike — finishing badly is still finishing, and re-reading will not change the answer.
      Returns:
      true unless this is BacktestOutcome.InProgress