Class Sweep

java.lang.Object
com.qtsurfer.api.sdk.Sweep

public final class Sweep extends Object
Handle for a running parameter sweep. Returned by QTSurfer.sweep(SweepRequest) once the platform has accepted the sweep; the leaderboard keeps being polled in the background.
  • await() resolves with the final ExecuteSweepResult.
  • results(SweepOrder) re-reads that leaderboard in another view — the untruncated one, for instance — without re-running anything.
  • progress() streams SweepProgressEvents.
  • cancel() asks the platform to stop between parameter vectors.
  • sensitivity() reads how the objective responds to each axis.
  • state() returns a local snapshot of the lifecycle.
  • accepted() carries what acceptance already answered — the effective seed, the grid size, whether this submission enqueued anything, and whether this is a walk-forward sweep.
  • Constructor Details

    • Sweep

      public Sweep(com.qtsurfer.api.client.model.ExecuteSweepAccepted accepted, String requestId, String strategyId, CompletableFuture<com.qtsurfer.api.client.model.ExecuteSweepResult> result, Flow.Publisher<SweepProgressEvent> progress, AtomicReference<Sweep.State> state, Runnable cancelHook, BiFunction<SweepOrder,SweepRanking,com.qtsurfer.api.client.model.ExecuteSweepResult> resultsReader, Function<SweepObjective,com.qtsurfer.api.client.model.SweepSensitivity> sensitivityReader)
      Internal constructor used by the SDK workflow; not part of the public contract.
      Parameters:
      accepted - the platform's acceptance of the sweep, exactly as it answered
      requestId - the prepared dataset this sweep runs on
      strategyId - the compilation every trial runs
      result - resolves with the final leaderboard
      progress - feed of progress events
      state - lifecycle holder, shared with the polling task
      cancelHook - requests server-side cancellation
      resultsReader - re-reads the leaderboard for an optional order and ranking
      sensitivityReader - reads sensitivity surfaces for an optional objective
  • Method Details

    • id

      public String id()
      Server-side sweepId.
      Returns:
      the sweep identifier
    • requestId

      public String requestId()
      The prepared dataset every trial ran against — the prepare jobId the workflow resolved before submitting, which is also what addresses this sweep on the wire.

      This is the value the workflow prepared with, not the acceptance echo of it. accepted() carries the echo, unmodified, for anyone who wants to compare the two.

      Returns:
      the prepared-dataset identifier
    • strategyId

      public String strategyId()
      The compiled strategy every trial shares.
      Returns:
      the strategyId
    • getAccepted

      public com.qtsurfer.api.client.model.ExecuteSweepAccepted getAccepted()
      What acceptance already answered, before a single trial has run, exactly as the platform sent it.

      Three of its fields are the reason this is exposed rather than folded away:

      • ExecuteSweepAccepted.getSeed() — the effective seed, generated platform-side when the request omitted one. Submitting it again is what makes a randomly sampled sweep replayable.
      • ExecuteSweepAccepted.getQueued() — false means an identical sweep already existed and nothing new was enqueued. The handle is still valid and still resolves; it is just reading a sweep this call did not start.
      • ExecuteSweepAccepted.getWalkForward() — present exactly when this is a walk-forward sweep. It is the discriminator, and it is available here immediately, so code watching progress() can branch on the answer's shape without waiting for await().
      Returns:
      the acceptance record
    • accepted

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.ExecuteSweepAccepted accepted()
      Deprecated.
    • getState

      public Sweep.State getState()
      Local snapshot of the sweep lifecycle; does not itself contact the server.
      Returns:
      the current state
    • state

      @Deprecated(forRemoval=false) public Sweep.State state()
      Deprecated.
    • getProgress

      public Flow.Publisher<SweepProgressEvent> getProgress()
      Reactive-streams feed of progress events; terminates when the sweep reaches a terminal state.
      Returns:
      the progress publisher
    • progress

      Deprecated.
    • await

      public CompletableFuture<com.qtsurfer.api.client.model.ExecuteSweepResult> await()
      Resolves with the final leaderboard once the sweep stops advancing.

      This resolves on every terminal status, cancellation included — COMPLETED, PARTIAL and CANCELLED all hand back the result rather than raising. That is a deliberate divergence from Backtest.await(), which completes exceptionally when its run is aborted: a cancelled sweep keeps every row it already finished, and throwing them away would lose the only reason to cancel a sweep late rather than early. Read ExecuteSweepResult.getStatus() to find out which of the three you got. The future completes exceptionally only for transport failures, HTTP errors, and stage timeouts.

      PARTIAL means at least one unit of work died and its runs are simply missing. There is no failed status for a sweep as a whole, so a sweep whose every shard died is PARTIAL with an empty leaderboard — check ExecuteSweepResult.getLeaderboardSize() before reading anything into a top row.

      Reading the leaderboard

      The default order is not the raw objective order. It is plateau order, and ExecuteSweepResult.getRanking() says which was actually applied — not always the one requested, because a sweep with no stored parameter grid cannot be plateau-ranked and falls back to raw. See SweepRanking.

      The default view is capped. When ExecuteSweepResult.getTruncated() is true, rows exist that the leaderboard does not carry — ExecuteSweepResult.getLeaderboardSize() counts what is available. results(SweepOrder.NATURAL) returns all of them, in runIx order and with no ranking applied, by re-reading this same sweep — nothing is re-run.

      SweepRunRow.getPlateauScore() and SweepRunRow.getNeighbourCount() are read together. A neighbour count of 0 means the point had no neighbours in the grid to compare against, so its plateau score is unevidenced rather than confirmed — on its own it is indistinguishable from a genuinely robust one.

      SweepRunRow.getDeflatedSharpe() is the probability that a row's Sharpe reflects real edge rather than the best draw from however many vectors were tried. Around 0.95 and up it survives the multiple-testing correction; near 0.5 or below it is not distinguishable from the best of a pile of coin flips. It is absent on aborted runs, and on sweeps with too few trials to establish any dispersion to deflate against.

      ExecuteSweepResult.getPbo() is the probability of backtest overfitting for the sweep as a whole: how often the configuration that won in-sample lands below median out-of-sample. Above roughly 0.5 the sweep is selecting noise, and that verdict is about the search, not about any one row — a high value discredits the top row however good it looks. It is computed once the last unit of work finishes, so it is absent while the sweep is still running and on sweeps too small for the statistic to mean anything.

      A walk-forward sweep answers in a different shape

      ExecuteSweepResult.getWalkForward() is the discriminator, and it appears as soon as the sweep is accepted — before any fold has finished — so it is safe to branch on while polling (it is also on accepted()). When it is present, the leaderboard is one row per completed fold: that fold's winner as it scored out-of-sample, with SweepRunRow.getRunIx() carrying the fold index rather than a position in the grid. No plateau score, deflated Sharpe or PBO figure is reported for one — the out-of-sample numbers are already the honest measurement.

      WalkForwardResult.getParamDrift() absent is not zero. The field is omitted whenever the figure could not be computed — fewer than two folds finished, or no stored grid to place the winners on — and zero is itself a meaningful reading there (winners that never moved), so a placeholder would be indistinguishable from perfect stability.

      An empty leaderboard is not always an empty answer. A sweep can finish having scored nothing — every shard failed before producing a row — and in that case ExecuteSweepResult.getFailReason() carries the cause reported by the first shard to fail, typically something the whole grid would have hit, such as a strategy that could not be loaded. Read it before concluding that a sweep with no rows simply found nothing: those are different outcomes and the leaderboard alone cannot tell them apart. Only the first failure is recorded, so where several shards failed differently this names one of them rather than summarising all — pair it with getProgress().getFailedShards() for the count.

      Returns:
      the final leaderboard
    • getResults

      public com.qtsurfer.api.client.model.ExecuteSweepResult getResults(SweepOrder order)
      Re-read this sweep's leaderboard in a different view.

      Equivalent to results(SweepOrder, SweepRanking) with no ranking preference.

      Parameters:
      order - which view to read; null takes the platform default
      Returns:
      the leaderboard as it stands now, in the requested view
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • getResults

      public com.qtsurfer.api.client.model.ExecuteSweepResult getResults(SweepOrder order, SweepRanking ranking)
      Re-read this sweep's leaderboard in a different view, without re-running anything.

      This is a read of the same sweep, addressed by the ids this handle already holds. It does not compile, does not prepare, does not submit, and does not create a second sweep — the view is a query parameter on the read, not a property of the run. await() resolves once, in whatever view SweepOptions fixed at submit time; this is how to look at the same rows another way afterwards.

      It is the route to rows the ranked view dropped. When ExecuteSweepResult.getTruncated() is true the leaderboard was capped for display; SweepOrder.NATURAL returns every available row instead, in deterministic runIx order.

      ranking is ignored when order is SweepOrder.NATURAL — that view is always ordered by runIx, and the response comes back raw whatever was asked for. Rank, plateau score and neighbour count belong to the ranked view and are not part of it.

      Works on a sweep still in flight, where it returns the rows finished so far — the same as sensitivity(). It is not only for completed sweeps, and it does not wait for one.

      Synchronous — blocks the calling thread for one HTTP round trip. Like every handle-scoped call, it does not take part in an AuthenticatedClient's refresh-on-401 policy.

      Parameters:
      order - which view to read; null takes the platform default (SweepOrder.RANKED)
      ranking - how to order the ranked view; null takes the platform default (SweepRanking.PLATEAU)
      Returns:
      the leaderboard as it stands now, in the requested view
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • results

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.ExecuteSweepResult results(SweepOrder order)
      Deprecated.
    • results

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.ExecuteSweepResult results(SweepOrder order, SweepRanking ranking)
    • cancel

      public boolean cancel()
      Ask the platform to stop the sweep between parameter vectors.

      The rows already finished stay readable, so this does not abandon the work done so far: the poll keeps running until the platform reports the sweep as CANCELLED, and await() then resolves normally with the partial leaderboard. Cancelling therefore depends on the platform answering; on a SweepOptions with no timeout, a sweep that never reports cancelled leaves await() waiting.

      A cancel that arrives after the last unit of work has finished changes nothing — the sweep completes and state() settles on Sweep.State.COMPLETED.

      Returns:
      true if the call caused a transition from Sweep.State.EXECUTING
    • getSensitivity

      public com.qtsurfer.api.client.model.SweepSensitivity getSensitivity()
      Sensitivity surfaces for the objective this sweep was submitted with.
      Returns:
      how the objective responds to each axis
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • getSensitivity

      public com.qtsurfer.api.client.model.SweepSensitivity getSensitivity(SweepObjective objective)
      How the objective moves as each parameter moves — the question a leaderboard cannot answer. A leaderboard says which point won; a sweep can spend its whole budget on an axis that never moved the objective at all, and the top rows hide that completely.

      A marginal takes one axis and collapses every other one: for each value of that axis it aggregates every run that used it, whatever the rest of the parameters were. A flat marginal means the axis did not matter over the range swept. best, mean and worst are all reported because them disagreeing is the signal — a value with a high best and a poor mean only works in specific company, which is an interaction, and a single number would hide it. A heatmap does the same over a pair of axes, where that interaction is visible directly.

      Check SweepSensitivity.getHeatmapsTruncated(). Marginals are always complete; the pair surfaces are quadratic in the axis count and may be capped to stay inside the response budget. When the flag is true, at least one pair was left out, so the list you have is not the full set of interactions. This method hands back the whole SweepSensitivity rather than just its surfaces precisely so that flag cannot be lost on the way out.

      Readable while the sweep is still running, in which case the aggregates describe the runs finished so far and SweepSensitivity.getRowsAnalysed() says how many that was. Aborted runs are excluded throughout: a run that threw measured nothing, and counting it as a bad outcome would invent evidence against a parameter value that was never really tested.

      Synchronous — blocks the calling thread for one HTTP round trip. Like every handle-scoped call, it does not take part in an AuthenticatedClient's refresh-on-401 policy.

      Parameters:
      objective - which metric to aggregate; null uses the objective the sweep was submitted with
      Returns:
      the marginals, the heatmaps, and whether the heatmaps are all of them
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
      See Also:
      • SweepMarginal
    • sensitivity

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.SweepSensitivity sensitivity()
      Deprecated.
    • sensitivity

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.SweepSensitivity sensitivity(SweepObjective objective)
    • toString

      public String toString()
      Compact debug representation including the sweep id and current state.
      Overrides:
      toString in class Object
      Returns:
      a short description of this handle