@qtsurfer/api-client 0.5.0 → 0.7.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.
package/dist/index.d.ts CHANGED
@@ -409,7 +409,7 @@ type BacktestJobResult = {
409
409
  state: JobState;
410
410
  };
411
411
  /**
412
- * Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below.
412
+ * Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below. `notices` carries what the run had to say about itself, and is absent when it had nothing.
413
413
  */
414
414
  type ResultMap = {
415
415
  /**
@@ -421,13 +421,36 @@ type ResultMap = {
421
421
  */
422
422
  iops?: number;
423
423
  /**
424
- * Identifier of the compiled strategy that produced this result
424
+ * **Not the `strategyId` you compiled with** this is the execution context id,
425
+ * `strategy:<user>:<strategyId>`. The compiled strategy's id is the last `:`-separated
426
+ * segment; that, not this whole string, is what `GET /strategy/{strategyId}` takes.
427
+ *
428
+ * Take the segment after the last `:` rather than counting from the front: the shape has
429
+ * changed once already and callers that indexed a fixed position broke on it.
430
+ *
425
431
  */
426
432
  strategyId: string;
427
433
  /**
428
434
  * The instrument (currency pair) that was backtested
429
435
  */
430
436
  instrument: string;
437
+ /**
438
+ * Diagnostics the engine raised over this run, each with `provenance: execute`.
439
+ *
440
+ * **Absent means nothing was raised.** This is the one surface where silence is a real
441
+ * answer: the run happened, over your data, start to finish, and the engine found nothing
442
+ * worth saying. That is not true of the compile path, where an empty list only means a
443
+ * short synthetic series reached nothing — see `GET /strategy/{strategyId}`.
444
+ *
445
+ * Notices are raised on failed and aborted runs too, and those are the ones most worth
446
+ * reading: a run that produced no trades often did so for a reason stated here.
447
+ *
448
+ */
449
+ notices?: Array<Notice>;
450
+ /**
451
+ * How many notices were dropped past the cap of 50. Absent when none were. A large value usually means one fault repeating per instrument or per parameter vector rather than 50 distinct problems.
452
+ */
453
+ noticesTruncated?: number;
431
454
  /**
432
455
  * Total profit and loss in the output currency
433
456
  */
@@ -507,9 +530,110 @@ type EquityPoint = {
507
530
  equity: number;
508
531
  };
509
532
  /**
510
- * Unique identifier for a compiled strategy
533
+ * Unique identifier for a compiled strategy, derived from the source itself: the same code
534
+ * always yields the same id, for every caller, whatever its formatting. See
535
+ * `POST /strategy` for exactly which rewrites preserve it and which do not.
536
+ *
511
537
  */
512
538
  type StrategyId = string;
539
+ /**
540
+ * A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
541
+ * knowing about how the strategy is wired, not necessarily an error.
542
+ *
543
+ */
544
+ type Notice = {
545
+ /**
546
+ * Severity as the engine classified it.
547
+ */
548
+ level: string;
549
+ /**
550
+ * Stable identifier for the kind of finding; safe to match on.
551
+ */
552
+ code: string;
553
+ /**
554
+ * Human-readable explanation.
555
+ */
556
+ message: string;
557
+ /**
558
+ * Where it came from, which matters because the two silences differ: an empty list from a
559
+ * real run (`execute`) is a clean bill of health, while an empty list from
560
+ * `compile-dry-run` is only a lower bound over a bounded synthetic series.
561
+ *
562
+ */
563
+ provenance?: 'execute' | 'compile-dry-run';
564
+ };
565
+ /**
566
+ * What is known about a registered strategy: that it compiled, and what validating it found.
567
+ *
568
+ * **`validation: passed` does not mean the strategy is correct.** It means the class loaded and
569
+ * survived the first event of a short synthetic run — a floor, not a guarantee. When
570
+ * `dryRunIncomplete` is true it is a lower floor still, because the run did not finish.
571
+ *
572
+ */
573
+ type StrategyState = {
574
+ strategyId: StrategyId;
575
+ /**
576
+ * * `not_validated` — registered, never checked. `POST /strategy/{strategyId}/validate`
577
+ * checks it.
578
+ * * `pending` — a check was asked for and has not answered yet.
579
+ * * `passed` — the class loaded and survived its first event.
580
+ * * `failed` — it did not; `detail` says how.
581
+ *
582
+ */
583
+ validation: 'not_validated' | 'pending' | 'passed' | 'failed';
584
+ /**
585
+ * When the live compilation was produced.
586
+ */
587
+ compiledAt?: string;
588
+ /**
589
+ * The market data a strategy needs, read off the compiled class rather than off anything
590
+ * you sent — `TickerStrategy`, `KlineStrategy` and `FundingRateStrategy` each declare one,
591
+ * and a `MultiSourceStrategy` declares a set.
592
+ *
593
+ * **Absent is not "needs nothing".** A strategy always needs market data, so an absent
594
+ * field never means an empty requirement — it means the platform could not establish the
595
+ * answer without constructing your strategy, which it will not do to fill in a field.
596
+ * That happens for a `MultiSourceStrategy`, for a class that overrides
597
+ * `getMarketDataSource()`, and for anything registered before this field existed;
598
+ * re-registering the source fills it in.
599
+ *
600
+ */
601
+ requiredSources?: Array<'Ticker' | 'KLine' | 'FundingRate'>;
602
+ /**
603
+ * When the verdict was recorded. Absent until there is one.
604
+ */
605
+ validatedAt?: string;
606
+ /**
607
+ * Why validation failed, or why a queued check has not reported. Present on `failed`, and
608
+ * alongside `validationStalled`.
609
+ *
610
+ */
611
+ detail?: string;
612
+ /**
613
+ * What the run surfaced. An empty or absent list is not a clean bill of health when
614
+ * `dryRunIncomplete` is true — see that field.
615
+ *
616
+ */
617
+ notices?: Array<Notice>;
618
+ /**
619
+ * How many notices were dropped past the cap. Absent when none were.
620
+ */
621
+ noticesTruncated?: number;
622
+ /**
623
+ * The check did not finish its budget — it ran out of time, was refused because the
624
+ * platform was already holding too many unfinishable runs, or hit a failure attributable to
625
+ * the synthetic instrument rather than to your strategy. The verdict stands as far as it
626
+ * went; it simply reached less than a full run would.
627
+ *
628
+ */
629
+ dryRunIncomplete?: boolean;
630
+ /**
631
+ * A queued check has not reported for far longer than one takes. Nothing is disproved about
632
+ * the strategy — the check has not run. Stop waiting and re-request it later.
633
+ *
634
+ */
635
+ validationStalled?: boolean;
636
+ };
513
637
  type AuthTokenResponse = {
514
638
  /**
515
639
  * Short-lived HS256 JWT. Send as `Authorization: Bearer <token>` on all other endpoints.
@@ -757,70 +881,89 @@ type CompileStrategyData = {
757
881
  * Raw strategy Java source code
758
882
  */
759
883
  body: string;
760
- headers?: {
761
- /**
762
- * When `true`, compile asynchronously and return `202` with a `jobId`.
763
- */
764
- 'X-Compile-Async'?: boolean;
765
- };
766
884
  path?: never;
767
885
  query?: never;
768
886
  url: '/strategy';
769
887
  };
770
888
  type CompileStrategyErrors = {
771
889
  /**
772
- * Invalid strategy (compilation error)
890
+ * The source is not valid Java; the message carries the compiler diagnostics. Nothing is
891
+ * registered, so there is no id to look up afterwards.
892
+ *
773
893
  */
774
894
  400: ResponseError;
895
+ /**
896
+ * Too many compilations in flight. Retry later.
897
+ */
898
+ 429: ResponseError;
775
899
  };
776
900
  type CompileStrategyError = CompileStrategyErrors[keyof CompileStrategyErrors];
777
901
  type CompileStrategyResponses = {
778
902
  /**
779
- * Strategy compiled successfully (sync mode)
903
+ * Compiled and registered
780
904
  */
781
905
  200: {
782
906
  strategyId: StrategyId;
783
907
  };
908
+ };
909
+ type CompileStrategyResponse = CompileStrategyResponses[keyof CompileStrategyResponses];
910
+ type ValidateStrategyData = {
911
+ body?: never;
912
+ path: {
913
+ /**
914
+ * The id returned by `POST /strategy`
915
+ */
916
+ strategyId: StrategyId;
917
+ };
918
+ query?: never;
919
+ url: '/strategy/{strategyId}/validate';
920
+ };
921
+ type ValidateStrategyErrors = {
784
922
  /**
785
- * Compile task accepted (async mode set `X-Compile-Async: true`)
923
+ * No such registered strategy for this user
786
924
  */
787
- 202: AcceptedJob;
925
+ 404: ResponseError;
788
926
  };
789
- type CompileStrategyResponse = CompileStrategyResponses[keyof CompileStrategyResponses];
927
+ type ValidateStrategyError = ValidateStrategyErrors[keyof ValidateStrategyErrors];
928
+ type ValidateStrategyResponses = {
929
+ /**
930
+ * Already validated; the recorded verdict, unchanged
931
+ */
932
+ 200: StrategyState;
933
+ /**
934
+ * Validation queued. Not a terminal outcome — poll `GET /strategy/{strategyId}` until
935
+ * `validation` leaves `pending`.
936
+ *
937
+ */
938
+ 202: {
939
+ strategyId: StrategyId;
940
+ validation: 'pending';
941
+ };
942
+ };
943
+ type ValidateStrategyResponse = ValidateStrategyResponses[keyof ValidateStrategyResponses];
790
944
  type GetStrategyData = {
791
945
  body?: never;
792
946
  path: {
793
947
  /**
794
- * The id returned by `POST /strategy` (sync) or the `jobId` returned in async mode
948
+ * The id returned by `POST /strategy`
795
949
  */
796
- strategyId: string;
950
+ strategyId: StrategyId;
797
951
  };
798
952
  query?: never;
799
953
  url: '/strategy/{strategyId}';
800
954
  };
801
955
  type GetStrategyErrors = {
802
956
  /**
803
- * Strategy compile job not found
957
+ * No such registered strategy for this user
804
958
  */
805
959
  404: ResponseError;
806
960
  };
807
961
  type GetStrategyError = GetStrategyErrors[keyof GetStrategyErrors];
808
962
  type GetStrategyResponses = {
809
963
  /**
810
- * Strategy compile state
964
+ * Strategy state
811
965
  */
812
- 200: {
813
- /**
814
- * Compile job id (only set in async mode)
815
- */
816
- jobId?: string;
817
- status: 'New' | 'Started' | 'Completed' | 'Aborted' | 'Failed';
818
- strategyId?: StrategyId;
819
- /**
820
- * Compilation error messages when `status` is `Failed`
821
- */
822
- statusDetail?: string | null;
823
- };
966
+ 200: StrategyState;
824
967
  };
825
968
  type GetStrategyResponse = GetStrategyResponses[keyof GetStrategyResponses];
826
969
  type PrepareBacktestData = {
@@ -1115,6 +1258,21 @@ type GetBacktestResultResponses = {
1115
1258
  * Backtesting execution result
1116
1259
  */
1117
1260
  200: BacktestJobResult;
1261
+ /**
1262
+ * The job is known but its result is not readable yet — keep polling.
1263
+ *
1264
+ * Returned in two situations, both of which mean "ask again", never "you are done":
1265
+ * the job has not produced its result yet, or the job reached a terminal status while
1266
+ * its stored result could not be read back. The response body is an empty object: it
1267
+ * deliberately carries no `state`, so a client cannot mistake it for a finished result.
1268
+ *
1269
+ * Treat any `202` as a signal to continue the poll loop under your existing timeout.
1270
+ * Never treat it as a terminal outcome.
1271
+ *
1272
+ */
1273
+ 202: {
1274
+ [key: string]: never;
1275
+ };
1118
1276
  };
1119
1277
  type GetBacktestResultResponse = GetBacktestResultResponses[keyof GetBacktestResultResponses];
1120
1278
  type ClientOptions = {
@@ -1216,29 +1374,58 @@ declare const downloadTickers: <ThrowOnError extends boolean = false>(options: O
1216
1374
  */
1217
1375
  declare const downloadKlines: <ThrowOnError extends boolean = false>(options: Options<DownloadKlinesData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<Blob | File, ResponseError, ThrowOnError>;
1218
1376
  /**
1219
- * Compile a strategy from source code
1220
- * Submits raw strategy source for compilation. By default the call is synchronous and returns
1221
- * the `strategyId` once compilation succeeds. Set the header `X-Compile-Async: true` to enqueue
1222
- * the compile task and return immediately with a `jobId` poll
1223
- * `GET /strategy/{strategyId}` to check status.
1377
+ * Compile and register a strategy
1378
+ * Compiles raw strategy source and registers it, returning its `strategyId`.
1379
+ *
1380
+ * **This answers one question: is the source valid Java.** It compiles, registers, and hands
1381
+ * back the id nothing more. Whether the class can actually run is
1382
+ * `POST /strategy/{strategyId}/validate`, and everything known about a strategy, validation
1383
+ * included, is read from `GET /strategy/{strategyId}`. One place to ask, so there is no second
1384
+ * answer to keep in step.
1385
+ *
1386
+ * The `strategyId` is derived from what the code *means*, not from how it is written. Adding a
1387
+ * comment, inserting a blank line, re-indenting, reordering imports, or moving a method around
1388
+ * all return the **same** id — you have not created a second strategy. Renaming a variable,
1389
+ * changing an identifier's case, reordering fields, or reordering statements inside a method
1390
+ * return a **different** one.
1391
+ *
1392
+ * Two rules follow, and they are worth designing around:
1393
+ *
1394
+ * - re-submitting a strategy you have only reformatted is free, and gives you back the id you
1395
+ * already had, along with any validation already recorded against it;
1396
+ * - the id says nothing about *behaviour*. Two sources that compute the same thing by
1397
+ * different means are two strategies, because deciding otherwise would mean deciding program
1398
+ * equivalence.
1399
+ *
1400
+ */
1401
+ declare const compileStrategy: <ThrowOnError extends boolean = false>(options: Options<CompileStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<{
1402
+ strategyId: StrategyId;
1403
+ }, ResponseError, ThrowOnError>;
1404
+ /**
1405
+ * Check that a registered strategy can actually run
1406
+ * Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring
1407
+ * fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any
1408
+ * engine notices — is recorded and served from `GET /strategy/{strategyId}`.
1409
+ *
1410
+ * **Idempotent.** If a verdict already exists for the current compilation it comes straight
1411
+ * back with `200` and nothing is queued. Otherwise the check is queued and this returns `202`;
1412
+ * poll `GET /strategy/{strategyId}` until `validation` is `passed` or `failed`.
1224
1413
  *
1225
- * The `strategyId` is deterministic: the same source for the same user always produces the
1226
- * same id.
1414
+ * Recompiling supersedes a verdict, which makes this callable again the old answer described
1415
+ * bytecode that is no longer what would run.
1227
1416
  *
1228
1417
  */
1229
- declare const compileStrategy: <ThrowOnError extends boolean = false>(options: Options<CompileStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<CompileStrategyResponse, ResponseError, ThrowOnError>;
1418
+ declare const validateStrategy: <ThrowOnError extends boolean = false>(options: Options<ValidateStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<ValidateStrategyResponse, ResponseError, ThrowOnError>;
1230
1419
  /**
1231
- * Get a strategy by id, including its compile status
1232
- * Polls the status of a strategy compilation. Useful when the strategy was submitted with
1233
- * `X-Compile-Async: true`. Returns the resolved `strategyId` once compilation completes.
1420
+ * Get a strategy by id, including its validation state
1421
+ * Reports that the strategy is registered — implied by a `200` at all and what validating it
1422
+ * found.
1423
+ *
1424
+ * A `404` means one thing: no such registered strategy for this user. It is never a stale or
1425
+ * expired answer; registration and verdict are stored durably, not cached.
1234
1426
  *
1235
1427
  */
1236
- declare const getStrategy: <ThrowOnError extends boolean = false>(options: Options<GetStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<{
1237
- jobId?: string;
1238
- status: "New" | "Started" | "Completed" | "Aborted" | "Failed";
1239
- strategyId?: StrategyId;
1240
- statusDetail?: string | null;
1241
- }, ResponseError, ThrowOnError>;
1428
+ declare const getStrategy: <ThrowOnError extends boolean = false>(options: Options<GetStrategyData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<StrategyState, ResponseError, ThrowOnError>;
1242
1429
  /**
1243
1430
  * Prepare backtest data
1244
1431
  * Enqueues a prepare task over the requested date range. Returns immediately with a `jobId`;
@@ -1311,9 +1498,13 @@ declare const cancelBacktest: <ThrowOnError extends boolean = false>(options: Op
1311
1498
  * Retrieves the current state and results of the execute job identified by `jobId`.
1312
1499
  * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.
1313
1500
  *
1501
+ * A `202` means the result is not readable yet — keep polling. It is never a terminal
1502
+ * outcome, and it carries no `state`, so a poll loop that stops on a terminal status will
1503
+ * not stop on it.
1504
+ *
1314
1505
  */
1315
- declare const getBacktestResult: <ThrowOnError extends boolean = false>(options: Options<GetBacktestResultData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<BacktestJobResult, ResponseError, ThrowOnError>;
1506
+ declare const getBacktestResult: <ThrowOnError extends boolean = false>(options: Options<GetBacktestResultData, ThrowOnError>) => _hey_api_client_fetch.RequestResult<GetBacktestResultResponse, ResponseError, ThrowOnError>;
1316
1507
 
1317
1508
  declare const client: _hey_api_client_fetch.Client;
1318
1509
 
1319
- export { type AcceptedJob, type AuthTokenError, type AuthTokenResponse, type AuthenticateData, type AuthenticateError, type AuthenticateErrors, type AuthenticateResponse, type AuthenticateResponses, type BacktestJobResult, type CancelBacktestData, type CancelBacktestError, type CancelBacktestErrors, type CancelBacktestResponse, type CancelBacktestResponses, type CancelSweepData, type CancelSweepError, type CancelSweepErrors, type CancelSweepResponse, type CancelSweepResponses, type ClientOptions, type CompileStrategyData, type CompileStrategyError, type CompileStrategyErrors, type CompileStrategyResponse, type CompileStrategyResponses, type CoverageWindow, type DataSourceType, type DownloadKlinesData, type DownloadKlinesError, type DownloadKlinesErrors, type DownloadKlinesResponse, type DownloadKlinesResponses, type DownloadTickersData, type DownloadTickersError, type DownloadTickersErrors, type DownloadTickersResponse, type DownloadTickersResponses, type EquityPoint, type Exchange, type ExecuteBacktestData, type ExecuteBacktestError, type ExecuteBacktestErrors, type ExecuteBacktestResponse, type ExecuteBacktestResponses, type ExecuteSweepAccepted, type ExecuteSweepData, type ExecuteSweepError, type ExecuteSweepErrors, type ExecuteSweepRequest, type ExecuteSweepResponse, type ExecuteSweepResponses, type ExecuteSweepResult, type GetBacktestResultData, type GetBacktestResultError, type GetBacktestResultErrors, type GetBacktestResultResponse, type GetBacktestResultResponses, type GetPrepareStatusData, type GetPrepareStatusError, type GetPrepareStatusErrors, type GetPrepareStatusResponse, type GetPrepareStatusResponses, type GetStrategyData, type GetStrategyError, type GetStrategyErrors, type GetStrategyResponse, type GetStrategyResponses, type GetSweepResultData, type GetSweepResultError, type GetSweepResultErrors, type GetSweepResultResponse, type GetSweepResultResponses, type HalLink, type Instrument, type InstrumentCoverage, type InstrumentDetail, type InstrumentLinks, type InstrumentListMeta, type InstrumentListResponse, type JobState, type ListExchangesData, type ListExchangesResponse, type ListExchangesResponses, type ListInstrumentsData, type ListInstrumentsError, type ListInstrumentsErrors, type ListInstrumentsResponse, type ListInstrumentsResponses, type ListSegmentInstrumentsData, type ListSegmentInstrumentsError, type ListSegmentInstrumentsErrors, type ListSegmentInstrumentsResponse, type ListSegmentInstrumentsResponses, type Options, type PrepareBacktestData, type PrepareBacktestError, type PrepareBacktestErrors, type PrepareBacktestResponse, type PrepareBacktestResponses, type PrepareJobState, type PrepareRequest, type ResponseError, type ResultMap, type StrategyId, type SweepAxis, type SweepBaseConfig, type SweepProgress, type SweepRunRow, type SweepSpecRequest, authenticate, cancelBacktest, cancelSweep, client, compileStrategy, downloadKlines, downloadTickers, executeBacktest, executeSweep, getBacktestResult, getPrepareStatus, getStrategy, getSweepResult, listExchanges, listInstruments, listSegmentInstruments, prepareBacktest };
1510
+ export { type AcceptedJob, type AuthTokenError, type AuthTokenResponse, type AuthenticateData, type AuthenticateError, type AuthenticateErrors, type AuthenticateResponse, type AuthenticateResponses, type BacktestJobResult, type CancelBacktestData, type CancelBacktestError, type CancelBacktestErrors, type CancelBacktestResponse, type CancelBacktestResponses, type CancelSweepData, type CancelSweepError, type CancelSweepErrors, type CancelSweepResponse, type CancelSweepResponses, type ClientOptions, type CompileStrategyData, type CompileStrategyError, type CompileStrategyErrors, type CompileStrategyResponse, type CompileStrategyResponses, type CoverageWindow, type DataSourceType, type DownloadKlinesData, type DownloadKlinesError, type DownloadKlinesErrors, type DownloadKlinesResponse, type DownloadKlinesResponses, type DownloadTickersData, type DownloadTickersError, type DownloadTickersErrors, type DownloadTickersResponse, type DownloadTickersResponses, type EquityPoint, type Exchange, type ExecuteBacktestData, type ExecuteBacktestError, type ExecuteBacktestErrors, type ExecuteBacktestResponse, type ExecuteBacktestResponses, type ExecuteSweepAccepted, type ExecuteSweepData, type ExecuteSweepError, type ExecuteSweepErrors, type ExecuteSweepRequest, type ExecuteSweepResponse, type ExecuteSweepResponses, type ExecuteSweepResult, type GetBacktestResultData, type GetBacktestResultError, type GetBacktestResultErrors, type GetBacktestResultResponse, type GetBacktestResultResponses, type GetPrepareStatusData, type GetPrepareStatusError, type GetPrepareStatusErrors, type GetPrepareStatusResponse, type GetPrepareStatusResponses, type GetStrategyData, type GetStrategyError, type GetStrategyErrors, type GetStrategyResponse, type GetStrategyResponses, type GetSweepResultData, type GetSweepResultError, type GetSweepResultErrors, type GetSweepResultResponse, type GetSweepResultResponses, type HalLink, type Instrument, type InstrumentCoverage, type InstrumentDetail, type InstrumentLinks, type InstrumentListMeta, type InstrumentListResponse, type JobState, type ListExchangesData, type ListExchangesResponse, type ListExchangesResponses, type ListInstrumentsData, type ListInstrumentsError, type ListInstrumentsErrors, type ListInstrumentsResponse, type ListInstrumentsResponses, type ListSegmentInstrumentsData, type ListSegmentInstrumentsError, type ListSegmentInstrumentsErrors, type ListSegmentInstrumentsResponse, type ListSegmentInstrumentsResponses, type Notice, type Options, type PrepareBacktestData, type PrepareBacktestError, type PrepareBacktestErrors, type PrepareBacktestResponse, type PrepareBacktestResponses, type PrepareJobState, type PrepareRequest, type ResponseError, type ResultMap, type StrategyId, type StrategyState, type SweepAxis, type SweepBaseConfig, type SweepProgress, type SweepRunRow, type SweepSpecRequest, type ValidateStrategyData, type ValidateStrategyError, type ValidateStrategyErrors, type ValidateStrategyResponse, type ValidateStrategyResponses, authenticate, cancelBacktest, cancelSweep, client, compileStrategy, downloadKlines, downloadTickers, executeBacktest, executeSweep, getBacktestResult, getPrepareStatus, getStrategy, getSweepResult, listExchanges, listInstruments, listSegmentInstruments, prepareBacktest, validateStrategy };
package/dist/index.js CHANGED
@@ -64,6 +64,18 @@ var compileStrategy = (options) => {
64
64
  }
65
65
  });
66
66
  };
67
+ var validateStrategy = (options) => {
68
+ return (options.client ?? client).post({
69
+ security: [
70
+ {
71
+ scheme: "bearer",
72
+ type: "http"
73
+ }
74
+ ],
75
+ url: "/strategy/{strategyId}/validate",
76
+ ...options
77
+ });
78
+ };
67
79
  var getStrategy = (options) => {
68
80
  return (options.client ?? client).get({
69
81
  security: [
@@ -201,6 +213,7 @@ export {
201
213
  listExchanges,
202
214
  listInstruments,
203
215
  listSegmentInstruments,
204
- prepareBacktest
216
+ prepareBacktest,
217
+ validateStrategy
205
218
  };
206
219
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generated/client.gen.ts","../src/generated/sdk.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { ClientOptions } from './types.gen';\nimport { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch';\n\n/**\n * The `createClientConfig()` function will be called on client initialization\n * and the returned object will become the client's initial configuration.\n *\n * You may want to initialize your client this way instead of calling\n * `setConfig()`. This is useful for example if you're using Next.js\n * to ensure your client always has the correct values.\n */\nexport type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;\n\nexport const client = createClient(createConfig<ClientOptions>({\n baseUrl: 'https://api.staging.qtsurfer.com/v1'\n}));","// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';\nimport type { AuthenticateData, AuthenticateResponse, AuthenticateError, ListExchangesData, ListExchangesResponse, ListInstrumentsData, ListInstrumentsResponse, ListInstrumentsError, ListSegmentInstrumentsData, ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, DownloadTickersData, DownloadTickersResponse, DownloadTickersError, DownloadKlinesData, DownloadKlinesResponse, DownloadKlinesError, CompileStrategyData, CompileStrategyResponse, CompileStrategyError, GetStrategyData, GetStrategyResponse, GetStrategyError, PrepareBacktestData, PrepareBacktestResponse, PrepareBacktestError, GetPrepareStatusData, GetPrepareStatusResponse, GetPrepareStatusError, ExecuteSweepData, ExecuteSweepResponse, ExecuteSweepError, CancelSweepData, CancelSweepResponse, CancelSweepError, GetSweepResultData, GetSweepResultResponse, GetSweepResultError, ExecuteBacktestData, ExecuteBacktestResponse, ExecuteBacktestError, CancelBacktestData, CancelBacktestResponse, CancelBacktestError, GetBacktestResultData, GetBacktestResultResponse, GetBacktestResultError } from './types.gen';\nimport { client as _heyApiClient } from './client.gen';\n\nexport type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {\n /**\n * You can provide a client instance returned by `createClient()` instead of\n * individual options. This might be also useful if you want to implement a\n * custom client.\n */\n client?: Client;\n /**\n * You can pass arbitrary values through the `meta` object. This can be\n * used to access values that aren't defined as part of the SDK function.\n */\n meta?: Record<string, unknown>;\n};\n\n/**\n * Exchange API key for a short-lived JWT\n * Exchanges a long-lived API key for a short-lived JWT used by every other\n * endpoint. This is the only endpoint that accepts an API key directly —\n * callers should obtain a JWT here, then send it as `Authorization: Bearer\n * <token>` to all other operations.\n *\n * The returned JWT carries the caller's subscription `tier` as a claim and\n * expires after `expires_in` seconds. Callers should refresh the token\n * before expiry (or on a `401` response) by calling this endpoint again.\n *\n */\nexport const authenticate = <ThrowOnError extends boolean = false>(options?: Options<AuthenticateData, ThrowOnError>) => {\n return (options?.client ?? _heyApiClient).post<AuthenticateResponse, AuthenticateError, ThrowOnError>({\n security: [\n {\n name: 'X-API-Key',\n type: 'apiKey'\n }\n ],\n url: '/auth/token',\n ...options\n });\n};\n\n/**\n * List the available exchanges\n */\nexport const listExchanges = <ThrowOnError extends boolean = false>(options?: Options<ListExchangesData, ThrowOnError>) => {\n return (options?.client ?? _heyApiClient).get<ListExchangesResponse, unknown, ThrowOnError>({\n url: '/exchanges',\n ...options\n });\n};\n\n/**\n * List an exchange's instruments (default spot segment)\n * \"Give me binance instruments\" — returns the exchange's DEFAULT segment (`spot`)\n * in `data`, each instrument with per-data-type coverage and market info. `meta`\n * confirms the served `segment` (`spot`); HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links.\n *\n */\nexport const listInstruments = <ThrowOnError extends boolean = false>(options: Options<ListInstrumentsData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<ListInstrumentsResponse, ListInstrumentsError, ThrowOnError>({\n url: '/exchange/{exchangeId}/instruments',\n ...options\n });\n};\n\n/**\n * List an exchange segment's instruments\n * Returns the instruments for one market segment of the exchange, each with\n * per-data-type coverage and market info. HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links; the default-segment shortcut is\n * `GET /exchange/{exchangeId}/instruments` (spot).\n *\n */\nexport const listSegmentInstruments = <ThrowOnError extends boolean = false>(options: Options<ListSegmentInstrumentsData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, ThrowOnError>({\n url: '/exchange/{exchangeId}/{segment}/instruments',\n ...options\n });\n};\n\n/**\n * Download one hour of tickers for an instrument as a Lastra segment\n * Serves exactly one hour of raw ticker data for the given instrument on the\n * requested exchange. The payload is a native [Lastra](https://github.com/QTSurfer/lastra-java)\n * file — QTSurfer's columnar format for tick-precision timeseries — with\n * no JSON envelope.\n *\n * One segment = one hour, aligned to UTC. The `hour` query parameter selects\n * the segment and must match `YYYY-MM-DDTHH` (no minutes/seconds, no timezone\n * suffix). Example: `hour=2026-01-15T10` returns `h10.lastra` for\n * 2026-01-15, covering `[10:00:00Z, 11:00:00Z)`. Hours not yet available\n * return `404`.\n *\n * A `format=parquet` query parameter switches the response to on-the-fly\n * Parquet conversion via [lastra-convert](https://github.com/QTSurfer/lastra-convert)\n * for clients that don't yet read Lastra. Lastra is the primary format\n * and cheaper when the client can consume it.\n *\n * Clients:\n * - [lastra-java](https://github.com/QTSurfer/lastra-java) — reference\n * Java reader/writer with per-column codecs (ALP, Gorilla, delta-varint,\n * ZSTD) and CRC32 integrity.\n * - [lastra-ts](https://github.com/QTSurfer/lastra-ts) — TypeScript reader\n * (~4 kB bundle, browser + Node.js).\n * - [duckdb-lastra](https://github.com/QTSurfer/duckdb-lastra) — DuckDB\n * extension for ad-hoc SQL over Lastra files.\n * - [lastra-convert](https://github.com/QTSurfer/lastra-convert) — CLI + Java\n * API for converting to/from Parquet, Reef, and CSV.\n * - `curl -OJ` for offline dumps (the `Content-Disposition` header sets a\n * descriptive filename).\n *\n */\nexport const downloadTickers = <ThrowOnError extends boolean = false>(options: Options<DownloadTickersData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<DownloadTickersResponse, DownloadTickersError, ThrowOnError>({\n url: '/exchange/{exchangeId}/tickers/{base}/{quote}',\n ...options\n });\n};\n\n/**\n * Download one hour of klines for an instrument as a Lastra segment\n * Same shape and semantics as `/exchange/{exchangeId}/tickers/{base}/{quote}`,\n * but serves klines (aggregated bars) instead of raw ticks. One\n * [Lastra](https://github.com/QTSurfer/lastra-java) segment = one hour of\n * klines at the exchange's native kline cadence, aligned to UTC.\n *\n * Klines use the same columnar layout as tickers — readers that handle one\n * format read the other with the same code. Use this endpoint when a\n * per-tick payload would be too large for the window of interest.\n *\n */\nexport const downloadKlines = <ThrowOnError extends boolean = false>(options: Options<DownloadKlinesData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<DownloadKlinesResponse, DownloadKlinesError, ThrowOnError>({\n url: '/exchange/{exchangeId}/klines/{base}/{quote}',\n ...options\n });\n};\n\n/**\n * Compile a strategy from source code\n * Submits raw strategy source for compilation. By default the call is synchronous and returns\n * the `strategyId` once compilation succeeds. Set the header `X-Compile-Async: true` to enqueue\n * the compile task and return immediately with a `jobId` — poll\n * `GET /strategy/{strategyId}` to check status.\n *\n * The `strategyId` is deterministic: the same source for the same user always produces the\n * same id.\n *\n */\nexport const compileStrategy = <ThrowOnError extends boolean = false>(options: Options<CompileStrategyData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<CompileStrategyResponse, CompileStrategyError, ThrowOnError>({\n bodySerializer: null,\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/strategy',\n ...options,\n headers: {\n 'Content-Type': 'text/plain',\n ...options?.headers\n }\n });\n};\n\n/**\n * Get a strategy by id, including its compile status\n * Polls the status of a strategy compilation. Useful when the strategy was submitted with\n * `X-Compile-Async: true`. Returns the resolved `strategyId` once compilation completes.\n *\n */\nexport const getStrategy = <ThrowOnError extends boolean = false>(options: Options<GetStrategyData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetStrategyResponse, GetStrategyError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/strategy/{strategyId}',\n ...options\n });\n};\n\n/**\n * Prepare backtest data\n * Enqueues a prepare task over the requested date range. Returns immediately with a `jobId`;\n * poll `GET /backtest/{exchangeId}/{type}/prepare/{jobId}` for completion.\n *\n * The same params always return the same `jobId` (idempotent). Repeated calls with identical\n * params do not enqueue duplicate work — they reuse the existing job.\n *\n */\nexport const prepareBacktest = <ThrowOnError extends boolean = false>(options: Options<PrepareBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<PrepareBacktestResponse, PrepareBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/prepare',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Get the status of a prepare job\n * Retrieves the current state of the prepare job identified by `jobId`.\n * Poll until `status` is `Completed`, `Failed`, or `Aborted`.\n *\n */\nexport const getPrepareStatus = <ThrowOnError extends boolean = false>(options: Options<GetPrepareStatusData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetPrepareStatusResponse, GetPrepareStatusError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/prepare/{jobId}',\n ...options\n });\n};\n\n/**\n * Execute a parameter sweep over prepared data\n * Runs a parameter matrix over the single immutable dataset identified by `requestId`.\n * The backend expands and executes the matrix internally; clients poll the returned\n * `sweepId` for incremental results.\n *\n */\nexport const executeSweep = <ThrowOnError extends boolean = false>(options: Options<ExecuteSweepData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<ExecuteSweepResponse, ExecuteSweepError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Cancel a running parameter sweep\n * Requests cancellation between parameter vectors. Completed rows remain readable.\n */\nexport const cancelSweep = <ThrowOnError extends boolean = false>(options: Options<CancelSweepData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).delete<CancelSweepResponse, CancelSweepError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}',\n ...options\n });\n};\n\n/**\n * Get sweep progress and results\n * Returns incremental sweep progress. The default `ranked` view sorts and may truncate the\n * display leaderboard. `order=natural` returns every available row, untruncated, ordered by\n * deterministic `runIx`; use that view when materialising durable trial rows.\n *\n */\nexport const getSweepResult = <ThrowOnError extends boolean = false>(options: Options<GetSweepResultData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetSweepResultResponse, GetSweepResultError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}',\n ...options\n });\n};\n\n/**\n * Execute a compiled strategy against a prepared dataset\n * Enqueues an execute task that runs the strategy identified by `strategyId` over the data\n * prepared by the prepare job identified by `prepareJobId`. The instrument and date range are\n * recovered from the prepare job — they do not need to be sent again.\n *\n * Returns immediately with a `jobId`; poll `GET /backtest/{exchangeId}/{type}/execute/{jobId}`\n * for the result.\n *\n * The same params (same `prepareJobId`, `strategyId`, `storeSignals`) always return the same\n * `jobId` (idempotent).\n *\n */\nexport const executeBacktest = <ThrowOnError extends boolean = false>(options: Options<ExecuteBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<ExecuteBacktestResponse, ExecuteBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Cancel a running backtest execution\n * Requests cancellation of the specified execution. The execution\n * status will transition to `Aborted` once the cancellation is\n * processed. Cancellation is asynchronous — poll the GET endpoint\n * to confirm the final status.\n *\n */\nexport const cancelBacktest = <ThrowOnError extends boolean = false>(options: Options<CancelBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).delete<CancelBacktestResponse, CancelBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute/{jobId}',\n ...options\n });\n};\n\n/**\n * Get the result of a backtest execution job\n * Retrieves the current state and results of the execute job identified by `jobId`.\n * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.\n *\n */\nexport const getBacktestResult = <ThrowOnError extends boolean = false>(options: Options<GetBacktestResultData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetBacktestResultResponse, GetBacktestResultError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute/{jobId}',\n ...options\n });\n};"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/generated/client.gen.ts","../src/generated/sdk.gen.ts"],"sourcesContent":["// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { ClientOptions } from './types.gen';\nimport { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from '@hey-api/client-fetch';\n\n/**\n * The `createClientConfig()` function will be called on client initialization\n * and the returned object will become the client's initial configuration.\n *\n * You may want to initialize your client this way instead of calling\n * `setConfig()`. This is useful for example if you're using Next.js\n * to ensure your client always has the correct values.\n */\nexport type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;\n\nexport const client = createClient(createConfig<ClientOptions>({\n baseUrl: 'https://api.staging.qtsurfer.com/v1'\n}));","// This file is auto-generated by @hey-api/openapi-ts\n\nimport type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';\nimport type { AuthenticateData, AuthenticateResponse, AuthenticateError, ListExchangesData, ListExchangesResponse, ListInstrumentsData, ListInstrumentsResponse, ListInstrumentsError, ListSegmentInstrumentsData, ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, DownloadTickersData, DownloadTickersResponse, DownloadTickersError, DownloadKlinesData, DownloadKlinesResponse, DownloadKlinesError, CompileStrategyData, CompileStrategyResponse, CompileStrategyError, ValidateStrategyData, ValidateStrategyResponse, ValidateStrategyError, GetStrategyData, GetStrategyResponse, GetStrategyError, PrepareBacktestData, PrepareBacktestResponse, PrepareBacktestError, GetPrepareStatusData, GetPrepareStatusResponse, GetPrepareStatusError, ExecuteSweepData, ExecuteSweepResponse, ExecuteSweepError, CancelSweepData, CancelSweepResponse, CancelSweepError, GetSweepResultData, GetSweepResultResponse, GetSweepResultError, ExecuteBacktestData, ExecuteBacktestResponse, ExecuteBacktestError, CancelBacktestData, CancelBacktestResponse, CancelBacktestError, GetBacktestResultData, GetBacktestResultResponse, GetBacktestResultError } from './types.gen';\nimport { client as _heyApiClient } from './client.gen';\n\nexport type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {\n /**\n * You can provide a client instance returned by `createClient()` instead of\n * individual options. This might be also useful if you want to implement a\n * custom client.\n */\n client?: Client;\n /**\n * You can pass arbitrary values through the `meta` object. This can be\n * used to access values that aren't defined as part of the SDK function.\n */\n meta?: Record<string, unknown>;\n};\n\n/**\n * Exchange API key for a short-lived JWT\n * Exchanges a long-lived API key for a short-lived JWT used by every other\n * endpoint. This is the only endpoint that accepts an API key directly —\n * callers should obtain a JWT here, then send it as `Authorization: Bearer\n * <token>` to all other operations.\n *\n * The returned JWT carries the caller's subscription `tier` as a claim and\n * expires after `expires_in` seconds. Callers should refresh the token\n * before expiry (or on a `401` response) by calling this endpoint again.\n *\n */\nexport const authenticate = <ThrowOnError extends boolean = false>(options?: Options<AuthenticateData, ThrowOnError>) => {\n return (options?.client ?? _heyApiClient).post<AuthenticateResponse, AuthenticateError, ThrowOnError>({\n security: [\n {\n name: 'X-API-Key',\n type: 'apiKey'\n }\n ],\n url: '/auth/token',\n ...options\n });\n};\n\n/**\n * List the available exchanges\n */\nexport const listExchanges = <ThrowOnError extends boolean = false>(options?: Options<ListExchangesData, ThrowOnError>) => {\n return (options?.client ?? _heyApiClient).get<ListExchangesResponse, unknown, ThrowOnError>({\n url: '/exchanges',\n ...options\n });\n};\n\n/**\n * List an exchange's instruments (default spot segment)\n * \"Give me binance instruments\" — returns the exchange's DEFAULT segment (`spot`)\n * in `data`, each instrument with per-data-type coverage and market info. `meta`\n * confirms the served `segment` (`spot`); HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links.\n *\n */\nexport const listInstruments = <ThrowOnError extends boolean = false>(options: Options<ListInstrumentsData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<ListInstrumentsResponse, ListInstrumentsError, ThrowOnError>({\n url: '/exchange/{exchangeId}/instruments',\n ...options\n });\n};\n\n/**\n * List an exchange segment's instruments\n * Returns the instruments for one market segment of the exchange, each with\n * per-data-type coverage and market info. HAL `_links` carry `self` plus the\n * `spot` / `futures` segment-discovery links; the default-segment shortcut is\n * `GET /exchange/{exchangeId}/instruments` (spot).\n *\n */\nexport const listSegmentInstruments = <ThrowOnError extends boolean = false>(options: Options<ListSegmentInstrumentsData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, ThrowOnError>({\n url: '/exchange/{exchangeId}/{segment}/instruments',\n ...options\n });\n};\n\n/**\n * Download one hour of tickers for an instrument as a Lastra segment\n * Serves exactly one hour of raw ticker data for the given instrument on the\n * requested exchange. The payload is a native [Lastra](https://github.com/QTSurfer/lastra-java)\n * file — QTSurfer's columnar format for tick-precision timeseries — with\n * no JSON envelope.\n *\n * One segment = one hour, aligned to UTC. The `hour` query parameter selects\n * the segment and must match `YYYY-MM-DDTHH` (no minutes/seconds, no timezone\n * suffix). Example: `hour=2026-01-15T10` returns `h10.lastra` for\n * 2026-01-15, covering `[10:00:00Z, 11:00:00Z)`. Hours not yet available\n * return `404`.\n *\n * A `format=parquet` query parameter switches the response to on-the-fly\n * Parquet conversion via [lastra-convert](https://github.com/QTSurfer/lastra-convert)\n * for clients that don't yet read Lastra. Lastra is the primary format\n * and cheaper when the client can consume it.\n *\n * Clients:\n * - [lastra-java](https://github.com/QTSurfer/lastra-java) — reference\n * Java reader/writer with per-column codecs (ALP, Gorilla, delta-varint,\n * ZSTD) and CRC32 integrity.\n * - [lastra-ts](https://github.com/QTSurfer/lastra-ts) — TypeScript reader\n * (~4 kB bundle, browser + Node.js).\n * - [duckdb-lastra](https://github.com/QTSurfer/duckdb-lastra) — DuckDB\n * extension for ad-hoc SQL over Lastra files.\n * - [lastra-convert](https://github.com/QTSurfer/lastra-convert) — CLI + Java\n * API for converting to/from Parquet, Reef, and CSV.\n * - `curl -OJ` for offline dumps (the `Content-Disposition` header sets a\n * descriptive filename).\n *\n */\nexport const downloadTickers = <ThrowOnError extends boolean = false>(options: Options<DownloadTickersData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<DownloadTickersResponse, DownloadTickersError, ThrowOnError>({\n url: '/exchange/{exchangeId}/tickers/{base}/{quote}',\n ...options\n });\n};\n\n/**\n * Download one hour of klines for an instrument as a Lastra segment\n * Same shape and semantics as `/exchange/{exchangeId}/tickers/{base}/{quote}`,\n * but serves klines (aggregated bars) instead of raw ticks. One\n * [Lastra](https://github.com/QTSurfer/lastra-java) segment = one hour of\n * klines at the exchange's native kline cadence, aligned to UTC.\n *\n * Klines use the same columnar layout as tickers — readers that handle one\n * format read the other with the same code. Use this endpoint when a\n * per-tick payload would be too large for the window of interest.\n *\n */\nexport const downloadKlines = <ThrowOnError extends boolean = false>(options: Options<DownloadKlinesData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<DownloadKlinesResponse, DownloadKlinesError, ThrowOnError>({\n url: '/exchange/{exchangeId}/klines/{base}/{quote}',\n ...options\n });\n};\n\n/**\n * Compile and register a strategy\n * Compiles raw strategy source and registers it, returning its `strategyId`.\n *\n * **This answers one question: is the source valid Java.** It compiles, registers, and hands\n * back the id — nothing more. Whether the class can actually run is\n * `POST /strategy/{strategyId}/validate`, and everything known about a strategy, validation\n * included, is read from `GET /strategy/{strategyId}`. One place to ask, so there is no second\n * answer to keep in step.\n *\n * The `strategyId` is derived from what the code *means*, not from how it is written. Adding a\n * comment, inserting a blank line, re-indenting, reordering imports, or moving a method around\n * all return the **same** id — you have not created a second strategy. Renaming a variable,\n * changing an identifier's case, reordering fields, or reordering statements inside a method\n * return a **different** one.\n *\n * Two rules follow, and they are worth designing around:\n *\n * - re-submitting a strategy you have only reformatted is free, and gives you back the id you\n * already had, along with any validation already recorded against it;\n * - the id says nothing about *behaviour*. Two sources that compute the same thing by\n * different means are two strategies, because deciding otherwise would mean deciding program\n * equivalence.\n *\n */\nexport const compileStrategy = <ThrowOnError extends boolean = false>(options: Options<CompileStrategyData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<CompileStrategyResponse, CompileStrategyError, ThrowOnError>({\n bodySerializer: null,\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/strategy',\n ...options,\n headers: {\n 'Content-Type': 'text/plain',\n ...options?.headers\n }\n });\n};\n\n/**\n * Check that a registered strategy can actually run\n * Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring\n * fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any\n * engine notices — is recorded and served from `GET /strategy/{strategyId}`.\n *\n * **Idempotent.** If a verdict already exists for the current compilation it comes straight\n * back with `200` and nothing is queued. Otherwise the check is queued and this returns `202`;\n * poll `GET /strategy/{strategyId}` until `validation` is `passed` or `failed`.\n *\n * Recompiling supersedes a verdict, which makes this callable again — the old answer described\n * bytecode that is no longer what would run.\n *\n */\nexport const validateStrategy = <ThrowOnError extends boolean = false>(options: Options<ValidateStrategyData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<ValidateStrategyResponse, ValidateStrategyError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/strategy/{strategyId}/validate',\n ...options\n });\n};\n\n/**\n * Get a strategy by id, including its validation state\n * Reports that the strategy is registered — implied by a `200` at all — and what validating it\n * found.\n *\n * A `404` means one thing: no such registered strategy for this user. It is never a stale or\n * expired answer; registration and verdict are stored durably, not cached.\n *\n */\nexport const getStrategy = <ThrowOnError extends boolean = false>(options: Options<GetStrategyData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetStrategyResponse, GetStrategyError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/strategy/{strategyId}',\n ...options\n });\n};\n\n/**\n * Prepare backtest data\n * Enqueues a prepare task over the requested date range. Returns immediately with a `jobId`;\n * poll `GET /backtest/{exchangeId}/{type}/prepare/{jobId}` for completion.\n *\n * The same params always return the same `jobId` (idempotent). Repeated calls with identical\n * params do not enqueue duplicate work — they reuse the existing job.\n *\n */\nexport const prepareBacktest = <ThrowOnError extends boolean = false>(options: Options<PrepareBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<PrepareBacktestResponse, PrepareBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/prepare',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Get the status of a prepare job\n * Retrieves the current state of the prepare job identified by `jobId`.\n * Poll until `status` is `Completed`, `Failed`, or `Aborted`.\n *\n */\nexport const getPrepareStatus = <ThrowOnError extends boolean = false>(options: Options<GetPrepareStatusData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetPrepareStatusResponse, GetPrepareStatusError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/prepare/{jobId}',\n ...options\n });\n};\n\n/**\n * Execute a parameter sweep over prepared data\n * Runs a parameter matrix over the single immutable dataset identified by `requestId`.\n * The backend expands and executes the matrix internally; clients poll the returned\n * `sweepId` for incremental results.\n *\n */\nexport const executeSweep = <ThrowOnError extends boolean = false>(options: Options<ExecuteSweepData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<ExecuteSweepResponse, ExecuteSweepError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Cancel a running parameter sweep\n * Requests cancellation between parameter vectors. Completed rows remain readable.\n */\nexport const cancelSweep = <ThrowOnError extends boolean = false>(options: Options<CancelSweepData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).delete<CancelSweepResponse, CancelSweepError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}',\n ...options\n });\n};\n\n/**\n * Get sweep progress and results\n * Returns incremental sweep progress. The default `ranked` view sorts and may truncate the\n * display leaderboard. `order=natural` returns every available row, untruncated, ordered by\n * deterministic `runIx`; use that view when materialising durable trial rows.\n *\n */\nexport const getSweepResult = <ThrowOnError extends boolean = false>(options: Options<GetSweepResultData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetSweepResultResponse, GetSweepResultError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/executeSweep/{requestId}/{sweepId}',\n ...options\n });\n};\n\n/**\n * Execute a compiled strategy against a prepared dataset\n * Enqueues an execute task that runs the strategy identified by `strategyId` over the data\n * prepared by the prepare job identified by `prepareJobId`. The instrument and date range are\n * recovered from the prepare job — they do not need to be sent again.\n *\n * Returns immediately with a `jobId`; poll `GET /backtest/{exchangeId}/{type}/execute/{jobId}`\n * for the result.\n *\n * The same params (same `prepareJobId`, `strategyId`, `storeSignals`) always return the same\n * `jobId` (idempotent).\n *\n */\nexport const executeBacktest = <ThrowOnError extends boolean = false>(options: Options<ExecuteBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).post<ExecuteBacktestResponse, ExecuteBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute',\n ...options,\n headers: {\n 'Content-Type': 'application/json',\n ...options?.headers\n }\n });\n};\n\n/**\n * Cancel a running backtest execution\n * Requests cancellation of the specified execution. The execution\n * status will transition to `Aborted` once the cancellation is\n * processed. Cancellation is asynchronous — poll the GET endpoint\n * to confirm the final status.\n *\n */\nexport const cancelBacktest = <ThrowOnError extends boolean = false>(options: Options<CancelBacktestData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).delete<CancelBacktestResponse, CancelBacktestError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute/{jobId}',\n ...options\n });\n};\n\n/**\n * Get the result of a backtest execution job\n * Retrieves the current state and results of the execute job identified by `jobId`.\n * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.\n *\n * A `202` means the result is not readable yet — keep polling. It is never a terminal\n * outcome, and it carries no `state`, so a poll loop that stops on a terminal status will\n * not stop on it.\n *\n */\nexport const getBacktestResult = <ThrowOnError extends boolean = false>(options: Options<GetBacktestResultData, ThrowOnError>) => {\n return (options.client ?? _heyApiClient).get<GetBacktestResultResponse, GetBacktestResultError, ThrowOnError>({\n security: [\n {\n scheme: 'bearer',\n type: 'http'\n }\n ],\n url: '/backtest/{exchangeId}/{type}/execute/{jobId}',\n ...options\n });\n};"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qtsurfer/api-client",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Auto-generated TypeScript API client for the QTSurfer API (from OpenAPI 3.1 spec)",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -22,7 +22,11 @@
22
22
  "generate": "openapi-ts",
23
23
  "build": "tsup",
24
24
  "lint": "tsc --noEmit",
25
- "prepublishOnly": "npm run build"
25
+ "docs": "typedoc",
26
+ "prepublishOnly": "npm run build",
27
+ "changeset": "changeset",
28
+ "changeset:version": "changeset version",
29
+ "changeset:publish": "changeset publish"
26
30
  },
27
31
  "repository": {
28
32
  "type": "git",
@@ -47,8 +51,11 @@
47
51
  "access": "public"
48
52
  },
49
53
  "devDependencies": {
54
+ "@changesets/changelog-github": "^0.6.0",
55
+ "@changesets/cli": "^2.31.1",
50
56
  "@hey-api/openapi-ts": "^0.66",
51
57
  "tsup": "^8.5.1",
58
+ "typedoc": "^0.28.20",
52
59
  "typescript": "^5.8.0"
53
60
  },
54
61
  "dependencies": {
@@ -783,7 +783,7 @@ export const BacktestJobResultSchema = {
783
783
 
784
784
  export const ResultMapSchema = {
785
785
  type: 'object',
786
- description: 'Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below.',
786
+ description: 'Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below. `notices` carries what the run had to say about itself, and is absent when it had nothing.',
787
787
  required: ['strategyId', 'instrument'],
788
788
  properties: {
789
789
  hostName: {
@@ -799,14 +799,41 @@ export const ResultMapSchema = {
799
799
  },
800
800
  strategyId: {
801
801
  type: 'string',
802
- description: 'Identifier of the compiled strategy that produced this result',
803
- example: 'strategy:00000000-0000-0000-0000-000000000000:ticker:2iyvtenlzh9dabqtxn7nbv'
802
+ description: `**Not the \`strategyId\` you compiled with** this is the execution context id,
803
+ \`strategy:<user>:<strategyId>\`. The compiled strategy's id is the last \`:\`-separated
804
+ segment; that, not this whole string, is what \`GET /strategy/{strategyId}\` takes.
805
+
806
+ Take the segment after the last \`:\` rather than counting from the front: the shape has
807
+ changed once already and callers that indexed a fixed position broke on it.
808
+ `,
809
+ example: 'strategy:00000000-0000-0000-0000-000000000000:2iyvtenlzh9dabqtxn7nbv'
804
810
  },
805
811
  instrument: {
806
812
  type: 'string',
807
813
  description: 'The instrument (currency pair) that was backtested',
808
814
  example: 'BTC/USDT'
809
815
  },
816
+ notices: {
817
+ type: 'array',
818
+ description: `Diagnostics the engine raised over this run, each with \`provenance: execute\`.
819
+
820
+ **Absent means nothing was raised.** This is the one surface where silence is a real
821
+ answer: the run happened, over your data, start to finish, and the engine found nothing
822
+ worth saying. That is not true of the compile path, where an empty list only means a
823
+ short synthetic series reached nothing — see \`GET /strategy/{strategyId}\`.
824
+
825
+ Notices are raised on failed and aborted runs too, and those are the ones most worth
826
+ reading: a run that produced no trades often did so for a reason stated here.
827
+ `,
828
+ items: {
829
+ '$ref': '#/components/schemas/Notice'
830
+ }
831
+ },
832
+ noticesTruncated: {
833
+ type: 'integer',
834
+ description: 'How many notices were dropped past the cap of 50. Absent when none were. A large value usually means one fault repeating per instrument or per parameter vector rather than 50 distinct problems.',
835
+ example: 3
836
+ },
810
837
  pnlTotal: {
811
838
  type: 'number',
812
839
  format: 'double',
@@ -939,11 +966,153 @@ export const EquityPointSchema = {
939
966
  } as const;
940
967
 
941
968
  export const strategyIdSchema = {
942
- description: 'Unique identifier for a compiled strategy',
969
+ description: `Unique identifier for a compiled strategy, derived from the source itself: the same code
970
+ always yields the same id, for every caller, whatever its formatting. See
971
+ \`POST /strategy\` for exactly which rewrites preserve it and which do not.
972
+ `,
943
973
  type: 'string',
944
974
  example: '6bsh31ikwkuivhtgcoa6s4'
945
975
  } as const;
946
976
 
977
+ export const NoticeSchema = {
978
+ type: 'object',
979
+ description: `A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
980
+ knowing about how the strategy is wired, not necessarily an error.
981
+ `,
982
+ required: ['level', 'code', 'message'],
983
+ properties: {
984
+ level: {
985
+ type: 'string',
986
+ description: 'Severity as the engine classified it.',
987
+ example: 'WARN'
988
+ },
989
+ code: {
990
+ type: 'string',
991
+ description: 'Stable identifier for the kind of finding; safe to match on.',
992
+ example: 'indicator.bar-data-on-ticker-path'
993
+ },
994
+ message: {
995
+ type: 'string',
996
+ description: 'Human-readable explanation.',
997
+ example: 'Indicator requires bar data but is on the ticker path'
998
+ },
999
+ provenance: {
1000
+ type: 'string',
1001
+ enum: ['execute', 'compile-dry-run'],
1002
+ description: `Where it came from, which matters because the two silences differ: an empty list from a
1003
+ real run (\`execute\`) is a clean bill of health, while an empty list from
1004
+ \`compile-dry-run\` is only a lower bound over a bounded synthetic series.
1005
+ `,
1006
+ example: 'compile-dry-run'
1007
+ }
1008
+ }
1009
+ } as const;
1010
+
1011
+ export const StrategyStateSchema = {
1012
+ type: 'object',
1013
+ description: `What is known about a registered strategy: that it compiled, and what validating it found.
1014
+
1015
+ **\`validation: passed\` does not mean the strategy is correct.** It means the class loaded and
1016
+ survived the first event of a short synthetic run — a floor, not a guarantee. When
1017
+ \`dryRunIncomplete\` is true it is a lower floor still, because the run did not finish.
1018
+ `,
1019
+ required: ['strategyId', 'validation'],
1020
+ properties: {
1021
+ strategyId: {
1022
+ '$ref': '#/components/schemas/strategyId'
1023
+ },
1024
+ validation: {
1025
+ type: 'string',
1026
+ enum: ['not_validated', 'pending', 'passed', 'failed'],
1027
+ description: `* \`not_validated\` — registered, never checked. \`POST /strategy/{strategyId}/validate\`
1028
+ checks it.
1029
+ * \`pending\` — a check was asked for and has not answered yet.
1030
+ * \`passed\` — the class loaded and survived its first event.
1031
+ * \`failed\` — it did not; \`detail\` says how.
1032
+ `,
1033
+ example: 'passed'
1034
+ },
1035
+ compiledAt: {
1036
+ type: 'string',
1037
+ format: 'date-time',
1038
+ description: 'When the live compilation was produced.'
1039
+ },
1040
+ requiredSources: {
1041
+ type: 'array',
1042
+ description: `The market data a strategy needs, read off the compiled class rather than off anything
1043
+ you sent — \`TickerStrategy\`, \`KlineStrategy\` and \`FundingRateStrategy\` each declare one,
1044
+ and a \`MultiSourceStrategy\` declares a set.
1045
+
1046
+ **Absent is not "needs nothing".** A strategy always needs market data, so an absent
1047
+ field never means an empty requirement — it means the platform could not establish the
1048
+ answer without constructing your strategy, which it will not do to fill in a field.
1049
+ That happens for a \`MultiSourceStrategy\`, for a class that overrides
1050
+ \`getMarketDataSource()\`, and for anything registered before this field existed;
1051
+ re-registering the source fills it in.
1052
+ `,
1053
+ items: {
1054
+ type: 'string',
1055
+ enum: ['Ticker', 'KLine', 'FundingRate']
1056
+ },
1057
+ example: ['Ticker']
1058
+ },
1059
+ validatedAt: {
1060
+ type: 'string',
1061
+ format: 'date-time',
1062
+ description: 'When the verdict was recorded. Absent until there is one.'
1063
+ },
1064
+ detail: {
1065
+ type: 'string',
1066
+ description: `Why validation failed, or why a queued check has not reported. Present on \`failed\`, and
1067
+ alongside \`validationStalled\`.
1068
+ `
1069
+ },
1070
+ notices: {
1071
+ type: 'array',
1072
+ description: `What the run surfaced. An empty or absent list is not a clean bill of health when
1073
+ \`dryRunIncomplete\` is true — see that field.
1074
+ `,
1075
+ items: {
1076
+ '$ref': '#/components/schemas/Notice'
1077
+ }
1078
+ },
1079
+ noticesTruncated: {
1080
+ type: 'integer',
1081
+ description: 'How many notices were dropped past the cap. Absent when none were.',
1082
+ example: 3
1083
+ },
1084
+ dryRunIncomplete: {
1085
+ type: 'boolean',
1086
+ description: `The check did not finish its budget — it ran out of time, was refused because the
1087
+ platform was already holding too many unfinishable runs, or hit a failure attributable to
1088
+ the synthetic instrument rather than to your strategy. The verdict stands as far as it
1089
+ went; it simply reached less than a full run would.
1090
+ `
1091
+ },
1092
+ validationStalled: {
1093
+ type: 'boolean',
1094
+ description: `A queued check has not reported for far longer than one takes. Nothing is disproved about
1095
+ the strategy — the check has not run. Stop waiting and re-request it later.
1096
+ `
1097
+ }
1098
+ },
1099
+ example: {
1100
+ strategyId: '6bsh31ikwkuivhtgcoa6s4',
1101
+ validation: 'passed',
1102
+ compiledAt: '2026-08-04T16:23:04Z',
1103
+ requiredSources: ['Ticker'],
1104
+ validatedAt: '2026-08-04T16:24:11Z',
1105
+ notices: [
1106
+ {
1107
+ level: 'WARN',
1108
+ code: 'indicator.bar-data-on-ticker-path',
1109
+ message: 'Indicator requires bar data but is on the ticker path',
1110
+ provenance: 'compile-dry-run'
1111
+ }
1112
+ ]
1113
+ }
1114
+ } as const;
1115
+
947
1116
  export const AuthTokenResponseSchema = {
948
1117
  type: 'object',
949
1118
  required: ['access_token', 'token_type', 'expires_in', 'tier'],
@@ -1,7 +1,7 @@
1
1
  // This file is auto-generated by @hey-api/openapi-ts
2
2
 
3
3
  import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
4
- import type { AuthenticateData, AuthenticateResponse, AuthenticateError, ListExchangesData, ListExchangesResponse, ListInstrumentsData, ListInstrumentsResponse, ListInstrumentsError, ListSegmentInstrumentsData, ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, DownloadTickersData, DownloadTickersResponse, DownloadTickersError, DownloadKlinesData, DownloadKlinesResponse, DownloadKlinesError, CompileStrategyData, CompileStrategyResponse, CompileStrategyError, GetStrategyData, GetStrategyResponse, GetStrategyError, PrepareBacktestData, PrepareBacktestResponse, PrepareBacktestError, GetPrepareStatusData, GetPrepareStatusResponse, GetPrepareStatusError, ExecuteSweepData, ExecuteSweepResponse, ExecuteSweepError, CancelSweepData, CancelSweepResponse, CancelSweepError, GetSweepResultData, GetSweepResultResponse, GetSweepResultError, ExecuteBacktestData, ExecuteBacktestResponse, ExecuteBacktestError, CancelBacktestData, CancelBacktestResponse, CancelBacktestError, GetBacktestResultData, GetBacktestResultResponse, GetBacktestResultError } from './types.gen';
4
+ import type { AuthenticateData, AuthenticateResponse, AuthenticateError, ListExchangesData, ListExchangesResponse, ListInstrumentsData, ListInstrumentsResponse, ListInstrumentsError, ListSegmentInstrumentsData, ListSegmentInstrumentsResponse, ListSegmentInstrumentsError, DownloadTickersData, DownloadTickersResponse, DownloadTickersError, DownloadKlinesData, DownloadKlinesResponse, DownloadKlinesError, CompileStrategyData, CompileStrategyResponse, CompileStrategyError, ValidateStrategyData, ValidateStrategyResponse, ValidateStrategyError, GetStrategyData, GetStrategyResponse, GetStrategyError, PrepareBacktestData, PrepareBacktestResponse, PrepareBacktestError, GetPrepareStatusData, GetPrepareStatusResponse, GetPrepareStatusError, ExecuteSweepData, ExecuteSweepResponse, ExecuteSweepError, CancelSweepData, CancelSweepResponse, CancelSweepError, GetSweepResultData, GetSweepResultResponse, GetSweepResultError, ExecuteBacktestData, ExecuteBacktestResponse, ExecuteBacktestError, CancelBacktestData, CancelBacktestResponse, CancelBacktestError, GetBacktestResultData, GetBacktestResultResponse, GetBacktestResultError } from './types.gen';
5
5
  import { client as _heyApiClient } from './client.gen';
6
6
 
7
7
  export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
@@ -142,14 +142,28 @@ export const downloadKlines = <ThrowOnError extends boolean = false>(options: Op
142
142
  };
143
143
 
144
144
  /**
145
- * Compile a strategy from source code
146
- * Submits raw strategy source for compilation. By default the call is synchronous and returns
147
- * the `strategyId` once compilation succeeds. Set the header `X-Compile-Async: true` to enqueue
148
- * the compile task and return immediately with a `jobId` — poll
149
- * `GET /strategy/{strategyId}` to check status.
145
+ * Compile and register a strategy
146
+ * Compiles raw strategy source and registers it, returning its `strategyId`.
150
147
  *
151
- * The `strategyId` is deterministic: the same source for the same user always produces the
152
- * same id.
148
+ * **This answers one question: is the source valid Java.** It compiles, registers, and hands
149
+ * back the id — nothing more. Whether the class can actually run is
150
+ * `POST /strategy/{strategyId}/validate`, and everything known about a strategy, validation
151
+ * included, is read from `GET /strategy/{strategyId}`. One place to ask, so there is no second
152
+ * answer to keep in step.
153
+ *
154
+ * The `strategyId` is derived from what the code *means*, not from how it is written. Adding a
155
+ * comment, inserting a blank line, re-indenting, reordering imports, or moving a method around
156
+ * all return the **same** id — you have not created a second strategy. Renaming a variable,
157
+ * changing an identifier's case, reordering fields, or reordering statements inside a method
158
+ * return a **different** one.
159
+ *
160
+ * Two rules follow, and they are worth designing around:
161
+ *
162
+ * - re-submitting a strategy you have only reformatted is free, and gives you back the id you
163
+ * already had, along with any validation already recorded against it;
164
+ * - the id says nothing about *behaviour*. Two sources that compute the same thing by
165
+ * different means are two strategies, because deciding otherwise would mean deciding program
166
+ * equivalence.
153
167
  *
154
168
  */
155
169
  export const compileStrategy = <ThrowOnError extends boolean = false>(options: Options<CompileStrategyData, ThrowOnError>) => {
@@ -171,9 +185,39 @@ export const compileStrategy = <ThrowOnError extends boolean = false>(options: O
171
185
  };
172
186
 
173
187
  /**
174
- * Get a strategy by id, including its compile status
175
- * Polls the status of a strategy compilation. Useful when the strategy was submitted with
176
- * `X-Compile-Async: true`. Returns the resolved `strategyId` once compilation completes.
188
+ * Check that a registered strategy can actually run
189
+ * Instantiates the compiled class and drives it through a bounded synthetic series, so a wiring
190
+ * fault surfaces here instead of at your first backtest. The verdict — pass or fail, plus any
191
+ * engine notices — is recorded and served from `GET /strategy/{strategyId}`.
192
+ *
193
+ * **Idempotent.** If a verdict already exists for the current compilation it comes straight
194
+ * back with `200` and nothing is queued. Otherwise the check is queued and this returns `202`;
195
+ * poll `GET /strategy/{strategyId}` until `validation` is `passed` or `failed`.
196
+ *
197
+ * Recompiling supersedes a verdict, which makes this callable again — the old answer described
198
+ * bytecode that is no longer what would run.
199
+ *
200
+ */
201
+ export const validateStrategy = <ThrowOnError extends boolean = false>(options: Options<ValidateStrategyData, ThrowOnError>) => {
202
+ return (options.client ?? _heyApiClient).post<ValidateStrategyResponse, ValidateStrategyError, ThrowOnError>({
203
+ security: [
204
+ {
205
+ scheme: 'bearer',
206
+ type: 'http'
207
+ }
208
+ ],
209
+ url: '/strategy/{strategyId}/validate',
210
+ ...options
211
+ });
212
+ };
213
+
214
+ /**
215
+ * Get a strategy by id, including its validation state
216
+ * Reports that the strategy is registered — implied by a `200` at all — and what validating it
217
+ * found.
218
+ *
219
+ * A `404` means one thing: no such registered strategy for this user. It is never a stale or
220
+ * expired answer; registration and verdict are stored durably, not cached.
177
221
  *
178
222
  */
179
223
  export const getStrategy = <ThrowOnError extends boolean = false>(options: Options<GetStrategyData, ThrowOnError>) => {
@@ -351,6 +395,10 @@ export const cancelBacktest = <ThrowOnError extends boolean = false>(options: Op
351
395
  * Retrieves the current state and results of the execute job identified by `jobId`.
352
396
  * Poll until `state.status` is `Completed`, `Failed`, or `Aborted`.
353
397
  *
398
+ * A `202` means the result is not readable yet — keep polling. It is never a terminal
399
+ * outcome, and it carries no `state`, so a poll loop that stops on a terminal status will
400
+ * not stop on it.
401
+ *
354
402
  */
355
403
  export const getBacktestResult = <ThrowOnError extends boolean = false>(options: Options<GetBacktestResultData, ThrowOnError>) => {
356
404
  return (options.client ?? _heyApiClient).get<GetBacktestResultResponse, GetBacktestResultError, ThrowOnError>({
@@ -432,7 +432,7 @@ export type BacktestJobResult = {
432
432
  };
433
433
 
434
434
  /**
435
- * Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below.
435
+ * Execution result map. Always includes core fields (hostName, iops, strategyId, instrument). Yield metrics (pnlTotal, pnlTotalPercent, totalTrades, winRate, equityCurve, etc.) are present when the strategy emitted at least one trade. When signal storage is enabled, includes signal fields described below. `notices` carries what the run had to say about itself, and is absent when it had nothing.
436
436
  */
437
437
  export type ResultMap = {
438
438
  /**
@@ -444,13 +444,36 @@ export type ResultMap = {
444
444
  */
445
445
  iops?: number;
446
446
  /**
447
- * Identifier of the compiled strategy that produced this result
447
+ * **Not the `strategyId` you compiled with** this is the execution context id,
448
+ * `strategy:<user>:<strategyId>`. The compiled strategy's id is the last `:`-separated
449
+ * segment; that, not this whole string, is what `GET /strategy/{strategyId}` takes.
450
+ *
451
+ * Take the segment after the last `:` rather than counting from the front: the shape has
452
+ * changed once already and callers that indexed a fixed position broke on it.
453
+ *
448
454
  */
449
455
  strategyId: string;
450
456
  /**
451
457
  * The instrument (currency pair) that was backtested
452
458
  */
453
459
  instrument: string;
460
+ /**
461
+ * Diagnostics the engine raised over this run, each with `provenance: execute`.
462
+ *
463
+ * **Absent means nothing was raised.** This is the one surface where silence is a real
464
+ * answer: the run happened, over your data, start to finish, and the engine found nothing
465
+ * worth saying. That is not true of the compile path, where an empty list only means a
466
+ * short synthetic series reached nothing — see `GET /strategy/{strategyId}`.
467
+ *
468
+ * Notices are raised on failed and aborted runs too, and those are the ones most worth
469
+ * reading: a run that produced no trades often did so for a reason stated here.
470
+ *
471
+ */
472
+ notices?: Array<Notice>;
473
+ /**
474
+ * How many notices were dropped past the cap of 50. Absent when none were. A large value usually means one fault repeating per instrument or per parameter vector rather than 50 distinct problems.
475
+ */
476
+ noticesTruncated?: number;
454
477
  /**
455
478
  * Total profit and loss in the output currency
456
479
  */
@@ -532,10 +555,113 @@ export type EquityPoint = {
532
555
  };
533
556
 
534
557
  /**
535
- * Unique identifier for a compiled strategy
558
+ * Unique identifier for a compiled strategy, derived from the source itself: the same code
559
+ * always yields the same id, for every caller, whatever its formatting. See
560
+ * `POST /strategy` for exactly which rewrites preserve it and which do not.
561
+ *
536
562
  */
537
563
  export type StrategyId = string;
538
564
 
565
+ /**
566
+ * A diagnostic the engine raised while the strategy ran. Advisory: it describes something worth
567
+ * knowing about how the strategy is wired, not necessarily an error.
568
+ *
569
+ */
570
+ export type Notice = {
571
+ /**
572
+ * Severity as the engine classified it.
573
+ */
574
+ level: string;
575
+ /**
576
+ * Stable identifier for the kind of finding; safe to match on.
577
+ */
578
+ code: string;
579
+ /**
580
+ * Human-readable explanation.
581
+ */
582
+ message: string;
583
+ /**
584
+ * Where it came from, which matters because the two silences differ: an empty list from a
585
+ * real run (`execute`) is a clean bill of health, while an empty list from
586
+ * `compile-dry-run` is only a lower bound over a bounded synthetic series.
587
+ *
588
+ */
589
+ provenance?: 'execute' | 'compile-dry-run';
590
+ };
591
+
592
+ /**
593
+ * What is known about a registered strategy: that it compiled, and what validating it found.
594
+ *
595
+ * **`validation: passed` does not mean the strategy is correct.** It means the class loaded and
596
+ * survived the first event of a short synthetic run — a floor, not a guarantee. When
597
+ * `dryRunIncomplete` is true it is a lower floor still, because the run did not finish.
598
+ *
599
+ */
600
+ export type StrategyState = {
601
+ strategyId: StrategyId;
602
+ /**
603
+ * * `not_validated` — registered, never checked. `POST /strategy/{strategyId}/validate`
604
+ * checks it.
605
+ * * `pending` — a check was asked for and has not answered yet.
606
+ * * `passed` — the class loaded and survived its first event.
607
+ * * `failed` — it did not; `detail` says how.
608
+ *
609
+ */
610
+ validation: 'not_validated' | 'pending' | 'passed' | 'failed';
611
+ /**
612
+ * When the live compilation was produced.
613
+ */
614
+ compiledAt?: string;
615
+ /**
616
+ * The market data a strategy needs, read off the compiled class rather than off anything
617
+ * you sent — `TickerStrategy`, `KlineStrategy` and `FundingRateStrategy` each declare one,
618
+ * and a `MultiSourceStrategy` declares a set.
619
+ *
620
+ * **Absent is not "needs nothing".** A strategy always needs market data, so an absent
621
+ * field never means an empty requirement — it means the platform could not establish the
622
+ * answer without constructing your strategy, which it will not do to fill in a field.
623
+ * That happens for a `MultiSourceStrategy`, for a class that overrides
624
+ * `getMarketDataSource()`, and for anything registered before this field existed;
625
+ * re-registering the source fills it in.
626
+ *
627
+ */
628
+ requiredSources?: Array<'Ticker' | 'KLine' | 'FundingRate'>;
629
+ /**
630
+ * When the verdict was recorded. Absent until there is one.
631
+ */
632
+ validatedAt?: string;
633
+ /**
634
+ * Why validation failed, or why a queued check has not reported. Present on `failed`, and
635
+ * alongside `validationStalled`.
636
+ *
637
+ */
638
+ detail?: string;
639
+ /**
640
+ * What the run surfaced. An empty or absent list is not a clean bill of health when
641
+ * `dryRunIncomplete` is true — see that field.
642
+ *
643
+ */
644
+ notices?: Array<Notice>;
645
+ /**
646
+ * How many notices were dropped past the cap. Absent when none were.
647
+ */
648
+ noticesTruncated?: number;
649
+ /**
650
+ * The check did not finish its budget — it ran out of time, was refused because the
651
+ * platform was already holding too many unfinishable runs, or hit a failure attributable to
652
+ * the synthetic instrument rather than to your strategy. The verdict stands as far as it
653
+ * went; it simply reached less than a full run would.
654
+ *
655
+ */
656
+ dryRunIncomplete?: boolean;
657
+ /**
658
+ * A queued check has not reported for far longer than one takes. Nothing is disproved about
659
+ * the strategy — the check has not run. Stop waiting and re-request it later.
660
+ *
661
+ */
662
+ validationStalled?: boolean;
663
+ };
664
+
539
665
  export type AuthTokenResponse = {
540
666
  /**
541
667
  * Short-lived HS256 JWT. Send as `Authorization: Bearer <token>` on all other endpoints.
@@ -813,12 +939,6 @@ export type CompileStrategyData = {
813
939
  * Raw strategy Java source code
814
940
  */
815
941
  body: string;
816
- headers?: {
817
- /**
818
- * When `true`, compile asynchronously and return `202` with a `jobId`.
819
- */
820
- 'X-Compile-Async'?: boolean;
821
- };
822
942
  path?: never;
823
943
  query?: never;
824
944
  url: '/strategy';
@@ -826,35 +946,76 @@ export type CompileStrategyData = {
826
946
 
827
947
  export type CompileStrategyErrors = {
828
948
  /**
829
- * Invalid strategy (compilation error)
949
+ * The source is not valid Java; the message carries the compiler diagnostics. Nothing is
950
+ * registered, so there is no id to look up afterwards.
951
+ *
830
952
  */
831
953
  400: ResponseError;
954
+ /**
955
+ * Too many compilations in flight. Retry later.
956
+ */
957
+ 429: ResponseError;
832
958
  };
833
959
 
834
960
  export type CompileStrategyError = CompileStrategyErrors[keyof CompileStrategyErrors];
835
961
 
836
962
  export type CompileStrategyResponses = {
837
963
  /**
838
- * Strategy compiled successfully (sync mode)
964
+ * Compiled and registered
839
965
  */
840
966
  200: {
841
967
  strategyId: StrategyId;
842
968
  };
969
+ };
970
+
971
+ export type CompileStrategyResponse = CompileStrategyResponses[keyof CompileStrategyResponses];
972
+
973
+ export type ValidateStrategyData = {
974
+ body?: never;
975
+ path: {
976
+ /**
977
+ * The id returned by `POST /strategy`
978
+ */
979
+ strategyId: StrategyId;
980
+ };
981
+ query?: never;
982
+ url: '/strategy/{strategyId}/validate';
983
+ };
984
+
985
+ export type ValidateStrategyErrors = {
843
986
  /**
844
- * Compile task accepted (async mode set `X-Compile-Async: true`)
987
+ * No such registered strategy for this user
845
988
  */
846
- 202: AcceptedJob;
989
+ 404: ResponseError;
847
990
  };
848
991
 
849
- export type CompileStrategyResponse = CompileStrategyResponses[keyof CompileStrategyResponses];
992
+ export type ValidateStrategyError = ValidateStrategyErrors[keyof ValidateStrategyErrors];
993
+
994
+ export type ValidateStrategyResponses = {
995
+ /**
996
+ * Already validated; the recorded verdict, unchanged
997
+ */
998
+ 200: StrategyState;
999
+ /**
1000
+ * Validation queued. Not a terminal outcome — poll `GET /strategy/{strategyId}` until
1001
+ * `validation` leaves `pending`.
1002
+ *
1003
+ */
1004
+ 202: {
1005
+ strategyId: StrategyId;
1006
+ validation: 'pending';
1007
+ };
1008
+ };
1009
+
1010
+ export type ValidateStrategyResponse = ValidateStrategyResponses[keyof ValidateStrategyResponses];
850
1011
 
851
1012
  export type GetStrategyData = {
852
1013
  body?: never;
853
1014
  path: {
854
1015
  /**
855
- * The id returned by `POST /strategy` (sync) or the `jobId` returned in async mode
1016
+ * The id returned by `POST /strategy`
856
1017
  */
857
- strategyId: string;
1018
+ strategyId: StrategyId;
858
1019
  };
859
1020
  query?: never;
860
1021
  url: '/strategy/{strategyId}';
@@ -862,7 +1023,7 @@ export type GetStrategyData = {
862
1023
 
863
1024
  export type GetStrategyErrors = {
864
1025
  /**
865
- * Strategy compile job not found
1026
+ * No such registered strategy for this user
866
1027
  */
867
1028
  404: ResponseError;
868
1029
  };
@@ -871,20 +1032,9 @@ export type GetStrategyError = GetStrategyErrors[keyof GetStrategyErrors];
871
1032
 
872
1033
  export type GetStrategyResponses = {
873
1034
  /**
874
- * Strategy compile state
1035
+ * Strategy state
875
1036
  */
876
- 200: {
877
- /**
878
- * Compile job id (only set in async mode)
879
- */
880
- jobId?: string;
881
- status: 'New' | 'Started' | 'Completed' | 'Aborted' | 'Failed';
882
- strategyId?: StrategyId;
883
- /**
884
- * Compilation error messages when `status` is `Failed`
885
- */
886
- statusDetail?: string | null;
887
- };
1037
+ 200: StrategyState;
888
1038
  };
889
1039
 
890
1040
  export type GetStrategyResponse = GetStrategyResponses[keyof GetStrategyResponses];
@@ -1219,6 +1369,21 @@ export type GetBacktestResultResponses = {
1219
1369
  * Backtesting execution result
1220
1370
  */
1221
1371
  200: BacktestJobResult;
1372
+ /**
1373
+ * The job is known but its result is not readable yet — keep polling.
1374
+ *
1375
+ * Returned in two situations, both of which mean "ask again", never "you are done":
1376
+ * the job has not produced its result yet, or the job reached a terminal status while
1377
+ * its stored result could not be read back. The response body is an empty object: it
1378
+ * deliberately carries no `state`, so a client cannot mistake it for a finished result.
1379
+ *
1380
+ * Treat any `202` as a signal to continue the poll loop under your existing timeout.
1381
+ * Never treat it as a terminal outcome.
1382
+ *
1383
+ */
1384
+ 202: {
1385
+ [key: string]: never;
1386
+ };
1222
1387
  };
1223
1388
 
1224
1389
  export type GetBacktestResultResponse = GetBacktestResultResponses[keyof GetBacktestResultResponses];