@qtsurfer/api-client 0.10.0 → 0.12.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.
@@ -180,8 +180,32 @@ export type Exchange = {
180
180
  */
181
181
  export type DataSourceType = "ticker";
182
182
 
183
+ /**
184
+ * Two shapes, chosen by the `exchangeId` path segment. Against a managed exchange,
185
+ * `instrument` is required and `datasetId`/`datasetVersionId` are ignored. Against the
186
+ * reserved `exchangeId: user`, send `datasetId` instead of `instrument` — `instrument` is
187
+ * ignored there, since it comes from the dataset itself.
188
+ *
189
+ */
183
190
  export type PrepareRequest = {
184
- instrument: Instrument;
191
+ /**
192
+ * Required unless `exchangeId` is the reserved value `user`, in which case send
193
+ * `datasetId` instead.
194
+ *
195
+ */
196
+ instrument?: Instrument;
197
+ /**
198
+ * Only for `exchangeId: user`: the id of a dataset created via `POST /datasets`, in place
199
+ * of `instrument`. Ignored against a managed exchange.
200
+ *
201
+ */
202
+ datasetId?: string;
203
+ /**
204
+ * Only for `exchangeId: user`, and optional even then: pins a specific past version of
205
+ * the dataset instead of its current one. Defaults to the dataset's current version.
206
+ *
207
+ */
208
+ datasetVersionId?: string;
185
209
  /**
186
210
  * Start date for the preparation process. Supports the following formats:
187
211
  * - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
@@ -206,7 +230,22 @@ export type PrepareRequest = {
206
230
  * labels return `400`.
207
231
  *
208
232
  */
209
- cadence?: "1s" | "5s" | "1m" | "5m" | "15m" | "1h" | "4h" | "1d";
233
+ cadence?:
234
+ | "1s"
235
+ | "5s"
236
+ | "1m"
237
+ | "3m"
238
+ | "5m"
239
+ | "15m"
240
+ | "30m"
241
+ | "1h"
242
+ | "2h"
243
+ | "4h"
244
+ | "8h"
245
+ | "12h"
246
+ | "1d"
247
+ | "1w"
248
+ | "1q";
210
249
  };
211
250
 
212
251
  /**
@@ -248,12 +287,19 @@ export type JobState = {
248
287
  };
249
288
 
250
289
  /**
251
- * State of a single-instrument prepare job — the `JobState` shape plus a per-hour
252
- * data-coverage summary. A single-instrument prepare is always terminal
253
- * (`status: Completed`): the client decides what to do from `coverageRatio` (e.g.
254
- * execute if it is at or above a chosen threshold) rather than polling for missing
255
- * hours that may never arrive — a missing hour for one instrument usually means low
256
- * activity, not missing data.
290
+ * State of a single-instrument prepare job — the `JobState` shape plus a coverage summary.
291
+ * A single-instrument prepare is always terminal (`status: Completed`): the client decides
292
+ * what to do from `coverageRatio` (e.g. execute if it is at or above a chosen threshold)
293
+ * rather than polling for missing hours that may never arrive — a missing hour for one
294
+ * instrument usually means low activity, not missing data.
295
+ *
296
+ * **Two coverage shapes, by exchange vs. dataset.** Against a managed exchange, coverage is
297
+ * walked hour by hour: `totalHours`/`hoursWithData`/`hoursWithoutData`. Against a
298
+ * dataset-backed prepare (`exchangeId: user`), coverage is reported on the dataset's own
299
+ * cadence grid instead — hour-walking a daily dataset would report `1/24` and read as
300
+ * broken — via `cadence`/`gaps`/`largestGapSteps`; `totalHours`/`hoursWithData`/
301
+ * `hoursWithoutData` are absent in that case. `dataFrom`/`dataTo`/`coverageRatio` are present
302
+ * either way, computed accordingly.
257
303
  *
258
304
  */
259
305
  export type PrepareJobState = JobState & {
@@ -266,21 +312,48 @@ export type PrepareJobState = JobState & {
266
312
  */
267
313
  dataTo?: string | null;
268
314
  /**
269
- * `hoursWithData / totalHours` in `[0,1]` (`1.0` when `totalHours` is 0) — the
270
- * fraction of hours in the requested range that have served data.
315
+ * Against a managed exchange: `hoursWithData / totalHours` in `[0,1]` (`1.0` when
316
+ * `totalHours` is 0), the fraction of hours in the requested range that have served
317
+ * data. Against a dataset (`exchangeId: user`): `rows / expectedStepsAtCadence`
318
+ * over the dataset version's own range — echoing what ingest computed once, not
319
+ * recomputed against a narrower prepare request.
271
320
  *
272
321
  */
273
322
  coverageRatio?: number;
274
323
  /**
275
- * Number of whole hours in the requested prepare range.
324
+ * Number of whole hours in the requested prepare range. Managed exchanges only —
325
+ * absent for a dataset-backed prepare.
326
+ *
276
327
  */
277
328
  totalHours?: number;
278
329
  /**
279
- * Number of hours in the range that have data.
330
+ * Number of hours in the range that have data. Managed exchanges only — absent for
331
+ * a dataset-backed prepare.
332
+ *
280
333
  */
281
334
  hoursWithData?: number;
282
335
  /**
283
- * One entry per hour in the range that has no data, with a rationale.
336
+ * The dataset version's own discovered cadence (e.g. `1m`, `1h`). Only present for a
337
+ * dataset-backed prepare (`exchangeId: user`).
338
+ *
339
+ */
340
+ cadence?: string;
341
+ /**
342
+ * Number of gaps in the dataset version at its own cadence, as discovered at ingest
343
+ * time. Only present for a dataset-backed prepare.
344
+ *
345
+ */
346
+ gaps?: number;
347
+ /**
348
+ * The largest gap in the dataset version, in units of its own cadence step. Only
349
+ * present for a dataset-backed prepare.
350
+ *
351
+ */
352
+ largestGapSteps?: number;
353
+ /**
354
+ * One entry per hour in the range that has no data, with a rationale. Managed
355
+ * exchanges only — absent for a dataset-backed prepare.
356
+ *
284
357
  */
285
358
  hoursWithoutData?: Array<{
286
359
  /**
@@ -361,6 +434,43 @@ export type ExecuteSweepRequest = {
361
434
  */
362
435
  minTradeFloor?: number;
363
436
  walkForward?: WalkForwardRequest;
437
+ equityCurve?: EquityCurveRequest;
438
+ };
439
+
440
+ /**
441
+ * Requested equity-curve transform, applied server-side in a fixed pipeline order: `resample` (point count) then `differential` (encoding) then `outMode` (JSON shape) — each stage assumes the previous one already ran. A server-side size guard can still force a smaller/deflated shape above its thresholds regardless of what is requested here — see `EquityCurveMeta` for what actually happened.
442
+ */
443
+ export type EquityCurveOptions = {
444
+ /**
445
+ * Downsample to at most this many points (extrema-preserving — the global max/min and the exact first/last point are always kept). Omit for no downsampling.
446
+ */
447
+ resample?: number;
448
+ /**
449
+ * Delta-encode both fields from the second (post-resample) point onward.
450
+ */
451
+ differential?: boolean;
452
+ /**
453
+ * Requested JSON shape.
454
+ */
455
+ outMode?: EquityCurveOutMode;
456
+ };
457
+
458
+ /**
459
+ * Selection (`mode`/`n`/`maxPct`) plus the transform preference (`resample`/`differential`/`outMode`) applied by `GET .../equityCurve` whenever ITS OWN query params are absent, for a curve this sweep retained. The transform half never affects retention or `sweepId` — a caller can always override it per-request at read time regardless of what was submitted here.
460
+ */
461
+ export type EquityCurveRequest = EquityCurveOptions & {
462
+ /**
463
+ * Which trials keep their per-point equity curve. `auto` retains curves only while the accumulated size stays within server limits; `topN`/`topPct` retain curves for the best-ranked trials explicitly; `none` retains no curves.
464
+ */
465
+ mode?: "auto" | "topN" | "topPct" | "none";
466
+ /**
467
+ * Trial count to retain when mode is topN.
468
+ */
469
+ n?: number;
470
+ /**
471
+ * Top percentage of trials to retain when mode is topPct.
472
+ */
473
+ maxPct?: number;
364
474
  };
365
475
 
366
476
  /**
@@ -478,6 +588,10 @@ export type SweepRunRow = {
478
588
  belowTradeFloor: boolean;
479
589
  aborted: boolean;
480
590
  runtimeMs: number;
591
+ /**
592
+ * Present only when this trial's curve was selected (`equityCurve.mode` was `topN`/`topPct` on the request, and this row ranked among the winners) — absent, not null, otherwise; a run that was never spilled and one that was spilled but not selected look identical here. Always a pointer today (`url` present, no inline points): a sweep's curves live in a spill store, not the response, until fetched separately. `GET` the `url` to fetch the curve itself; its own `meta` there is the real, possibly size-guarded outcome, while this outer `meta` is a raw, untransformed preview from selection time and can differ.
593
+ */
594
+ equityCurve?: EquityCurveResult;
481
595
  };
482
596
 
483
597
  /**
@@ -543,6 +657,9 @@ export type SweepHeatmapCell = {
543
657
 
544
658
  export type ExecuteSweepResult = {
545
659
  sweepId: string;
660
+ /**
661
+ * The sweep's own status vocabulary — not the same set `state.status` below uses. See `state` for why.
662
+ */
546
663
  status: "RUNNING" | "COMPLETED" | "PARTIAL" | "CANCELLED";
547
664
  objective: "sharpe" | "sortino" | "pnl" | "maxdd";
548
665
  order: "ranked" | "natural";
@@ -574,6 +691,13 @@ export type ExecuteSweepResult = {
574
691
  truncated: boolean;
575
692
  leaderboard: Array<SweepRunRow>;
576
693
  walkForward?: WalkForwardResult;
694
+ /**
695
+ * The same `JobState` shape a single-execute `BacktestJobResult` carries — not a sweep-specific lookalike, the actual type, so field names and timestamp formatting match exactly.
696
+ * `state.status` uses `JobState`'s own vocabulary (`New`/`Started`/`Completed`/ `Aborted`/`Failed`), mapped from the sweep's `status` field above rather than copying it: `PARTIAL` and `CANCELLED` both map to `Aborted`, because a sweep's `PARTIAL` is already terminal (some shards finished, some failed, nothing more is coming) unlike a single job's non-terminal `Partial`, which has no equivalent here at all.
697
+ * `state.completed` is real ticks processed on a plain sweep. On a `walkForward` sweep it is currently always `0` — the walk-forward fold runner was not wired to count ticks when this shipped, unlike the plain shard path.
698
+ * `state.size` is always `0` on every path (single execute, plain sweep, walk-forward alike) — nothing populates it anywhere yet. That is a known gap across the whole API, not a sweep-specific omission.
699
+ */
700
+ state: JobState;
577
701
  };
578
702
 
579
703
  /**
@@ -739,9 +863,9 @@ export type ResultMap = {
739
863
  */
740
864
  maxDrawdownPercent?: number;
741
865
  /**
742
- * Equity curve over the backtest. Element 0 is an anchor at the backtest `from` with `initialCapital`; the remaining points are one sample per emitted yield, in order. Use it to plot the strategy's running equity without re-deriving it from the yield history.
866
+ * Equity curve over the backtest. `points[0]` (or `timestamps[0]`/`equities[0]` in `SHORT` mode) is an anchor at the backtest `from` with `initialCapital`; the remaining points are one sample per emitted yield, in order. Use it to plot the strategy's running equity without re-deriving it from the yield history. Always inline for a plain backtest today — never a pointer (`url`); that shape exists only for a sweep row's top-N winners.
743
867
  */
744
- equityCurve?: Array<EquityPoint>;
868
+ equityCurve?: EquityCurveResult;
745
869
  /**
746
870
  * Number of signals emitted during strategy execution
747
871
  */
@@ -782,6 +906,60 @@ export type EquityPoint = {
782
906
  equity: number;
783
907
  };
784
908
 
909
+ /**
910
+ * JSON shape for an equity curve's points. `ARRAY` is `[{timestamp, equity}, ...]`; `SHORT` is `{timestamps: [...], equities: [...]}` (parallel arrays, no repeated key text). The one schema shared by every place `outMode` appears, request or response, so the two cannot drift to different value sets.
911
+ */
912
+ export type EquityCurveOutMode = "ARRAY" | "SHORT";
913
+
914
+ /**
915
+ * What the transform pipeline actually did, computed from the observed outcome — never a copy of what was requested. Lets a caller detect a forced or no-op transform (e.g. a `resample` ceiling already above the curve's size is a legal no-op, reported honestly as `resampled: false`).
916
+ */
917
+ export type EquityCurveMeta = {
918
+ /**
919
+ * Size of the curve the transform pipeline received.
920
+ */
921
+ inputPointCount: number;
922
+ /**
923
+ * Size after the full pipeline (resample, then differential, then outMode).
924
+ */
925
+ outputPointCount: number;
926
+ /**
927
+ * True only if the resample stage actually changed the point count.
928
+ */
929
+ resampled: boolean;
930
+ /**
931
+ * True only if delta-encoding actually ran. Requesting it on a curve of 0 or 1 points has nothing to encode, so it does not run even if asked.
932
+ */
933
+ differential: boolean;
934
+ /**
935
+ * The actual JSON shape served. Present even when the points themselves are not (a pointer curve, `EquityCurveResult.url`) — a caller resolving that URL separately still needs to know how to parse what it gets back before fetching it. May override an explicit request above a server-side size threshold; this field, not the request, is the source of truth for what shape actually came back.
936
+ */
937
+ outMode: EquityCurveOutMode;
938
+ };
939
+
940
+ /**
941
+ * An equity curve, shaped per `meta.outMode`: `points` when `ARRAY`, `timestamps` + `equities` (parallel arrays) when `SHORT`. Used identically wherever a curve is returned — a plain backtest's inline `equityCurve` and a sweep row's `equityCurve` are the same type. `url` is present *instead of* any points when the curve is served by pointer rather than inline (a sweep row's top-N winners only): `GET` it separately to fetch this exact same shape with the points populated.
942
+ */
943
+ export type EquityCurveResult = {
944
+ meta: EquityCurveMeta;
945
+ /**
946
+ * Present when `meta.outMode` is `ARRAY` and the curve is inline (not a pointer).
947
+ */
948
+ points?: Array<EquityPoint>;
949
+ /**
950
+ * Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer).
951
+ */
952
+ timestamps?: Array<number>;
953
+ /**
954
+ * Present when `meta.outMode` is `SHORT` and the curve is inline (not a pointer), parallel to `timestamps` (same index, same point).
955
+ */
956
+ equities?: Array<number>;
957
+ /**
958
+ * Present only for a sweep row's pointer curve. `GET` this to fetch the curve itself, in this exact `{points|timestamps+equities, meta}` shape — `meta` there is the real, possibly size-guarded outcome; this outer `meta` is a raw, untransformed preview from the moment the sweep selected this trial's curve, and the two can legitimately differ.
959
+ */
960
+ url?: string;
961
+ };
962
+
785
963
  /**
786
964
  * Unique identifier for a compiled strategy, derived from the source itself: the same code
787
965
  * always yields the same id, for every caller, whatever its formatting. See
@@ -790,6 +968,52 @@ export type EquityPoint = {
790
968
  */
791
969
  export type StrategyId = string;
792
970
 
971
+ /**
972
+ * One property name `POST /strategy` could establish without constructing the strategy —
973
+ * either declared with `@StrategyProperty` on the compiled source, or one of the small set of
974
+ * base properties every strategy carries (`amnt`, `enabled`, `multiEntry`, ...).
975
+ *
976
+ * **Best-effort, not exhaustive.** A property registered through an attached risk/backtest
977
+ * config needs a live instance to discover and is not listed here. Use this to catch a typo'd
978
+ * sweep key before submitting, not as the definitive list of what a sweep will accept — a
979
+ * name absent from this list may still be valid.
980
+ *
981
+ */
982
+ export type DeclaredProperty = {
983
+ /**
984
+ * The key a sweep or execute param map uses for this property.
985
+ */
986
+ name: string;
987
+ /**
988
+ * Human-readable label, as declared.
989
+ */
990
+ description?: string;
991
+ /**
992
+ * The declared default, as a string, if one was given. Absent, not null, when none was
993
+ * declared.
994
+ *
995
+ */
996
+ defaultValue?: string;
997
+ /**
998
+ * Whether a value for this key is injected into the strategy's field (`true`) or only
999
+ * available through the property map (`false`).
1000
+ *
1001
+ */
1002
+ reflected?: boolean;
1003
+ /**
1004
+ * Suggested sweep/range minimum, if declared. Advisory only, never validated.
1005
+ */
1006
+ min?: number;
1007
+ /**
1008
+ * Suggested sweep/range maximum, if declared. Advisory only, never validated.
1009
+ */
1010
+ max?: number;
1011
+ /**
1012
+ * Suggested sweep/range step, if declared. Advisory only, never validated.
1013
+ */
1014
+ step?: number;
1015
+ };
1016
+
793
1017
  /**
794
1018
  * A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
795
1019
  * knowing about how the strategy is wired, not necessarily an error.
@@ -912,6 +1136,165 @@ export type StrategyState = {
912
1136
  _links?: StrategyLinks;
913
1137
  };
914
1138
 
1139
+ /**
1140
+ * A dataset's own metadata — not its data. `currentVersionId` is what a prepare against
1141
+ * `exchangeId: user` reads by default; see `DatasetVersion` for what a version carries.
1142
+ *
1143
+ * `from`/`to`/`cadence` mirror that current version's own discovered range and cadence, so
1144
+ * you don't need a second call to `GET /datasets/{datasetId}/uploads/{uploadId}` just to see
1145
+ * what a dataset covers. Absent until a version exists.
1146
+ *
1147
+ */
1148
+ export type Dataset = {
1149
+ /**
1150
+ * Opaque id, returned by `POST /datasets`.
1151
+ */
1152
+ datasetId: string;
1153
+ /**
1154
+ * Unique among your datasets.
1155
+ */
1156
+ name: string;
1157
+ /**
1158
+ * Always `ticker` in v1.
1159
+ */
1160
+ type: "ticker";
1161
+ instrument: Instrument;
1162
+ /**
1163
+ * When the dataset was created.
1164
+ */
1165
+ createdAt: string;
1166
+ /**
1167
+ * The id of the most recently finalized, successfully ingested version. Absent until at
1168
+ * least one upload has finished ingesting.
1169
+ *
1170
+ */
1171
+ currentVersionId?: string;
1172
+ /**
1173
+ * When `currentVersionId` last changed. Absent until it has a value.
1174
+ */
1175
+ updatedAt?: string;
1176
+ /**
1177
+ * Start of `currentVersionId`'s own data range, as discovered at ingest time. Absent
1178
+ * until a version exists.
1179
+ *
1180
+ */
1181
+ from?: string;
1182
+ /**
1183
+ * End of `currentVersionId`'s own data range, as discovered at ingest time. Absent until
1184
+ * a version exists.
1185
+ *
1186
+ */
1187
+ to?: string;
1188
+ /**
1189
+ * `currentVersionId`'s own discovered bar cadence (e.g. `1s`, `1m`, `1h`). Absent until a
1190
+ * version exists.
1191
+ *
1192
+ */
1193
+ cadence?: string;
1194
+ };
1195
+
1196
+ /**
1197
+ * A `Dataset` plus a self link. Returned by `GET /datasets/{datasetId}`.
1198
+ */
1199
+ export type DatasetWithLinks = Dataset & {
1200
+ _links?: {
1201
+ self?: {
1202
+ href?: string;
1203
+ };
1204
+ };
1205
+ };
1206
+
1207
+ /**
1208
+ * A `Dataset` plus the first upload session — the presigned URL to PUT the file to.
1209
+ *
1210
+ */
1211
+ export type DatasetCreated = Dataset & {
1212
+ /**
1213
+ * Identifies this upload session. Pass to
1214
+ * `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
1215
+ *
1216
+ */
1217
+ uploadId: string;
1218
+ upload: {
1219
+ /**
1220
+ * Presigned URL. `PUT` the raw CSV file here directly — no `Authorization`
1221
+ * header, no other API credentials.
1222
+ *
1223
+ */
1224
+ url: string;
1225
+ /**
1226
+ * How long `url` stays valid.
1227
+ */
1228
+ expiresInMinutes: number;
1229
+ };
1230
+ };
1231
+
1232
+ /**
1233
+ * One successfully ingested upload. Cadence and timestamp unit are discovered from the file,
1234
+ * not declared by the caller.
1235
+ *
1236
+ */
1237
+ export type DatasetVersion = {
1238
+ datasetId: string;
1239
+ /**
1240
+ * The version id. Pass as `datasetVersionId` on `POST .../prepare` to pin it.
1241
+ */
1242
+ id?: string;
1243
+ /**
1244
+ * Size of the uploaded file.
1245
+ */
1246
+ bytes?: number;
1247
+ /**
1248
+ * Number of data rows.
1249
+ */
1250
+ rows?: number;
1251
+ /**
1252
+ * The discovered bar cadence (e.g. `1s`, `1m`, `1h`).
1253
+ */
1254
+ cadence?: string;
1255
+ /**
1256
+ * The unit the `timestamp` column was uploaded in — ISO-8601, or the epoch band its
1257
+ * numeric values fell in (seconds, millis, or micros).
1258
+ *
1259
+ */
1260
+ timestampUnit?: "iso" | "s" | "ms" | "us";
1261
+ /**
1262
+ * Number of gaps at the discovered cadence.
1263
+ */
1264
+ gaps?: number;
1265
+ /**
1266
+ * The largest gap, in units of the discovered cadence step.
1267
+ */
1268
+ largestGapSteps?: number;
1269
+ };
1270
+
1271
+ /**
1272
+ * Progress of one upload, from staged through ingest. Postgres-backed once a version exists,
1273
+ * so `ready`/`failed` are permanent answers; `uploading`/`ingesting` reflect in-flight state
1274
+ * that can itself age out — see the `404` case on `GET .../uploads/{uploadId}`.
1275
+ *
1276
+ */
1277
+ export type DatasetUploadState = {
1278
+ uploadId: string;
1279
+ /**
1280
+ * * `uploading` — the file was PUT to the presigned URL, but `finalize` has not been
1281
+ * called yet.
1282
+ * * `ingesting` — `finalize` was called; the worker is parsing and validating the file.
1283
+ * * `ready` — ingested successfully. `version` carries the result.
1284
+ * * `failed` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units).
1285
+ *
1286
+ */
1287
+ status: "uploading" | "ingesting" | "ready" | "failed";
1288
+ /**
1289
+ * The ingest job id, while `status` is `ingesting`.
1290
+ */
1291
+ jobId?: string;
1292
+ /**
1293
+ * Present when `status` is `ready` or `failed`.
1294
+ */
1295
+ version?: DatasetVersion;
1296
+ };
1297
+
915
1298
  export type AuthTokenResponse = {
916
1299
  /**
917
1300
  * Short-lived HS256 JWT. Send as `Authorization: Bearer <token>` on all other endpoints.
@@ -1246,6 +1629,12 @@ export type CompileStrategyResponses = {
1246
1629
  */
1247
1630
  200: {
1248
1631
  strategyId: StrategyId;
1632
+ /**
1633
+ * What could be established about this strategy's sweep-key vocabulary without
1634
+ * constructing it. See `DeclaredProperty` — best-effort, not exhaustive.
1635
+ *
1636
+ */
1637
+ declaredProperties?: Array<DeclaredProperty>;
1249
1638
  };
1250
1639
  };
1251
1640
 
@@ -1407,7 +1796,9 @@ export type PrepareBacktestData = {
1407
1796
  body: PrepareRequest;
1408
1797
  path: {
1409
1798
  /**
1410
- * ID of the exchange to prepare the backtesting for
1799
+ * ID of the exchange to prepare the backtesting for (e.g. `binance`), or the reserved
1800
+ * value `user` to prepare from a dataset you uploaded instead of a managed exchange.
1801
+ *
1411
1802
  */
1412
1803
  exchangeId: string;
1413
1804
  /**
@@ -1422,17 +1813,23 @@ export type PrepareBacktestData = {
1422
1813
  export type PrepareBacktestErrors = {
1423
1814
  /**
1424
1815
  * Invalid request or parameters. Also returned when `from` is older than the configured
1425
- * lookback window or `to` is in the future.
1816
+ * lookback window or `to` is in the future. For `exchangeId: user`, also returned when
1817
+ * the dataset's current upload has not finished ingesting, or `cadence` asks for a finer
1818
+ * granularity than the dataset's own discovered cadence, or the requested range exceeds
1819
+ * your tier's range limit.
1426
1820
  *
1427
1821
  */
1428
1822
  400: ResponseError;
1429
1823
  /**
1430
- * Exchange or data source type not found
1824
+ * Exchange or data source type not found. For `exchangeId: user`, also returned when
1825
+ * `datasetId` (or a pinned `datasetVersionId`) doesn't exist or isn't yours.
1826
+ *
1431
1827
  */
1432
1828
  404: ResponseError;
1433
1829
  /**
1434
1830
  * Rate limited. Returned when the global queue exceeds capacity or the user has too many
1435
- * active backtests.
1831
+ * active backtests. Does not apply to `exchangeId: user` — a dataset prepare reads an
1832
+ * already-ingested file rather than enqueueing worker capacity.
1436
1833
  *
1437
1834
  */
1438
1835
  429: ResponseError;
@@ -1455,7 +1852,9 @@ export type GetPrepareStatusData = {
1455
1852
  body?: never;
1456
1853
  path: {
1457
1854
  /**
1458
- * ID of the exchange for the backtesting process
1855
+ * ID of the exchange for the backtesting process, or the reserved value `user` for a
1856
+ * dataset-backed prepare.
1857
+ *
1459
1858
  */
1460
1859
  exchangeId: string;
1461
1860
  /**
@@ -1649,6 +2048,55 @@ export type GetSweepSensitivityResponses = {
1649
2048
  export type GetSweepSensitivityResponse =
1650
2049
  GetSweepSensitivityResponses[keyof GetSweepSensitivityResponses];
1651
2050
 
2051
+ export type GetSweepRunEquityCurveData = {
2052
+ body?: never;
2053
+ path: {
2054
+ exchangeId: string;
2055
+ type: DataSourceType;
2056
+ requestId: string;
2057
+ sweepId: string;
2058
+ /**
2059
+ * The trial's `runIx`, as it appears on its leaderboard row.
2060
+ */
2061
+ runIx: number;
2062
+ };
2063
+ query?: {
2064
+ /**
2065
+ * Requested JSON shape. Omit to use the sweep's submitted default; may be overridden either way by the server's size guard.
2066
+ */
2067
+ outMode?: EquityCurveOutMode;
2068
+ /**
2069
+ * Downsample to at most this many points (extrema-preserving — the global max/min and the exact first/last point are always kept). Omit to use the sweep's submitted default (itself omittable, for no downsampling).
2070
+ */
2071
+ resample?: number;
2072
+ /**
2073
+ * Delta-encode both fields from the second point onward. Omit to use the sweep's submitted default.
2074
+ */
2075
+ differential?: boolean;
2076
+ };
2077
+ url: "/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/runs/{runIx}/equityCurve";
2078
+ };
2079
+
2080
+ export type GetSweepRunEquityCurveErrors = {
2081
+ /**
2082
+ * Sweep or `runIx` not found, or that trial's curve was never selected — indistinguishable from a caller's perspective.
2083
+ */
2084
+ 404: ResponseError;
2085
+ };
2086
+
2087
+ export type GetSweepRunEquityCurveError =
2088
+ GetSweepRunEquityCurveErrors[keyof GetSweepRunEquityCurveErrors];
2089
+
2090
+ export type GetSweepRunEquityCurveResponses = {
2091
+ /**
2092
+ * The trial's equity curve, shaped per the resolved options.
2093
+ */
2094
+ 200: EquityCurveResult;
2095
+ };
2096
+
2097
+ export type GetSweepRunEquityCurveResponse =
2098
+ GetSweepRunEquityCurveResponses[keyof GetSweepRunEquityCurveResponses];
2099
+
1652
2100
  export type ExecuteBacktestData = {
1653
2101
  /**
1654
2102
  * Execute task parameters
@@ -1665,10 +2113,13 @@ export type ExecuteBacktestData = {
1665
2113
  *
1666
2114
  */
1667
2115
  storeSignals?: boolean;
2116
+ equityCurve?: EquityCurveOptions;
1668
2117
  };
1669
2118
  path: {
1670
2119
  /**
1671
- * ID of the exchange for the backtesting process
2120
+ * ID of the exchange for the backtesting process, or the reserved value `user` if
2121
+ * `prepareJobId` came from a dataset-backed prepare.
2122
+ *
1672
2123
  */
1673
2124
  exchangeId: string;
1674
2125
  /**
@@ -1804,6 +2255,214 @@ export type GetBacktestResultResponses = {
1804
2255
  export type GetBacktestResultResponse =
1805
2256
  GetBacktestResultResponses[keyof GetBacktestResultResponses];
1806
2257
 
2258
+ export type ListDatasetsData = {
2259
+ body?: never;
2260
+ path?: never;
2261
+ query?: never;
2262
+ url: "/datasets";
2263
+ };
2264
+
2265
+ export type ListDatasetsResponses = {
2266
+ /**
2267
+ * Your datasets
2268
+ */
2269
+ 200: {
2270
+ datasets: Array<Dataset>;
2271
+ };
2272
+ };
2273
+
2274
+ export type ListDatasetsResponse =
2275
+ ListDatasetsResponses[keyof ListDatasetsResponses];
2276
+
2277
+ export type CreateDatasetData = {
2278
+ /**
2279
+ * The dataset to create
2280
+ */
2281
+ body: {
2282
+ /**
2283
+ * A name unique among your datasets. `409` if already taken.
2284
+ */
2285
+ name: string;
2286
+ instrument: Instrument;
2287
+ };
2288
+ path?: never;
2289
+ query?: never;
2290
+ url: "/datasets";
2291
+ };
2292
+
2293
+ export type CreateDatasetErrors = {
2294
+ /**
2295
+ * Invalid request, or `instrument` is not a plain spot pair
2296
+ */
2297
+ 400: ResponseError;
2298
+ /**
2299
+ * You already have a dataset with this `name`
2300
+ */
2301
+ 409: ResponseError;
2302
+ /**
2303
+ * Your tier's dataset count limit is reached. Delete one, or upgrade.
2304
+ */
2305
+ 429: ResponseError;
2306
+ };
2307
+
2308
+ export type CreateDatasetError = CreateDatasetErrors[keyof CreateDatasetErrors];
2309
+
2310
+ export type CreateDatasetResponses = {
2311
+ /**
2312
+ * Dataset created, with an upload session ready for its first version
2313
+ */
2314
+ 201: DatasetCreated;
2315
+ };
2316
+
2317
+ export type CreateDatasetResponse =
2318
+ CreateDatasetResponses[keyof CreateDatasetResponses];
2319
+
2320
+ export type DeleteDatasetData = {
2321
+ body?: never;
2322
+ path: {
2323
+ /**
2324
+ * The id returned by `POST /datasets`
2325
+ */
2326
+ datasetId: string;
2327
+ };
2328
+ query?: never;
2329
+ url: "/datasets/{datasetId}";
2330
+ };
2331
+
2332
+ export type DeleteDatasetErrors = {
2333
+ /**
2334
+ * No such dataset for this user, or already deleted
2335
+ */
2336
+ 404: ResponseError;
2337
+ };
2338
+
2339
+ export type DeleteDatasetError = DeleteDatasetErrors[keyof DeleteDatasetErrors];
2340
+
2341
+ export type DeleteDatasetResponses = {
2342
+ /**
2343
+ * Deleted
2344
+ */
2345
+ 200: {
2346
+ datasetId: string;
2347
+ deleted: true;
2348
+ };
2349
+ };
2350
+
2351
+ export type DeleteDatasetResponse =
2352
+ DeleteDatasetResponses[keyof DeleteDatasetResponses];
2353
+
2354
+ export type GetDatasetData = {
2355
+ body?: never;
2356
+ path: {
2357
+ /**
2358
+ * The id returned by `POST /datasets`
2359
+ */
2360
+ datasetId: string;
2361
+ };
2362
+ query?: never;
2363
+ url: "/datasets/{datasetId}";
2364
+ };
2365
+
2366
+ export type GetDatasetErrors = {
2367
+ /**
2368
+ * No such dataset for this user
2369
+ */
2370
+ 404: ResponseError;
2371
+ };
2372
+
2373
+ export type GetDatasetError = GetDatasetErrors[keyof GetDatasetErrors];
2374
+
2375
+ export type GetDatasetResponses = {
2376
+ /**
2377
+ * Dataset detail
2378
+ */
2379
+ 200: DatasetWithLinks;
2380
+ };
2381
+
2382
+ export type GetDatasetResponse = GetDatasetResponses[keyof GetDatasetResponses];
2383
+
2384
+ export type FinalizeDatasetUploadData = {
2385
+ body?: never;
2386
+ path: {
2387
+ /**
2388
+ * The id returned by `POST /datasets`
2389
+ */
2390
+ datasetId: string;
2391
+ /**
2392
+ * The `uploadId` returned by `POST /datasets`
2393
+ */
2394
+ uploadId: string;
2395
+ };
2396
+ query?: never;
2397
+ url: "/datasets/{datasetId}/uploads/{uploadId}/finalize";
2398
+ };
2399
+
2400
+ export type FinalizeDatasetUploadErrors = {
2401
+ /**
2402
+ * No such dataset for this user, or nothing was PUT to `upload.url` yet — a finalize with
2403
+ * nothing to finalize.
2404
+ *
2405
+ */
2406
+ 404: ResponseError;
2407
+ /**
2408
+ * The uploaded file exceeds your tier's size limit for a dataset.
2409
+ */
2410
+ 413: ResponseError;
2411
+ };
2412
+
2413
+ export type FinalizeDatasetUploadError =
2414
+ FinalizeDatasetUploadErrors[keyof FinalizeDatasetUploadErrors];
2415
+
2416
+ export type FinalizeDatasetUploadResponses = {
2417
+ /**
2418
+ * Ingest queued
2419
+ */
2420
+ 202: {
2421
+ jobId: string;
2422
+ };
2423
+ };
2424
+
2425
+ export type FinalizeDatasetUploadResponse =
2426
+ FinalizeDatasetUploadResponses[keyof FinalizeDatasetUploadResponses];
2427
+
2428
+ export type GetDatasetUploadData = {
2429
+ body?: never;
2430
+ path: {
2431
+ /**
2432
+ * The id returned by `POST /datasets`
2433
+ */
2434
+ datasetId: string;
2435
+ /**
2436
+ * The `uploadId` returned by `POST /datasets`
2437
+ */
2438
+ uploadId: string;
2439
+ };
2440
+ query?: never;
2441
+ url: "/datasets/{datasetId}/uploads/{uploadId}";
2442
+ };
2443
+
2444
+ export type GetDatasetUploadErrors = {
2445
+ /**
2446
+ * No such dataset for this user, or genuinely nothing is known about this `uploadId` — no
2447
+ * version, no in-flight job, and nothing was ever PUT to its upload URL.
2448
+ *
2449
+ */
2450
+ 404: ResponseError;
2451
+ };
2452
+
2453
+ export type GetDatasetUploadError =
2454
+ GetDatasetUploadErrors[keyof GetDatasetUploadErrors];
2455
+
2456
+ export type GetDatasetUploadResponses = {
2457
+ /**
2458
+ * Current upload/ingest state
2459
+ */
2460
+ 200: DatasetUploadState;
2461
+ };
2462
+
2463
+ export type GetDatasetUploadResponse =
2464
+ GetDatasetUploadResponses[keyof GetDatasetUploadResponses];
2465
+
1807
2466
  export type ClientOptions = {
1808
2467
  baseUrl:
1809
2468
  | "https://api.qtsurfer.net/v1"