@blackcube/xgate-sdk 0.20.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/LICENSE +28 -0
- package/README.md +142 -0
- package/dist/index.cjs +1165 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1048 -0
- package/dist/index.d.ts +1048 -0
- package/dist/index.js +1147 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Le module d'XGate : `imports: [XgateModule]`, puis on injecte le SEUL service dont on a besoin.
|
|
3
|
+
*
|
|
4
|
+
* Une capacité = un service, indépendant des autres. Le REST et le temps réel sont eux-mêmes
|
|
5
|
+
* séparés (`CandlesService` / `WsCandlesService`) : rejouer le passé et suivre le présent ne se
|
|
6
|
+
* consomment pas de la même façon, et une application qui ne fait que du backfill n'a aucune
|
|
7
|
+
* socket à ouvrir.
|
|
8
|
+
*/
|
|
9
|
+
declare class XgateModule {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Les venues qu'XGate sait interroger.
|
|
14
|
+
*
|
|
15
|
+
* Une seule énumération pour les CEX et les DEX : la distinction ne change rien à l'API — on
|
|
16
|
+
* interroge un catalogue de la même façon des deux côtés. Elle deviendra une donnée portée par la
|
|
17
|
+
* venue le jour où quelque chose en dépendra.
|
|
18
|
+
*
|
|
19
|
+
* **`drift` en est absent**, et ce n'est pas un oubli : son SDK ne porte aucun test (seul de la
|
|
20
|
+
* famille), sa précision de taille est un placeholder assumé, et son catalogue est une constante
|
|
21
|
+
* figée dans le paquet. On ne l'agrège pas tant qu'il n'est pas vérifiable.
|
|
22
|
+
*/
|
|
23
|
+
declare enum XgateEx {
|
|
24
|
+
Hyperliquid = "hyperliquid",
|
|
25
|
+
Pacifica = "pacifica",
|
|
26
|
+
Aster = "aster",
|
|
27
|
+
Lighter = "lighter",
|
|
28
|
+
Extended = "extended",
|
|
29
|
+
Paradex = "paradex",
|
|
30
|
+
Bullet = "bullet",
|
|
31
|
+
Blofin = "blofin",
|
|
32
|
+
/**
|
|
33
|
+
* Les deux seules venues dont XGate sert aussi le **comptant** (cf. `IXexSpotSource`). Leurs SDK
|
|
34
|
+
* ne couvrent que la lecture publique : catalogue, bougies et prix. Toute route signée y lève.
|
|
35
|
+
*/
|
|
36
|
+
Binance = "binance",
|
|
37
|
+
Bybit = "bybit"
|
|
38
|
+
}
|
|
39
|
+
/** Toutes les venues connues, dans l'ordre de déclaration. */
|
|
40
|
+
declare const XGATE_EXCHANGES: readonly XgateEx[];
|
|
41
|
+
|
|
42
|
+
/** Type de marché. */
|
|
43
|
+
type MarketKind = 'perp' | 'spot';
|
|
44
|
+
/**
|
|
45
|
+
* UNE PAIRE, TELLE QU'XGATE LA REND — quelle que soit la venue d'où elle vient.
|
|
46
|
+
*
|
|
47
|
+
* Trois identifiants coexistent, et ils ne servent pas à la même chose :
|
|
48
|
+
*
|
|
49
|
+
* - `symbol` est le nom **canonique** de l'actif (celui de CoinMarketCap) : `PEPE`, jamais
|
|
50
|
+
* `kPEPE` ni `1000PEPE`. C'est la clé qui permet de rapprocher le même actif entre venues.
|
|
51
|
+
* - `symbolXex` est ce que **la venue** attend : c'est lui qu'on lui renvoie pour souscrire,
|
|
52
|
+
* passer un ordre ou lire des bougies. Le confondre avec `symbol` fait échouer la requête.
|
|
53
|
+
* - `xex` dit **d'où vient la ligne**. Sans lui, un catalogue multi-venues perd l'information la
|
|
54
|
+
* plus utile qu'il porte : le même actif y figure autant de fois qu'il est listé.
|
|
55
|
+
*
|
|
56
|
+
* Le multiplicateur de cotation (`k`, `1000`) disparaît du `symbol` sans rien fausser : il est
|
|
57
|
+
* absorbé par le prix, donc `prix × taille` reste le notionnel quelle que soit l'unité de la
|
|
58
|
+
* venue.
|
|
59
|
+
*/
|
|
60
|
+
interface IPair {
|
|
61
|
+
/** Nom canonique de l'actif (`PEPE`, `BTC`). */
|
|
62
|
+
symbol: string;
|
|
63
|
+
/** Actif de cotation (`USDC`, `USDT`). */
|
|
64
|
+
quote: string;
|
|
65
|
+
/** Le symbole que la venue attend (`kPEPE`, `1000PEPE`, `BTC-USDT`, `@1`). */
|
|
66
|
+
symbolXex: string;
|
|
67
|
+
/** La venue d'où provient cette paire. */
|
|
68
|
+
xex: XgateEx;
|
|
69
|
+
/** Type de marché (`perp`/`spot`). */
|
|
70
|
+
kind: MarketKind;
|
|
71
|
+
/**
|
|
72
|
+
* L'identifiant TECHNIQUE que la venue exige pour agir, quand il diffère du symbole (index de
|
|
73
|
+
* marché chez lighter, index spot chez hyperliquid). `null` quand le symbole suffit.
|
|
74
|
+
*/
|
|
75
|
+
ref: string | null;
|
|
76
|
+
/** Décimales de taille → pas de quantité = `10^-szDecimals`. */
|
|
77
|
+
szDecimals: number;
|
|
78
|
+
/** Pas de prix, si fourni. */
|
|
79
|
+
tickSize?: string;
|
|
80
|
+
/** Pas de quantité, si fourni. */
|
|
81
|
+
stepSize?: string;
|
|
82
|
+
/** Quantité minimale d'un ordre, si fournie. */
|
|
83
|
+
minQty?: string;
|
|
84
|
+
/** Notionnel minimum d'un ordre, si fourni. */
|
|
85
|
+
minNotional?: string;
|
|
86
|
+
/** Levier maximum (perp), si fourni. */
|
|
87
|
+
maxLeverage?: number;
|
|
88
|
+
/** État du marché tel que la venue l'écrit, si publié. */
|
|
89
|
+
status?: string;
|
|
90
|
+
/** Marché mort chez la venue. `undefined` = la venue ne se prononce pas. */
|
|
91
|
+
delisted?: boolean;
|
|
92
|
+
/** Date de listing, quand la venue la publie. */
|
|
93
|
+
listedAt?: Date;
|
|
94
|
+
/** Champs natifs hors cœur unifié, tels que le SDK de la venue les a conservés. */
|
|
95
|
+
xtras?: Record<string, unknown>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* LE CATALOGUE, toutes venues confondues.
|
|
100
|
+
*
|
|
101
|
+
* L'appelant ne connaît qu'{@link XgateEx} et les types unifiés ; il n'importe aucun SDK, ne
|
|
102
|
+
* construit aucune façade, et ne se soucie pas de ce que chaque venue nomme comment.
|
|
103
|
+
*
|
|
104
|
+
* **Un service par capacité, pas une façade unique** : une application qui ne veut que le
|
|
105
|
+
* catalogue n'a aucune raison d'injecter de quoi trader. Les capacités suivantes vivront dans
|
|
106
|
+
* leurs propres services, injectables séparément.
|
|
107
|
+
*/
|
|
108
|
+
declare class CatalogService {
|
|
109
|
+
private readonly logger;
|
|
110
|
+
/**
|
|
111
|
+
* Le catalogue **perp** d'une ou plusieurs venues, en une seule liste.
|
|
112
|
+
*
|
|
113
|
+
* Chaque ligne porte sa venue d'origine (`xex`) et les deux symboles — le canonique pour
|
|
114
|
+
* rapprocher un actif entre venues, le brut pour parler à celle-ci.
|
|
115
|
+
*
|
|
116
|
+
* **Le traitement d'une venue en échec dépend de ce qui a été demandé** : sur une seule venue,
|
|
117
|
+
* un échec est l'échec de l'appel et il est relancé ; sur plusieurs, la liste partielle garde
|
|
118
|
+
* sa valeur et l'incident est journalisé. Rendre `[]` en silence serait le pire des deux — un
|
|
119
|
+
* catalogue vide se lit comme « cette venue ne liste rien », pas comme « je n'ai pas pu
|
|
120
|
+
* demander ».
|
|
121
|
+
*/
|
|
122
|
+
catalog(...xexes: XgateEx[]): Promise<IPair[]>;
|
|
123
|
+
/** Le catalogue d'UNE venue, traduit vers {@link IPair}. */
|
|
124
|
+
private catalogOf;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Les intervalles de bougies qu'XGate promet — **les mêmes que Blips**, mêmes noms, mêmes valeurs
|
|
129
|
+
* (`Blips/server/src/shared/timeframe.enum.ts`). Le consommateur n'a pas à traduire pour parler à
|
|
130
|
+
* la passerelle, ni la passerelle à inventer une échelle concurrente.
|
|
131
|
+
*
|
|
132
|
+
* Toutes les venues ne les servent pas : paradex s'arrête à l'heure (`resolution: must be one of
|
|
133
|
+
* [1 3 5 15 30 60]`, dit la venue), lighter refuse `1w`, extended rend une liste vide pour ce même
|
|
134
|
+
* intervalle. Ce qu'une venue ne sert pas se **fabrique par agrégation** depuis un intervalle plus
|
|
135
|
+
* fin, jamais ne se réclame à l'appelant.
|
|
136
|
+
*/
|
|
137
|
+
declare enum Timeframe {
|
|
138
|
+
M1 = "1m",
|
|
139
|
+
M5 = "5m",
|
|
140
|
+
M15 = "15m",
|
|
141
|
+
H1 = "1h",
|
|
142
|
+
H4 = "4h",
|
|
143
|
+
D = "1d",
|
|
144
|
+
W = "1w"
|
|
145
|
+
}
|
|
146
|
+
/** Durée d'un intervalle, en minutes. */
|
|
147
|
+
declare const TIMEFRAME_MINUTES: Record<Timeframe, number>;
|
|
148
|
+
/** Durée d'un intervalle, en millisecondes. */
|
|
149
|
+
declare function timeframeMs(timeframe: Timeframe): number;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* UNE BOUGIE, TELLE QU'XGATE LA REND.
|
|
153
|
+
*
|
|
154
|
+
* **Des dates, jamais des timestamps.** Les venues comptent en millisecondes, en secondes ou en
|
|
155
|
+
* chaînes datées ; aucune ne dit laquelle dans son type. Ici, `openedAt` et `closedAt` sont des
|
|
156
|
+
* `Date` — l'unité ne se devine plus. C'est la même règle que `IPair.listedAt`, et le même
|
|
157
|
+
* vocabulaire que Blips (`ICandleRow`).
|
|
158
|
+
*
|
|
159
|
+
* **Des prix en chaîne, pas en nombre.** Les SDK les rendent ainsi pour préserver la précision
|
|
160
|
+
* décimale, et il y a une raison : `0.1 × 0.1` vaut `0.010000000000000002` en JavaScript. XGate est
|
|
161
|
+
* une couche de venue, pas une couche de stockage — celui qui écrit en base convertit chez lui, en
|
|
162
|
+
* connaissance de cause.
|
|
163
|
+
*/
|
|
164
|
+
interface ICandle {
|
|
165
|
+
/** Nom canonique de l'actif (`PEPE`, `BTC`). */
|
|
166
|
+
symbol: string;
|
|
167
|
+
/** Actif de cotation (`USDC`, `USDT`). */
|
|
168
|
+
quote: string;
|
|
169
|
+
/** Le symbole que la venue attend. */
|
|
170
|
+
symbolXex: string;
|
|
171
|
+
/** La venue d'où provient cette bougie. */
|
|
172
|
+
xex: XgateEx;
|
|
173
|
+
/** Type de marché (`perp`/`spot`). */
|
|
174
|
+
kind: MarketKind;
|
|
175
|
+
/** L'intervalle de cette bougie. */
|
|
176
|
+
interval: Timeframe;
|
|
177
|
+
/** Ouverture du créneau. */
|
|
178
|
+
openedAt: Date;
|
|
179
|
+
/** Fin du créneau — dernière milliseconde incluse. */
|
|
180
|
+
closedAt: Date;
|
|
181
|
+
open: string;
|
|
182
|
+
high: string;
|
|
183
|
+
low: string;
|
|
184
|
+
close: string;
|
|
185
|
+
/**
|
|
186
|
+
* Volume en actif de base. `null` = **inconnu**, pas « aucun échange » : une bougie venue d'un
|
|
187
|
+
* flux temps réel n'en porte pas toujours. Un `0` reste un vrai volume, celui d'une minute sans
|
|
188
|
+
* échange.
|
|
189
|
+
*/
|
|
190
|
+
volume: string | null;
|
|
191
|
+
/** Volume en cotation. `null` quand la venue ne le publie pas. */
|
|
192
|
+
quoteVolume: string | null;
|
|
193
|
+
/** Part achetée à l'initiative du taker, en cotation. Avec `quoteVolume`, donne le CVD. */
|
|
194
|
+
takerBuyQuoteVolume: string | null;
|
|
195
|
+
/** Nombre de transactions. `null` quand la venue ne le compte pas. */
|
|
196
|
+
trades: number | null;
|
|
197
|
+
/**
|
|
198
|
+
* `true` = bougie **fabriquée par agrégation**, la venue ne sert pas cet intervalle. C'est une
|
|
199
|
+
* vraie bougie, mais personne ne l'a servie telle quelle.
|
|
200
|
+
*/
|
|
201
|
+
derived: boolean;
|
|
202
|
+
}
|
|
203
|
+
/** Ce qu'on demande à `CandlesService`. */
|
|
204
|
+
interface ICandlesQuery {
|
|
205
|
+
/** Le symbole que la venue attend (`symbolXex` d'un {@link IPair}). */
|
|
206
|
+
symbol: string;
|
|
207
|
+
interval: Timeframe;
|
|
208
|
+
/** Début de la plage. Une **date**, jamais un timestamp. */
|
|
209
|
+
startTime?: Date;
|
|
210
|
+
/** Fin de la plage. */
|
|
211
|
+
endTime?: Date;
|
|
212
|
+
limit?: number;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* LES BOUGIES EN REST — l'historique.
|
|
217
|
+
*
|
|
218
|
+
* Le temps réel vit dans `WsCandlesService` : ce sont deux besoins distincts (rejouer le passé,
|
|
219
|
+
* suivre le présent), et une application qui fait du backfill n'a pas à ouvrir de socket.
|
|
220
|
+
*/
|
|
221
|
+
declare class CandlesService {
|
|
222
|
+
private readonly logger;
|
|
223
|
+
/**
|
|
224
|
+
* Les bougies d'une venue, sur une plage de **dates**.
|
|
225
|
+
*
|
|
226
|
+
* L'appelant donne des `Date` et un {@link Timeframe} ; le service traduit vers ce que la venue
|
|
227
|
+
* attend — chaîne UTC pour huit d'entre elles, millisecondes pour blofin.
|
|
228
|
+
*
|
|
229
|
+
* **Un intervalle que la venue ne sert pas est FABRIQUÉ**, pas refusé : on tire l'intervalle
|
|
230
|
+
* exploitable le plus grand et on agrège. Paradex s'arrête à l'heure, lighter et extended ne
|
|
231
|
+
* font pas la semaine — l'appelant n'a pas à le savoir. Les bougies fabriquées portent
|
|
232
|
+
* `derived: true`.
|
|
233
|
+
*
|
|
234
|
+
* **Lève si la venue ne sert rien du tout** (bullet, qui n'a pas de REST). Rendre une liste vide
|
|
235
|
+
* ferait passer une incapacité pour une absence de données, et un backfill silencieusement troué
|
|
236
|
+
* est pire qu'un backfill en erreur.
|
|
237
|
+
*/
|
|
238
|
+
candles(xex: XgateEx, query: ICandlesQuery): Promise<ICandle[]>;
|
|
239
|
+
/** Un appel REST à la venue, dans l'intervalle qu'elle sait servir. */
|
|
240
|
+
private fetch;
|
|
241
|
+
/**
|
|
242
|
+
* Les bougies d'un même marché chez PLUSIEURS venues, en une liste.
|
|
243
|
+
*
|
|
244
|
+
* Comme pour le catalogue : une venue en échec parmi d'autres est journalisée et ignorée ; seule,
|
|
245
|
+
* son échec est celui de l'appel. Chaque bougie porte son `xex`, donc la liste reste
|
|
246
|
+
* exploitable telle quelle.
|
|
247
|
+
*/
|
|
248
|
+
candlesOf(xexes: XgateEx[], query: ICandlesQuery): Promise<ICandle[]>;
|
|
249
|
+
/**
|
|
250
|
+
* La cotation, déduite du symbole natif. Les bougies ne la portent pas — seul le catalogue la
|
|
251
|
+
* publie —, et refaire un appel de catalogue pour chaque série coûterait plus cher que ce
|
|
252
|
+
* qu'elle apporte. `BTC-USDT` → `USDT` ; à défaut de séparateur, on ne prétend rien.
|
|
253
|
+
*/
|
|
254
|
+
private quoteOf;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** Couper le flux. */
|
|
258
|
+
type Unsubscribe = () => void;
|
|
259
|
+
/**
|
|
260
|
+
* LES BOUGIES EN TEMPS RÉEL — le présent.
|
|
261
|
+
*
|
|
262
|
+
* Séparé de `CandlesService` à dessein : rejouer un historique et suivre un flux ne se
|
|
263
|
+
* consomment pas de la même façon, et n'ont pas les mêmes coûts. Une application qui ne fait que
|
|
264
|
+
* du backfill n'a aucune socket à ouvrir.
|
|
265
|
+
*/
|
|
266
|
+
declare class WsCandlesService {
|
|
267
|
+
private readonly logger;
|
|
268
|
+
/**
|
|
269
|
+
* Souscrit aux bougies d'un marché chez une venue. Rend la fonction de désabonnement.
|
|
270
|
+
*
|
|
271
|
+
* Le handler reçoit **une bougie à la fois**, au format unifié — c'est la forme commune aux neuf
|
|
272
|
+
* SDK. La bougie en cours est repoussée à chaque mise à jour tant qu'elle n'est pas close ;
|
|
273
|
+
* `closedAt` dit jusqu'où elle porte.
|
|
274
|
+
*
|
|
275
|
+
* **Attention à extended** : sur un intervalle qu'elle ne connaît pas, sa façade rend un
|
|
276
|
+
* désabonnement vide au lieu de lever. L'appelant se croit abonné et n'entend jamais rien — un
|
|
277
|
+
* silence indiscernable d'un marché sans échange. C'est un défaut du SDK, pas d'XGate, et il est
|
|
278
|
+
* dans la file des corrections.
|
|
279
|
+
*/
|
|
280
|
+
subscribe(xex: XgateEx, query: {
|
|
281
|
+
symbol: string;
|
|
282
|
+
interval: Timeframe;
|
|
283
|
+
}, handler: (candle: ICandle) => void): Unsubscribe;
|
|
284
|
+
/**
|
|
285
|
+
* Souscrit au MÊME marché chez plusieurs venues, avec un seul handler.
|
|
286
|
+
*
|
|
287
|
+
* Chaque bougie porte son `xex` : l'appelant sait toujours qui parle. La fonction rendue coupe
|
|
288
|
+
* tous les flux d'un coup — sinon il faudrait en garder N et n'en oublier aucun.
|
|
289
|
+
*/
|
|
290
|
+
subscribeAll(xexes: XgateEx[], query: {
|
|
291
|
+
symbol: string;
|
|
292
|
+
interval: Timeframe;
|
|
293
|
+
}, handler: (candle: ICandle) => void): Unsubscribe;
|
|
294
|
+
/** Voir `CandlesService.quoteOf` : la cotation se déduit du symbole natif, faute d'être publiée. */
|
|
295
|
+
private quoteOf;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* UNE COTATION, TELLE QU'XGATE LA REND — quelle que soit la venue d'où elle vient.
|
|
300
|
+
*
|
|
301
|
+
* Les trois identifiants sont ceux d'{@link IPair} et jouent le même rôle : `symbol` est le nom
|
|
302
|
+
* **canonique** de l'actif (celui qui permet de comparer un prix entre venues), `symbolXex` est ce
|
|
303
|
+
* que **la venue** attend, et `xex` dit d'où vient la ligne. Sans ce dernier, un relevé
|
|
304
|
+
* multi-venues perd ce qu'il a de plus utile : le même actif y figure autant de fois qu'il est
|
|
305
|
+
* coté, et c'est précisément l'écart entre ces cotations qu'on vient chercher.
|
|
306
|
+
*
|
|
307
|
+
* **Tous les prix sont des `string`**, jamais des `number` : ils viennent de la venue en décimal
|
|
308
|
+
* exact, et les passer en flottant perdrait des décimales avant même le premier calcul.
|
|
309
|
+
*
|
|
310
|
+
* **`null` signifie « la venue ne le publie pas »**, et ce n'est pas la même chose que zéro.
|
|
311
|
+
* Aucune venue ne sert les quatorze champs — voir le DICTIONARY de chaque SDK pour savoir qui
|
|
312
|
+
* remplit quoi. Deux nuances qui coûtent cher si on les ignore :
|
|
313
|
+
*
|
|
314
|
+
* - `mid` n'est servi que par hyperliquid et pacifica. Le dériver de `bid`/`ask` ailleurs
|
|
315
|
+
* fabriquerait une valeur qu'aucune venue n'a cotée.
|
|
316
|
+
* - chez aster, le remplissage varie **d'un symbole à l'autre** : les cotations viennent de deux
|
|
317
|
+
* endpoints aux univers différents, et les marchés exotiques n'ont ni `volume24h`, ni
|
|
318
|
+
* `prevDayPrice`, ni `last`. C'est la seule venue où deux `IPrice` du même appel n'ont pas les
|
|
319
|
+
* mêmes champs servis.
|
|
320
|
+
*/
|
|
321
|
+
interface IPrice {
|
|
322
|
+
/** Nom canonique de l'actif (`PEPE`, `BTC`). */
|
|
323
|
+
symbol: string;
|
|
324
|
+
/** Le symbole que la venue attend (`kPEPE`, `1000PEPE`, `BTC-USDT`). */
|
|
325
|
+
symbolXex: string;
|
|
326
|
+
/** La venue d'où provient cette cotation. */
|
|
327
|
+
xex: XgateEx;
|
|
328
|
+
/** Type de marché (`perp`/`spot`). */
|
|
329
|
+
kind: MarketKind;
|
|
330
|
+
/** Prix de valorisation des positions — la référence pour un PnL. */
|
|
331
|
+
mark: string | null;
|
|
332
|
+
/** Prix oracle/index. */
|
|
333
|
+
oracle: string | null;
|
|
334
|
+
/** Milieu de fourchette, tel que la venue le cote. */
|
|
335
|
+
mid: string | null;
|
|
336
|
+
/** Meilleure offre d'achat. */
|
|
337
|
+
bid: string | null;
|
|
338
|
+
/** Meilleure offre de vente. */
|
|
339
|
+
ask: string | null;
|
|
340
|
+
/** Dernier prix négocié. */
|
|
341
|
+
last: string | null;
|
|
342
|
+
/** Taux de financement courant. */
|
|
343
|
+
funding: string | null;
|
|
344
|
+
/** Position ouverte totale sur le marché. */
|
|
345
|
+
openInterest: string | null;
|
|
346
|
+
/** Volume des dernières 24 heures, en notionnel. */
|
|
347
|
+
volume24h: string | null;
|
|
348
|
+
/** Prix de référence de la veille, pour une variation sur 24 heures. */
|
|
349
|
+
prevDayPrice: string | null;
|
|
350
|
+
/** Moment de la cotation ; `null` quand la venue ne le publie pas. */
|
|
351
|
+
quotedAt: Date | null;
|
|
352
|
+
/** Champs natifs hors cœur unifié, tels que le SDK de la venue les a conservés. */
|
|
353
|
+
xtras?: Record<string, unknown>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* LES PRIX, toutes venues confondues.
|
|
358
|
+
*
|
|
359
|
+
* Même forme que `CatalogService`, et pour la même raison : l'appelant ne connaît qu'{@link XgateEx}
|
|
360
|
+
* et les types unifiés. Chaque ligne porte sa venue d'origine, sans quoi un relevé multi-venues
|
|
361
|
+
* perdrait ce qu'il a de plus utile — l'écart entre les cotations d'un même actif.
|
|
362
|
+
*
|
|
363
|
+
* **Ce service ne calcule rien.** Il ne dérive pas un `mid` d'un `bid`/`ask`, ne convertit pas un
|
|
364
|
+
* volume, ne comble aucun trou : un champ à `null` dit que la venue ne le publie pas, et c'est une
|
|
365
|
+
* information. Fabriquer la valeur manquante la rendrait indiscernable d'une valeur cotée.
|
|
366
|
+
*/
|
|
367
|
+
declare class PricesService {
|
|
368
|
+
private readonly logger;
|
|
369
|
+
/**
|
|
370
|
+
* Les prix **perp** d'une ou plusieurs venues, en une seule liste.
|
|
371
|
+
*
|
|
372
|
+
* **Le traitement d'une venue en échec dépend de ce qui a été demandé** : sur une seule venue, un
|
|
373
|
+
* échec est l'échec de l'appel et il est relancé ; sur plusieurs, la liste partielle garde sa
|
|
374
|
+
* valeur et l'incident est journalisé. Rendre `[]` en silence serait le pire des deux — un relevé
|
|
375
|
+
* vide se lit comme « ce marché ne cote rien », pas comme « je n'ai pas pu demander ».
|
|
376
|
+
*/
|
|
377
|
+
prices(...xexes: XgateEx[]): Promise<IPrice[]>;
|
|
378
|
+
/** Les prix d'UNE venue, traduits vers {@link IPrice}. */
|
|
379
|
+
private pricesOf;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* LE CATALOGUE COMPTANT, séparé du catalogue perpétuel.
|
|
384
|
+
*
|
|
385
|
+
* **Deux services, pas un service à drapeau.** Un `catalog(xex, { spot: true })` serait plus court
|
|
386
|
+
* à écrire et plus dangereux à lire : on croirait interroger des perpétuels et on interrogerait du
|
|
387
|
+
* comptant, sans que rien ne le signale. Ici, le nom de la classe dit ce qu'on lit.
|
|
388
|
+
*
|
|
389
|
+
* Le comptant est **minoritaire** dans la famille : deux venues sur dix le servent. Ce n'est pas un
|
|
390
|
+
* manque à combler, c'est la réalité d'un ensemble de SDK bâti pour le perpétuel.
|
|
391
|
+
*/
|
|
392
|
+
declare class SpotCatalogService {
|
|
393
|
+
private readonly logger;
|
|
394
|
+
/** Les venues dont XGate sert le comptant. */
|
|
395
|
+
venues(): readonly XgateEx[];
|
|
396
|
+
/**
|
|
397
|
+
* Le catalogue **spot** d'une ou plusieurs venues, en une seule liste.
|
|
398
|
+
*
|
|
399
|
+
* Même politique d'échec que le catalogue perpétuel : sur une seule venue, l'échec est relancé ;
|
|
400
|
+
* sur plusieurs, la liste partielle garde sa valeur et l'incident est journalisé.
|
|
401
|
+
*
|
|
402
|
+
* **Une venue sans comptant lève**, elle ne rend pas une liste vide : « cette venue n'a pas de
|
|
403
|
+
* spot » et « cette venue ne cote rien au comptant » ne se corrigent pas de la même façon.
|
|
404
|
+
*/
|
|
405
|
+
catalog(...xexes: XgateEx[]): Promise<IPair[]>;
|
|
406
|
+
/** Le catalogue comptant d'UNE venue, traduit vers {@link IPair}. */
|
|
407
|
+
private catalogOf;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** Une paire, telle qu'un SDK de venue la rend. */
|
|
411
|
+
interface IXexPair {
|
|
412
|
+
name: string;
|
|
413
|
+
/**
|
|
414
|
+
* L'actif de base, **tel que la venue le NOMME**. Décisif chez binance et bybit : leur symbole
|
|
415
|
+
* concatène la cotation (`BTCUSDT`), et re-parser cette chaîne donnerait un faux canonique sur
|
|
416
|
+
* tout ce qui n'est pas coté en USDT — `ROSEBTC` deviendrait `ROSEBTC` au lieu de `ROSE`.
|
|
417
|
+
*/
|
|
418
|
+
base: string;
|
|
419
|
+
quote: string;
|
|
420
|
+
kind: string;
|
|
421
|
+
ref?: string | null;
|
|
422
|
+
szDecimals: number;
|
|
423
|
+
tickSize?: string;
|
|
424
|
+
stepSize?: string;
|
|
425
|
+
minQty?: string;
|
|
426
|
+
minNotional?: string;
|
|
427
|
+
maxLeverage?: number;
|
|
428
|
+
status?: string;
|
|
429
|
+
delisted?: boolean;
|
|
430
|
+
listedAt?: Date;
|
|
431
|
+
xtras?: Record<string, unknown>;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Une bougie, telle qu'un SDK de venue la rend : **clés longues et `Date`**.
|
|
435
|
+
*
|
|
436
|
+
* Les clés courtes (`t`, `T`, `o`, `qv`…) restent au niveau du wire, dans chaque SDK. Elles ne
|
|
437
|
+
* remontent plus jusqu'ici — un `candle.T` ne se lit pas, et un `number` ne dit pas s'il compte des
|
|
438
|
+
* secondes ou des millisecondes.
|
|
439
|
+
*/
|
|
440
|
+
interface IXexCandle {
|
|
441
|
+
openedAt: Date;
|
|
442
|
+
closedAt: Date;
|
|
443
|
+
symbol: string;
|
|
444
|
+
interval: string;
|
|
445
|
+
open: string;
|
|
446
|
+
close: string;
|
|
447
|
+
high: string;
|
|
448
|
+
low: string;
|
|
449
|
+
volume: string;
|
|
450
|
+
trades: number;
|
|
451
|
+
kind: string;
|
|
452
|
+
quoteVolume: string | null;
|
|
453
|
+
takerBuyBaseVolume: string | null;
|
|
454
|
+
takerBuyQuoteVolume: string | null;
|
|
455
|
+
xtras?: Record<string, unknown>;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Une cotation, telle qu'un SDK de venue la rend : **clés longues et `Date`**, comme
|
|
459
|
+
* {@link IXexCandle}. Le `time: number` d'origine est devenu `quotedAt: Date` — un nombre nu ne dit
|
|
460
|
+
* pas s'il compte des secondes ou des millisecondes, et les deux existent selon les venues.
|
|
461
|
+
*/
|
|
462
|
+
interface IXexPrice {
|
|
463
|
+
name: string;
|
|
464
|
+
kind: string;
|
|
465
|
+
mark: string | null;
|
|
466
|
+
oracle: string | null;
|
|
467
|
+
mid: string | null;
|
|
468
|
+
bid: string | null;
|
|
469
|
+
ask: string | null;
|
|
470
|
+
last: string | null;
|
|
471
|
+
funding: string | null;
|
|
472
|
+
openInterest: string | null;
|
|
473
|
+
volume24h: string | null;
|
|
474
|
+
prevDayPrice: string | null;
|
|
475
|
+
quotedAt: Date | null;
|
|
476
|
+
xtras?: Record<string, unknown>;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Ce qu'XGate attend d'une façade de venue **côté COMPTANT**.
|
|
480
|
+
*
|
|
481
|
+
* **Volontairement séparé d'{@link IXexSource}, et non fondu dedans avec un paramètre.** Un
|
|
482
|
+
* `perp()` qui servirait parfois du spot selon un drapeau serait une source d'erreurs silencieuses :
|
|
483
|
+
* on croirait lire des perpétuels et on lirait du comptant, sans que rien ne le signale. Deux
|
|
484
|
+
* contrats, deux registres, deux services — l'appelant choisit explicitement ce qu'il interroge.
|
|
485
|
+
*
|
|
486
|
+
* **Toutes les venues n'ont pas de spot** : c'est même minoritaire. Le registre lève pour celles qui
|
|
487
|
+
* n'en servent pas, plutôt que de rendre une liste vide qui se lirait comme « ce marché ne cote
|
|
488
|
+
* rien ».
|
|
489
|
+
*/
|
|
490
|
+
interface IXexSpotSource {
|
|
491
|
+
spot(): {
|
|
492
|
+
getPairs(): Promise<IXexPair[]>;
|
|
493
|
+
getCandles?(query: {
|
|
494
|
+
name: string;
|
|
495
|
+
interval: string;
|
|
496
|
+
startTime?: Date;
|
|
497
|
+
endTime?: Date;
|
|
498
|
+
limit?: number;
|
|
499
|
+
}): Promise<IXexCandle[]>;
|
|
500
|
+
getPrices?(): Promise<IXexPrice[]>;
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* De quoi lire UN compte chez une venue.
|
|
505
|
+
*
|
|
506
|
+
* **Un sac d'accès volontairement lâche**, dont chaque venue prend ce qu'il lui faut : les huit
|
|
507
|
+
* n'ont pas la même notion d'identité. Un DEX se lit à l'adresse ; lighter veut un index de compte,
|
|
508
|
+
* extended une clé d'API plus un coffre, blofin un triplet clé/secret/passphrase. Un type strict par
|
|
509
|
+
* venue obligerait l'appelant à connaître ces huit formes — c'est précisément ce qu'XGate existe
|
|
510
|
+
* pour lui épargner.
|
|
511
|
+
*
|
|
512
|
+
* Le registre **lève** quand ce qu'il faut manque, avec le nom du champ attendu : mieux vaut un
|
|
513
|
+
* message précis qu'un compte vide qui se lirait comme « pas d'argent ».
|
|
514
|
+
*/
|
|
515
|
+
interface IXexAccess {
|
|
516
|
+
/** L'adresse publique du compte. Suffit seule sur les DEX en lecture. */
|
|
517
|
+
address?: string;
|
|
518
|
+
privateKey?: string;
|
|
519
|
+
/**
|
|
520
|
+
* Le réseau de la venue. **Défaut `mainnet`** — c'est le cas courant, et un défaut `testnet`
|
|
521
|
+
* ferait lire des soldes fantômes sans que personne ne s'en aperçoive.
|
|
522
|
+
*
|
|
523
|
+
* Il devient indispensable dès qu'on écrit : sans lui, `TradingService` ne pourrait ouvrir que sur
|
|
524
|
+
* de l'argent réel, et le cycle ne serait exerçable nulle part.
|
|
525
|
+
*/
|
|
526
|
+
network?: 'mainnet' | 'testnet';
|
|
527
|
+
apiKey?: string;
|
|
528
|
+
secret?: string;
|
|
529
|
+
passphrase?: string;
|
|
530
|
+
accountIndex?: number;
|
|
531
|
+
apiKeyIndex?: number;
|
|
532
|
+
vault?: number;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Les venues dont XGate sait lire le portefeuille aujourd'hui.
|
|
536
|
+
*
|
|
537
|
+
* 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.
|
|
539
|
+
*/
|
|
540
|
+
declare const WALLET_XEXES: readonly XgateEx[];
|
|
541
|
+
/**
|
|
542
|
+
* Les venues dont XGate sert le **comptant**. Les autres n'ont pas de marché spot exploitable, ou
|
|
543
|
+
* ne l'exposent pas — et une venue absente d'ici n'est pas un oubli, c'est un fait mesuré.
|
|
544
|
+
*/
|
|
545
|
+
declare const SPOT_XEXES: readonly XgateEx[];
|
|
546
|
+
/** Cette venue sert-elle du comptant via XGate ? */
|
|
547
|
+
declare function servesSpot(xex: XgateEx): boolean;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* UN SOLDE, TEL QU'XGATE LE REND.
|
|
551
|
+
*
|
|
552
|
+
* `xex` dit d'où vient la ligne : sans lui, un relevé multi-venues additionnerait des USDC détenus
|
|
553
|
+
* à des endroits différents, et on ne saurait plus lequel est mobilisable où.
|
|
554
|
+
*
|
|
555
|
+
* **Tous les montants sont des `string`**, jamais des `number` : ce sont des décimaux exacts venus
|
|
556
|
+
* de la venue, et les passer en flottant perdrait des décimales avant le premier calcul.
|
|
557
|
+
*/
|
|
558
|
+
interface IBalance {
|
|
559
|
+
/** La venue qui détient ce solde. */
|
|
560
|
+
xex: XgateEx;
|
|
561
|
+
/** L'actif (`USDC`, `USDT`). */
|
|
562
|
+
asset: string;
|
|
563
|
+
/**
|
|
564
|
+
* Le solde total. Attention : chez la plupart des venues perp, il porte l'**equity** — dépôt plus
|
|
565
|
+
* PnL latent — et non le seul dépôt. Deux venues ne comptent donc pas forcément la même chose.
|
|
566
|
+
*/
|
|
567
|
+
total: string;
|
|
568
|
+
/** Ce qui est mobilisable maintenant ; `null` si la venue ne le distingue pas. */
|
|
569
|
+
available: string | null;
|
|
570
|
+
/** Contre-valeur en dollars, quand la venue la publie. */
|
|
571
|
+
usdValue: string | null;
|
|
572
|
+
/** Champs natifs hors cœur unifié. */
|
|
573
|
+
xtras?: Record<string, unknown>;
|
|
574
|
+
}
|
|
575
|
+
/** Nature normalisée d'un mouvement d'argent. */
|
|
576
|
+
type MovementKind = 'deposit' | 'withdraw' | 'transfer';
|
|
577
|
+
/** État d'un mouvement. **Seul `completed` a réellement déplacé de l'argent.** */
|
|
578
|
+
type MovementStatus = 'completed' | 'pending' | 'failed';
|
|
579
|
+
/**
|
|
580
|
+
* UN DÉPLACEMENT D'ARGENT, entrant ou sortant.
|
|
581
|
+
*
|
|
582
|
+
* **Le montant est SIGNÉ** — `+` entrant, `-` sortant — alors que plusieurs venues le rendent
|
|
583
|
+
* toujours positif et n'indiquent le sens que par un type natif. Sans ce signe, la somme des
|
|
584
|
+
* mouvements ne donnerait pas la variation du solde, ce qui est pourtant le seul usage sérieux de
|
|
585
|
+
* cette liste.
|
|
586
|
+
*
|
|
587
|
+
* Les frais sont **exclus** du montant et portés à part : les mélanger empêcherait de rapprocher un
|
|
588
|
+
* dépôt de ce que la contrepartie a réellement envoyé.
|
|
589
|
+
*/
|
|
590
|
+
interface IMovement {
|
|
591
|
+
/** La venue où le mouvement a eu lieu. */
|
|
592
|
+
xex: XgateEx;
|
|
593
|
+
/** Nature normalisée (le type natif exact reste dans `xtras`). */
|
|
594
|
+
kind: MovementKind;
|
|
595
|
+
/** Montant **signé**, frais exclus. */
|
|
596
|
+
amount: string;
|
|
597
|
+
/** Frais prélevés par la venue, positifs ; `'0'` si aucun ou non publié. */
|
|
598
|
+
fee: string;
|
|
599
|
+
/** Actif déplacé. */
|
|
600
|
+
asset: string;
|
|
601
|
+
/** Moment où le mouvement a eu lieu. */
|
|
602
|
+
occurredAt: Date;
|
|
603
|
+
/** État côté venue. */
|
|
604
|
+
status: MovementStatus;
|
|
605
|
+
/**
|
|
606
|
+
* Clé de déduplication **stable** : hash de transaction ou identifiant natif quand la venue en
|
|
607
|
+
* publie un, sinon une clé dérivée. C'est ce qui rend un import idempotent — relire la même
|
|
608
|
+
* fenêtre deux fois ne doit pas créer de doublon.
|
|
609
|
+
*/
|
|
610
|
+
externalId: string;
|
|
611
|
+
/** Champs natifs hors cœur unifié. */
|
|
612
|
+
xtras?: Record<string, unknown>;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* UN POINT DE VALEUR DE COMPTE dans le temps.
|
|
616
|
+
*
|
|
617
|
+
* **Seules hyperliquid et pacifica le publient.** Les six autres venues n'ont rien d'équivalent, et
|
|
618
|
+
* XGate ne le fabrique pas : reconstituer une courbe depuis les mouvements et les prix produirait
|
|
619
|
+
* une valeur qu'aucune venue n'a cotée, avec une méthode différente de celle des deux qui la
|
|
620
|
+
* publient — donc incomparable avec elles.
|
|
621
|
+
*/
|
|
622
|
+
interface IEquityPoint {
|
|
623
|
+
/** La venue mesurée. */
|
|
624
|
+
xex: XgateEx;
|
|
625
|
+
/** Moment de la mesure. */
|
|
626
|
+
measuredAt: Date;
|
|
627
|
+
/** Valeur du compte, marquée au marché. */
|
|
628
|
+
equity: number;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/** Une venue et de quoi y lire un compte. */
|
|
632
|
+
interface IWalletAccess extends IXexAccess {
|
|
633
|
+
xex: XgateEx;
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* LE PORTEFEUILLE, toutes venues confondues : soldes, mouvements d'argent, valeur du compte.
|
|
637
|
+
*
|
|
638
|
+
* **Les positions n'en font pas partie** — elles appartiennent au bloc trading, indissociable des
|
|
639
|
+
* ordres. Un portefeuille répond à « qu'est-ce que j'ai et d'où ça vient » ; une position répond à
|
|
640
|
+
* « qu'est-ce que je risque », et les deux ne se lisent pas au même moment.
|
|
641
|
+
*
|
|
642
|
+
* **Ce service ne calcule rien.** Il n'additionne pas les soldes entre venues, ne convertit aucun
|
|
643
|
+
* montant, ne reconstitue aucune courbe d'équité là où la venue n'en publie pas. Chaque ligne porte
|
|
644
|
+
* son `xex` : c'est à l'appelant de décider ce qui s'additionne, parce que lui seul sait si deux
|
|
645
|
+
* USDC à deux endroits sont fongibles pour son usage.
|
|
646
|
+
*
|
|
647
|
+
* **Lire un portefeuille est PUBLIC sur les DEX** : l'adresse suffit, aucune signature n'est
|
|
648
|
+
* produite. Seuls les CEX exigent une clé, même en lecture.
|
|
649
|
+
*/
|
|
650
|
+
declare class WalletService {
|
|
651
|
+
private readonly logger;
|
|
652
|
+
/** Les venues dont le portefeuille est lisible aujourd'hui. */
|
|
653
|
+
venues(): readonly XgateEx[];
|
|
654
|
+
/**
|
|
655
|
+
* Les soldes d'un ou plusieurs comptes.
|
|
656
|
+
*
|
|
657
|
+
* Même politique d'échec que les autres services : sur un seul accès, l'échec est relancé ; sur
|
|
658
|
+
* plusieurs, la liste partielle garde sa valeur et l'incident est journalisé. Rendre `[]` en
|
|
659
|
+
* silence ferait passer une venue injoignable pour un compte vide — et un compte vide, ça se
|
|
660
|
+
* regarde autrement qu'une panne.
|
|
661
|
+
*/
|
|
662
|
+
balances(...accesses: IWalletAccess[]): Promise<IBalance[]>;
|
|
663
|
+
/**
|
|
664
|
+
* Les mouvements d'argent, triés du plus ancien au plus récent, montants **signés**.
|
|
665
|
+
*
|
|
666
|
+
* **Lève si la venue ne les sert pas** plutôt que de rendre une liste vide : « cette venue
|
|
667
|
+
* n'expose pas son historique » et « ce compte n'a jamais bougé » appellent des suites très
|
|
668
|
+
* différentes.
|
|
669
|
+
*/
|
|
670
|
+
movements(...accesses: IWalletAccess[]): Promise<IMovement[]>;
|
|
671
|
+
/**
|
|
672
|
+
* La valeur du compte dans le temps.
|
|
673
|
+
*
|
|
674
|
+
* **Seules hyperliquid et pacifica la publient**, et XGate ne la fabrique pas ailleurs :
|
|
675
|
+
* reconstituer une courbe depuis les mouvements et les prix donnerait une valeur qu'aucune venue
|
|
676
|
+
* n'a cotée, avec une méthode incomparable à celle des deux qui la publient. Lève sur une venue
|
|
677
|
+
* qui n'en sert pas.
|
|
678
|
+
*/
|
|
679
|
+
equityHistory(...accesses: IWalletAccess[]): Promise<IEquityPoint[]>;
|
|
680
|
+
/** Le motif commun aux trois lectures : un accès seul relance, plusieurs journalisent. */
|
|
681
|
+
private collect;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Sens de la position. Le SL et les TP se posent au sens OPPOSÉ. */
|
|
685
|
+
type Direction = 'long' | 'short';
|
|
686
|
+
/** Un ordre de protection, prêt à être posé : déclenchement, taille, borne d'exécution. */
|
|
687
|
+
interface IProtectionLeg {
|
|
688
|
+
/** Prix auquel l'ordre se déclenche. */
|
|
689
|
+
triggerPrice: string;
|
|
690
|
+
/** Taille **en unités de base**. */
|
|
691
|
+
size: string;
|
|
692
|
+
/**
|
|
693
|
+
* Borne d'exécution du déclenché : sous le trigger en long, au-dessus en short. Hyperliquid
|
|
694
|
+
* l'exige ; les autres venues l'ignorent sans que ce soit une erreur.
|
|
695
|
+
*/
|
|
696
|
+
price: string;
|
|
697
|
+
}
|
|
698
|
+
/** Ce qu'xgate rend : un SL pleine taille, et N take-profits partiels. */
|
|
699
|
+
interface IProtection {
|
|
700
|
+
sl: IProtectionLeg;
|
|
701
|
+
tps: IProtectionLeg[];
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Ce que l'appelant fournit. **Les pourcentages sont des écarts au prix d'entrée**, en fraction
|
|
705
|
+
* (0.01 = 1 %) : `slPct: 0.02` place le stop 2 % sous l'entrée en long, 2 % au-dessus en short.
|
|
706
|
+
*/
|
|
707
|
+
interface IProtectionInput {
|
|
708
|
+
/** Prix d'entrée de référence. */
|
|
709
|
+
entry: number;
|
|
710
|
+
direction: Direction;
|
|
711
|
+
/** Taille de la position, **en unités de base**. */
|
|
712
|
+
size: number;
|
|
713
|
+
/** Écart du stop au prix d'entrée, en fraction. Toujours du côté perdant. */
|
|
714
|
+
slPct: number;
|
|
715
|
+
/**
|
|
716
|
+
* Les take-profits, dans l'ordre. `pct` est l'écart au prix d'entrée (côté gagnant) et `part` la
|
|
717
|
+
* fraction de la position à sortir à ce niveau.
|
|
718
|
+
*
|
|
719
|
+
* **Une part à 0 ne produit AUCUN leg** — c'est un marqueur (un palier de breakeven, par exemple),
|
|
720
|
+
* pas un ordre. Sauf s'il s'agit du dernier : celui-là clôture toujours.
|
|
721
|
+
*/
|
|
722
|
+
tps: Array<{
|
|
723
|
+
pct: number;
|
|
724
|
+
part: number;
|
|
725
|
+
}>;
|
|
726
|
+
/** Pas de prix de la paire. */
|
|
727
|
+
tickSize?: string | null;
|
|
728
|
+
/** Pas de quantité de la paire. */
|
|
729
|
+
lotSize?: string | null;
|
|
730
|
+
/**
|
|
731
|
+
* Écart entre le déclenchement et la borne d'exécution, en fraction (défaut 0,5 %). Le déclenché
|
|
732
|
+
* sort SOUS son trigger en long, AU-DESSUS en short.
|
|
733
|
+
*/
|
|
734
|
+
slippagePct?: number;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Arrondit à un pas. `floor` pour les tailles — jamais `nearest` : arrondir au plus proche ferait
|
|
738
|
+
* dépasser la somme des legs au-delà de la position, et la venue rejetterait le dernier.
|
|
739
|
+
*/
|
|
740
|
+
declare function roundToStep(value: number, step: string | null | undefined, mode: 'floor' | 'nearest'): number;
|
|
741
|
+
/**
|
|
742
|
+
* Arrondit un PRIX à la grille de la paire : `tickSize` s'il est fourni, sinon la règle Hyperliquid
|
|
743
|
+
* — cinq chiffres significatifs ET au plus `6 − szDecimals` décimales, `szDecimals` étant déduit du
|
|
744
|
+
* pas de quantité. Sans cette seconde branche, une venue sans tick fixe reçoit un prix qu'elle
|
|
745
|
+
* refuse.
|
|
746
|
+
*/
|
|
747
|
+
declare function roundPrice(value: number, tickSize?: string | null, lotSize?: string | null): number;
|
|
748
|
+
/**
|
|
749
|
+
* LES NIVEAUX DE PROTECTION, calculés depuis des POURCENTAGES.
|
|
750
|
+
*
|
|
751
|
+
* L'appelant donne un prix d'entrée, un sens et des écarts ; il ne calcule aucun prix lui-même.
|
|
752
|
+
* Le stop part du côté perdant, les take-profits du côté gagnant — le sens s'en occupe.
|
|
753
|
+
*/
|
|
754
|
+
declare function buildLevels(entry: number, direction: Direction, slPct: number, tpPcts: readonly number[]): {
|
|
755
|
+
sl: number;
|
|
756
|
+
tps: number[];
|
|
757
|
+
};
|
|
758
|
+
/**
|
|
759
|
+
* LA PROTECTION COMPLÈTE — un stop pleine taille et N take-profits partiels, prêts à poser.
|
|
760
|
+
*
|
|
761
|
+
* **GARANTIE ANTI-RÉSIDU, sous condition de seuil.** Quand les parts somment à **plus de 80 %**, le
|
|
762
|
+
* DERNIER take-profit prend le reliquat EXACT (`size − Σ des précédents`) et la position ferme
|
|
763
|
+
* entièrement : trois paliers à 33 % font 99 %, et ce 1 % restant est un arrondi, pas une intention.
|
|
764
|
+
*
|
|
765
|
+
* **À 80 % ou en dessous, on laisse courir.** Deux paliers à 33 % sortent 66 % et laissent 34 % de
|
|
766
|
+
* position ouverte — c'est une sortie partielle voulue, et la compléter d'office la trahirait. Le
|
|
767
|
+
* dernier palier prend alors sa part nominale, sans plus.
|
|
768
|
+
*
|
|
769
|
+
* Les paliers intermédiaires s'arrondissent au **plancher**, jamais au plus proche : arrondir au
|
|
770
|
+
* plus proche ferait dépasser la somme, et c'est le dernier leg — celui qui clôture — que la venue
|
|
771
|
+
* rejetterait.
|
|
772
|
+
*
|
|
773
|
+
* **LE STOP COUVRE TOUJOURS LA TAILLE PLEINE.** Un stop partiel laisserait une fraction de position
|
|
774
|
+
* sans protection, ce qui est précisément ce qu'on veut rendre impossible : jamais une position
|
|
775
|
+
* nue, même partiellement.
|
|
776
|
+
*
|
|
777
|
+
* Deux cas font disparaître un leg, et c'est voulu :
|
|
778
|
+
* · une part à 0 sur un palier intermédiaire — c'est un marqueur, pas un ordre ;
|
|
779
|
+
* · une taille qui tombe à 0 après arrondi — un ordre de taille nulle serait refusé.
|
|
780
|
+
* Le dernier palier, lui, survit toujours : c'est lui qui ferme.
|
|
781
|
+
*/
|
|
782
|
+
declare function buildProtection(input: IProtectionInput): IProtection;
|
|
783
|
+
|
|
784
|
+
/** Côté d'un ordre. À ne pas confondre avec le SENS d'une position ({@link Direction}). */
|
|
785
|
+
type Side = 'buy' | 'sell';
|
|
786
|
+
/** Nature d'un ordre, telle qu'XGate la nomme. Le type natif exact reste dans `xtras`. */
|
|
787
|
+
type OrderType = 'limit' | 'market' | 'stop' | 'stopMarket' | 'takeProfit' | 'takeProfitMarket' | 'other';
|
|
788
|
+
/**
|
|
789
|
+
* ÉTAT D'UN ORDRE — **exactement les sept valeurs des SDK de venue**, orthographe comprise.
|
|
790
|
+
*
|
|
791
|
+
* `canceled` prend un seul L : c'est ce que rendent les huit SDK, unanimement. XGate écrivait
|
|
792
|
+
* `cancelled` et perdait `partiallyFilled` et `expired` ; le `as OrderStatus` de la conversion
|
|
793
|
+
* masquait l'écart au lieu de le signaler, si bien qu'un consommateur testant `=== 'cancelled'`
|
|
794
|
+
* ne matchait **jamais** — un ordre annulé passait pour un ordre encore vivant.
|
|
795
|
+
*
|
|
796
|
+
* Seul `filled` a déplacé tout ce qui était demandé ; `partiallyFilled` en a déplacé une part et
|
|
797
|
+
* garde le reste au carnet.
|
|
798
|
+
*/
|
|
799
|
+
type OrderStatus = 'open' | 'partiallyFilled' | 'filled' | 'canceled' | 'rejected' | 'expired' | 'other';
|
|
800
|
+
/**
|
|
801
|
+
* Les sept statuts, énumérés — pour VALIDER ce qu'une venue rend plutôt que de le caster.
|
|
802
|
+
*
|
|
803
|
+
* Un `as OrderStatus` sur une chaîne inconnue produit une valeur qui n'existe dans aucun `switch` et
|
|
804
|
+
* que personne ne rattrape ; passer par cette liste fait retomber l'inconnu sur `other`, qui lui est
|
|
805
|
+
* traité partout.
|
|
806
|
+
*/
|
|
807
|
+
declare const ORDER_STATUSES: readonly OrderStatus[];
|
|
808
|
+
/** Durée de validité. `alo` = post-only : l'ordre est refusé s'il devait s'exécuter immédiatement. */
|
|
809
|
+
type TimeInForce = 'gtc' | 'ioc' | 'fok' | 'alo';
|
|
810
|
+
/**
|
|
811
|
+
* UN ORDRE, tel qu'XGate le rend.
|
|
812
|
+
*
|
|
813
|
+
* `xex` dit d'où il vient : un identifiant d'ordre n'a de sens que chez la venue qui l'a émis, et
|
|
814
|
+
* deux venues peuvent parfaitement produire le même.
|
|
815
|
+
*/
|
|
816
|
+
interface IOrder {
|
|
817
|
+
xex: XgateEx;
|
|
818
|
+
/** Le symbole que la venue attend. */
|
|
819
|
+
symbolXex: string;
|
|
820
|
+
/** Type de marché. */
|
|
821
|
+
kind: 'perp' | 'spot';
|
|
822
|
+
/** L'identifiant de la venue. */
|
|
823
|
+
id: string;
|
|
824
|
+
/** L'identifiant fourni par l'appelant, quand il en a donné un. */
|
|
825
|
+
clientId: string | null;
|
|
826
|
+
side: Side;
|
|
827
|
+
type: OrderType;
|
|
828
|
+
/** `null` pour un ordre au marché. */
|
|
829
|
+
price: string | null;
|
|
830
|
+
/** Taille demandée, **en unités de base**. */
|
|
831
|
+
size: string;
|
|
832
|
+
/** Taille déjà exécutée, **en unités de base**. */
|
|
833
|
+
filled: string;
|
|
834
|
+
status: OrderStatus;
|
|
835
|
+
tif: TimeInForce | null;
|
|
836
|
+
/** L'ordre ne peut que réduire une position existante — jamais en ouvrir une. */
|
|
837
|
+
reduceOnly: boolean | null;
|
|
838
|
+
/** Moment où l'ordre a été soumis. */
|
|
839
|
+
placedAt: Date;
|
|
840
|
+
xtras?: Record<string, unknown>;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* UNE POSITION ouverte.
|
|
844
|
+
*
|
|
845
|
+
* `size` est **toujours positive** : le sens est porté par `side`. Mélanger les deux — une taille
|
|
846
|
+
* négative pour un short — oblige chaque consommateur à se souvenir de la convention, et l'un d'eux
|
|
847
|
+
* l'oubliera.
|
|
848
|
+
*/
|
|
849
|
+
interface IPosition {
|
|
850
|
+
xex: XgateEx;
|
|
851
|
+
symbolXex: string;
|
|
852
|
+
/** Nom canonique de l'actif, pour rapprocher la même position entre venues. */
|
|
853
|
+
symbol: string;
|
|
854
|
+
side: 'long' | 'short' | null;
|
|
855
|
+
/** Taille **en unités de base**, toujours positive. */
|
|
856
|
+
size: string;
|
|
857
|
+
entryPrice: string | null;
|
|
858
|
+
markPrice: string | null;
|
|
859
|
+
unrealizedPnl: string | null;
|
|
860
|
+
leverage: number | null;
|
|
861
|
+
liquidationPrice: string | null;
|
|
862
|
+
/** Marge immobilisée par la position. */
|
|
863
|
+
margin: string | null;
|
|
864
|
+
xtras?: Record<string, unknown>;
|
|
865
|
+
}
|
|
866
|
+
/** UNE EXÉCUTION du compte — ce qui a réellement été rempli, par opposition à ce qui a été demandé. */
|
|
867
|
+
interface ITrade {
|
|
868
|
+
xex: XgateEx;
|
|
869
|
+
symbolXex: string;
|
|
870
|
+
kind: 'perp' | 'spot';
|
|
871
|
+
id: string;
|
|
872
|
+
/** L'ordre dont provient cette exécution. */
|
|
873
|
+
orderId: string;
|
|
874
|
+
side: Side;
|
|
875
|
+
price: string;
|
|
876
|
+
size: string;
|
|
877
|
+
fee: string;
|
|
878
|
+
feeAsset: string | null;
|
|
879
|
+
/** PnL réalisé, avant et après frais ; `null` quand la venue ne permet pas de l'établir. */
|
|
880
|
+
grossPnl: string | null;
|
|
881
|
+
netPnl: string | null;
|
|
882
|
+
/** L'exécution était côté maker. `null` si la venue ne le dit pas. */
|
|
883
|
+
maker: boolean | null;
|
|
884
|
+
/** Moment de l'exécution. */
|
|
885
|
+
filledAt: Date;
|
|
886
|
+
xtras?: Record<string, unknown>;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* CE QU'ON DEMANDE POUR OUVRIR UNE POSITION PROTÉGÉE.
|
|
890
|
+
*
|
|
891
|
+
* **Les protections sont données en POURCENTAGES**, pas en prix : xgate calcule les niveaux depuis
|
|
892
|
+
* le prix d'entrée. L'appelant n'a ni à arrondir au tick, ni à se souvenir que le stop d'un short
|
|
893
|
+
* est au-dessus de l'entrée.
|
|
894
|
+
*
|
|
895
|
+
* **`slPct` est OBLIGATOIRE.** C'est la règle qui a motivé tout ce chemin : jamais un ordre sans
|
|
896
|
+
* stop. Une position ouverte pendant que le serveur tombe reste protégée par la venue elle-même.
|
|
897
|
+
*/
|
|
898
|
+
interface IEntryWithProtection {
|
|
899
|
+
xex: XgateEx;
|
|
900
|
+
/** Le symbole que la venue attend. */
|
|
901
|
+
symbolXex: string;
|
|
902
|
+
/** Sens de la POSITION à ouvrir. Le stop et les take-profits se posent au sens opposé. */
|
|
903
|
+
direction: Direction;
|
|
904
|
+
/** Taille à ouvrir, **en unités de base**. */
|
|
905
|
+
size: number;
|
|
906
|
+
/** Prix d'entrée visé. Sert de référence aux pourcentages, et de prix limite. */
|
|
907
|
+
entry: number;
|
|
908
|
+
/** Écart du stop au prix d'entrée, en fraction (0.02 = 2 %). Obligatoire. */
|
|
909
|
+
slPct: number;
|
|
910
|
+
/**
|
|
911
|
+
* Les take-profits, dans l'ordre. `part` est la fraction de la position à sortir.
|
|
912
|
+
*
|
|
913
|
+
* Au-dessus de 80 % cumulés, le dernier absorbe le reliquat et la position ferme entièrement ;
|
|
914
|
+
* à 80 % ou en dessous, le reste court.
|
|
915
|
+
*/
|
|
916
|
+
tps: Array<{
|
|
917
|
+
pct: number;
|
|
918
|
+
part: number;
|
|
919
|
+
}>;
|
|
920
|
+
/** `ioc` pour entrer au marché (taker), `alo` pour poster (maker). Défaut : `ioc`. */
|
|
921
|
+
tif?: TimeInForce;
|
|
922
|
+
/** Identifiant applicatif, remonté tel quel par la venue quand elle le supporte. */
|
|
923
|
+
clientId?: string;
|
|
924
|
+
/** Pas de prix et de quantité de la paire — sans eux, la venue refuse des niveaux mal arrondis. */
|
|
925
|
+
tickSize?: string | null;
|
|
926
|
+
lotSize?: string | null;
|
|
927
|
+
/** Écart entre déclenchement et borne d'exécution du déclenché. Défaut 0,5 %. */
|
|
928
|
+
slippagePct?: number;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/** Une venue et de quoi y agir. */
|
|
932
|
+
interface ITradingAccess extends IXexAccess {
|
|
933
|
+
xex: XgateEx;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* LE TRADING : positions, ordres et exécutions — un seul service, et c'est délibéré.
|
|
937
|
+
*
|
|
938
|
+
* Les séparer laisserait diverger trois vues du même état : une position n'est que la somme des
|
|
939
|
+
* ordres exécutés, et un ordre ne se lit pas sans savoir ce qu'il a ouvert.
|
|
940
|
+
*
|
|
941
|
+
* **JAMAIS UN ORDRE SANS STOP.** `openWithProtection` est la seule façon d'ouvrir une position par
|
|
942
|
+
* ce service, et elle exige un `slPct`. La protection part avec l'entrée, en un geste que la venue
|
|
943
|
+
* traite atomiquement : si l'entrée ne remplit pas, la protection ne naît jamais — aucun orphelin.
|
|
944
|
+
* Poser le stop *après* l'entrée laisse une fenêtre pendant laquelle la position est nue, et cette
|
|
945
|
+
* fenêtre s'ouvre précisément quand le serveur tombe.
|
|
946
|
+
*
|
|
947
|
+
* **L'appelant donne des POURCENTAGES**, jamais des prix : xgate calcule les niveaux, arrondit à la
|
|
948
|
+
* grille de la paire, et garantit qu'au-dessus de 80 % de parts cumulées la position ferme
|
|
949
|
+
* entièrement — sans reliquat.
|
|
950
|
+
*/
|
|
951
|
+
declare class TradingService {
|
|
952
|
+
private readonly logger;
|
|
953
|
+
/**
|
|
954
|
+
* OUVRE UNE POSITION AVEC SA PROTECTION, en un geste atomique.
|
|
955
|
+
*
|
|
956
|
+
* Le premier ordre rendu est **l'entrée** ; les suivants sont le stop et les take-profits, dans
|
|
957
|
+
* l'ordre où ils ont été posés.
|
|
958
|
+
*
|
|
959
|
+
* Chaque venue applique son mécanisme natif — hyperliquid groupe les enfants sous l'entrée et les
|
|
960
|
+
* annule lui-même si elle rate, pacifica les embarque dans l'ordre, aster envoie un lot de
|
|
961
|
+
* conditionnels. Le SDK de la venue s'en charge ; xgate fournit des niveaux justes.
|
|
962
|
+
*/
|
|
963
|
+
openWithProtection(access: ITradingAccess, input: IEntryWithProtection): Promise<IOrder[]>;
|
|
964
|
+
/** Les positions ouvertes d'un compte. */
|
|
965
|
+
positions(access: ITradingAccess, symbolXex?: string): Promise<IPosition[]>;
|
|
966
|
+
/** Les ordres encore au carnet. */
|
|
967
|
+
openOrders(access: ITradingAccess): Promise<IOrder[]>;
|
|
968
|
+
/**
|
|
969
|
+
* Les exécutions du compte — ce qui a réellement été rempli.
|
|
970
|
+
*
|
|
971
|
+
* À ne pas confondre avec les ordres : un ordre annoncé `filled` peut avoir été rempli en
|
|
972
|
+
* plusieurs fois, à des prix différents. C'est ici que se lit le prix moyen réel.
|
|
973
|
+
*/
|
|
974
|
+
trades(access: ITradingAccess): Promise<ITrade[]>;
|
|
975
|
+
/**
|
|
976
|
+
* FERME UNE POSITION ENTIÈREMENT, sur la taille que **la venue** déclare — jamais celle qu'on croit.
|
|
977
|
+
*
|
|
978
|
+
* C'est toute la différence : fermer la taille planifiée laisse un reliquat quand le fill a différé du
|
|
979
|
+
* plan (une entrée partiellement remplie, un take-profit passé entre-temps), et ce reliquat reste
|
|
980
|
+
* ouvert, sans stop, à la charge du compte. On relit donc la position juste avant de la fermer.
|
|
981
|
+
*
|
|
982
|
+
* Rend `null` quand il n'y a rien à fermer — un appel sur une position déjà close n'est pas une erreur.
|
|
983
|
+
*/
|
|
984
|
+
closePosition(access: ITradingAccess, symbolXex: string): Promise<IOrder | null>;
|
|
985
|
+
/**
|
|
986
|
+
* LE PRIX DE SORTIE RÉEL d'une position, lu dans l'historique de la venue.
|
|
987
|
+
*
|
|
988
|
+
* Le prix planifié ne dit pas à quel prix on est sorti : un stop se déclenche au marché, un
|
|
989
|
+
* take-profit peut remplir en plusieurs fois. Seul l'historique porte le prix réellement obtenu, et
|
|
990
|
+
* c'est lui qui doit servir au calcul du résultat — sinon le PnL affiché est une fiction.
|
|
991
|
+
*
|
|
992
|
+
* On retient le **dernier** ordre de sortie rempli après `openedAt` : les précédents sont les
|
|
993
|
+
* take-profits partiels, celui-là est la clôture.
|
|
994
|
+
*/
|
|
995
|
+
exitPrice(access: ITradingAccess, symbolXex: string, direction: Direction, openedAt: Date): Promise<string | null>;
|
|
996
|
+
/** Annule un ordre par son identifiant de venue. */
|
|
997
|
+
cancel(access: ITradingAccess, symbolXex: string, orderId: string): Promise<void>;
|
|
998
|
+
/** Le scope `perp()` de la venue, typé au plus près de ce que le contrat commun garantit. */
|
|
999
|
+
private perpOf;
|
|
1000
|
+
/**
|
|
1001
|
+
* Le statut de la venue, VALIDÉ — jamais casté. Une venue qui inventerait un statut rendrait sinon
|
|
1002
|
+
* une valeur absente de tous les `switch` en aval, et personne ne la rattraperait.
|
|
1003
|
+
*/
|
|
1004
|
+
private toStatus;
|
|
1005
|
+
private toOrder;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/** La venue sert-elle cet intervalle de première main ? */
|
|
1009
|
+
declare function servesNatively(xex: XgateEx, interval: Timeframe): boolean;
|
|
1010
|
+
/**
|
|
1011
|
+
* L'intervalle SOURCE à demander pour fabriquer `target` chez cette venue.
|
|
1012
|
+
*
|
|
1013
|
+
* On prend le **plus grand** intervalle servi nativement qui divise la cible : le plus grand,
|
|
1014
|
+
* parce qu'il minimise le nombre de bougies à tirer — reconstruire une semaine depuis des minutes
|
|
1015
|
+
* demanderait 10 080 points là où sept journalières suffisent.
|
|
1016
|
+
*
|
|
1017
|
+
* `undefined` = la venue ne sert rien d'exploitable, il n'y a pas de fabrication possible.
|
|
1018
|
+
*/
|
|
1019
|
+
declare function sourceFor(xex: XgateEx, target: Timeframe): Timeframe | undefined;
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* SYMBOLE BRUT D'UNE VENUE → CANONIQUE.
|
|
1023
|
+
*
|
|
1024
|
+
* DÉPEND DE LA VENUE, pour ne JAMAIS amputer un stablecoin ni un actif finissant par USD(T/C) :
|
|
1025
|
+
* - aster/binance/bybit : quote concaténé → on strippe `USDT`/`USDC` FINAL (USDEUSDT→USDE, BTCUSDT→BTC) ;
|
|
1026
|
+
* - les autres : uniquement les suffixes SÉPARÉS (`-USD-PERP`, `-PERP`, `-USDT`, `-USDC`, `-USD`). Jamais de
|
|
1027
|
+
* strip nu → un actif nu (USDC, USDT, USDE, BUSD, BTC) reste intact ;
|
|
1028
|
+
* - dans tous les cas, le multiplicateur tombe d'abord (kPEPE → PEPE).
|
|
1029
|
+
*
|
|
1030
|
+
* `-USDT` A ÉTÉ AJOUTÉ le 2026-08-05, en agrégeant blofin pour la première fois : elle cote
|
|
1031
|
+
* `BTC-USDT` avec un tiret, forme qu'aucune venue de Blips n'utilisait. Sans lui, **477 des 487
|
|
1032
|
+
* symboles blofin restaient non canonisés** (`BTC-USDT` au lieu de `BTC`) et aucun ne se
|
|
1033
|
+
* rapprochait de son homologue chez une autre venue. Le tiret rend le strip sûr : l'actif nu
|
|
1034
|
+
* `USDT` n'y correspond pas.
|
|
1035
|
+
*/
|
|
1036
|
+
declare function canonicalFromXex(rawSymbol: string, xexId: string): string;
|
|
1037
|
+
/**
|
|
1038
|
+
* ACTIF DE BASE NU → CANONIQUE, pour les venues qui NOMMENT leur actif au lieu de le concaténer : binance rend
|
|
1039
|
+
* `baseAsset`, bybit `baseCoin`. On prend ce qu'elles DISENT plutôt que de re-parser leur symbole — sans quoi
|
|
1040
|
+
* `ROSEBTC` (cotée en BTC, pas en USDT) donnerait le faux canonique `ROSEBTC`.
|
|
1041
|
+
*
|
|
1042
|
+
* Mêmes règles que `canonicalFromXex` MOINS le strip de cotation : l'appliquer à une base nue amputerait les
|
|
1043
|
+
* stablecoins (`USDC` → chaîne VIDE). C'est exactement le piège que la connaissance de la venue évite déjà côté
|
|
1044
|
+
* symbole.
|
|
1045
|
+
*/
|
|
1046
|
+
declare function canonicalFromBase(base: string): string;
|
|
1047
|
+
|
|
1048
|
+
export { CandlesService, CatalogService, type Direction, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, 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 };
|