@qtsurfer/api-client 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,20 +68,30 @@ pluggable token stores so callers don't reinvent that plumbing.
68
68
 
69
69
  All operations are exported as standalone functions; every operation accepts an `Options` object and returns `{ data, error, response }`.
70
70
 
71
+ The table is exhaustive: `src/generated/` is produced from the OpenAPI spec, so **all 18 operations**
72
+ the spec declares are exported. The rows below describe **spec version 0.107.0**, which is versioned
73
+ independently of this package.
74
+
71
75
  | Function | Method | Path | Purpose |
72
76
  | -------- | ------ | ---- | ------- |
73
77
  | `authenticate` | POST | `/auth/token` | Exchange an API key for a short-lived JWT |
74
- | `listExchanges` | GET | `/exchanges` | List available exchanges |
75
- | `listInstruments` | GET | `/exchange/{exchangeId}/instruments` | List instruments for an exchange |
76
- | `downloadTickers` | GET | `/exchange/{exchangeId}/tickers/{base}/{quote}` | Download one hour of tickers as Lastra/Parquet |
77
- | `downloadKlines` | GET | `/exchange/{exchangeId}/klines/{base}/{quote}` | Download one hour of klines as Lastra/Parquet |
78
- | `compileStrategy` | POST | `/strategy` | Compile a strategy |
79
- | `getStrategy` | GET | `/strategy/{strategyId}` | Poll strategy compilation status |
80
- | `prepareBacktest` | POST | `/backtesting/prepare` | Start a data preparation job |
81
- | `getPrepareStatus` | GET | `/backtesting/prepare/{jobId}` | Poll preparation status |
82
- | `executeBacktest` | POST | `/backtesting/execute` | Start a backtest execution |
83
- | `cancelBacktest` | POST | `/backtesting/execute/{jobId}/cancel` | Cancel a running execution |
84
- | `getBacktestResult` | GET | `/backtesting/execute/{jobId}` | Poll or fetch execution results |
78
+ | `listExchanges` | GET | `/exchanges` | List the available exchanges |
79
+ | `listInstruments` | GET | `/exchange/{exchangeId}/instruments` | List an exchange's instruments (default spot segment) |
80
+ | `listSegmentInstruments` | GET | `/exchange/{exchangeId}/{segment}/instruments` | List an exchange segment's instruments |
81
+ | `downloadTickers` | GET | `/exchange/{exchangeId}/tickers/{base}/{quote}` | Download one hour of tickers as a Lastra segment |
82
+ | `downloadKlines` | GET | `/exchange/{exchangeId}/klines/{base}/{quote}` | Download one hour of klines as a Lastra segment |
83
+ | `compileStrategy` | POST | `/strategy` | Compile and register a strategy |
84
+ | `validateStrategy` | POST | `/strategy/{strategyId}/validate` | Check that a registered strategy can actually run |
85
+ | `getStrategy` | GET | `/strategy/{strategyId}` | Get a strategy by id, including its validation state |
86
+ | `prepareBacktest` | POST | `/backtest/{exchangeId}/{type}/prepare` | Prepare backtest data |
87
+ | `getPrepareStatus` | GET | `/backtest/{exchangeId}/{type}/prepare/{jobId}` | Get the status of a prepare job |
88
+ | `executeSweep` | POST | `/backtest/{exchangeId}/{type}/executeSweep/{requestId}` | Execute a parameter sweep over prepared data |
89
+ | `getSweepResult` | GET | `/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}` | Get sweep progress and results |
90
+ | `cancelSweep` | DELETE | `/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}` | Cancel a running parameter sweep |
91
+ | `getSweepSensitivity` | GET | `/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/sensitivity` | Get sweep sensitivity surfaces |
92
+ | `executeBacktest` | POST | `/backtest/{exchangeId}/{type}/execute` | Execute a compiled strategy against a prepared dataset |
93
+ | `cancelBacktest` | DELETE | `/backtest/{exchangeId}/{type}/execute/{jobId}` | Cancel a running backtest execution |
94
+ | `getBacktestResult` | GET | `/backtest/{exchangeId}/{type}/execute/{jobId}` | Get the result of a backtest execution job |
85
95
 
86
96
  All generated types (`Exchange`, `InstrumentDetail`, `BacktestJobResult`, `PrepareJobState`, `ResultMap`, etc.) are re-exported from the root.
87
97
 
package/dist/index.d.ts CHANGED
@@ -512,6 +512,11 @@ type ExecuteSweepResult = {
512
512
  * How many train/test splits the `pbo` figure was averaged over.
513
513
  */
514
514
  pboSplits?: number;
515
+ /**
516
+ * Why the sweep produced less than it should have — the cause reported by the **first** shard to fail, not a list. It is what turns an inscrutable empty leaderboard into an answer: a sweep can come back `PARTIAL` with `done: 0` because the strategy could not be loaded at all, and without this the response says only that nothing finished.
517
+ * First failure wins and later ones are not recorded, so on a sweep where several shards failed for different reasons this names one of them rather than all. Absent when no shard reported a cause, which is the normal case for a healthy sweep — read it together with `progress.failedShards` rather than as a count of anything.
518
+ */
519
+ failReason?: string;
515
520
  progress: SweepProgress;
516
521
  /**
517
522
  * Total result rows currently available.
@@ -1129,11 +1134,14 @@ type ValidateStrategyResponses = {
1129
1134
  * Validation queued. Not a terminal outcome — poll `GET /strategy/{strategyId}` until
1130
1135
  * `validation` leaves `pending`.
1131
1136
  *
1137
+ * The body is a `StrategyState` carrying only what is known at this point: the id and
1138
+ * `validation: pending`. **The status code, not the body, is what tells the two responses
1139
+ * apart** — a `200` can also carry `validation: pending`, left by a check an earlier call
1140
+ * queued. So `202` means *this call started a check*, while `pending` means only *a check
1141
+ * is outstanding*.
1142
+ *
1132
1143
  */
1133
- 202: {
1134
- strategyId: StrategyId;
1135
- validation: "pending";
1136
- };
1144
+ 202: StrategyState;
1137
1145
  };
1138
1146
  type ValidateStrategyResponse = ValidateStrategyResponses[keyof ValidateStrategyResponses];
1139
1147
  type GetStrategyData = {
@@ -1644,7 +1652,7 @@ declare const compileStrategy: <ThrowOnError extends boolean = false>(options: O
1644
1652
  * bytecode that is no longer what would run.
1645
1653
  *
1646
1654
  */
1647
- declare const validateStrategy: <ThrowOnError extends boolean = false>(options: Options<ValidateStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<ValidateStrategyResponse, ResponseError, ThrowOnError>;
1655
+ declare const validateStrategy: <ThrowOnError extends boolean = false>(options: Options<ValidateStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<StrategyState, ResponseError, ThrowOnError>;
1648
1656
  /**
1649
1657
  * Get a strategy by id, including its validation state
1650
1658
  * Reports that the strategy is registered — implied by a `200` at all — and what validating it
package/dist/index.js CHANGED
@@ -163,6 +163,12 @@ var getSweepResult = (options) => {
163
163
  };
164
164
  var getSweepSensitivity = (options) => {
165
165
  return (options.client ?? client).get({
166
+ security: [
167
+ {
168
+ scheme: "bearer",
169
+ type: "http"
170
+ }
171
+ ],
166
172
  url: "/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/sensitivity",
167
173
  ...options
168
174
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/client.gen.ts","../src/generated/sdk.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { ClientOptions } from \"./types.gen\";\nimport {\n type Config,\n type ClientOptions as DefaultClientOptions,\n createClient,\n createConfig,\n} from \"@hey-api/client-fetch\";\n\n/**\n * The `createClientConfig()` function will be called on client initialization\n * and the returned object will become the client's initial configuration.\n *\n * You may want to initialize your client this way instead of calling\n * `setConfig()`. This is useful for example if you're using Next.js\n * to ensure your client always has the correct values.\n */\nexport type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> =\n (\n override?: Config<DefaultClientOptions & T>\n ) => Config<Required<DefaultClientOptions> & T>;\n\nexport const client = createClient(\n createConfig<ClientOptions>({\n baseUrl: \"https://api.staging.qtsurfer.com/v1\",\n })\n);\n","// This file is auto-generated by @hey-api/openapi-ts\n\nimport type {\n Options as ClientOptions,\n TDataShape,\n Client,\n} from \"@hey-api/client-fetch\";\nimport type {\n AuthenticateData,\n AuthenticateResponse,\n AuthenticateError,\n ListExchangesData,\n ListExchangesResponse,\n ListInstrumentsData,\n ListInstrumentsResponse,\n ListInstrumentsError,\n ListSegmentInstrumentsData,\n ListSegmentInstrumentsResponse,\n ListSegmentInstrumentsError,\n DownloadTickersData,\n DownloadTickersResponse,\n DownloadTickersError,\n DownloadKlinesData,\n DownloadKlinesResponse,\n DownloadKlinesError,\n CompileStrategyData,\n CompileStrategyResponse,\n CompileStrategyError,\n ValidateStrategyData,\n ValidateStrategyResponse,\n ValidateStrategyError,\n GetStrategyData,\n GetStrategyResponse,\n GetStrategyError,\n PrepareBacktestData,\n PrepareBacktestResponse,\n PrepareBacktestError,\n GetPrepareStatusData,\n GetPrepareStatusResponse,\n GetPrepareStatusError,\n ExecuteSweepData,\n ExecuteSweepResponse,\n ExecuteSweepError,\n CancelSweepData,\n CancelSweepResponse,\n CancelSweepError,\n GetSweepResultData,\n GetSweepResultResponse,\n GetSweepResultError,\n GetSweepSensitivityData,\n GetSweepSensitivityResponse,\n GetSweepSensitivityError,\n ExecuteBacktestData,\n ExecuteBacktestResponse,\n ExecuteBacktestError,\n CancelBacktestData,\n CancelBacktestResponse,\n CancelBacktestError,\n GetBacktestResultData,\n GetBacktestResultResponse,\n GetBacktestResultError,\n} from \"./types.gen\";\nimport { client as _heyApiClient } from \"./client.gen\";\n\nexport type Options<\n TData extends TDataShape = TDataShape,\n ThrowOnError extends boolean = boolean\n> = ClientOptions<TData, ThrowOnError> & {\n /**\n * You can provide a client instance returned by `createClient()` instead of\n * individual options. This might be also useful if you want to implement a\n * custom client.\n */\n client?: Client;\n /**\n * You can pass arbitrary values through the `meta` object. This can be\n * used to access values that aren't defined as part of the SDK function.\n */\n meta?: Record<string, unknown>;\n};\n\n/**\n * Exchange API key for a short-lived JWT\n * Exchanges a long-lived API key for a short-lived JWT used by every other\n * endpoint. This is the only endpoint that accepts an API key directly —\n * callers should obtain a JWT here, then send it as `Authorization: Bearer\n * <token>` to all other operations.\n *\n * The returned JWT carries the caller's subscription `tier` as a claim and\n * expires after `expires_in` seconds. Callers should refresh the token\n * before expiry (or on a `401` response) by calling this endpoint again.\n *\n */\nexport const authenticate = <ThrowOnError extends boolean = false>(\n options?: Options<AuthenticateData, ThrowOnError>\n) => {\n return (options?.client ?? _heyApiClient).post<\n AuthenticateResponse,\n AuthenticateError,\n ThrowOnError\n >({\n security: [\n {\n name: \"X-API-Key\",\n type: \"apiKey\",\n },\n ],\n url: \"/auth/token\",\n ...options,\n });\n};\n\n/**\n * List the available exchanges\n */\nexport const listExchanges = <ThrowOnError extends boolean = false>(\n options?: Options<ListExchangesData, ThrowOnError>\n) => {\n return (options?.client ?? _heyApiClient).get<\n ListExchangesResponse,\n unknown,\n ThrowOnError\n >({\n url: \"/exchanges\",\n ...options,\n });\n};\n\n/**\n * List an exchange's instruments (default spot segment)\n * \"Give me binance instruments\" — returns the exchange's DEFAULT segment (`spot`)\n * in `data`, each instrument with per-data-type coverage and market info. `meta`\n * confirms the served `segment` (`spot`); HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links.\n *\n */\nexport const listInstruments = <ThrowOnError extends boolean = false>(\n options: Options<ListInstrumentsData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n ListInstrumentsResponse,\n ListInstrumentsError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/instruments\",\n ...options,\n });\n};\n\n/**\n * List an exchange segment's instruments\n * Returns the instruments for one market segment of the exchange, each with\n * per-data-type coverage and market info. HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links; the default-segment shortcut is\n * `GET /exchange/{exchangeId}/instruments` (spot).\n *\n */\nexport const listSegmentInstruments = <ThrowOnError extends boolean = false>(\n options: Options<ListSegmentInstrumentsData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n ListSegmentInstrumentsResponse,\n ListSegmentInstrumentsError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/{segment}/instruments\",\n ...options,\n });\n};\n\n/**\n * Download one hour of tickers for an instrument as a Lastra segment\n * Serves exactly one hour of raw ticker data for the given instrument on the\n * requested exchange. The payload is a native [Lastra](https://github.com/QTSurfer/lastra-java)\n * file — QTSurfer's columnar format for tick-precision timeseries — with\n * no JSON envelope.\n *\n * One segment = one hour, aligned to UTC. The `hour` query parameter selects\n * the segment and must match `YYYY-MM-DDTHH` (no minutes/seconds, no timezone\n * suffix). Example: `hour=2026-01-15T10` returns `h10.lastra` for\n * 2026-01-15, covering `[10:00:00Z, 11:00:00Z)`. Hours not yet available\n * return `404`.\n *\n * A `format=parquet` query parameter switches the response to on-the-fly\n * Parquet conversion via [lastra-convert](https://github.com/QTSurfer/lastra-convert)\n * for clients that don't yet read Lastra. Lastra is the primary format\n * and cheaper when the client can consume it.\n *\n * Clients:\n * - [lastra-java](https://github.com/QTSurfer/lastra-java) — reference\n * Java reader/writer with per-column codecs (ALP, Gorilla, delta-varint,\n * ZSTD) and CRC32 integrity.\n * - [lastra-ts](https://github.com/QTSurfer/lastra-ts) — TypeScript reader\n * (~4 kB bundle, browser + Node.js).\n * - [duckdb-lastra](https://github.com/QTSurfer/duckdb-lastra) — DuckDB\n * extension for ad-hoc SQL over Lastra files.\n * - [lastra-convert](https://github.com/QTSurfer/lastra-convert) — CLI + Java\n * API for converting to/from Parquet, Reef, and CSV.\n * - `curl -OJ` for offline dumps (the `Content-Disposition` header sets a\n * descriptive filename).\n *\n */\nexport const downloadTickers = <ThrowOnError extends boolean = false>(\n options: Options<DownloadTickersData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n DownloadTickersResponse,\n DownloadTickersError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/tickers/{base}/{quote}\",\n ...options,\n });\n};\n\n/**\n * Download one hour of klines for an instrument as a Lastra segment\n * Same shape and semantics as `/exchange/{exchangeId}/tickers/{base}/{quote}`,\n * but serves klines (aggregated bars) instead of raw ticks. One\n * [Lastra](https://github.com/QTSurfer/lastra-java) segment = one hour of\n * klines at the exchange's native kline cadence, aligned to UTC.\n *\n * Klines use the same columnar layout as tickers — readers that handle one\n * format read the other with the same code. Use this endpoint when a\n * per-tick payload would be too large for the window of interest.\n *\n */\nexport const downloadKlines = <ThrowOnError extends boolean = false>(\n options: Options<DownloadKlinesData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n DownloadKlinesResponse,\n DownloadKlinesError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/klines/{base}/{quote}\",\n ...options,\n });\n};\n\n/**\n * Compile and register a strategy\n * Compiles raw strategy source and registers it, returning its `strategyId`.\n *\n * **This answers one question: is the source valid Java.** It compiles, registers, and hands\n * back the id — nothing more. Whether the class can actually run is\n * `POST /strategy/{strategyId}/validate`, and everything known about a strategy, validation\n * included, is read from `GET /strategy/{strategyId}`. One place to ask, so there is no second\n * answer to keep in step.\n *\n * The `strategyId` is derived from what the code *means*, not from how it is written. Adding a\n * comment, inserting a blank line, re-indenting, reordering imports, or moving a method around\n * all return the **same** id — you have not created a second strategy. Renaming a variable,\n * changing an identifier's case, reordering fields, or reordering statements inside a method\n * return a **different** one.\n *\n * Two rules follow, and they are worth designing around:\n *\n * - re-submitting a strategy you have only reformatted is free, and gives you back the id you\n * already had, along with any validation already recorded against it;\n * - the id says nothing about *behaviour*. Two sources that compute the same thing by\n * different means are two strategies, because deciding otherwise would mean deciding program\n * equivalence.\n *\n */\nexport const compileStrategy = <ThrowOnError extends boolean = false>(\n options: Options<CompileStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n CompileStrategyResponse,\n CompileStrategyError,\n ThrowOnError\n >({\n bodySerializer: null,\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy\",\n ...options,\n headers: {\n \"Content-Type\": \"text/plain\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Check that a registered strategy can actually run\n * Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring\n * fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any\n * engine notices — is recorded and served from `GET /strategy/{strategyId}`.\n *\n * **Idempotent.** If a verdict already exists for the current compilation it comes straight\n * back with `200` and nothing is queued. Otherwise the check is queued and this returns `202`;\n * poll `GET /strategy/{strategyId}` until `validation` is `passed` or `failed`.\n *\n * Recompiling supersedes a verdict, which makes this callable again — the old answer described\n * bytecode that is no longer what would run.\n *\n */\nexport const validateStrategy = <ThrowOnError extends boolean = false>(\n options: Options<ValidateStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ValidateStrategyResponse,\n ValidateStrategyError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy/{strategyId}/validate\",\n ...options,\n });\n};\n\n/**\n * Get a strategy by id, including its validation state\n * Reports that the strategy is registered — implied by a `200` at all — and what validating it\n * found.\n *\n * A `404` means one thing: no such registered strategy for this user. It is never a stale or\n * expired answer; registration and verdict are stored durably, not cached.\n *\n */\nexport const getStrategy = <ThrowOnError extends boolean = false>(\n options: Options<GetStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetStrategyResponse,\n GetStrategyError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy/{strategyId}\",\n ...options,\n });\n};\n\n/**\n * Prepare backtest data\n * Enqueues a prepare task over the requested date range. Returns immediately with a `jobId`;\n * poll `GET /backtest/{exchangeId}/{type}/prepare/{jobId}` for completion.\n *\n * The same params always return the same `jobId` (idempotent). Repeated calls with identical\n * params do not enqueue duplicate work — they reuse the existing job.\n *\n */\nexport const prepareBacktest = <ThrowOnError extends boolean = false>(\n options: Options<PrepareBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n PrepareBacktestResponse,\n PrepareBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/prepare\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Get the status of a prepare job\n * Retrieves the current state of the prepare job identified by `jobId`.\n * Poll until `status` is `Completed`, `Failed`, or `Aborted`.\n *\n */\nexport const getPrepareStatus = <ThrowOnError extends boolean = false>(\n options: Options<GetPrepareStatusData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetPrepareStatusResponse,\n GetPrepareStatusError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/prepare/{jobId}\",\n ...options,\n });\n};\n\n/**\n * Execute a parameter sweep over prepared data\n * Runs a parameter matrix over the single immutable dataset identified by `requestId`.\n * The backend expands and executes the matrix internally; clients poll the returned\n * `sweepId` for incremental results.\n *\n * Supplying `walkForward` runs the sweep in a different mode entirely. Instead of scoring\n * every parameter vector once over the whole range, the data is split into F sequential\n * folds; each fold optimizes the full grid on its own window and then scores only its winner\n * on the window immediately after — data that winner was not chosen on. It answers a harder\n * question than a leaderboard: not \"which parameters won\", but \"does re-optimizing this\n * periodically actually work\". Omit the block and nothing changes, including the response.\n *\n * The cost is the reason it is opt-in rather than always on: F folds × N vectors, so a\n * 4-fold run over a 500-point grid is 2004 backtests where the plain sweep is 500. The\n * request is rejected when `folds × totalRuns` exceeds the server's sweep budget.\n *\n */\nexport const executeSweep = <ThrowOnError extends boolean = false>(\n options: Options<ExecuteSweepData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ExecuteSweepResponse,\n ExecuteSweepError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Cancel a running parameter sweep\n * Requests cancellation between parameter vectors. Completed rows remain readable.\n */\nexport const cancelSweep = <ThrowOnError extends boolean = false>(\n options: Options<CancelSweepData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).delete<\n CancelSweepResponse,\n CancelSweepError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}\",\n ...options,\n });\n};\n\n/**\n * Get sweep progress and results\n * Returns incremental sweep progress. The default `ranked` view sorts and may truncate the\n * display leaderboard. `order=natural` returns every available row, untruncated, ordered by\n * deterministic `runIx`; use that view when materialising durable trial rows.\n *\n * The `ranked` view is ordered by **plateau score** by default, not by the raw objective. A\n * plateau score is the objective of the worst run in a parameter point's immediate\n * neighbourhood, so a point scores well only if the region around it also does — the highest\n * raw score is frequently a spike that does not survive the parameters moving slightly. Pass\n * `ranking=raw` for the unadjusted objective order.\n *\n * Rows in the `ranked` view carry `plateauScore` and `neighbourCount` when plateau ranking\n * applied. Read them together: `neighbourCount: 0` means the point had no neighbours to\n * compare against, so its plateau score is unevidenced rather than confirmed. Sweeps\n * submitted before plateau ranking existed have no stored parameter grid to rebuild a\n * neighbourhood from and are always ranked raw; the response's `ranking` field says which\n * ordering was actually used.\n *\n * A sweep submitted with `walkForward` answers in a different shape, and the `walkForward`\n * field on the response is what tells the two apart — it appears as soon as the sweep is\n * accepted, before any fold has finished, so it is safe to branch on while polling. There\n * the leaderboard is one row per completed fold: that fold's winner as it scored\n * **out-of-sample**, with `runIx` carrying the fold index rather than a grid position. The\n * in-sample runs behind those winners are not retained — they are an optimization's working\n * set, and only the winner survives its fold. `ranking` is always `raw` and no plateau, DSR\n * or PBO figure is reported: the out-of-sample scores are already the honest number, and\n * layering a certification computed over F observations on top of them would overstate what\n * was measured.\n *\n */\nexport const getSweepResult = <ThrowOnError extends boolean = false>(\n options: Options<GetSweepResultData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetSweepResultResponse,\n GetSweepResultError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}\",\n ...options,\n });\n};\n\n/**\n * Get sweep sensitivity surfaces\n * How the objective moves as each parameter moves — the question a leaderboard cannot answer.\n * A leaderboard says which point won; a sweep can spend its entire budget on an axis that\n * never moved the objective at all, and showing only the top rows hides that completely.\n *\n * A **marginal** takes one axis and collapses every other one: for each value of that axis,\n * it aggregates every run that used it, whatever the rest of the parameters were. A flat\n * marginal means the axis is irrelevant over the range swept. `best`, `mean` and `worst` are\n * all reported because them disagreeing is itself the signal — a value with a high `best` and\n * a poor `mean` works only in specific company, which is an interaction between parameters\n * and would be invisible behind a single number.\n *\n * A **heatmap** does the same over a pair of axes, where that interaction becomes visible\n * directly.\n *\n * Served from the sweep's stored rows: no re-run, no engine call, and it works on a sweep\n * still in flight — the aggregates then describe the runs finished so far. Aborted runs are\n * excluded throughout, since a run that threw measured nothing and counting it as a bad\n * outcome would invent evidence against a parameter value that was never really tested.\n *\n * This is a separate endpoint rather than extra fields on the result view because the\n * two-dimensional half is quadratic in the axis count (N axes give N(N-1)/2 surfaces, each\n * the product of two axes' value counts) and is not wanted on the poll that drives progress.\n *\n */\nexport const getSweepSensitivity = <ThrowOnError extends boolean = false>(\n options: Options<GetSweepSensitivityData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetSweepSensitivityResponse,\n GetSweepSensitivityError,\n ThrowOnError\n >({\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/sensitivity\",\n ...options,\n });\n};\n\n/**\n * Execute a compiled strategy against a prepared dataset\n * Enqueues an execute task that runs the strategy identified by `strategyId` over the data\n * prepared by the prepare job identified by `prepareJobId`. The instrument and date range are\n * recovered from the prepare job — they do not need to be sent again.\n *\n * Returns immediately with a `jobId`; poll `GET /backtest/{exchangeId}/{type}/execute/{jobId}`\n * for the result.\n *\n * The same params (same `prepareJobId`, `strategyId`, `storeSignals`) always return the same\n * `jobId` (idempotent).\n *\n */\nexport const executeBacktest = <ThrowOnError extends boolean = false>(\n options: Options<ExecuteBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ExecuteBacktestResponse,\n ExecuteBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Cancel a running backtest execution\n * Requests cancellation of the specified execution. The execution\n * status will transition to `Aborted` once the cancellation is\n * processed. Cancellation is asynchronous — poll the GET endpoint\n * to confirm the final status.\n *\n */\nexport const cancelBacktest = <ThrowOnError extends boolean = false>(\n options: Options<CancelBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).delete<\n CancelBacktestResponse,\n CancelBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute/{jobId}\",\n ...options,\n });\n};\n\n/**\n * Get the result of a backtest execution job\n * Retrieves the current state and results of the execute job identified by `jobId`.\n * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.\n *\n * A `202` means the result is not readable yet — keep polling. It is never a terminal\n * outcome, and it carries no `state`, so a poll loop that stops on a terminal status will\n * not stop on it.\n *\n */\nexport const getBacktestResult = <ThrowOnError extends boolean = false>(\n options: Options<GetBacktestResultData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetBacktestResultResponse,\n GetBacktestResultError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute/{jobId}\",\n ...options,\n });\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/generated/client.gen.ts","../src/generated/sdk.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { ClientOptions } from \"./types.gen\";\nimport {\n type Config,\n type ClientOptions as DefaultClientOptions,\n createClient,\n createConfig,\n} from \"@hey-api/client-fetch\";\n\n/**\n * The `createClientConfig()` function will be called on client initialization\n * and the returned object will become the client's initial configuration.\n *\n * You may want to initialize your client this way instead of calling\n * `setConfig()`. This is useful for example if you're using Next.js\n * to ensure your client always has the correct values.\n */\nexport type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> =\n (\n override?: Config<DefaultClientOptions & T>\n ) => Config<Required<DefaultClientOptions> & T>;\n\nexport const client = createClient(\n createConfig<ClientOptions>({\n baseUrl: \"https://api.staging.qtsurfer.com/v1\",\n })\n);\n","// This file is auto-generated by @hey-api/openapi-ts\n\nimport type {\n Options as ClientOptions,\n TDataShape,\n Client,\n} from \"@hey-api/client-fetch\";\nimport type {\n AuthenticateData,\n AuthenticateResponse,\n AuthenticateError,\n ListExchangesData,\n ListExchangesResponse,\n ListInstrumentsData,\n ListInstrumentsResponse,\n ListInstrumentsError,\n ListSegmentInstrumentsData,\n ListSegmentInstrumentsResponse,\n ListSegmentInstrumentsError,\n DownloadTickersData,\n DownloadTickersResponse,\n DownloadTickersError,\n DownloadKlinesData,\n DownloadKlinesResponse,\n DownloadKlinesError,\n CompileStrategyData,\n CompileStrategyResponse,\n CompileStrategyError,\n ValidateStrategyData,\n ValidateStrategyResponse,\n ValidateStrategyError,\n GetStrategyData,\n GetStrategyResponse,\n GetStrategyError,\n PrepareBacktestData,\n PrepareBacktestResponse,\n PrepareBacktestError,\n GetPrepareStatusData,\n GetPrepareStatusResponse,\n GetPrepareStatusError,\n ExecuteSweepData,\n ExecuteSweepResponse,\n ExecuteSweepError,\n CancelSweepData,\n CancelSweepResponse,\n CancelSweepError,\n GetSweepResultData,\n GetSweepResultResponse,\n GetSweepResultError,\n GetSweepSensitivityData,\n GetSweepSensitivityResponse,\n GetSweepSensitivityError,\n ExecuteBacktestData,\n ExecuteBacktestResponse,\n ExecuteBacktestError,\n CancelBacktestData,\n CancelBacktestResponse,\n CancelBacktestError,\n GetBacktestResultData,\n GetBacktestResultResponse,\n GetBacktestResultError,\n} from \"./types.gen\";\nimport { client as _heyApiClient } from \"./client.gen\";\n\nexport type Options<\n TData extends TDataShape = TDataShape,\n ThrowOnError extends boolean = boolean\n> = ClientOptions<TData, ThrowOnError> & {\n /**\n * You can provide a client instance returned by `createClient()` instead of\n * individual options. This might be also useful if you want to implement a\n * custom client.\n */\n client?: Client;\n /**\n * You can pass arbitrary values through the `meta` object. This can be\n * used to access values that aren't defined as part of the SDK function.\n */\n meta?: Record<string, unknown>;\n};\n\n/**\n * Exchange API key for a short-lived JWT\n * Exchanges a long-lived API key for a short-lived JWT used by every other\n * endpoint. This is the only endpoint that accepts an API key directly —\n * callers should obtain a JWT here, then send it as `Authorization: Bearer\n * <token>` to all other operations.\n *\n * The returned JWT carries the caller's subscription `tier` as a claim and\n * expires after `expires_in` seconds. Callers should refresh the token\n * before expiry (or on a `401` response) by calling this endpoint again.\n *\n */\nexport const authenticate = <ThrowOnError extends boolean = false>(\n options?: Options<AuthenticateData, ThrowOnError>\n) => {\n return (options?.client ?? _heyApiClient).post<\n AuthenticateResponse,\n AuthenticateError,\n ThrowOnError\n >({\n security: [\n {\n name: \"X-API-Key\",\n type: \"apiKey\",\n },\n ],\n url: \"/auth/token\",\n ...options,\n });\n};\n\n/**\n * List the available exchanges\n */\nexport const listExchanges = <ThrowOnError extends boolean = false>(\n options?: Options<ListExchangesData, ThrowOnError>\n) => {\n return (options?.client ?? _heyApiClient).get<\n ListExchangesResponse,\n unknown,\n ThrowOnError\n >({\n url: \"/exchanges\",\n ...options,\n });\n};\n\n/**\n * List an exchange's instruments (default spot segment)\n * \"Give me binance instruments\" — returns the exchange's DEFAULT segment (`spot`)\n * in `data`, each instrument with per-data-type coverage and market info. `meta`\n * confirms the served `segment` (`spot`); HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links.\n *\n */\nexport const listInstruments = <ThrowOnError extends boolean = false>(\n options: Options<ListInstrumentsData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n ListInstrumentsResponse,\n ListInstrumentsError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/instruments\",\n ...options,\n });\n};\n\n/**\n * List an exchange segment's instruments\n * Returns the instruments for one market segment of the exchange, each with\n * per-data-type coverage and market info. HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links; the default-segment shortcut is\n * `GET /exchange/{exchangeId}/instruments` (spot).\n *\n */\nexport const listSegmentInstruments = <ThrowOnError extends boolean = false>(\n options: Options<ListSegmentInstrumentsData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n ListSegmentInstrumentsResponse,\n ListSegmentInstrumentsError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/{segment}/instruments\",\n ...options,\n });\n};\n\n/**\n * Download one hour of tickers for an instrument as a Lastra segment\n * Serves exactly one hour of raw ticker data for the given instrument on the\n * requested exchange. The payload is a native [Lastra](https://github.com/QTSurfer/lastra-java)\n * file — QTSurfer's columnar format for tick-precision timeseries — with\n * no JSON envelope.\n *\n * One segment = one hour, aligned to UTC. The `hour` query parameter selects\n * the segment and must match `YYYY-MM-DDTHH` (no minutes/seconds, no timezone\n * suffix). Example: `hour=2026-01-15T10` returns `h10.lastra` for\n * 2026-01-15, covering `[10:00:00Z, 11:00:00Z)`. Hours not yet available\n * return `404`.\n *\n * A `format=parquet` query parameter switches the response to on-the-fly\n * Parquet conversion via [lastra-convert](https://github.com/QTSurfer/lastra-convert)\n * for clients that don't yet read Lastra. Lastra is the primary format\n * and cheaper when the client can consume it.\n *\n * Clients:\n * - [lastra-java](https://github.com/QTSurfer/lastra-java) — reference\n * Java reader/writer with per-column codecs (ALP, Gorilla, delta-varint,\n * ZSTD) and CRC32 integrity.\n * - [lastra-ts](https://github.com/QTSurfer/lastra-ts) — TypeScript reader\n * (~4 kB bundle, browser + Node.js).\n * - [duckdb-lastra](https://github.com/QTSurfer/duckdb-lastra) — DuckDB\n * extension for ad-hoc SQL over Lastra files.\n * - [lastra-convert](https://github.com/QTSurfer/lastra-convert) — CLI + Java\n * API for converting to/from Parquet, Reef, and CSV.\n * - `curl -OJ` for offline dumps (the `Content-Disposition` header sets a\n * descriptive filename).\n *\n */\nexport const downloadTickers = <ThrowOnError extends boolean = false>(\n options: Options<DownloadTickersData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n DownloadTickersResponse,\n DownloadTickersError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/tickers/{base}/{quote}\",\n ...options,\n });\n};\n\n/**\n * Download one hour of klines for an instrument as a Lastra segment\n * Same shape and semantics as `/exchange/{exchangeId}/tickers/{base}/{quote}`,\n * but serves klines (aggregated bars) instead of raw ticks. One\n * [Lastra](https://github.com/QTSurfer/lastra-java) segment = one hour of\n * klines at the exchange's native kline cadence, aligned to UTC.\n *\n * Klines use the same columnar layout as tickers — readers that handle one\n * format read the other with the same code. Use this endpoint when a\n * per-tick payload would be too large for the window of interest.\n *\n */\nexport const downloadKlines = <ThrowOnError extends boolean = false>(\n options: Options<DownloadKlinesData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n DownloadKlinesResponse,\n DownloadKlinesError,\n ThrowOnError\n >({\n url: \"/exchange/{exchangeId}/klines/{base}/{quote}\",\n ...options,\n });\n};\n\n/**\n * Compile and register a strategy\n * Compiles raw strategy source and registers it, returning its `strategyId`.\n *\n * **This answers one question: is the source valid Java.** It compiles, registers, and hands\n * back the id — nothing more. Whether the class can actually run is\n * `POST /strategy/{strategyId}/validate`, and everything known about a strategy, validation\n * included, is read from `GET /strategy/{strategyId}`. One place to ask, so there is no second\n * answer to keep in step.\n *\n * The `strategyId` is derived from what the code *means*, not from how it is written. Adding a\n * comment, inserting a blank line, re-indenting, reordering imports, or moving a method around\n * all return the **same** id — you have not created a second strategy. Renaming a variable,\n * changing an identifier's case, reordering fields, or reordering statements inside a method\n * return a **different** one.\n *\n * Two rules follow, and they are worth designing around:\n *\n * - re-submitting a strategy you have only reformatted is free, and gives you back the id you\n * already had, along with any validation already recorded against it;\n * - the id says nothing about *behaviour*. Two sources that compute the same thing by\n * different means are two strategies, because deciding otherwise would mean deciding program\n * equivalence.\n *\n */\nexport const compileStrategy = <ThrowOnError extends boolean = false>(\n options: Options<CompileStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n CompileStrategyResponse,\n CompileStrategyError,\n ThrowOnError\n >({\n bodySerializer: null,\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy\",\n ...options,\n headers: {\n \"Content-Type\": \"text/plain\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Check that a registered strategy can actually run\n * Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring\n * fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any\n * engine notices — is recorded and served from `GET /strategy/{strategyId}`.\n *\n * **Idempotent.** If a verdict already exists for the current compilation it comes straight\n * back with `200` and nothing is queued. Otherwise the check is queued and this returns `202`;\n * poll `GET /strategy/{strategyId}` until `validation` is `passed` or `failed`.\n *\n * Recompiling supersedes a verdict, which makes this callable again — the old answer described\n * bytecode that is no longer what would run.\n *\n */\nexport const validateStrategy = <ThrowOnError extends boolean = false>(\n options: Options<ValidateStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ValidateStrategyResponse,\n ValidateStrategyError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy/{strategyId}/validate\",\n ...options,\n });\n};\n\n/**\n * Get a strategy by id, including its validation state\n * Reports that the strategy is registered — implied by a `200` at all — and what validating it\n * found.\n *\n * A `404` means one thing: no such registered strategy for this user. It is never a stale or\n * expired answer; registration and verdict are stored durably, not cached.\n *\n */\nexport const getStrategy = <ThrowOnError extends boolean = false>(\n options: Options<GetStrategyData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetStrategyResponse,\n GetStrategyError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/strategy/{strategyId}\",\n ...options,\n });\n};\n\n/**\n * Prepare backtest data\n * Enqueues a prepare task over the requested date range. Returns immediately with a `jobId`;\n * poll `GET /backtest/{exchangeId}/{type}/prepare/{jobId}` for completion.\n *\n * The same params always return the same `jobId` (idempotent). Repeated calls with identical\n * params do not enqueue duplicate work — they reuse the existing job.\n *\n */\nexport const prepareBacktest = <ThrowOnError extends boolean = false>(\n options: Options<PrepareBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n PrepareBacktestResponse,\n PrepareBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/prepare\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Get the status of a prepare job\n * Retrieves the current state of the prepare job identified by `jobId`.\n * Poll until `status` is `Completed`, `Failed`, or `Aborted`.\n *\n */\nexport const getPrepareStatus = <ThrowOnError extends boolean = false>(\n options: Options<GetPrepareStatusData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetPrepareStatusResponse,\n GetPrepareStatusError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/prepare/{jobId}\",\n ...options,\n });\n};\n\n/**\n * Execute a parameter sweep over prepared data\n * Runs a parameter matrix over the single immutable dataset identified by `requestId`.\n * The backend expands and executes the matrix internally; clients poll the returned\n * `sweepId` for incremental results.\n *\n * Supplying `walkForward` runs the sweep in a different mode entirely. Instead of scoring\n * every parameter vector once over the whole range, the data is split into F sequential\n * folds; each fold optimizes the full grid on its own window and then scores only its winner\n * on the window immediately after — data that winner was not chosen on. It answers a harder\n * question than a leaderboard: not \"which parameters won\", but \"does re-optimizing this\n * periodically actually work\". Omit the block and nothing changes, including the response.\n *\n * The cost is the reason it is opt-in rather than always on: F folds × N vectors, so a\n * 4-fold run over a 500-point grid is 2004 backtests where the plain sweep is 500. The\n * request is rejected when `folds × totalRuns` exceeds the server's sweep budget.\n *\n */\nexport const executeSweep = <ThrowOnError extends boolean = false>(\n options: Options<ExecuteSweepData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ExecuteSweepResponse,\n ExecuteSweepError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Cancel a running parameter sweep\n * Requests cancellation between parameter vectors. Completed rows remain readable.\n */\nexport const cancelSweep = <ThrowOnError extends boolean = false>(\n options: Options<CancelSweepData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).delete<\n CancelSweepResponse,\n CancelSweepError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}\",\n ...options,\n });\n};\n\n/**\n * Get sweep progress and results\n * Returns incremental sweep progress. The default `ranked` view sorts and may truncate the\n * display leaderboard. `order=natural` returns every available row, untruncated, ordered by\n * deterministic `runIx`; use that view when materialising durable trial rows.\n *\n * The `ranked` view is ordered by **plateau score** by default, not by the raw objective. A\n * plateau score is the objective of the worst run in a parameter point's immediate\n * neighbourhood, so a point scores well only if the region around it also does — the highest\n * raw score is frequently a spike that does not survive the parameters moving slightly. Pass\n * `ranking=raw` for the unadjusted objective order.\n *\n * Rows in the `ranked` view carry `plateauScore` and `neighbourCount` when plateau ranking\n * applied. Read them together: `neighbourCount: 0` means the point had no neighbours to\n * compare against, so its plateau score is unevidenced rather than confirmed. Sweeps\n * submitted before plateau ranking existed have no stored parameter grid to rebuild a\n * neighbourhood from and are always ranked raw; the response's `ranking` field says which\n * ordering was actually used.\n *\n * A sweep submitted with `walkForward` answers in a different shape, and the `walkForward`\n * field on the response is what tells the two apart — it appears as soon as the sweep is\n * accepted, before any fold has finished, so it is safe to branch on while polling. There\n * the leaderboard is one row per completed fold: that fold's winner as it scored\n * **out-of-sample**, with `runIx` carrying the fold index rather than a grid position. The\n * in-sample runs behind those winners are not retained — they are an optimization's working\n * set, and only the winner survives its fold. `ranking` is always `raw` and no plateau, DSR\n * or PBO figure is reported: the out-of-sample scores are already the honest number, and\n * layering a certification computed over F observations on top of them would overstate what\n * was measured.\n *\n */\nexport const getSweepResult = <ThrowOnError extends boolean = false>(\n options: Options<GetSweepResultData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetSweepResultResponse,\n GetSweepResultError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}\",\n ...options,\n });\n};\n\n/**\n * Get sweep sensitivity surfaces\n * How the objective moves as each parameter moves — the question a leaderboard cannot answer.\n * A leaderboard says which point won; a sweep can spend its entire budget on an axis that\n * never moved the objective at all, and showing only the top rows hides that completely.\n *\n * A **marginal** takes one axis and collapses every other one: for each value of that axis,\n * it aggregates every run that used it, whatever the rest of the parameters were. A flat\n * marginal means the axis is irrelevant over the range swept. `best`, `mean` and `worst` are\n * all reported because them disagreeing is itself the signal — a value with a high `best` and\n * a poor `mean` works only in specific company, which is an interaction between parameters\n * and would be invisible behind a single number.\n *\n * A **heatmap** does the same over a pair of axes, where that interaction becomes visible\n * directly.\n *\n * Served from the sweep's stored rows: no re-run, no engine call, and it works on a sweep\n * still in flight — the aggregates then describe the runs finished so far. Aborted runs are\n * excluded throughout, since a run that threw measured nothing and counting it as a bad\n * outcome would invent evidence against a parameter value that was never really tested.\n *\n * This is a separate endpoint rather than extra fields on the result view because the\n * two-dimensional half is quadratic in the axis count (N axes give N(N-1)/2 surfaces, each\n * the product of two axes' value counts) and is not wanted on the poll that drives progress.\n *\n */\nexport const getSweepSensitivity = <ThrowOnError extends boolean = false>(\n options: Options<GetSweepSensitivityData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetSweepSensitivityResponse,\n GetSweepSensitivityError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/sensitivity\",\n ...options,\n });\n};\n\n/**\n * Execute a compiled strategy against a prepared dataset\n * Enqueues an execute task that runs the strategy identified by `strategyId` over the data\n * prepared by the prepare job identified by `prepareJobId`. The instrument and date range are\n * recovered from the prepare job — they do not need to be sent again.\n *\n * Returns immediately with a `jobId`; poll `GET /backtest/{exchangeId}/{type}/execute/{jobId}`\n * for the result.\n *\n * The same params (same `prepareJobId`, `strategyId`, `storeSignals`) always return the same\n * `jobId` (idempotent).\n *\n */\nexport const executeBacktest = <ThrowOnError extends boolean = false>(\n options: Options<ExecuteBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).post<\n ExecuteBacktestResponse,\n ExecuteBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute\",\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n ...options?.headers,\n },\n });\n};\n\n/**\n * Cancel a running backtest execution\n * Requests cancellation of the specified execution. The execution\n * status will transition to `Aborted` once the cancellation is\n * processed. Cancellation is asynchronous — poll the GET endpoint\n * to confirm the final status.\n *\n */\nexport const cancelBacktest = <ThrowOnError extends boolean = false>(\n options: Options<CancelBacktestData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).delete<\n CancelBacktestResponse,\n CancelBacktestError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute/{jobId}\",\n ...options,\n });\n};\n\n/**\n * Get the result of a backtest execution job\n * Retrieves the current state and results of the execute job identified by `jobId`.\n * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.\n *\n * A `202` means the result is not readable yet — keep polling. It is never a terminal\n * outcome, and it carries no `state`, so a poll loop that stops on a terminal status will\n * not stop on it.\n *\n */\nexport const getBacktestResult = <ThrowOnError extends boolean = false>(\n options: Options<GetBacktestResultData, ThrowOnError>\n) => {\n return (options.client ?? _heyApiClient).get<\n GetBacktestResultResponse,\n GetBacktestResultError,\n ThrowOnError\n >({\n security: [\n {\n scheme: \"bearer\",\n type: \"http\",\n },\n ],\n url: \"/backtest/{exchangeId}/{type}/execute/{jobId}\",\n ...options,\n });\n};\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qtsurfer/api-client",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Auto-generated TypeScript API client for the QTSurfer API (from OpenAPI 3.1 spec)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1020,6 +1020,12 @@ export const ExecuteSweepResultSchema = {
1020
1020
  description:
1021
1021
  "How many train/test splits the `pbo` figure was averaged over.",
1022
1022
  },
1023
+ failReason: {
1024
+ type: "string",
1025
+ description: `Why the sweep produced less than it should have — the cause reported by the **first** shard to fail, not a list. It is what turns an inscrutable empty leaderboard into an answer: a sweep can come back \`PARTIAL\` with \`done: 0\` because the strategy could not be loaded at all, and without this the response says only that nothing finished.
1026
+ First failure wins and later ones are not recorded, so on a sweep where several shards failed for different reasons this names one of them rather than all. Absent when no shard reported a cause, which is the normal case for a healthy sweep — read it together with \`progress.failedShards\` rather than as a count of anything.`,
1027
+ example: "Failed to load/configure strategy",
1028
+ },
1023
1029
  progress: {
1024
1030
  $ref: "#/components/schemas/SweepProgress",
1025
1031
  },
@@ -553,6 +553,12 @@ export const getSweepSensitivity = <ThrowOnError extends boolean = false>(
553
553
  GetSweepSensitivityError,
554
554
  ThrowOnError
555
555
  >({
556
+ security: [
557
+ {
558
+ scheme: "bearer",
559
+ type: "http",
560
+ },
561
+ ],
556
562
  url: "/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/sensitivity",
557
563
  ...options,
558
564
  });
@@ -541,6 +541,11 @@ export type ExecuteSweepResult = {
541
541
  * How many train/test splits the `pbo` figure was averaged over.
542
542
  */
543
543
  pboSplits?: number;
544
+ /**
545
+ * Why the sweep produced less than it should have — the cause reported by the **first** shard to fail, not a list. It is what turns an inscrutable empty leaderboard into an answer: a sweep can come back `PARTIAL` with `done: 0` because the strategy could not be loaded at all, and without this the response says only that nothing finished.
546
+ * First failure wins and later ones are not recorded, so on a sweep where several shards failed for different reasons this names one of them rather than all. Absent when no shard reported a cause, which is the normal case for a healthy sweep — read it together with `progress.failedShards` rather than as a count of anything.
547
+ */
548
+ failReason?: string;
544
549
  progress: SweepProgress;
545
550
  /**
546
551
  * Total result rows currently available.
@@ -1219,11 +1224,14 @@ export type ValidateStrategyResponses = {
1219
1224
  * Validation queued. Not a terminal outcome — poll `GET /strategy/{strategyId}` until
1220
1225
  * `validation` leaves `pending`.
1221
1226
  *
1227
+ * The body is a `StrategyState` carrying only what is known at this point: the id and
1228
+ * `validation: pending`. **The status code, not the body, is what tells the two responses
1229
+ * apart** — a `200` can also carry `validation: pending`, left by a check an earlier call
1230
+ * queued. So `202` means *this call started a check*, while `pending` means only *a check
1231
+ * is outstanding*.
1232
+ *
1222
1233
  */
1223
- 202: {
1224
- strategyId: StrategyId;
1225
- validation: "pending";
1226
- };
1234
+ 202: StrategyState;
1227
1235
  };
1228
1236
 
1229
1237
  export type ValidateStrategyResponse =