@qtsurfer/api-client 0.3.0 → 0.5.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.
@@ -237,6 +237,51 @@ export const DataSourceTypeSchema = {
237
237
  example: 'ticker'
238
238
  } as const;
239
239
 
240
+ export const PrepareRequestSchema = {
241
+ type: 'object',
242
+ required: ['instrument', 'from', 'to'],
243
+ properties: {
244
+ instrument: {
245
+ '$ref': '#/components/schemas/Instrument'
246
+ },
247
+ from: {
248
+ type: 'string',
249
+ description: `Start date for the preparation process. Supports the following formats:
250
+ - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
251
+ - ISO DATE (e.g. 2024-12-14)
252
+ - BASIC ISO DATE (e.g., 20241214)
253
+ `,
254
+ example: '2024-12-13T00:00:00Z'
255
+ },
256
+ to: {
257
+ type: 'string',
258
+ description: `End date for the preparation process. Supports the following formats:
259
+ - ISO-8601 (e.g. 2024-12-14T23:59:59Z)
260
+ - ISO DATE (e.g. 2024-12-14)
261
+ - BASIC ISO DATE (e.g., 20241214)
262
+ `,
263
+ example: '2024-12-14'
264
+ },
265
+ cadence: {
266
+ type: 'string',
267
+ description: `Output bar cadence for the prepared range. Defaults to the publisher's
268
+ native cadence (\`1s\`); coarser cadences are produced on demand via
269
+ resampling and stored alongside the native blob in cache. Coarser-than-
270
+ source values must be exact multiples of the source cadence — invalid
271
+ labels return \`400\`.
272
+ `,
273
+ enum: ['1s', '5s', '1m', '5m', '15m', '1h', '4h', '1d'],
274
+ default: '1s'
275
+ }
276
+ },
277
+ example: {
278
+ instrument: 'BTC/USDT',
279
+ from: '2024-12-13T00:00:00Z',
280
+ to: '2024-12-14T00:00:00Z',
281
+ cadence: '1m'
282
+ }
283
+ } as const;
284
+
240
285
  export const JobStateSchema = {
241
286
  type: 'object',
242
287
  description: 'Information about a single job',
@@ -249,12 +294,12 @@ export const JobStateSchema = {
249
294
  },
250
295
  status: {
251
296
  type: 'string',
252
- description: `Current status of the job. \`Partial\` (prepare only) means some
253
- hours are still being produced asynchronously — keep polling.
254
- Treat \`Completed | Aborted | Failed\` as terminal; anything else
255
- means keep polling.
297
+ description: `Current status of the job. Treat \`Completed | Aborted | Failed\` as
298
+ terminal; \`New | Started\` mean keep polling. A single-instrument prepare
299
+ is always terminal (\`Completed\`) decide from
300
+ \`PrepareJobState.coverageRatio\`, not by polling.
256
301
  `,
257
- enum: ['New', 'Started', 'Partial', 'Completed', 'Aborted', 'Failed'],
302
+ enum: ['New', 'Started', 'Completed', 'Aborted', 'Failed'],
258
303
  example: 'Completed'
259
304
  },
260
305
  statusDetail: {
@@ -287,6 +332,423 @@ means keep polling.
287
332
  }
288
333
  } as const;
289
334
 
335
+ export const PrepareJobStateSchema = {
336
+ description: `State of a single-instrument prepare job — the \`JobState\` shape plus a per-hour
337
+ data-coverage summary. A single-instrument prepare is always terminal
338
+ (\`status: Completed\`): the client decides what to do from \`coverageRatio\` (e.g.
339
+ execute if it is at or above a chosen threshold) rather than polling for missing
340
+ hours that may never arrive — a missing hour for one instrument usually means low
341
+ activity, not missing data.
342
+ `,
343
+ allOf: [
344
+ {
345
+ '$ref': '#/components/schemas/JobState'
346
+ },
347
+ {
348
+ type: 'object',
349
+ properties: {
350
+ dataFrom: {
351
+ type: ['string', 'null'],
352
+ format: 'date-time',
353
+ description: 'Start of the available data range for the prepared instrument.',
354
+ example: '2026-04-14T13:00:00Z'
355
+ },
356
+ dataTo: {
357
+ type: ['string', 'null'],
358
+ format: 'date-time',
359
+ description: 'End of the available data range for the prepared instrument.',
360
+ example: '2026-04-14T15:30:05Z'
361
+ },
362
+ coverageRatio: {
363
+ type: 'number',
364
+ format: 'double',
365
+ minimum: 0,
366
+ maximum: 1,
367
+ description: `\`hoursWithData / totalHours\` in \`[0,1]\` (\`1.0\` when \`totalHours\` is 0) — the
368
+ fraction of hours in the requested range that have served data.
369
+ `,
370
+ example: 0.994
371
+ },
372
+ totalHours: {
373
+ type: 'integer',
374
+ description: 'Number of whole hours in the requested prepare range.',
375
+ example: 168
376
+ },
377
+ hoursWithData: {
378
+ type: 'integer',
379
+ description: 'Number of hours in the range that have data.',
380
+ example: 167
381
+ },
382
+ hoursWithoutData: {
383
+ type: 'array',
384
+ description: 'One entry per hour in the range that has no data, with a rationale.',
385
+ items: {
386
+ type: 'object',
387
+ properties: {
388
+ hour: {
389
+ type: 'string',
390
+ format: 'date-time',
391
+ description: 'The hour (UTC, hour-aligned) that has no data.',
392
+ example: '2026-04-14T02:00:00Z'
393
+ },
394
+ expected: {
395
+ type: 'integer',
396
+ description: `Expected row count for the hour (currently always 0; reserved for
397
+ future use). The rationale never depends on it.
398
+ `,
399
+ example: 0
400
+ },
401
+ rationale: {
402
+ type: 'string',
403
+ description: `Why the hour has no data. \`pending_conversion\`: data for this hour is
404
+ still being produced — a re-poll may fill it. \`low_activity\`: the
405
+ instrument did not trade that hour. \`unknown\`: no data to classify by.
406
+ `,
407
+ enum: ['pending_conversion', 'low_activity', 'unknown'],
408
+ example: 'low_activity'
409
+ }
410
+ }
411
+ }
412
+ }
413
+ }
414
+ }
415
+ ]
416
+ } as const;
417
+
418
+ export const SweepAxisSchema = {
419
+ description: 'A numeric range or an explicit list of values for one strategy property.',
420
+ oneOf: [
421
+ {
422
+ type: 'object',
423
+ required: ['from', 'to', 'step'],
424
+ additionalProperties: false,
425
+ properties: {
426
+ from: {
427
+ type: 'number',
428
+ format: 'double'
429
+ },
430
+ to: {
431
+ type: 'number',
432
+ format: 'double'
433
+ },
434
+ step: {
435
+ type: 'number',
436
+ format: 'double',
437
+ exclusiveMinimum: 0
438
+ }
439
+ }
440
+ },
441
+ {
442
+ type: 'object',
443
+ required: ['values'],
444
+ additionalProperties: false,
445
+ properties: {
446
+ values: {
447
+ type: 'array',
448
+ minItems: 1,
449
+ items: {
450
+ oneOf: [
451
+ {
452
+ type: 'number'
453
+ },
454
+ {
455
+ type: 'boolean'
456
+ }
457
+ ]
458
+ }
459
+ }
460
+ }
461
+ }
462
+ ]
463
+ } as const;
464
+
465
+ export const SweepSpecRequestSchema = {
466
+ type: 'object',
467
+ required: ['params'],
468
+ properties: {
469
+ sampler: {
470
+ type: 'string',
471
+ enum: ['grid', 'random', 'lhs'],
472
+ default: 'grid'
473
+ },
474
+ seed: {
475
+ type: 'integer',
476
+ format: 'int64',
477
+ minimum: -9007199254740991,
478
+ maximum: 9007199254740991,
479
+ description: `Reproducibility seed. If omitted, the server generates one with Java's
480
+ \`L64X128MixRandom\` generator and returns the effective value. The range
481
+ is limited to JavaScript-safe integers so generated clients can replay it exactly.
482
+ `
483
+ },
484
+ samples: {
485
+ type: 'integer',
486
+ minimum: 1,
487
+ description: 'Number of samples for `random` and `lhs`; ignored by `grid`.'
488
+ },
489
+ objective: {
490
+ type: 'string',
491
+ enum: ['sharpe', 'sortino', 'pnl', 'maxdd'],
492
+ default: 'sharpe'
493
+ },
494
+ params: {
495
+ type: 'object',
496
+ minProperties: 1,
497
+ additionalProperties: {
498
+ '$ref': '#/components/schemas/SweepAxis'
499
+ }
500
+ }
501
+ },
502
+ example: {
503
+ sampler: 'lhs',
504
+ seed: 487221,
505
+ samples: 100,
506
+ objective: 'sharpe',
507
+ params: {
508
+ rsiPeriod: {
509
+ from: 7,
510
+ to: 28,
511
+ step: 1
512
+ },
513
+ useTrendFilter: {
514
+ values: [true, false]
515
+ }
516
+ }
517
+ }
518
+ } as const;
519
+
520
+ export const SweepBaseConfigSchema = {
521
+ type: 'object',
522
+ properties: {
523
+ initialFunding: {
524
+ type: 'number',
525
+ format: 'double',
526
+ exclusiveMinimum: 0,
527
+ default: 10000
528
+ },
529
+ feeRate: {
530
+ type: 'number',
531
+ format: 'double',
532
+ minimum: 0,
533
+ default: 0.001
534
+ },
535
+ buyFeeRate: {
536
+ type: 'number',
537
+ format: 'double',
538
+ minimum: 0
539
+ },
540
+ sellFeeRate: {
541
+ type: 'number',
542
+ format: 'double',
543
+ minimum: 0
544
+ },
545
+ feeLeg: {
546
+ type: 'string',
547
+ enum: ['RECEIVED', 'QUOTE', 'BASE'],
548
+ default: 'RECEIVED'
549
+ },
550
+ percentAmountToLock: {
551
+ type: 'number',
552
+ format: 'double',
553
+ exclusiveMinimum: 0,
554
+ maximum: 100
555
+ }
556
+ }
557
+ } as const;
558
+
559
+ export const ExecuteSweepRequestSchema = {
560
+ type: 'object',
561
+ required: ['strategyId', 'sweep'],
562
+ properties: {
563
+ strategyId: {
564
+ '$ref': '#/components/schemas/strategyId'
565
+ },
566
+ sweep: {
567
+ '$ref': '#/components/schemas/SweepSpecRequest'
568
+ },
569
+ baseConfig: {
570
+ '$ref': '#/components/schemas/SweepBaseConfig'
571
+ },
572
+ storeSignals: {
573
+ type: 'boolean',
574
+ default: false,
575
+ description: 'Store signals for every trial. Keep false for normal sweeps.'
576
+ },
577
+ shards: {
578
+ type: 'integer',
579
+ minimum: 0,
580
+ description: 'Requested horizontal shard count; 0 or omitted selects automatically.',
581
+ default: 0
582
+ },
583
+ minTradeFloor: {
584
+ type: 'integer',
585
+ minimum: 0,
586
+ default: 30,
587
+ description: 'Trials below this trade count are flagged but remain in the results.'
588
+ }
589
+ }
590
+ } as const;
591
+
592
+ export const ExecuteSweepAcceptedSchema = {
593
+ type: 'object',
594
+ required: ['sweepId', 'requestId', 'totalRuns', 'shards', 'seed', 'queued'],
595
+ properties: {
596
+ sweepId: {
597
+ type: 'string',
598
+ example: 'swp_95e47a7f0966ce11'
599
+ },
600
+ requestId: {
601
+ type: 'string'
602
+ },
603
+ totalRuns: {
604
+ type: 'integer',
605
+ minimum: 1
606
+ },
607
+ shards: {
608
+ type: 'integer',
609
+ minimum: 1
610
+ },
611
+ seed: {
612
+ type: 'integer',
613
+ format: 'int64',
614
+ minimum: -9007199254740991,
615
+ maximum: 9007199254740991,
616
+ description: 'Effective seed used to expand the sweep.'
617
+ },
618
+ queued: {
619
+ type: 'boolean',
620
+ description: 'False when an identical sweep already exists and was not enqueued again.'
621
+ }
622
+ }
623
+ } as const;
624
+
625
+ export const SweepProgressSchema = {
626
+ type: 'object',
627
+ required: ['done', 'total', 'aborted', 'shardCount', 'pendingShards'],
628
+ properties: {
629
+ done: {
630
+ type: 'integer',
631
+ format: 'int64'
632
+ },
633
+ total: {
634
+ type: 'integer'
635
+ },
636
+ aborted: {
637
+ type: 'integer',
638
+ format: 'int64'
639
+ },
640
+ shardCount: {
641
+ type: 'integer'
642
+ },
643
+ pendingShards: {
644
+ type: 'integer'
645
+ }
646
+ }
647
+ } as const;
648
+
649
+ export const SweepRunRowSchema = {
650
+ type: 'object',
651
+ required: ['runIx', 'params', 'sharpe', 'sortino', 'pnl', 'pnlPct', 'cagr', 'maxDdPct', 'trades', 'winRate', 'belowTradeFloor', 'aborted', 'runtimeMs'],
652
+ properties: {
653
+ runIx: {
654
+ type: 'integer',
655
+ minimum: 0,
656
+ description: 'Deterministic zero-based expansion index, stable across shards and ranking.'
657
+ },
658
+ rank: {
659
+ type: 'integer',
660
+ minimum: 1,
661
+ description: 'Present only in the `ranked` view.'
662
+ },
663
+ params: {
664
+ type: 'object',
665
+ additionalProperties: true
666
+ },
667
+ sharpe: {
668
+ type: 'number',
669
+ format: 'double'
670
+ },
671
+ sortino: {
672
+ type: 'number',
673
+ format: 'double'
674
+ },
675
+ pnl: {
676
+ type: 'number',
677
+ format: 'double',
678
+ description: 'Absolute net PnL in the output currency.'
679
+ },
680
+ pnlPct: {
681
+ type: 'number',
682
+ format: 'double'
683
+ },
684
+ cagr: {
685
+ type: 'number',
686
+ format: 'double'
687
+ },
688
+ maxDdPct: {
689
+ type: 'number',
690
+ format: 'double'
691
+ },
692
+ trades: {
693
+ type: 'integer',
694
+ format: 'int64'
695
+ },
696
+ winRate: {
697
+ type: 'number',
698
+ format: 'double'
699
+ },
700
+ belowTradeFloor: {
701
+ type: 'boolean'
702
+ },
703
+ aborted: {
704
+ type: 'boolean'
705
+ },
706
+ runtimeMs: {
707
+ type: 'integer',
708
+ format: 'int64'
709
+ }
710
+ }
711
+ } as const;
712
+
713
+ export const ExecuteSweepResultSchema = {
714
+ type: 'object',
715
+ required: ['sweepId', 'status', 'objective', 'order', 'progress', 'leaderboardSize', 'truncated', 'leaderboard'],
716
+ properties: {
717
+ sweepId: {
718
+ type: 'string'
719
+ },
720
+ status: {
721
+ type: 'string',
722
+ enum: ['RUNNING', 'COMPLETED', 'PARTIAL', 'CANCELLED']
723
+ },
724
+ objective: {
725
+ type: 'string',
726
+ enum: ['sharpe', 'sortino', 'pnl', 'maxdd']
727
+ },
728
+ order: {
729
+ type: 'string',
730
+ enum: ['ranked', 'natural']
731
+ },
732
+ progress: {
733
+ '$ref': '#/components/schemas/SweepProgress'
734
+ },
735
+ leaderboardSize: {
736
+ type: 'integer',
737
+ description: 'Total result rows currently available.'
738
+ },
739
+ truncated: {
740
+ type: 'boolean',
741
+ description: 'True only when the ranked view exceeds its display limit.'
742
+ },
743
+ leaderboard: {
744
+ type: 'array',
745
+ items: {
746
+ '$ref': '#/components/schemas/SweepRunRow'
747
+ }
748
+ }
749
+ }
750
+ } as const;
751
+
290
752
  export const AcceptedJobSchema = {
291
753
  type: 'object',
292
754
  description: `Response returned by async endpoints (\`202 Accepted\`). The \`jobId\` is deterministic for the