@qtsurfer/api-client 0.18.0 → 0.19.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.
@@ -859,6 +859,8 @@ export const SweepBaseConfigSchema = {
859
859
  format: "double",
860
860
  exclusiveMinimum: 0,
861
861
  maximum: 100,
862
+ description:
863
+ "Share of the free balance each entry locks, in percent (0-100]. Omitted, a backtest sizes every entry with everything available.",
862
864
  },
863
865
  },
864
866
  } as const;
@@ -2860,6 +2862,73 @@ export const StartLiveRequestSchema = {
2860
2862
  description: {
2861
2863
  type: "string",
2862
2864
  },
2865
+ paper: {
2866
+ $ref: "#/components/schemas/LivePaperConfig",
2867
+ },
2868
+ },
2869
+ } as const;
2870
+
2871
+ export const LivePaperConfigSchema = {
2872
+ type: "object",
2873
+ additionalProperties: false,
2874
+ description: `Paper trading for this run: the same economics as a backtest's \`baseConfig\` (same fields,
2875
+ defaults and limits), plus \`output\`. An empty object takes every default. Each quote
2876
+ currency the run trades gets its own simulated account, opened with \`initialFunding\` in
2877
+ that currency; accounts are never added together. As returned on a run, the block is
2878
+ normalised: \`feeRate\` is resolved into \`buyFeeRate\`/\`sellFeeRate\` and defaults are filled
2879
+ in.
2880
+ `,
2881
+ properties: {
2882
+ initialFunding: {
2883
+ type: "number",
2884
+ format: "double",
2885
+ exclusiveMinimum: 0,
2886
+ maximum: 1000000000,
2887
+ default: 100,
2888
+ description:
2889
+ "Starting capital of each account, in that account's own quote currency.",
2890
+ },
2891
+ feeRate: {
2892
+ type: "number",
2893
+ format: "double",
2894
+ minimum: 0,
2895
+ default: 0.001,
2896
+ description:
2897
+ "Fee rate for both sides (0.001 = 0.1%). Accepted on start; returned resolved into the two fields below.",
2898
+ },
2899
+ buyFeeRate: {
2900
+ type: "number",
2901
+ format: "double",
2902
+ minimum: 0,
2903
+ description: "Buy-side fee rate; overrides `feeRate`.",
2904
+ },
2905
+ sellFeeRate: {
2906
+ type: "number",
2907
+ format: "double",
2908
+ minimum: 0,
2909
+ description: "Sell-side fee rate; overrides `feeRate`.",
2910
+ },
2911
+ feeLeg: {
2912
+ type: "string",
2913
+ enum: ["RECEIVED", "QUOTE", "BASE"],
2914
+ default: "RECEIVED",
2915
+ description:
2916
+ "Which asset fees are charged in. Case-insensitive on start.",
2917
+ },
2918
+ percentAmountToLock: {
2919
+ type: "number",
2920
+ format: "double",
2921
+ exclusiveMinimum: 0,
2922
+ maximum: 100,
2923
+ description:
2924
+ "Share of the account's free balance each entry locks, in percent. Omitted, the strategy's own setting applies, and without one 10%.",
2925
+ },
2926
+ output: {
2927
+ type: "string",
2928
+ enum: ["separate", "mix"],
2929
+ default: "separate",
2930
+ description: `\`separate\` keeps paper output out of the run's signals (read it with \`GET /live/{runId}/paper\`); \`mix\` also interleaves it into the run's signals as \`type: paper\`, right after the signal that caused it.`,
2931
+ },
2863
2932
  },
2864
2933
  } as const;
2865
2934
 
@@ -2979,6 +3048,15 @@ export const LiveRunSchema = {
2979
3048
  description:
2980
3049
  "The sandbox trial's promotion verdict, once one exists. Shape is not yet stabilized as public API — treat as opaque diagnostics.",
2981
3050
  },
3051
+ paper: {
3052
+ allOf: [
3053
+ {
3054
+ $ref: "#/components/schemas/LivePaperConfig",
3055
+ },
3056
+ ],
3057
+ description:
3058
+ "The run's paper trading configuration as accepted at start, normalised. Absent when the run has no paper trading.",
3059
+ },
2982
3060
  },
2983
3061
  } as const;
2984
3062
 
@@ -3191,6 +3269,217 @@ export const LiveSignalPageSchema = {
3191
3269
  },
3192
3270
  } as const;
3193
3271
 
3272
+ export const LivePaperSchema = {
3273
+ type: "object",
3274
+ required: ["runId", "stage", "accounts"],
3275
+ properties: {
3276
+ runId: {
3277
+ type: "string",
3278
+ },
3279
+ stage: {
3280
+ type: "string",
3281
+ enum: ["SANDBOX", "LIVE"],
3282
+ },
3283
+ accounts: {
3284
+ type: "array",
3285
+ items: {
3286
+ $ref: "#/components/schemas/LivePaperAccount",
3287
+ },
3288
+ },
3289
+ },
3290
+ } as const;
3291
+
3292
+ export const LivePaperAccountSchema = {
3293
+ type: "object",
3294
+ required: [
3295
+ "currency",
3296
+ "initialFunding",
3297
+ "equity",
3298
+ "realisedPnl",
3299
+ "trades",
3300
+ "gaps",
3301
+ "openPositions",
3302
+ ],
3303
+ description: "One simulated account — one per quote currency the run trades.",
3304
+ properties: {
3305
+ currency: {
3306
+ type: "string",
3307
+ description: "The account's quote currency; every amount below is in it.",
3308
+ },
3309
+ initialFunding: {
3310
+ type: "number",
3311
+ format: "double",
3312
+ },
3313
+ equity: {
3314
+ type: "number",
3315
+ format: "double",
3316
+ description: "Latest recorded equity — see `equityKind`.",
3317
+ },
3318
+ equityAtMs: {
3319
+ type: "integer",
3320
+ format: "int64",
3321
+ description:
3322
+ "Market time of that value. Absent while the account holds its starting capital.",
3323
+ },
3324
+ equityKind: {
3325
+ type: "string",
3326
+ enum: ["equity", "mark"],
3327
+ description: `\`equity\` at a closed trade, \`mark\` at a periodic mark-to-market (includes open positions at market price).`,
3328
+ },
3329
+ realisedPnl: {
3330
+ type: "number",
3331
+ format: "double",
3332
+ description: "Sum of the PnL of the closed trades.",
3333
+ },
3334
+ trades: {
3335
+ type: "integer",
3336
+ format: "int64",
3337
+ description: "Closed trades.",
3338
+ },
3339
+ gaps: {
3340
+ type: "integer",
3341
+ description:
3342
+ "Times open positions were lost because the run was restarted with them open.",
3343
+ },
3344
+ openPositions: {
3345
+ type: "array",
3346
+ items: {
3347
+ $ref: "#/components/schemas/LivePaperPosition",
3348
+ },
3349
+ },
3350
+ kpi: {
3351
+ $ref: "#/components/schemas/LivePaperKpi",
3352
+ },
3353
+ },
3354
+ } as const;
3355
+
3356
+ export const LivePaperPositionSchema = {
3357
+ type: "object",
3358
+ required: ["instrument", "base", "cost"],
3359
+ properties: {
3360
+ instrument: {
3361
+ type: "string",
3362
+ example: "BTC/USDT",
3363
+ },
3364
+ base: {
3365
+ type: "number",
3366
+ format: "double",
3367
+ description: "Amount held, in the base asset.",
3368
+ },
3369
+ cost: {
3370
+ type: "number",
3371
+ format: "double",
3372
+ description: "What it cost, in the account's currency.",
3373
+ },
3374
+ },
3375
+ } as const;
3376
+
3377
+ export const LivePaperKpiSchema = {
3378
+ type: "object",
3379
+ description:
3380
+ "The same KPIs a backtest reports, over the trades closed so far. Absent until the first closed trade is recorded.",
3381
+ properties: {
3382
+ totalTrades: {
3383
+ type: "integer",
3384
+ format: "int64",
3385
+ },
3386
+ winCount: {
3387
+ type: "integer",
3388
+ format: "int64",
3389
+ },
3390
+ lossCount: {
3391
+ type: "integer",
3392
+ format: "int64",
3393
+ },
3394
+ winRate: {
3395
+ type: "number",
3396
+ format: "double",
3397
+ description: "Ratio (0.5 = half the trades won).",
3398
+ },
3399
+ pnlTotal: {
3400
+ type: "number",
3401
+ format: "double",
3402
+ },
3403
+ pnlTotalPercent: {
3404
+ type: "number",
3405
+ format: "double",
3406
+ description: "Percent of `initialFunding` (0-100 scale).",
3407
+ },
3408
+ sharpeRatio: {
3409
+ type: ["number", "null"],
3410
+ format: "double",
3411
+ },
3412
+ sortinoRatio: {
3413
+ type: ["number", "null"],
3414
+ format: "double",
3415
+ },
3416
+ cagr: {
3417
+ type: ["number", "null"],
3418
+ format: "double",
3419
+ description: "Ratio (0.15 for 15%).",
3420
+ },
3421
+ maxDrawdown: {
3422
+ type: "number",
3423
+ format: "double",
3424
+ },
3425
+ maxDrawdownPercent: {
3426
+ type: "number",
3427
+ format: "double",
3428
+ description: "Percent (0-100 scale).",
3429
+ },
3430
+ },
3431
+ } as const;
3432
+
3433
+ export const LivePaperEquityPageSchema = {
3434
+ type: "object",
3435
+ required: ["points"],
3436
+ properties: {
3437
+ points: {
3438
+ type: "array",
3439
+ items: {
3440
+ $ref: "#/components/schemas/LivePaperEquityPoint",
3441
+ },
3442
+ },
3443
+ _links: {
3444
+ type: "object",
3445
+ properties: {
3446
+ next: {
3447
+ type: "object",
3448
+ properties: {
3449
+ href: {
3450
+ type: "string",
3451
+ },
3452
+ },
3453
+ },
3454
+ },
3455
+ },
3456
+ },
3457
+ } as const;
3458
+
3459
+ export const LivePaperEquityPointSchema = {
3460
+ type: "object",
3461
+ required: ["currency", "kind", "eventTsMs"],
3462
+ properties: {
3463
+ currency: {
3464
+ type: "string",
3465
+ },
3466
+ kind: {
3467
+ type: "string",
3468
+ enum: ["equity", "mark", "gap"],
3469
+ },
3470
+ eventTsMs: {
3471
+ type: "integer",
3472
+ format: "int64",
3473
+ description: "Market time of the point.",
3474
+ },
3475
+ equity: {
3476
+ type: "number",
3477
+ format: "double",
3478
+ description: "Absent on a `gap`.",
3479
+ },
3480
+ },
3481
+ } as const;
3482
+
3194
3483
  export const LiveSignalSchema = {
3195
3484
  type: "object",
3196
3485
  required: [
@@ -3230,11 +3519,12 @@ export const LiveSignalSchema = {
3230
3519
  },
3231
3520
  type: {
3232
3521
  type: "string",
3233
- enum: ["hint", "info", "marker", "command"],
3522
+ enum: ["hint", "info", "marker", "command", "paper"],
3523
+ description: `\`paper\` items appear only for a run whose \`paper.output\` is \`mix\`; they are not relayed over the WebSocket channel.`,
3234
3524
  },
3235
3525
  kind: {
3236
3526
  type: ["string", "null"],
3237
- description: `\`BUY\`/\`SELL\` for a \`hint\`, the command name for a \`command\`, absent otherwise.`,
3527
+ description: `\`BUY\`/\`SELL\` for a \`hint\`, the command name for a \`command\`; for \`paper\`, what the item is: \`fill\`, \`trade\`, \`equity\`, \`mark\`, \`kpi\` or \`gap\`. Absent otherwise.`,
3238
3528
  },
3239
3529
  eventTsMs: {
3240
3530
  type: "integer",
@@ -3247,7 +3537,16 @@ export const LiveSignalSchema = {
3247
3537
  description: "Time it was published — always at or after `eventTsMs`.",
3248
3538
  },
3249
3539
  instrument: {
3250
- $ref: "#/components/schemas/LiveSignalInstrument",
3540
+ anyOf: [
3541
+ {
3542
+ $ref: "#/components/schemas/LiveSignalInstrument",
3543
+ },
3544
+ {
3545
+ type: "null",
3546
+ },
3547
+ ],
3548
+ description:
3549
+ "The instrument the signal is about. `null` only for a `paper` item about a whole account (`equity`, `mark`, `kpi`), whose `data.currency` names the account.",
3251
3550
  },
3252
3551
  order: {
3253
3552
  $ref: "#/components/schemas/LiveSignalOrder",
@@ -124,6 +124,12 @@ import type {
124
124
  GetLiveRunSignalsData,
125
125
  GetLiveRunSignalsResponse,
126
126
  GetLiveRunSignalsError,
127
+ GetLiveRunPaperData,
128
+ GetLiveRunPaperResponse,
129
+ GetLiveRunPaperError,
130
+ GetLiveRunPaperEquityData,
131
+ GetLiveRunPaperEquityResponse,
132
+ GetLiveRunPaperEquityError,
127
133
  MintLiveConnectionTokenData,
128
134
  MintLiveConnectionTokenResponse,
129
135
  } from "./types.gen";
@@ -1342,6 +1348,14 @@ export const getLive = <ThrowOnError extends boolean = false>(
1342
1348
  * the "Live execution" guide linked from this tag's description for the full flow (minting a
1343
1349
  * connection token, the channel and RPC method).
1344
1350
  *
1351
+ * **Paper trading.** Pass a `paper` block to have the run's hints executed in simulation from
1352
+ * its first tick, as a backtest would execute them: fills, closed trades, equity and KPIs,
1353
+ * with one simulated account per quote currency. Omitted, the run has no paper trading. It
1354
+ * takes the same economics as a backtest's `baseConfig` plus where its output goes; read it
1355
+ * back with `GET /live/{runId}/paper`. A strategy that listens to its own execution events
1356
+ * (it overrides `getExecutionCallback()`) has no other execution venue, so it cannot start
1357
+ * without a `paper` block.
1358
+ *
1345
1359
  */
1346
1360
  export const startLive = <ThrowOnError extends boolean = false>(
1347
1361
  options: Options<StartLiveData, ThrowOnError>
@@ -1530,6 +1544,74 @@ export const getLiveRunSignals = <ThrowOnError extends boolean = false>(
1530
1544
  });
1531
1545
  };
1532
1546
 
1547
+ /**
1548
+ * Read a run's paper trading
1549
+ * The run's paper trading as last recorded: one entry per simulated account (one per quote
1550
+ * currency the run trades — never added together), with its starting capital, current
1551
+ * equity, realised PnL, open positions and KPIs. The KPIs are the same a backtest reports,
1552
+ * computed over the trades closed so far.
1553
+ *
1554
+ * `equity` is the account's latest recorded value: at the last closed trade (`equityKind:
1555
+ * equity`), or the last periodic mark-to-market while positions are open (`equityKind:
1556
+ * mark`, taken every minute of market time). Until either exists the account holds its
1557
+ * starting capital.
1558
+ *
1559
+ * Readable by the run's owner, and by anyone if the run is `public`. A run started without
1560
+ * a `paper` block answers `404`.
1561
+ *
1562
+ */
1563
+ export const getLiveRunPaper = <ThrowOnError extends boolean = false>(
1564
+ options: Options<GetLiveRunPaperData, ThrowOnError>
1565
+ ) => {
1566
+ return (options.client ?? _heyApiClient).get<
1567
+ GetLiveRunPaperResponse,
1568
+ GetLiveRunPaperError,
1569
+ ThrowOnError
1570
+ >({
1571
+ security: [
1572
+ {
1573
+ scheme: "bearer",
1574
+ type: "http",
1575
+ },
1576
+ ],
1577
+ url: "/live/{runId}/paper",
1578
+ ...options,
1579
+ });
1580
+ };
1581
+
1582
+ /**
1583
+ * Read a run's paper equity curve
1584
+ * The run's paper equity curve, oldest first, page by page. It is kept for the life of the
1585
+ * run, so unlike signals it has no moving window. Points are `equity` at every closed trade,
1586
+ * `mark` every minute of market time while positions are open, and `gap` where the run was
1587
+ * restarted with positions open: those positions are not carried over, so the curve has no
1588
+ * value there.
1589
+ *
1590
+ * `currency` narrows to one account; without it, every account's points come interleaved by
1591
+ * time, each carrying its currency.
1592
+ *
1593
+ * Readable by the run's owner, and by anyone if the run is `public`.
1594
+ *
1595
+ */
1596
+ export const getLiveRunPaperEquity = <ThrowOnError extends boolean = false>(
1597
+ options: Options<GetLiveRunPaperEquityData, ThrowOnError>
1598
+ ) => {
1599
+ return (options.client ?? _heyApiClient).get<
1600
+ GetLiveRunPaperEquityResponse,
1601
+ GetLiveRunPaperEquityError,
1602
+ ThrowOnError
1603
+ >({
1604
+ security: [
1605
+ {
1606
+ scheme: "bearer",
1607
+ type: "http",
1608
+ },
1609
+ ],
1610
+ url: "/live/{runId}/paper/equity",
1611
+ ...options,
1612
+ });
1613
+ };
1614
+
1533
1615
  /**
1534
1616
  * Mint a WebSocket connection token
1535
1617
  * Mints a short-lived token for the WebSocket connection used to receive a run's signals in