@qtsurfer/api-client 0.10.0 → 0.11.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
  /**
@@ -912,6 +985,165 @@ export type StrategyState = {
912
985
  _links?: StrategyLinks;
913
986
  };
914
987
 
988
+ /**
989
+ * A dataset's own metadata — not its data. `currentVersionId` is what a prepare against
990
+ * `exchangeId: user` reads by default; see `DatasetVersion` for what a version carries.
991
+ *
992
+ * `from`/`to`/`cadence` mirror that current version's own discovered range and cadence, so
993
+ * you don't need a second call to `GET /datasets/{datasetId}/uploads/{uploadId}` just to see
994
+ * what a dataset covers. Absent until a version exists.
995
+ *
996
+ */
997
+ export type Dataset = {
998
+ /**
999
+ * Opaque id, returned by `POST /datasets`.
1000
+ */
1001
+ datasetId: string;
1002
+ /**
1003
+ * Unique among your datasets.
1004
+ */
1005
+ name: string;
1006
+ /**
1007
+ * Always `ticker` in v1.
1008
+ */
1009
+ type: "ticker";
1010
+ instrument: Instrument;
1011
+ /**
1012
+ * When the dataset was created.
1013
+ */
1014
+ createdAt: string;
1015
+ /**
1016
+ * The id of the most recently finalized, successfully ingested version. Absent until at
1017
+ * least one upload has finished ingesting.
1018
+ *
1019
+ */
1020
+ currentVersionId?: string;
1021
+ /**
1022
+ * When `currentVersionId` last changed. Absent until it has a value.
1023
+ */
1024
+ updatedAt?: string;
1025
+ /**
1026
+ * Start of `currentVersionId`'s own data range, as discovered at ingest time. Absent
1027
+ * until a version exists.
1028
+ *
1029
+ */
1030
+ from?: string;
1031
+ /**
1032
+ * End of `currentVersionId`'s own data range, as discovered at ingest time. Absent until
1033
+ * a version exists.
1034
+ *
1035
+ */
1036
+ to?: string;
1037
+ /**
1038
+ * `currentVersionId`'s own discovered bar cadence (e.g. `1s`, `1m`, `1h`). Absent until a
1039
+ * version exists.
1040
+ *
1041
+ */
1042
+ cadence?: string;
1043
+ };
1044
+
1045
+ /**
1046
+ * A `Dataset` plus a self link. Returned by `GET /datasets/{datasetId}`.
1047
+ */
1048
+ export type DatasetWithLinks = Dataset & {
1049
+ _links?: {
1050
+ self?: {
1051
+ href?: string;
1052
+ };
1053
+ };
1054
+ };
1055
+
1056
+ /**
1057
+ * A `Dataset` plus the first upload session — the presigned URL to PUT the file to.
1058
+ *
1059
+ */
1060
+ export type DatasetCreated = Dataset & {
1061
+ /**
1062
+ * Identifies this upload session. Pass to
1063
+ * `POST /datasets/{datasetId}/uploads/{uploadId}/finalize` once the PUT completes.
1064
+ *
1065
+ */
1066
+ 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
+ };
1079
+ };
1080
+
1081
+ /**
1082
+ * One successfully ingested upload. Cadence and timestamp unit are discovered from the file,
1083
+ * not declared by the caller (D-11).
1084
+ *
1085
+ */
1086
+ export type DatasetVersion = {
1087
+ datasetId: string;
1088
+ /**
1089
+ * The version id. Pass as `datasetVersionId` on `POST .../prepare` to pin it.
1090
+ */
1091
+ id?: string;
1092
+ /**
1093
+ * Size of the uploaded file.
1094
+ */
1095
+ bytes?: number;
1096
+ /**
1097
+ * Number of data rows.
1098
+ */
1099
+ rows?: number;
1100
+ /**
1101
+ * The discovered bar cadence (e.g. `1s`, `1m`, `1h`).
1102
+ */
1103
+ cadence?: string;
1104
+ /**
1105
+ * The unit the `timestamp` column was uploaded in — ISO-8601, or the epoch band its
1106
+ * numeric values fell in (seconds, millis, or micros).
1107
+ *
1108
+ */
1109
+ timestampUnit?: "iso" | "s" | "ms" | "us";
1110
+ /**
1111
+ * Number of gaps at the discovered cadence.
1112
+ */
1113
+ gaps?: number;
1114
+ /**
1115
+ * The largest gap, in units of the discovered cadence step.
1116
+ */
1117
+ largestGapSteps?: number;
1118
+ };
1119
+
1120
+ /**
1121
+ * Progress of one upload, from staged through ingest. Postgres-backed once a version exists,
1122
+ * so `ready`/`failed` are permanent answers; `uploading`/`ingesting` reflect in-flight state
1123
+ * that can itself age out — see the `404` case on `GET .../uploads/{uploadId}`.
1124
+ *
1125
+ */
1126
+ export type DatasetUploadState = {
1127
+ uploadId: string;
1128
+ /**
1129
+ * * `uploading` — the file was PUT to the presigned URL, but `finalize` has not been
1130
+ * called yet.
1131
+ * * `ingesting` — `finalize` was called; the worker is parsing and validating the file.
1132
+ * * `ready` — ingested successfully. `version` carries the result.
1133
+ * * `failed` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units).
1134
+ *
1135
+ */
1136
+ status: "uploading" | "ingesting" | "ready" | "failed";
1137
+ /**
1138
+ * The ingest job id, while `status` is `ingesting`.
1139
+ */
1140
+ jobId?: string;
1141
+ /**
1142
+ * Present when `status` is `ready` or `failed`.
1143
+ */
1144
+ version?: DatasetVersion;
1145
+ };
1146
+
915
1147
  export type AuthTokenResponse = {
916
1148
  /**
917
1149
  * Short-lived HS256 JWT. Send as `Authorization: Bearer <token>` on all other endpoints.
@@ -1407,7 +1639,9 @@ export type PrepareBacktestData = {
1407
1639
  body: PrepareRequest;
1408
1640
  path: {
1409
1641
  /**
1410
- * ID of the exchange to prepare the backtesting for
1642
+ * ID of the exchange to prepare the backtesting for (e.g. `binance`), or the reserved
1643
+ * value `user` to prepare from a dataset you uploaded instead of a managed exchange.
1644
+ *
1411
1645
  */
1412
1646
  exchangeId: string;
1413
1647
  /**
@@ -1422,17 +1656,23 @@ export type PrepareBacktestData = {
1422
1656
  export type PrepareBacktestErrors = {
1423
1657
  /**
1424
1658
  * Invalid request or parameters. Also returned when `from` is older than the configured
1425
- * lookback window or `to` is in the future.
1659
+ * lookback window or `to` is in the future. For `exchangeId: user`, also returned when
1660
+ * the dataset's current upload has not finished ingesting, or `cadence` asks for a finer
1661
+ * granularity than the dataset's own discovered cadence, or the requested range exceeds
1662
+ * your tier's range limit.
1426
1663
  *
1427
1664
  */
1428
1665
  400: ResponseError;
1429
1666
  /**
1430
- * Exchange or data source type not found
1667
+ * Exchange or data source type not found. For `exchangeId: user`, also returned when
1668
+ * `datasetId` (or a pinned `datasetVersionId`) doesn't exist or isn't yours.
1669
+ *
1431
1670
  */
1432
1671
  404: ResponseError;
1433
1672
  /**
1434
1673
  * Rate limited. Returned when the global queue exceeds capacity or the user has too many
1435
- * active backtests.
1674
+ * active backtests. Does not apply to `exchangeId: user` — a dataset prepare reads an
1675
+ * already-ingested file rather than enqueueing worker capacity.
1436
1676
  *
1437
1677
  */
1438
1678
  429: ResponseError;
@@ -1455,7 +1695,9 @@ export type GetPrepareStatusData = {
1455
1695
  body?: never;
1456
1696
  path: {
1457
1697
  /**
1458
- * ID of the exchange for the backtesting process
1698
+ * ID of the exchange for the backtesting process, or the reserved value `user` for a
1699
+ * dataset-backed prepare.
1700
+ *
1459
1701
  */
1460
1702
  exchangeId: string;
1461
1703
  /**
@@ -1668,7 +1910,9 @@ export type ExecuteBacktestData = {
1668
1910
  };
1669
1911
  path: {
1670
1912
  /**
1671
- * ID of the exchange for the backtesting process
1913
+ * ID of the exchange for the backtesting process, or the reserved value `user` if
1914
+ * `prepareJobId` came from a dataset-backed prepare.
1915
+ *
1672
1916
  */
1673
1917
  exchangeId: string;
1674
1918
  /**
@@ -1804,6 +2048,214 @@ export type GetBacktestResultResponses = {
1804
2048
  export type GetBacktestResultResponse =
1805
2049
  GetBacktestResultResponses[keyof GetBacktestResultResponses];
1806
2050
 
2051
+ export type ListDatasetsData = {
2052
+ body?: never;
2053
+ path?: never;
2054
+ query?: never;
2055
+ url: "/datasets";
2056
+ };
2057
+
2058
+ export type ListDatasetsResponses = {
2059
+ /**
2060
+ * Your datasets
2061
+ */
2062
+ 200: {
2063
+ datasets: Array<Dataset>;
2064
+ };
2065
+ };
2066
+
2067
+ export type ListDatasetsResponse =
2068
+ ListDatasetsResponses[keyof ListDatasetsResponses];
2069
+
2070
+ export type CreateDatasetData = {
2071
+ /**
2072
+ * The dataset to create
2073
+ */
2074
+ body: {
2075
+ /**
2076
+ * A name unique among your datasets. `409` if already taken.
2077
+ */
2078
+ name: string;
2079
+ instrument: Instrument;
2080
+ };
2081
+ path?: never;
2082
+ query?: never;
2083
+ url: "/datasets";
2084
+ };
2085
+
2086
+ export type CreateDatasetErrors = {
2087
+ /**
2088
+ * Invalid request, or `instrument` is not a plain spot pair
2089
+ */
2090
+ 400: ResponseError;
2091
+ /**
2092
+ * You already have a dataset with this `name`
2093
+ */
2094
+ 409: ResponseError;
2095
+ /**
2096
+ * Your tier's dataset count limit is reached. Delete one, or upgrade.
2097
+ */
2098
+ 429: ResponseError;
2099
+ };
2100
+
2101
+ export type CreateDatasetError = CreateDatasetErrors[keyof CreateDatasetErrors];
2102
+
2103
+ export type CreateDatasetResponses = {
2104
+ /**
2105
+ * Dataset created, with an upload session ready for its first version
2106
+ */
2107
+ 201: DatasetCreated;
2108
+ };
2109
+
2110
+ export type CreateDatasetResponse =
2111
+ CreateDatasetResponses[keyof CreateDatasetResponses];
2112
+
2113
+ export type DeleteDatasetData = {
2114
+ body?: never;
2115
+ path: {
2116
+ /**
2117
+ * The id returned by `POST /datasets`
2118
+ */
2119
+ datasetId: string;
2120
+ };
2121
+ query?: never;
2122
+ url: "/datasets/{datasetId}";
2123
+ };
2124
+
2125
+ export type DeleteDatasetErrors = {
2126
+ /**
2127
+ * No such dataset for this user, or already deleted
2128
+ */
2129
+ 404: ResponseError;
2130
+ };
2131
+
2132
+ export type DeleteDatasetError = DeleteDatasetErrors[keyof DeleteDatasetErrors];
2133
+
2134
+ export type DeleteDatasetResponses = {
2135
+ /**
2136
+ * Deleted
2137
+ */
2138
+ 200: {
2139
+ datasetId: string;
2140
+ deleted: true;
2141
+ };
2142
+ };
2143
+
2144
+ export type DeleteDatasetResponse =
2145
+ DeleteDatasetResponses[keyof DeleteDatasetResponses];
2146
+
2147
+ export type GetDatasetData = {
2148
+ body?: never;
2149
+ path: {
2150
+ /**
2151
+ * The id returned by `POST /datasets`
2152
+ */
2153
+ datasetId: string;
2154
+ };
2155
+ query?: never;
2156
+ url: "/datasets/{datasetId}";
2157
+ };
2158
+
2159
+ export type GetDatasetErrors = {
2160
+ /**
2161
+ * No such dataset for this user
2162
+ */
2163
+ 404: ResponseError;
2164
+ };
2165
+
2166
+ export type GetDatasetError = GetDatasetErrors[keyof GetDatasetErrors];
2167
+
2168
+ export type GetDatasetResponses = {
2169
+ /**
2170
+ * Dataset detail
2171
+ */
2172
+ 200: DatasetWithLinks;
2173
+ };
2174
+
2175
+ export type GetDatasetResponse = GetDatasetResponses[keyof GetDatasetResponses];
2176
+
2177
+ export type FinalizeDatasetUploadData = {
2178
+ body?: never;
2179
+ path: {
2180
+ /**
2181
+ * The id returned by `POST /datasets`
2182
+ */
2183
+ datasetId: string;
2184
+ /**
2185
+ * The `uploadId` returned by `POST /datasets`
2186
+ */
2187
+ uploadId: string;
2188
+ };
2189
+ query?: never;
2190
+ url: "/datasets/{datasetId}/uploads/{uploadId}/finalize";
2191
+ };
2192
+
2193
+ export type FinalizeDatasetUploadErrors = {
2194
+ /**
2195
+ * No such dataset for this user, or nothing was PUT to `upload.url` yet — a finalize with
2196
+ * nothing to finalize.
2197
+ *
2198
+ */
2199
+ 404: ResponseError;
2200
+ /**
2201
+ * The uploaded file exceeds your tier's size limit for a dataset.
2202
+ */
2203
+ 413: ResponseError;
2204
+ };
2205
+
2206
+ export type FinalizeDatasetUploadError =
2207
+ FinalizeDatasetUploadErrors[keyof FinalizeDatasetUploadErrors];
2208
+
2209
+ export type FinalizeDatasetUploadResponses = {
2210
+ /**
2211
+ * Ingest queued
2212
+ */
2213
+ 202: {
2214
+ jobId: string;
2215
+ };
2216
+ };
2217
+
2218
+ export type FinalizeDatasetUploadResponse =
2219
+ FinalizeDatasetUploadResponses[keyof FinalizeDatasetUploadResponses];
2220
+
2221
+ export type GetDatasetUploadData = {
2222
+ body?: never;
2223
+ path: {
2224
+ /**
2225
+ * The id returned by `POST /datasets`
2226
+ */
2227
+ datasetId: string;
2228
+ /**
2229
+ * The `uploadId` returned by `POST /datasets`
2230
+ */
2231
+ uploadId: string;
2232
+ };
2233
+ query?: never;
2234
+ url: "/datasets/{datasetId}/uploads/{uploadId}";
2235
+ };
2236
+
2237
+ export type GetDatasetUploadErrors = {
2238
+ /**
2239
+ * No such dataset for this user, or genuinely nothing is known about this `uploadId` — no
2240
+ * version, no in-flight job, and nothing was ever PUT to its upload URL.
2241
+ *
2242
+ */
2243
+ 404: ResponseError;
2244
+ };
2245
+
2246
+ export type GetDatasetUploadError =
2247
+ GetDatasetUploadErrors[keyof GetDatasetUploadErrors];
2248
+
2249
+ export type GetDatasetUploadResponses = {
2250
+ /**
2251
+ * Current upload/ingest state
2252
+ */
2253
+ 200: DatasetUploadState;
2254
+ };
2255
+
2256
+ export type GetDatasetUploadResponse =
2257
+ GetDatasetUploadResponses[keyof GetDatasetUploadResponses];
2258
+
1807
2259
  export type ClientOptions = {
1808
2260
  baseUrl:
1809
2261
  | "https://api.qtsurfer.net/v1"