@blackcube/xgate-sdk 0.21.1 → 0.22.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.cts CHANGED
@@ -68,6 +68,23 @@ interface IPair {
68
68
  xex: XgateEx;
69
69
  /** Type de marché (`perp`/`spot`). */
70
70
  kind: MarketKind;
71
+ /**
72
+ * FACTEUR D'ÉCHELLE DE COTATION — `1000` pour `kPEPE`, `1000000` pour `1MBABYDOGE`, `1` sinon.
73
+ *
74
+ * **Aucune venue ne le publie** : l'information n'existe que dans le préfixe du nom, et le
75
+ * canonique la fait disparaître. Sans elle, comparer des prix entre venues est faux — `SATS`
76
+ * porte trois échelles sur quatre sources (×1, ×1000, ×10000). Diviser le prix par ce facteur
77
+ * ramène toutes les venues à la même unité.
78
+ */
79
+ multiplier: number;
80
+ /**
81
+ * ÉCHÉANCE du contrat. `undefined` pour un perpétuel — l'immense majorité.
82
+ *
83
+ * Un future daté ne cote pas le comptant : sa base peut s'écarter de plusieurs pour cent. Neuf
84
+ * lignes BTC de bybit sont dans ce cas. Dérivée du **type de contrat**, jamais de la date de
85
+ * livraison : binance publie l'an 2100 sur ses perpétuels.
86
+ */
87
+ expiresAt?: Date;
71
88
  /**
72
89
  * L'identifiant TECHNIQUE que la venue exige pour agir, quand il diffère du symbole (index de
73
90
  * marché chez lighter, index spot chez hyperliquid). `null` quand le symbole suffit.
@@ -407,6 +424,45 @@ declare class SpotCatalogService {
407
424
  private catalogOf;
408
425
  }
409
426
 
427
+ /**
428
+ * LES BOUGIES DU **COMPTANT**, en REST — binance et bybit.
429
+ *
430
+ * **Un service à part, et non un drapeau sur {@link CandlesService}.** C'est la même règle que pour
431
+ * le catalogue : un `candles()` qui servirait tantôt du perpétuel tantôt du comptant selon un
432
+ * paramètre produirait des erreurs silencieuses — on croirait lire des perpétuels et on lirait du
433
+ * comptant, sans que rien ne le signale. Deux marchés, deux services ; l'appelant choisit
434
+ * explicitement ce qu'il interroge.
435
+ *
436
+ * La différence n'est pas cosmétique : le perpétuel porte un funding et peut s'écarter du comptant,
437
+ * et **les échelles de cotation diffèrent d'un marché à l'autre** — `SATSUSDT` au comptant chez
438
+ * bybit cote ×1 quand son perpétuel cote ×10000. Mélanger les deux séries fabriquerait un graphe
439
+ * qui saute d'un facteur 10 000.
440
+ *
441
+ * **Aucune agrégation ici**, contrairement au perpétuel : binance et bybit servent nativement tous
442
+ * les intervalles de `1m` à `1w`. Le jour où une venue comptant en manquerait un, ce serait à
443
+ * ajouter — pas à supposer.
444
+ */
445
+ declare class SpotCandlesService {
446
+ private readonly logger;
447
+ /** Les venues dont XGate sert le comptant. */
448
+ venues(): readonly XgateEx[];
449
+ /**
450
+ * Les bougies comptant d'une venue, sur une plage de **dates**.
451
+ *
452
+ * **Lève** si la venue ne sert pas de comptant, plutôt que de rendre une liste vide : « cette
453
+ * venue n'a pas de marché comptant » et « ce marché n'a pas coté » appellent des suites très
454
+ * différentes.
455
+ */
456
+ candles(xex: XgateEx, query: ICandlesQuery): Promise<ICandle[]>;
457
+ /**
458
+ * Les bougies comptant d'un même marché chez PLUSIEURS venues, en une liste.
459
+ *
460
+ * Même politique d'échec que partout ailleurs : seule, une venue en échec fait échouer l'appel ;
461
+ * parmi d'autres, elle est journalisée et ignorée. Chaque bougie porte son `xex`.
462
+ */
463
+ candlesOf(xexes: XgateEx[], query: ICandlesQuery): Promise<ICandle[]>;
464
+ }
465
+
410
466
  /** Une paire, telle qu'un SDK de venue la rend. */
411
467
  interface IXexPair {
412
468
  name: string;
@@ -535,7 +591,7 @@ interface IXexAccess {
535
591
  * Les venues dont XGate sait lire le portefeuille aujourd'hui.
536
592
  *
537
593
  * Les autres ne sont pas exclues par principe : il leur manque un accès (clé d'API pour bullet, une
538
- * adresse active pour lighter, extended et paradex). Voir le backlog.
594
+ * adresse active pour extended et paradex). Voir le backlog.
539
595
  */
540
596
  declare const WALLET_XEXES: readonly XgateEx[];
541
597
  /**
@@ -1104,5 +1160,48 @@ declare function canonicalFromXex(rawSymbol: string, xexId: string): string;
1104
1160
  * symbole.
1105
1161
  */
1106
1162
  declare function canonicalFromBase(base: string): string;
1163
+ /**
1164
+ * LE MULTIPLICATEUR DE COTATION, extrait du nom — **la seule source qui existe**.
1165
+ *
1166
+ * Aucune venue ne le publie : `kPEPE` chez hyperliquid ne porte que `{"marginTableId":52}`,
1167
+ * `1000PEPEUSDT` chez binance ne mentionne aucun facteur. L'information n'est QUE dans le préfixe
1168
+ * du nom, et le canonique la fait disparaître — jusqu'ici sans la conserver nulle part.
1169
+ *
1170
+ * Or elle est indispensable dès qu'on compare des prix entre venues. `SATS` porte **trois échelles
1171
+ * différentes sur quatre sources** : `SATSUSDT` (×1) chez bybit-spot, `1000SATSUSDT` (×1000) chez
1172
+ * binance, `10000SATSUSDT` (×10000) chez bybit. Une médiane calculée sur ces prix bruts ne veut
1173
+ * rien dire ; divisés par leur facteur, ils redeviennent comparables.
1174
+ *
1175
+ * Rend `1` quand le nom ne porte aucune échelle — le cas de l'immense majorité.
1176
+ */
1177
+ declare function multiplierFromXex(rawSymbol: string): number;
1178
+
1179
+ /**
1180
+ * LES CONTRATS À ÉCHÉANCE, séparés des perpétuels — parce qu'un future daté ne cote pas le comptant.
1181
+ *
1182
+ * Le catalogue les mélangeait : 44 lignes portaient `kind: 'perp'` alors qu'elles expirent, dont
1183
+ * neuf sur le seul BTC chez bybit. Un consommateur qui prend « le prix du BTC » y ramassait une
1184
+ * échéance de décembre, dont la base au comptant peut s'écarter de plusieurs pour cent.
1185
+ *
1186
+ * **`contractType` fait foi, JAMAIS la date de livraison.** binance publie `deliveryDate:
1187
+ * 4133404800000` — le 1er janvier 2100 — sur ses **perpétuels** : s'y fier marquerait tout le
1188
+ * catalogue comme daté. bybit, lui, met `deliveryTime: '0'` sur les siens. Deux conventions
1189
+ * incompatibles pour dire « ceci n'expire pas », d'où la lecture du type et de lui seul.
1190
+ */
1191
+ /**
1192
+ * L'ÉCHÉANCE D'UN CONTRAT, ou `undefined` s'il est perpétuel.
1193
+ *
1194
+ * On ne lit la date **que** si le type de contrat annonce une échéance : c'est ce qui neutralise le
1195
+ * `deliveryDate` de l'an 2100 des perpétuels binance. Un type inconnu est traité comme perpétuel —
1196
+ * l'immense majorité des lignes, et se tromper dans ce sens n'invente pas d'échéance.
1197
+ */
1198
+ declare function expiryOf(xtras?: Record<string, unknown>): Date | undefined;
1199
+ /**
1200
+ * LE CONTRAT EXPIRE-T-IL ? Vrai pour un future daté, faux pour un perpétuel.
1201
+ *
1202
+ * Séparé d'{@link expiryOf} à dessein : une venue peut annoncer un type daté sans publier de date
1203
+ * exploitable, et il faut alors pouvoir écarter la ligne quand même.
1204
+ */
1205
+ declare function isDated(xtras?: Record<string, unknown>): boolean;
1107
1206
 
1108
- export { CandlesService, CatalogService, type Direction, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, SPOT_XEXES, type Side, SpotCatalogService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
1207
+ export { CandlesService, CatalogService, type Direction, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
package/dist/index.d.ts CHANGED
@@ -68,6 +68,23 @@ interface IPair {
68
68
  xex: XgateEx;
69
69
  /** Type de marché (`perp`/`spot`). */
70
70
  kind: MarketKind;
71
+ /**
72
+ * FACTEUR D'ÉCHELLE DE COTATION — `1000` pour `kPEPE`, `1000000` pour `1MBABYDOGE`, `1` sinon.
73
+ *
74
+ * **Aucune venue ne le publie** : l'information n'existe que dans le préfixe du nom, et le
75
+ * canonique la fait disparaître. Sans elle, comparer des prix entre venues est faux — `SATS`
76
+ * porte trois échelles sur quatre sources (×1, ×1000, ×10000). Diviser le prix par ce facteur
77
+ * ramène toutes les venues à la même unité.
78
+ */
79
+ multiplier: number;
80
+ /**
81
+ * ÉCHÉANCE du contrat. `undefined` pour un perpétuel — l'immense majorité.
82
+ *
83
+ * Un future daté ne cote pas le comptant : sa base peut s'écarter de plusieurs pour cent. Neuf
84
+ * lignes BTC de bybit sont dans ce cas. Dérivée du **type de contrat**, jamais de la date de
85
+ * livraison : binance publie l'an 2100 sur ses perpétuels.
86
+ */
87
+ expiresAt?: Date;
71
88
  /**
72
89
  * L'identifiant TECHNIQUE que la venue exige pour agir, quand il diffère du symbole (index de
73
90
  * marché chez lighter, index spot chez hyperliquid). `null` quand le symbole suffit.
@@ -407,6 +424,45 @@ declare class SpotCatalogService {
407
424
  private catalogOf;
408
425
  }
409
426
 
427
+ /**
428
+ * LES BOUGIES DU **COMPTANT**, en REST — binance et bybit.
429
+ *
430
+ * **Un service à part, et non un drapeau sur {@link CandlesService}.** C'est la même règle que pour
431
+ * le catalogue : un `candles()` qui servirait tantôt du perpétuel tantôt du comptant selon un
432
+ * paramètre produirait des erreurs silencieuses — on croirait lire des perpétuels et on lirait du
433
+ * comptant, sans que rien ne le signale. Deux marchés, deux services ; l'appelant choisit
434
+ * explicitement ce qu'il interroge.
435
+ *
436
+ * La différence n'est pas cosmétique : le perpétuel porte un funding et peut s'écarter du comptant,
437
+ * et **les échelles de cotation diffèrent d'un marché à l'autre** — `SATSUSDT` au comptant chez
438
+ * bybit cote ×1 quand son perpétuel cote ×10000. Mélanger les deux séries fabriquerait un graphe
439
+ * qui saute d'un facteur 10 000.
440
+ *
441
+ * **Aucune agrégation ici**, contrairement au perpétuel : binance et bybit servent nativement tous
442
+ * les intervalles de `1m` à `1w`. Le jour où une venue comptant en manquerait un, ce serait à
443
+ * ajouter — pas à supposer.
444
+ */
445
+ declare class SpotCandlesService {
446
+ private readonly logger;
447
+ /** Les venues dont XGate sert le comptant. */
448
+ venues(): readonly XgateEx[];
449
+ /**
450
+ * Les bougies comptant d'une venue, sur une plage de **dates**.
451
+ *
452
+ * **Lève** si la venue ne sert pas de comptant, plutôt que de rendre une liste vide : « cette
453
+ * venue n'a pas de marché comptant » et « ce marché n'a pas coté » appellent des suites très
454
+ * différentes.
455
+ */
456
+ candles(xex: XgateEx, query: ICandlesQuery): Promise<ICandle[]>;
457
+ /**
458
+ * Les bougies comptant d'un même marché chez PLUSIEURS venues, en une liste.
459
+ *
460
+ * Même politique d'échec que partout ailleurs : seule, une venue en échec fait échouer l'appel ;
461
+ * parmi d'autres, elle est journalisée et ignorée. Chaque bougie porte son `xex`.
462
+ */
463
+ candlesOf(xexes: XgateEx[], query: ICandlesQuery): Promise<ICandle[]>;
464
+ }
465
+
410
466
  /** Une paire, telle qu'un SDK de venue la rend. */
411
467
  interface IXexPair {
412
468
  name: string;
@@ -535,7 +591,7 @@ interface IXexAccess {
535
591
  * Les venues dont XGate sait lire le portefeuille aujourd'hui.
536
592
  *
537
593
  * Les autres ne sont pas exclues par principe : il leur manque un accès (clé d'API pour bullet, une
538
- * adresse active pour lighter, extended et paradex). Voir le backlog.
594
+ * adresse active pour extended et paradex). Voir le backlog.
539
595
  */
540
596
  declare const WALLET_XEXES: readonly XgateEx[];
541
597
  /**
@@ -1104,5 +1160,48 @@ declare function canonicalFromXex(rawSymbol: string, xexId: string): string;
1104
1160
  * symbole.
1105
1161
  */
1106
1162
  declare function canonicalFromBase(base: string): string;
1163
+ /**
1164
+ * LE MULTIPLICATEUR DE COTATION, extrait du nom — **la seule source qui existe**.
1165
+ *
1166
+ * Aucune venue ne le publie : `kPEPE` chez hyperliquid ne porte que `{"marginTableId":52}`,
1167
+ * `1000PEPEUSDT` chez binance ne mentionne aucun facteur. L'information n'est QUE dans le préfixe
1168
+ * du nom, et le canonique la fait disparaître — jusqu'ici sans la conserver nulle part.
1169
+ *
1170
+ * Or elle est indispensable dès qu'on compare des prix entre venues. `SATS` porte **trois échelles
1171
+ * différentes sur quatre sources** : `SATSUSDT` (×1) chez bybit-spot, `1000SATSUSDT` (×1000) chez
1172
+ * binance, `10000SATSUSDT` (×10000) chez bybit. Une médiane calculée sur ces prix bruts ne veut
1173
+ * rien dire ; divisés par leur facteur, ils redeviennent comparables.
1174
+ *
1175
+ * Rend `1` quand le nom ne porte aucune échelle — le cas de l'immense majorité.
1176
+ */
1177
+ declare function multiplierFromXex(rawSymbol: string): number;
1178
+
1179
+ /**
1180
+ * LES CONTRATS À ÉCHÉANCE, séparés des perpétuels — parce qu'un future daté ne cote pas le comptant.
1181
+ *
1182
+ * Le catalogue les mélangeait : 44 lignes portaient `kind: 'perp'` alors qu'elles expirent, dont
1183
+ * neuf sur le seul BTC chez bybit. Un consommateur qui prend « le prix du BTC » y ramassait une
1184
+ * échéance de décembre, dont la base au comptant peut s'écarter de plusieurs pour cent.
1185
+ *
1186
+ * **`contractType` fait foi, JAMAIS la date de livraison.** binance publie `deliveryDate:
1187
+ * 4133404800000` — le 1er janvier 2100 — sur ses **perpétuels** : s'y fier marquerait tout le
1188
+ * catalogue comme daté. bybit, lui, met `deliveryTime: '0'` sur les siens. Deux conventions
1189
+ * incompatibles pour dire « ceci n'expire pas », d'où la lecture du type et de lui seul.
1190
+ */
1191
+ /**
1192
+ * L'ÉCHÉANCE D'UN CONTRAT, ou `undefined` s'il est perpétuel.
1193
+ *
1194
+ * On ne lit la date **que** si le type de contrat annonce une échéance : c'est ce qui neutralise le
1195
+ * `deliveryDate` de l'an 2100 des perpétuels binance. Un type inconnu est traité comme perpétuel —
1196
+ * l'immense majorité des lignes, et se tromper dans ce sens n'invente pas d'échéance.
1197
+ */
1198
+ declare function expiryOf(xtras?: Record<string, unknown>): Date | undefined;
1199
+ /**
1200
+ * LE CONTRAT EXPIRE-T-IL ? Vrai pour un future daté, faux pour un perpétuel.
1201
+ *
1202
+ * Séparé d'{@link expiryOf} à dessein : une venue peut annoncer un type daté sans publier de date
1203
+ * exploitable, et il faut alors pouvoir écarter la ligne quand même.
1204
+ */
1205
+ declare function isDated(xtras?: Record<string, unknown>): boolean;
1107
1206
 
1108
- export { CandlesService, CatalogService, type Direction, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, SPOT_XEXES, type Side, SpotCatalogService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
1207
+ export { CandlesService, CatalogService, type Direction, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
package/dist/index.js CHANGED
@@ -148,6 +148,8 @@ function sum(left, right) {
148
148
  var CONCAT_QUOTE_XEXS = /* @__PURE__ */ new Set(["aster", "binance", "bybit"]);
149
149
  var K_MULTIPLIER = /^k(?=[A-Z])/u;
150
150
  var NUMERIC_MULTIPLIER = /^10{3,}(?=[A-Z0-9])/u;
151
+ var ABBREV_MULTIPLIER = /^1([MK])(?=[A-Z]{2,})/u;
152
+ var ABBREV_FACTOR = { M: 1e6, K: 1e3 };
151
153
  var STOCK_24_5 = /_24_5$/u;
152
154
  var TICKER_ALIASES = {
153
155
  GOOG: "GOOGL",
@@ -172,13 +174,26 @@ function canonicalFromXex(rawSymbol, xexId) {
172
174
  s = s.replace(/-USD-PERP$/u, "").replace(/-PERP$/u, "").replace(/-USDT$/u, "").replace(/-USDC$/u, "").replace(/-USD$/u, "");
173
175
  }
174
176
  s = s.replace(NUMERIC_MULTIPLIER, "");
177
+ s = s.replace(ABBREV_MULTIPLIER, "");
175
178
  s = s.replace(STOCK_24_5, "");
176
179
  return TICKER_ALIASES[s] ?? s;
177
180
  }
178
181
  function canonicalFromBase(base) {
179
- const s = base.replace(K_MULTIPLIER, "").toUpperCase().replace(NUMERIC_MULTIPLIER, "").replace(STOCK_24_5, "");
182
+ const s = base.replace(K_MULTIPLIER, "").toUpperCase().replace(NUMERIC_MULTIPLIER, "").replace(ABBREV_MULTIPLIER, "").replace(STOCK_24_5, "");
180
183
  return TICKER_ALIASES[s] ?? s;
181
184
  }
185
+ function multiplierFromXex(rawSymbol) {
186
+ if (K_MULTIPLIER.test(rawSymbol)) {
187
+ return 1e3;
188
+ }
189
+ const upper = rawSymbol.toUpperCase();
190
+ const abbrev = ABBREV_MULTIPLIER.exec(upper);
191
+ if (abbrev !== null) {
192
+ return ABBREV_FACTOR[abbrev[1]] ?? 1;
193
+ }
194
+ const zeros = NUMERIC_MULTIPLIER.exec(upper);
195
+ return zeros === null ? 1 : Number(zeros[0]);
196
+ }
182
197
 
183
198
  // src/helpers/candle-mapper.ts
184
199
  function toCandle(wire, xex, interval, quote) {
@@ -263,6 +278,19 @@ function createAccountXex(xex, access) {
263
278
  },
264
279
  { default: "a" }
265
280
  );
281
+ case "lighter" /* Lighter */:
282
+ return new Lighter(
283
+ {
284
+ a: {
285
+ apiPrivateKey: access.privateKey ?? NULL_KEY,
286
+ apiKeyIndex: access.apiKeyIndex ?? 0,
287
+ accountIndex: access.accountIndex ?? 0,
288
+ l1Address: adresse(),
289
+ network: access.network ?? "mainnet"
290
+ }
291
+ },
292
+ { default: "a" }
293
+ );
266
294
  case "aster" /* Aster */:
267
295
  if (access.privateKey === void 0 || access.privateKey === "") {
268
296
  throw new Error(
@@ -303,6 +331,7 @@ var WALLET_XEXES = [
303
331
  "hyperliquid" /* Hyperliquid */,
304
332
  "pacifica" /* Pacifica */,
305
333
  "aster" /* Aster */,
334
+ "lighter" /* Lighter */,
306
335
  "blofin" /* Blofin */
307
336
  ];
308
337
  function createPublicXex(xex) {
@@ -347,8 +376,11 @@ function createPublicSpotXex(xex) {
347
376
  }
348
377
 
349
378
  // src/helpers/xex-timeframes.ts
379
+ var HYPERLIQUID_NATIVE = Object.values(Timeframe).filter(
380
+ (interval) => interval !== "1w" /* W */
381
+ );
350
382
  var NATIVE = {
351
- ["hyperliquid" /* Hyperliquid */]: Object.values(Timeframe),
383
+ ["hyperliquid" /* Hyperliquid */]: HYPERLIQUID_NATIVE,
352
384
  ["pacifica" /* Pacifica */]: Object.values(Timeframe),
353
385
  ["aster" /* Aster */]: Object.values(Timeframe),
354
386
  ["blofin" /* Blofin */]: Object.values(Timeframe),
@@ -476,6 +508,43 @@ var CandlesService = class {
476
508
  CandlesService = __decorateClass([
477
509
  Injectable()
478
510
  ], CandlesService);
511
+
512
+ // src/helpers/contract-expiry.ts
513
+ var DATED_CONTRACT_TYPES = /* @__PURE__ */ new Set([
514
+ // binance : les trimestriels (`BTCUSDT_260925`, `ETHUSDT_261225`).
515
+ "CURRENT_QUARTER",
516
+ "NEXT_QUARTER",
517
+ "CURRENT_QUARTER_DELIVERING",
518
+ "NEXT_QUARTER_DELIVERING",
519
+ "PERPETUAL_DELIVERING",
520
+ // bybit : les datés (`BTCUSDT-25DEC26`), face à `LinearPerpetual`.
521
+ "LinearFutures",
522
+ "InverseFutures"
523
+ ]);
524
+ var EXPIRY_FIELDS = ["deliveryTime", "deliveryDate", "expiryTime", "settleTime"];
525
+ function expiryOf(xtras) {
526
+ if (xtras === void 0) {
527
+ return void 0;
528
+ }
529
+ const type = xtras.contractType;
530
+ if (typeof type !== "string" || DATED_CONTRACT_TYPES.has(type) === false) {
531
+ return void 0;
532
+ }
533
+ for (const field of EXPIRY_FIELDS) {
534
+ const brut = xtras[field];
535
+ const ms = typeof brut === "string" ? Number(brut) : brut;
536
+ if (typeof ms === "number" && Number.isFinite(ms) === true && ms > 0) {
537
+ return new Date(ms);
538
+ }
539
+ }
540
+ return void 0;
541
+ }
542
+ function isDated(xtras) {
543
+ const type = xtras?.contractType;
544
+ return typeof type === "string" && DATED_CONTRACT_TYPES.has(type);
545
+ }
546
+
547
+ // src/services/catalog.service.ts
479
548
  var NAMES_ITS_BASE = /* @__PURE__ */ new Set(["binance" /* Binance */, "bybit" /* Bybit */]);
480
549
  var CatalogService = class {
481
550
  logger = new Logger(CatalogService.name);
@@ -521,6 +590,8 @@ var CatalogService = class {
521
590
  symbolXex: pair.name,
522
591
  xex,
523
592
  kind: pair.kind,
593
+ multiplier: multiplierFromXex(pair.name),
594
+ expiresAt: expiryOf(pair.xtras),
524
595
  ref: pair.ref ?? null,
525
596
  szDecimals: pair.szDecimals,
526
597
  tickSize: pair.tickSize,
@@ -595,6 +666,65 @@ var PricesService = class {
595
666
  PricesService = __decorateClass([
596
667
  Injectable()
597
668
  ], PricesService);
669
+ var SpotCandlesService = class {
670
+ logger = new Logger(SpotCandlesService.name);
671
+ /** Les venues dont XGate sert le comptant. */
672
+ venues() {
673
+ return SPOT_XEXES;
674
+ }
675
+ /**
676
+ * Les bougies comptant d'une venue, sur une plage de **dates**.
677
+ *
678
+ * **Lève** si la venue ne sert pas de comptant, plutôt que de rendre une liste vide : « cette
679
+ * venue n'a pas de marché comptant » et « ce marché n'a pas coté » appellent des suites très
680
+ * différentes.
681
+ */
682
+ async candles(xex, query) {
683
+ if (servesSpot(xex) === false) {
684
+ throw new Error(
685
+ `spotCandles(${xex}) : cette venue ne sert pas de comptant. Disponibles : ${SPOT_XEXES.join(", ")}.`
686
+ );
687
+ }
688
+ const spot = createPublicSpotXex(xex).spot();
689
+ if (spot.getCandles === void 0) {
690
+ throw new Error(`spotCandles(${xex}) : cette venue ne sert pas de bougies comptant en REST.`);
691
+ }
692
+ const wires = await spot.getCandles(toXexQuery(query));
693
+ return wires.map((wire) => toCandle(wire, xex, query.interval, ""));
694
+ }
695
+ /**
696
+ * Les bougies comptant d'un même marché chez PLUSIEURS venues, en une liste.
697
+ *
698
+ * Même politique d'échec que partout ailleurs : seule, une venue en échec fait échouer l'appel ;
699
+ * parmi d'autres, elle est journalisée et ignorée. Chaque bougie porte son `xex`.
700
+ */
701
+ async candlesOf(xexes, query) {
702
+ if (xexes.length === 0) {
703
+ throw new Error("spotCandlesOf() : aucune venue demand\xE9e \u2014 pr\xE9cise au moins un XgateEx.");
704
+ }
705
+ const solo = xexes.length === 1;
706
+ const collected = await Promise.all(
707
+ xexes.map(async (xex) => {
708
+ try {
709
+ return await this.candles(xex, query);
710
+ } catch (error) {
711
+ if (solo === true) {
712
+ throw error;
713
+ }
714
+ const message = error instanceof Error ? error.message : String(error);
715
+ this.logger.error(
716
+ `spotCandles(${xex}, ${query.interval}) a \xE9chou\xE9, venue ignor\xE9e : ${message}`
717
+ );
718
+ return [];
719
+ }
720
+ })
721
+ );
722
+ return collected.flat();
723
+ }
724
+ };
725
+ SpotCandlesService = __decorateClass([
726
+ Injectable()
727
+ ], SpotCandlesService);
598
728
  var SpotCatalogService = class {
599
729
  logger = new Logger(SpotCatalogService.name);
600
730
  /** Les venues dont XGate sert le comptant. */
@@ -646,6 +776,9 @@ var SpotCatalogService = class {
646
776
  symbolXex: pair.name,
647
777
  xex,
648
778
  kind: pair.kind,
779
+ // Le comptant porte les mêmes échelles que le perp : `SATSUSDT` (×1) et `1000SATSUSDT`
780
+ // coexistent, et c'est justement entre eux que la comparaison serait fausse.
781
+ multiplier: multiplierFromXex(pair.name),
649
782
  ref: pair.ref ?? null,
650
783
  szDecimals: pair.szDecimals,
651
784
  tickSize: pair.tickSize,
@@ -1210,6 +1343,7 @@ XgateModule = __decorateClass([
1210
1343
  WsCandlesService,
1211
1344
  PricesService,
1212
1345
  SpotCatalogService,
1346
+ SpotCandlesService,
1213
1347
  WalletService
1214
1348
  ],
1215
1349
  exports: [
@@ -1218,11 +1352,12 @@ XgateModule = __decorateClass([
1218
1352
  WsCandlesService,
1219
1353
  PricesService,
1220
1354
  SpotCatalogService,
1355
+ SpotCandlesService,
1221
1356
  WalletService
1222
1357
  ]
1223
1358
  })
1224
1359
  ], XgateModule);
1225
1360
 
1226
- export { CandlesService, CatalogService, ORDER_STATUSES, PricesService, SPOT_XEXES, SpotCatalogService, TIMEFRAME_MINUTES, Timeframe, TradingService, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
1361
+ export { CandlesService, CatalogService, ORDER_STATUSES, PricesService, SPOT_XEXES, SpotCandlesService, SpotCatalogService, TIMEFRAME_MINUTES, Timeframe, TradingService, WALLET_XEXES, WalletService, WsCandlesService, XGATE_EXCHANGES, XgateEx, XgateModule, buildLevels, buildProtection, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, timeframeMs };
1227
1362
  //# sourceMappingURL=index.js.map
1228
1363
  //# sourceMappingURL=index.js.map