@qtsurfer/api-client 0.14.0 → 0.15.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.
@@ -18,6 +18,21 @@ export const ResponseErrorSchema = {
18
18
  },
19
19
  } as const;
20
20
 
21
+ export const ScalarStrategyParamValueSchema = {
22
+ description: "A scalar strategy property value for one execution.",
23
+ oneOf: [
24
+ {
25
+ type: "number",
26
+ },
27
+ {
28
+ type: "string",
29
+ },
30
+ {
31
+ type: "boolean",
32
+ },
33
+ ],
34
+ } as const;
35
+
21
36
  export const InstrumentSchema = {
22
37
  description: "Exchange instrument identifier (e.g. a currency pair)",
23
38
  type: "string",
@@ -320,30 +335,20 @@ the dataset instead of its current one. Defaults to the dataset's current versio
320
335
  },
321
336
  cadence: {
322
337
  type: "string",
323
- description: `Output bar cadence for the prepared range. Defaults to the publisher's
324
- native cadence (\`1s\`); coarser cadences are produced on demand via
325
- resampling and stored alongside the native blob in cache. Coarser-than-
326
- source values must be exact multiples of the source cadence — invalid
327
- labels return \`400\`.
328
- `,
329
- enum: [
330
- "1s",
331
- "5s",
332
- "1m",
333
- "3m",
334
- "5m",
335
- "15m",
336
- "30m",
337
- "1h",
338
- "2h",
339
- "4h",
340
- "8h",
341
- "12h",
342
- "1d",
343
- "1w",
344
- "1q",
345
- ],
346
- default: "1s",
338
+ description: `Output bar cadence for the prepared range. Coarser cadences are produced on demand by
339
+ resampling the source and stored alongside the native blob in cache. A target finer
340
+ than the source, or not an exact multiple of it, returns \`400\`. What's accepted, and
341
+ what omitting it means, depends on the source:
342
+
343
+ * Managed exchange — one of \`1s\`, \`5s\`, \`1m\`, \`3m\`, \`5m\`, \`15m\`, \`30m\`, \`1h\`, \`2h\`,
344
+ \`4h\`, \`8h\`, \`12h\`, \`1d\`, \`1w\`, \`1q\`; any other label returns \`400\`. Omitted = \`1s\`,
345
+ the publisher's native cadence.
346
+ * Dataset (\`exchangeId: user\`) — omitted = the dataset version's own discovered
347
+ \`cadence\` (see \`DatasetVersion.cadence\`), served as-is. Any cadence equal to or
348
+ coarser than it and an exact multiple of it is accepted, including ones outside the
349
+ managed-exchange list (e.g. \`15s\`); an \`rt\` dataset can be resampled to any fixed
350
+ cadence.
351
+ `,
347
352
  },
348
353
  },
349
354
  example: {
@@ -371,7 +376,8 @@ export const JobStateSchema = {
371
376
  description: `Current status of the job. Treat \`Completed | Aborted | Failed\` as
372
377
  terminal; \`New | Started\` mean keep polling. A single-instrument prepare
373
378
  is always terminal (\`Completed\`) — decide from
374
- \`PrepareJobState.coverageRatio\`, not by polling.
379
+ \`PrepareJobState.coverageRatio\` (or \`dataFrom\`/\`dataTo\` against an \`rt\` dataset,
380
+ which has no ratio), not by polling.
375
381
  `,
376
382
  enum: ["New", "Started", "Completed", "Aborted", "Failed"],
377
383
  example: "Completed",
@@ -418,8 +424,10 @@ walked hour by hour: \`totalHours\`/\`hoursWithData\`/\`hoursWithoutData\`. Agai
418
424
  dataset-backed prepare (\`exchangeId: user\`), coverage is reported on the dataset's own
419
425
  cadence grid instead — hour-walking a daily dataset would report \`1/24\` and read as
420
426
  broken — via \`cadence\`/\`gaps\`/\`largestGapSteps\`; \`totalHours\`/\`hoursWithData\`/
421
- \`hoursWithoutData\` are absent in that case. \`dataFrom\`/\`dataTo\`/\`coverageRatio\` are present
422
- either way, computed accordingly.
427
+ \`hoursWithoutData\` are absent in that case. \`dataFrom\`/\`dataTo\` are present either way, and
428
+ \`coverageRatio\` too, computed accordingly — except against a dataset whose \`cadence\` is \`rt\`:
429
+ with no fixed step there is no expected row count to measure against, so \`coverageRatio\`
430
+ is absent and \`gaps\`/\`largestGapSteps\` are \`0\`.
423
431
  `,
424
432
  allOf: [
425
433
  {
@@ -451,7 +459,8 @@ either way, computed accordingly.
451
459
  \`totalHours\` is 0), the fraction of hours in the requested range that have served
452
460
  data. Against a dataset (\`exchangeId: user\`): \`rows / expectedStepsAtCadence\`
453
461
  over the dataset version's own range — echoing what ingest computed once, not
454
- recomputed against a narrower prepare request.
462
+ recomputed against a narrower prepare request. Absent for an \`rt\` dataset (no
463
+ fixed step, so no expected row count).
455
464
  `,
456
465
  example: 0.994,
457
466
  },
@@ -471,22 +480,23 @@ a dataset-backed prepare.
471
480
  },
472
481
  cadence: {
473
482
  type: "string",
474
- description: `The dataset version's own discovered cadence (e.g. \`1m\`, \`1h\`). Only present for a
475
- dataset-backed prepare (\`exchangeId: user\`).
483
+ description: `The dataset version's own discovered cadence — a fixed grid (e.g. \`1m\`, \`1h\`) or
484
+ \`rt\` (see \`DatasetVersion.cadence\`). Only present for a dataset-backed prepare
485
+ (\`exchangeId: user\`).
476
486
  `,
477
487
  example: "1m",
478
488
  },
479
489
  gaps: {
480
490
  type: "integer",
481
491
  description: `Number of gaps in the dataset version at its own cadence, as discovered at ingest
482
- time. Only present for a dataset-backed prepare.
492
+ time. \`0\` for \`rt\`. Only present for a dataset-backed prepare.
483
493
  `,
484
494
  example: 0,
485
495
  },
486
496
  largestGapSteps: {
487
497
  type: "integer",
488
- description: `The largest gap in the dataset version, in units of its own cadence step. Only
489
- present for a dataset-backed prepare.
498
+ description: `The largest gap in the dataset version, in units of its own cadence step. \`0\` for
499
+ \`rt\`. Only present for a dataset-backed prepare.
490
500
  `,
491
501
  example: 0,
492
502
  },
@@ -639,7 +649,7 @@ export const SweepBaseConfigSchema = {
639
649
  type: "number",
640
650
  format: "double",
641
651
  exclusiveMinimum: 0,
642
- default: 10000,
652
+ default: 100,
643
653
  },
644
654
  feeRate: {
645
655
  type: "number",
@@ -969,7 +979,7 @@ export const SweepRunRowSchema = {
969
979
  minimum: 0,
970
980
  maximum: 1,
971
981
  description:
972
- "Probability that this run's Sharpe reflects real edge rather than the best draw from however many parameter vectors were tried. Above ~0.95 the result survives the multiple-testing correction; near 0.5 or below it is indistinguishable from the best of a pile of coin flips. Absent on aborted runs, and on sweeps with too few trials to establish any dispersion to deflate against.",
982
+ "Probability that this run's Sharpe reflects real edge rather than the best draw from however many parameter vectors were tried. Above ~0.95 the result survives the multiple-testing correction; near 0.5 or below it is indistinguishable from the best of a pile of coin flips. Absent on aborted runs, on sweeps with too few trials to establish any dispersion to deflate against, on runs with fewer than 3 period returns, and on a degenerate (near-constant) return series — all cases where the underlying statistic isn't meaningfully computable, rather than genuinely zero. A present value is the computed probability, however small.",
973
983
  },
974
984
  params: {
975
985
  type: "object",
@@ -1032,8 +1042,10 @@ export const SweepRunRowSchema = {
1032
1042
  $ref: "#/components/schemas/EquityCurveResult",
1033
1043
  },
1034
1044
  ],
1035
- description:
1036
- "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.",
1045
+ description: `Present whenever this trial actually has a curve — which is any completed run of a sweep that requested curves at all (\`equityCurve.mode\` \`topN\`/\`topPct\`), not only the ranked winners. Absent, not null, when there is genuinely nothing: the run aborted, it made no trades, or the sweep never retained curves (\`mode\` \`auto\`/\`none\`).
1046
+ Selection decides how the curve travels, not whether it exists. A row may carry \`url\` alone, or \`url\` together with the points inline.
1047
+ **Read \`points\`/\`equities\` to tell whether the curve is inline — never the presence of this object, and never the absence of \`url\`.** Both mislead, and in opposite directions: this object is present on rows that carry only a pointer, and \`url\` stays present alongside an inline curve so a caller can still re-request a different transform. An inline curve is emitted on the materialisation view (\`?order=natural\`), where each row is read once; the ranked view keeps the pointer, being polled.
1048
+ When only a pointer is present, \`GET\` the \`url\` for the curve. Its own \`meta\` there is the real, possibly size-guarded outcome. This outer \`meta\` may be a declaration rather than a measurement — for a row whose curve was not promoted, the point count is derived from the trade count (one equity point per closed trade) instead of being read from the store, to keep a polled response from doing per-row work.`,
1037
1049
  },
1038
1050
  },
1039
1051
  } as const;
@@ -1246,7 +1258,8 @@ First failure wins and later ones are not recorded, so on a sweep where several
1246
1258
  description: `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.
1247
1259
  \`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.
1248
1260
  \`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.
1249
- \`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.`,
1261
+ \`state.size\` is an upfront estimate — range x the prepare's target cadence, set before any data is even loaded, not a query against loaded data — on a single execute and a plain sweep alike. A plain sweep's value is the sum of every shard's own \`size\` (each shard's per-run estimate x its own vector slice), the same additive shape \`state.completed\` already used above. \`0\` means the prepare context behind the job predates this field (a job whose prepare ran before the estimate existed) — never a guessed cadence standing in for a real one.
1262
+ On a \`walkForward\` sweep, \`state.size\` is still always \`0\`: fold runs were out of scope for the estimate the same way they are for \`state.completed\` above — a fold's \`size\` is simply never set, so the sum stays honest at \`0\` rather than needing a special case.`,
1250
1263
  },
1251
1264
  },
1252
1265
  } as const;
@@ -1525,17 +1538,7 @@ reading: a run that produced no trades often did so for a reason stated here.
1525
1538
  params: {
1526
1539
  type: "object",
1527
1540
  additionalProperties: {
1528
- oneOf: [
1529
- {
1530
- type: "number",
1531
- },
1532
- {
1533
- type: "string",
1534
- },
1535
- {
1536
- type: "boolean",
1537
- },
1538
- ],
1541
+ $ref: "#/components/schemas/ScalarStrategyParamValue",
1539
1542
  },
1540
1543
  description:
1541
1544
  "The strategy properties this run was given, echoed back as sent. Absent when the request carried none, so its presence is what distinguishes a parameterised run from one at the declared defaults — a stored result cannot otherwise say which vector produced it, and this endpoint is meant to be called repeatedly over one prepare.",
@@ -1979,8 +1982,11 @@ what a dataset covers. Absent until a version exists.
1979
1982
  },
1980
1983
  type: {
1981
1984
  type: "string",
1982
- enum: ["ticker"],
1983
- description: "Always `ticker` in v1.",
1985
+ enum: ["ticker", "klines"],
1986
+ description: `\`ticker\` for an upload or a \`dex\` import with no \`cadence\` requested (native per-trade
1987
+ data). \`klines\` for a \`dex\` import that requested a candle \`cadence\` — pre-aggregated
1988
+ bars rather than raw ticks. Purely informational; both shapes are read the same way.
1989
+ `,
1984
1990
  example: "ticker",
1985
1991
  },
1986
1992
  instrument: {
@@ -2024,8 +2030,8 @@ a version exists.
2024
2030
  },
2025
2031
  cadence: {
2026
2032
  type: "string",
2027
- description: `\`currentVersionId\`'s own discovered bar cadence (e.g. \`1s\`, \`1m\`, \`1h\`). Absent until a
2028
- version exists.
2033
+ description: `\`currentVersionId\`'s own discovered cadence — a fixed grid (e.g. \`1s\`, \`1m\`, \`1h\`) or
2034
+ \`rt\` (see \`DatasetVersion.cadence\`). Absent until a version exists.
2029
2035
  `,
2030
2036
  example: "1m",
2031
2037
  },
@@ -2042,6 +2048,27 @@ export const DatasetWithLinksSchema = {
2042
2048
  {
2043
2049
  type: "object",
2044
2050
  properties: {
2051
+ dataUrl: {
2052
+ type: "string",
2053
+ description: `Presigned GET URL to the current version's stored file — see \`dataFormat\` for
2054
+ which format it's actually in. Present only once the current version's status is
2055
+ \`ready\`. Long-lived (day-scale, not permanent): a DuckDB-WASM/\`lastra-ts\`-style
2056
+ reader issues HTTP range requests against it lazily over an extended viewing
2057
+ session, not in one shot like a browser upload.
2058
+ `,
2059
+ example:
2060
+ "https://storage.qtsurfer.com/00000000-.../ds_3f9a1c2e7b0d4a5f/dsv_8e2b4f19c6a03d7e/ticker_BTC_USDT_1700000000000_1700086400000_1m.lastra?X-Amz-...",
2061
+ },
2062
+ dataFormat: {
2063
+ type: "string",
2064
+ enum: ["lastra", "parquet"],
2065
+ description: `Which format \`dataUrl\` is actually in — check this rather than assuming it
2066
+ matches how you uploaded it. \`lastra\` — our native columnar format — for a CSV (or
2067
+ gzip/zip of one) upload, always converted on ingest. \`parquet\` for a parquet
2068
+ upload, stored as-is today.
2069
+ `,
2070
+ example: "lastra",
2071
+ },
2045
2072
  _links: {
2046
2073
  type: "object",
2047
2074
  properties: {
@@ -2069,8 +2096,9 @@ export const DatasetUploadTargetSchema = {
2069
2096
  properties: {
2070
2097
  url: {
2071
2098
  type: "string",
2072
- description: `Presigned URL. \`PUT\` the raw CSV file here directly — no \`Authorization\` header,
2073
- no other API credentials.
2099
+ description: `Presigned URL. \`PUT\` the file here directly — the CSV or parquet itself, or a \`.gz\`/\`.zip\` of it
2100
+ (see \`createDataset\`'s own description) — no \`Authorization\` header, no other API
2101
+ credentials.
2074
2102
  `,
2075
2103
  example:
2076
2104
  "https://storage.qtsurfer.com/uploads/00000000-.../up_1a2b3c4d5e6f7a8b/raw.csv?X-Amz-...",
@@ -2161,7 +2189,8 @@ not declared by the caller.
2161
2189
  },
2162
2190
  bytes: {
2163
2191
  type: "integer",
2164
- description: "Size of the uploaded file.",
2192
+ description:
2193
+ "Size of the stored file `dataUrl` points at — a converted `lastra` for a CSV/gzip/zip upload, or the parquet file itself, unconverted, for a parquet upload. Not the size of the bytes originally PUT to storage; see `dataFormat`.",
2165
2194
  example: 4831022,
2166
2195
  },
2167
2196
  rows: {
@@ -2171,7 +2200,13 @@ not declared by the caller.
2171
2200
  },
2172
2201
  cadence: {
2173
2202
  type: "string",
2174
- description: "The discovered bar cadence (e.g. `1s`, `1m`, `1h`).",
2203
+ description: `The cadence discovered from the data's own timestamps. Either a fixed grid — \`1s\`,
2204
+ \`5s\`, \`15s\`, \`1m\`, \`5m\`, \`15m\`, \`30m\`, \`1h\`, \`4h\`, \`1d\` — when at least half the
2205
+ intervals between consecutive rows fall on that step (small clock jitter tolerated), or
2206
+ \`rt\`: native data at the rate it was captured, each row at its own timestamp with no
2207
+ fixed step — per-trade on-chain swaps, block-spaced or sub-second ticks, irregular
2208
+ intervals. An \`rt\` dataset can be resampled to any fixed cadence at prepare time.
2209
+ `,
2175
2210
  example: "1s",
2176
2211
  },
2177
2212
  timestampUnit: {
@@ -2184,20 +2219,42 @@ numeric values fell in (seconds, millis, or micros).
2184
2219
  },
2185
2220
  gaps: {
2186
2221
  type: "integer",
2187
- description: "Number of gaps at the discovered cadence.",
2222
+ description:
2223
+ "Number of gaps at the discovered cadence. Always `0` for `rt`.",
2188
2224
  example: 0,
2189
2225
  },
2190
2226
  largestGapSteps: {
2191
2227
  type: "integer",
2192
- description: "The largest gap, in units of the discovered cadence step.",
2228
+ description:
2229
+ "The largest gap, in units of the discovered cadence step. Always `0` for `rt`.",
2193
2230
  example: 0,
2194
2231
  },
2232
+ dataUrl: {
2233
+ type: "string",
2234
+ description: `Presigned GET URL to the stored file — see \`dataFormat\` for which format it's in.
2235
+ Present once the version is \`ready\`. Long-lived (day-scale, not permanent): a
2236
+ DuckDB-WASM/\`lastra-ts\`-style reader issues HTTP range requests against it lazily over
2237
+ an extended viewing session, not in one shot like a browser upload.
2238
+ `,
2239
+ example:
2240
+ "https://storage.qtsurfer.com/00000000-.../ds_3f9a1c2e7b0d4a5f/dsv_8e2b4f19c6a03d7e/ticker_BTC_USDT_1700000000000_1700086400000_1m.lastra?X-Amz-...",
2241
+ },
2242
+ dataFormat: {
2243
+ type: "string",
2244
+ enum: ["lastra", "parquet"],
2245
+ description: `Which format \`dataUrl\` is actually in — check this rather than assuming it matches
2246
+ how you uploaded it. \`lastra\` — our native columnar format — for a CSV (or gzip/zip
2247
+ of one) upload, always converted on ingest. \`parquet\` for a parquet upload, stored
2248
+ as-is today.
2249
+ `,
2250
+ example: "lastra",
2251
+ },
2195
2252
  },
2196
2253
  } as const;
2197
2254
 
2198
2255
  export const DatasetUploadStateSchema = {
2199
2256
  type: "object",
2200
- description: `Progress of one upload, from staged through ingest. Postgres-backed once a version exists,
2257
+ description: `Progress of one upload, from staged through ingest. Durably recorded once a version exists,
2201
2258
  so \`ready\`/\`failed\` are permanent answers; \`uploading\`/\`ingesting\` reflect in-flight state
2202
2259
  that can itself age out — see the \`404\` case on \`GET .../uploads/{uploadId}\`.
2203
2260
  `,
@@ -2214,7 +2271,8 @@ that can itself age out — see the \`404\` case on \`GET .../uploads/{uploadId}
2214
2271
  called yet.
2215
2272
  * \`ingesting\` — \`finalize\` was called; the worker is parsing and validating the file.
2216
2273
  * \`ready\` — ingested successfully. \`version\` carries the result.
2217
- * \`failed\` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units).
2274
+ * \`failed\` — ingest rejected the file (e.g. bad CSV contract, mixed timestamp units, a
2275
+ \`.zip\` with no file inside or more than one).
2218
2276
  `,
2219
2277
  example: "ready",
2220
2278
  },
@@ -2222,6 +2280,209 @@ that can itself age out — see the \`404\` case on \`GET .../uploads/{uploadId}
2222
2280
  type: "string",
2223
2281
  description: "The ingest job id, while `status` is `ingesting`.",
2224
2282
  },
2283
+ error: {
2284
+ type: "string",
2285
+ description: `A human-readable reason, present when \`status\` is \`failed\` (e.g. bad CSV contract,
2286
+ mixed timestamp units, a \`.zip\` with no file inside or more than one). Durably
2287
+ recorded alongside the failure itself, so it stays available however long after the
2288
+ fact you poll — not tied to how recently the failure happened.
2289
+ `,
2290
+ example: "line 3: column 'close' is not a number: not-a-number",
2291
+ },
2292
+ version: {
2293
+ allOf: [
2294
+ {
2295
+ $ref: "#/components/schemas/DatasetVersion",
2296
+ },
2297
+ ],
2298
+ description: "Present when `status` is `ready` or `failed`.",
2299
+ },
2300
+ },
2301
+ } as const;
2302
+
2303
+ export const DatasetImportRequestSchema = {
2304
+ type: "object",
2305
+ description: `\`POST /datasets/imports\`'s request body. A common block plus one type-specific block,
2306
+ selected by \`type\` — \`dex\` is the only value today.
2307
+ `,
2308
+ required: ["name", "instrument", "from", "to", "type"],
2309
+ properties: {
2310
+ name: {
2311
+ type: "string",
2312
+ description: "A name unique among your datasets. `409` if already taken.",
2313
+ example: "weth-usdc-week",
2314
+ },
2315
+ instrument: {
2316
+ $ref: "#/components/schemas/Instrument",
2317
+ },
2318
+ from: {
2319
+ type: "string",
2320
+ format: "date-time",
2321
+ description:
2322
+ "Start of the range to fetch, inclusive. Must be before `to`.",
2323
+ example: "2026-08-01T00:00:00Z",
2324
+ },
2325
+ to: {
2326
+ type: "string",
2327
+ format: "date-time",
2328
+ description: `End of the range to fetch, exclusive. The total span is capped by your tier — a
2329
+ request wider than that ceiling is \`400\`, regardless of source type.
2330
+ `,
2331
+ example: "2026-08-08T00:00:00Z",
2332
+ },
2333
+ cadence: {
2334
+ type: "string",
2335
+ enum: ["1s", "1m", "5m"],
2336
+ description: `Optional. Omitted/blank keeps native per-trade event cadence — each swap at its own
2337
+ timestamp, so the resulting version's \`cadence\` is \`rt\` unless the swaps happen to sit
2338
+ on a fixed grid (see \`DatasetVersion.cadence\`). Set to \`1s\`, \`1m\` or
2339
+ \`5m\` instead to get pre-aggregated candles at that width rather than raw trades (the
2340
+ resulting dataset's \`type\` becomes \`klines\`); any other value is \`400\`. Not every
2341
+ network supports every cadence — an unsupported combination fails asynchronously, not
2342
+ at request time (see \`DatasetImportState.error\`).
2343
+ `,
2344
+ },
2345
+ type: {
2346
+ type: "string",
2347
+ enum: ["dex"],
2348
+ description: "The source to fetch from. `dex` is the only value today.",
2349
+ example: "dex",
2350
+ },
2351
+ dex: {
2352
+ $ref: "#/components/schemas/DatasetImportDexRequest",
2353
+ },
2354
+ },
2355
+ } as const;
2356
+
2357
+ export const DatasetImportDexRequestSchema = {
2358
+ type: "object",
2359
+ description: `The \`dex\` source's own fields — required when \`type\` is \`dex\`. \`id\`/\`version\` are required
2360
+ for a plain (native-cadence) import; both are ignored if the top-level \`cadence\` requested
2361
+ pre-aggregated candles instead, since that path needs neither a protocol nor a version
2362
+ distinction.
2363
+ `,
2364
+ required: ["network", "contract"],
2365
+ properties: {
2366
+ network: {
2367
+ type: "string",
2368
+ enum: ["ethereum", "robinhood"],
2369
+ description: "Which chain the pool/pair lives on.",
2370
+ example: "ethereum",
2371
+ },
2372
+ id: {
2373
+ type: "string",
2374
+ enum: ["uniswap"],
2375
+ description: `Which on-chain DEX protocol \`contract\` implements. Required unless the top-level
2376
+ \`cadence\` requested pre-aggregated candles, in which case it's ignored.
2377
+ `,
2378
+ example: "uniswap",
2379
+ },
2380
+ version: {
2381
+ type: "string",
2382
+ enum: ["v2", "v3"],
2383
+ description: `Uniswap version the pool/pair contract implements. Required unless the top-level
2384
+ \`cadence\` requested pre-aggregated candles, in which case it's ignored.
2385
+ `,
2386
+ example: "v3",
2387
+ },
2388
+ contract: {
2389
+ type: "string",
2390
+ description: "The pool (v3) or pair (v2) contract address.",
2391
+ example: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
2392
+ },
2393
+ factory: {
2394
+ type: "string",
2395
+ description: `The factory that deployed \`contract\`. Optional — when omitted, it is discovered
2396
+ on-chain from \`contract\` itself at fetch time. Supply it explicitly only if you
2397
+ already know it, or the pool/pair belongs to a factory other than the canonical one
2398
+ for \`network\`/\`version\`. Either way, the pool/pair is validated against whichever
2399
+ factory is used before anything is fetched — a wrong or unrelated factory fails the
2400
+ import rather than silently fetching from the wrong pool. Ignored if the top-level
2401
+ \`cadence\` requested pre-aggregated candles.
2402
+ `,
2403
+ },
2404
+ },
2405
+ example: {
2406
+ network: "ethereum",
2407
+ id: "uniswap",
2408
+ version: "v3",
2409
+ contract: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640",
2410
+ },
2411
+ } as const;
2412
+
2413
+ export const DatasetImportCreatedSchema = {
2414
+ type: "object",
2415
+ description:
2416
+ "The response to `POST /datasets/imports` — the dataset now exists, and its fetch has started.",
2417
+ required: ["datasetId", "importId", "jobId", "status"],
2418
+ properties: {
2419
+ datasetId: {
2420
+ type: "string",
2421
+ description:
2422
+ "Opaque id of the newly created dataset — same id space as `POST /datasets`.",
2423
+ example: "ds_3f9a1c2e7b0d4a5f",
2424
+ },
2425
+ importId: {
2426
+ type: "string",
2427
+ description: `Identifies this import. Pass to \`GET /datasets/{datasetId}/imports/{importId}\` to poll
2428
+ it — there is no separate "finalize" step the way an upload has.
2429
+ `,
2430
+ example: "imp_01j9z1x2y3z4a5b6c7d8e9f0g1",
2431
+ },
2432
+ jobId: {
2433
+ type: "string",
2434
+ description: "The fetch/ingest job id.",
2435
+ example:
2436
+ "dataset-import:00000000-.../ds_3f9a1c2e7b0d4a5f:imp_01j9z1x2y3z4a5b6c7d8e9f0g1",
2437
+ },
2438
+ status: {
2439
+ type: "string",
2440
+ enum: ["fetching"],
2441
+ description:
2442
+ "Always `fetching` in this response — the fetch has only just started.",
2443
+ example: "fetching",
2444
+ },
2445
+ },
2446
+ } as const;
2447
+
2448
+ export const DatasetImportStateSchema = {
2449
+ type: "object",
2450
+ description: `Progress of one import, from fetching through ingest. \`fetching\` is the one status only an
2451
+ import ever reports — an upload's file already exists by the time you can poll it; an
2452
+ import's doesn't, until this source finishes fetching it.
2453
+ `,
2454
+ required: ["importId", "status"],
2455
+ properties: {
2456
+ importId: {
2457
+ type: "string",
2458
+ example: "imp_01j9z1x2y3z4a5b6c7d8e9f0g1",
2459
+ },
2460
+ status: {
2461
+ type: "string",
2462
+ enum: ["fetching", "ingesting", "ready", "failed"],
2463
+ description: `* \`fetching\` — reading from the source; nothing staged yet.
2464
+ * \`ingesting\` — fetch complete, staged, and re-entered the same ingest chain an
2465
+ upload uses; the worker is parsing and validating it.
2466
+ * \`ready\` — ingested successfully. \`version\` carries the result.
2467
+ * \`failed\` — the fetch or the ingest that followed it was rejected. \`error\` names why.
2468
+ `,
2469
+ example: "ready",
2470
+ },
2471
+ jobId: {
2472
+ type: "string",
2473
+ description:
2474
+ "The fetch/ingest job id, while `status` is `fetching` or `ingesting`.",
2475
+ },
2476
+ error: {
2477
+ type: "string",
2478
+ description: `A human-readable reason, present when \`status\` is \`failed\` — an unresolvable
2479
+ pool/pair, no data in the requested range, a range older than the configured source
2480
+ retains, the fetch exceeding your tier's time ceiling, or any of
2481
+ \`DatasetUploadState.error\`'s own ingest-side reasons once fetching hands off to it.
2482
+ Durably recorded, same as on the upload path.
2483
+ `,
2484
+ example: "Import exceeded the tier's 12 minute ceiling",
2485
+ },
2225
2486
  version: {
2226
2487
  allOf: [
2227
2488
  {