@qtsurfer/api-client 0.11.0 → 0.13.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.
@@ -434,6 +434,43 @@ export type ExecuteSweepRequest = {
434
434
  */
435
435
  minTradeFloor?: number;
436
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;
437
474
  };
438
475
 
439
476
  /**
@@ -551,6 +588,10 @@ export type SweepRunRow = {
551
588
  belowTradeFloor: boolean;
552
589
  aborted: boolean;
553
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;
554
595
  };
555
596
 
556
597
  /**
@@ -616,6 +657,9 @@ export type SweepHeatmapCell = {
616
657
 
617
658
  export type ExecuteSweepResult = {
618
659
  sweepId: string;
660
+ /**
661
+ * The sweep's own status vocabulary — not the same set `state.status` below uses. See `state` for why.
662
+ */
619
663
  status: "RUNNING" | "COMPLETED" | "PARTIAL" | "CANCELLED";
620
664
  objective: "sharpe" | "sortino" | "pnl" | "maxdd";
621
665
  order: "ranked" | "natural";
@@ -647,6 +691,13 @@ export type ExecuteSweepResult = {
647
691
  truncated: boolean;
648
692
  leaderboard: Array<SweepRunRow>;
649
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;
650
701
  };
651
702
 
652
703
  /**
@@ -812,9 +863,9 @@ export type ResultMap = {
812
863
  */
813
864
  maxDrawdownPercent?: number;
814
865
  /**
815
- * 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.
816
867
  */
817
- equityCurve?: Array<EquityPoint>;
868
+ equityCurve?: EquityCurveResult;
818
869
  /**
819
870
  * Number of signals emitted during strategy execution
820
871
  */
@@ -855,6 +906,60 @@ export type EquityPoint = {
855
906
  equity: number;
856
907
  };
857
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
+
858
963
  /**
859
964
  * Unique identifier for a compiled strategy, derived from the source itself: the same code
860
965
  * always yields the same id, for every caller, whatever its formatting. See
@@ -863,6 +968,52 @@ export type EquityPoint = {
863
968
  */
864
969
  export type StrategyId = string;
865
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
+
866
1017
  /**
867
1018
  * A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
868
1019
  * knowing about how the strategy is wired, not necessarily an error.
@@ -1054,33 +1205,63 @@ export type DatasetWithLinks = Dataset & {
1054
1205
  };
1055
1206
 
1056
1207
  /**
1057
- * A `Dataset` plus the first upload session — the presigned URL to PUT the file to.
1208
+ * A presigned destination for uploading a raw dataset file directly to storage.
1209
+ */
1210
+ export type DatasetUploadTarget = {
1211
+ /**
1212
+ * Presigned URL. `PUT` the raw CSV file here directly — no `Authorization` header,
1213
+ * no other API credentials.
1214
+ *
1215
+ */
1216
+ url: string;
1217
+ /**
1218
+ * How long `url` stays valid.
1219
+ */
1220
+ expiresInMinutes: number;
1221
+ };
1222
+
1223
+ /**
1224
+ * An upload session — an id plus the presigned URL to PUT the raw file to. Returned both by
1225
+ * `POST /datasets` (as part of the new dataset) and by `POST /datasets/{datasetId}/uploads`
1226
+ * (on its own, for an existing one).
1058
1227
  *
1059
1228
  */
1060
- export type DatasetCreated = Dataset & {
1229
+ export type DatasetUploadSession = {
1061
1230
  /**
1062
1231
  * Identifies this upload session. Pass to
1063
1232
  * `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
1064
1233
  *
1065
1234
  */
1066
1235
  uploadId: string;
1067
- upload: {
1068
- /**
1069
- * Presigned URL. `PUT` the raw CSV file here directly — no `Authorization`
1070
- * header, no other API credentials.
1071
- *
1072
- */
1073
- url: string;
1074
- /**
1075
- * How long `url` stays valid.
1076
- */
1077
- expiresInMinutes: number;
1078
- };
1236
+ upload: DatasetUploadTarget;
1079
1237
  };
1080
1238
 
1239
+ /**
1240
+ * The metadata available immediately after creating a dataset, plus its first upload
1241
+ * session — the presigned URL to PUT the file to. Version-derived fields such as
1242
+ * `createdAt`, `currentVersionId`, range, and cadence are available from `GET /datasets/{datasetId}`
1243
+ * after the relevant lifecycle stages, not in this creation response.
1244
+ *
1245
+ */
1246
+ export type DatasetCreated = {
1247
+ /**
1248
+ * Opaque id of the newly created dataset.
1249
+ */
1250
+ datasetId: string;
1251
+ /**
1252
+ * Unique name of the newly created dataset.
1253
+ */
1254
+ name: string;
1255
+ /**
1256
+ * Always `ticker` in v1.
1257
+ */
1258
+ type: "ticker";
1259
+ instrument: Instrument;
1260
+ } & DatasetUploadSession;
1261
+
1081
1262
  /**
1082
1263
  * One successfully ingested upload. Cadence and timestamp unit are discovered from the file,
1083
- * not declared by the caller (D-11).
1264
+ * not declared by the caller.
1084
1265
  *
1085
1266
  */
1086
1267
  export type DatasetVersion = {
@@ -1478,6 +1659,12 @@ export type CompileStrategyResponses = {
1478
1659
  */
1479
1660
  200: {
1480
1661
  strategyId: StrategyId;
1662
+ /**
1663
+ * What could be established about this strategy's sweep-key vocabulary without
1664
+ * constructing it. See `DeclaredProperty` — best-effort, not exhaustive.
1665
+ *
1666
+ */
1667
+ declaredProperties?: Array<DeclaredProperty>;
1481
1668
  };
1482
1669
  };
1483
1670
 
@@ -1891,6 +2078,55 @@ export type GetSweepSensitivityResponses = {
1891
2078
  export type GetSweepSensitivityResponse =
1892
2079
  GetSweepSensitivityResponses[keyof GetSweepSensitivityResponses];
1893
2080
 
2081
+ export type GetSweepRunEquityCurveData = {
2082
+ body?: never;
2083
+ path: {
2084
+ exchangeId: string;
2085
+ type: DataSourceType;
2086
+ requestId: string;
2087
+ sweepId: string;
2088
+ /**
2089
+ * The trial's `runIx`, as it appears on its leaderboard row.
2090
+ */
2091
+ runIx: number;
2092
+ };
2093
+ query?: {
2094
+ /**
2095
+ * Requested JSON shape. Omit to use the sweep's submitted default; may be overridden either way by the server's size guard.
2096
+ */
2097
+ outMode?: EquityCurveOutMode;
2098
+ /**
2099
+ * 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).
2100
+ */
2101
+ resample?: number;
2102
+ /**
2103
+ * Delta-encode both fields from the second point onward. Omit to use the sweep's submitted default.
2104
+ */
2105
+ differential?: boolean;
2106
+ };
2107
+ url: "/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}/runs/{runIx}/equityCurve";
2108
+ };
2109
+
2110
+ export type GetSweepRunEquityCurveErrors = {
2111
+ /**
2112
+ * Sweep or `runIx` not found, or that trial's curve was never selected — indistinguishable from a caller's perspective.
2113
+ */
2114
+ 404: ResponseError;
2115
+ };
2116
+
2117
+ export type GetSweepRunEquityCurveError =
2118
+ GetSweepRunEquityCurveErrors[keyof GetSweepRunEquityCurveErrors];
2119
+
2120
+ export type GetSweepRunEquityCurveResponses = {
2121
+ /**
2122
+ * The trial's equity curve, shaped per the resolved options.
2123
+ */
2124
+ 200: EquityCurveResult;
2125
+ };
2126
+
2127
+ export type GetSweepRunEquityCurveResponse =
2128
+ GetSweepRunEquityCurveResponses[keyof GetSweepRunEquityCurveResponses];
2129
+
1894
2130
  export type ExecuteBacktestData = {
1895
2131
  /**
1896
2132
  * Execute task parameters
@@ -1907,6 +2143,7 @@ export type ExecuteBacktestData = {
1907
2143
  *
1908
2144
  */
1909
2145
  storeSignals?: boolean;
2146
+ equityCurve?: EquityCurveOptions;
1910
2147
  };
1911
2148
  path: {
1912
2149
  /**
@@ -2174,6 +2411,38 @@ export type GetDatasetResponses = {
2174
2411
 
2175
2412
  export type GetDatasetResponse = GetDatasetResponses[keyof GetDatasetResponses];
2176
2413
 
2414
+ export type OpenDatasetUploadData = {
2415
+ body?: never;
2416
+ path: {
2417
+ /**
2418
+ * The id returned by `POST /datasets`
2419
+ */
2420
+ datasetId: string;
2421
+ };
2422
+ query?: never;
2423
+ url: "/datasets/{datasetId}/uploads";
2424
+ };
2425
+
2426
+ export type OpenDatasetUploadErrors = {
2427
+ /**
2428
+ * No such dataset for this user
2429
+ */
2430
+ 404: ResponseError;
2431
+ };
2432
+
2433
+ export type OpenDatasetUploadError =
2434
+ OpenDatasetUploadErrors[keyof OpenDatasetUploadErrors];
2435
+
2436
+ export type OpenDatasetUploadResponses = {
2437
+ /**
2438
+ * An upload session — new, or the one already open for this dataset
2439
+ */
2440
+ 201: DatasetUploadSession;
2441
+ };
2442
+
2443
+ export type OpenDatasetUploadResponse =
2444
+ OpenDatasetUploadResponses[keyof OpenDatasetUploadResponses];
2445
+
2177
2446
  export type FinalizeDatasetUploadData = {
2178
2447
  body?: never;
2179
2448
  path: {
@@ -2192,11 +2461,18 @@ export type FinalizeDatasetUploadData = {
2192
2461
 
2193
2462
  export type FinalizeDatasetUploadErrors = {
2194
2463
  /**
2195
- * No such dataset for this user, or nothing was PUT to `upload.url` yet — a finalize with
2196
- * nothing to finalize.
2464
+ * No such dataset for this user; `uploadId` was not issued for this dataset (never
2465
+ * minted, or minted for a different one); or nothing was PUT to `upload.url` yet — a
2466
+ * finalize with nothing to finalize.
2197
2467
  *
2198
2468
  */
2199
2469
  404: ResponseError;
2470
+ /**
2471
+ * `uploadId` already produced a version. The error message names it. Open a new upload
2472
+ * session (`POST /datasets/{datasetId}/uploads`) for anything new.
2473
+ *
2474
+ */
2475
+ 409: ResponseError;
2200
2476
  /**
2201
2477
  * The uploaded file exceeds your tier's size limit for a dataset.
2202
2478
  */