@blackcube/xgate-sdk 0.56.3 → 0.58.1
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.cjs +156 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -7
- package/dist/index.d.ts +53 -7
- package/dist/index.js +156 -24
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.d.cts
CHANGED
|
@@ -1323,6 +1323,8 @@ interface IOrder {
|
|
|
1323
1323
|
type: OrderType;
|
|
1324
1324
|
/** `null` pour un ordre au marché. */
|
|
1325
1325
|
price: string | null;
|
|
1326
|
+
/** Prix de DÉCLENCHEMENT d'un ordre conditionnel (stop, take-profit) ; `null` pour un ordre ordinaire. */
|
|
1327
|
+
triggerPrice: string | null;
|
|
1326
1328
|
/** Taille demandée, **en unités de base**. */
|
|
1327
1329
|
size: string;
|
|
1328
1330
|
/** Taille déjà exécutée, **en unités de base**. */
|
|
@@ -1335,6 +1337,24 @@ interface IOrder {
|
|
|
1335
1337
|
placedAt: Date;
|
|
1336
1338
|
xtras?: Record<string, unknown>;
|
|
1337
1339
|
}
|
|
1340
|
+
/** Le rôle d'un ordre dans un trade : l'entrée, le stop, ou un take-profit. */
|
|
1341
|
+
type TradeOrderRole = 'entry' | 'sl' | 'tp';
|
|
1342
|
+
/**
|
|
1343
|
+
* UN ORDRE DU TRADE — ce que {@link TradingService.openWithProtection} rend, une ligne par ordre posé.
|
|
1344
|
+
*
|
|
1345
|
+
* La même forme sur toutes les venues : l'appelant garde cette liste avec son trade et la gère sans savoir
|
|
1346
|
+
* comment chaque venue a posé ses protections (hyperliquid en lot, aster une à une, pacifica embarquées dans
|
|
1347
|
+
* l'entrée). C'est XGate qui fait le pont.
|
|
1348
|
+
*
|
|
1349
|
+
* **L'identifiant n'est vide que si l'ordre n'existe pas ou plus** — refusé, ou déjà consommé (un stop touché
|
|
1350
|
+
* dans la seconde) : `status` dit lequel. Une venue qui ne rend pas l'identifiant d'une protection à l'envoi
|
|
1351
|
+
* (hyperliquid `normalTpsl`, pacifica embarqué) le voit retrouvé au carnet par XGate.
|
|
1352
|
+
*/
|
|
1353
|
+
interface ITradeOrder extends IOrder {
|
|
1354
|
+
role: TradeOrderRole;
|
|
1355
|
+
/** La fraction de la position initiale que l'ordre couvre : 1 pour l'entrée et le stop. */
|
|
1356
|
+
part: number;
|
|
1357
|
+
}
|
|
1338
1358
|
/**
|
|
1339
1359
|
* UNE POSITION ouverte.
|
|
1340
1360
|
*
|
|
@@ -1562,8 +1582,11 @@ declare class TradingService {
|
|
|
1562
1582
|
/**
|
|
1563
1583
|
* OUVRE UNE POSITION AVEC SA PROTECTION, en un geste atomique.
|
|
1564
1584
|
*
|
|
1565
|
-
*
|
|
1566
|
-
* l'ordre
|
|
1585
|
+
* **Ce qui revient est la liste des ORDRES DU TRADE** ({@link ITradeOrder}), la même sur toutes les
|
|
1586
|
+
* venues : l'entrée, le stop, puis chaque take-profit dans l'ordre du plan — chacun avec son rôle, sa
|
|
1587
|
+
* part et un identifiant réel, retrouvé au carnet quand la venue ne l'a pas rendu à l'envoi (0.58.0,
|
|
1588
|
+
* cf. `ordresDuTrade`). L'appelant la garde avec son trade : annuler une protection, c'est
|
|
1589
|
+
* {@link cancel} sur son identifiant, quel que soit le rôle.
|
|
1567
1590
|
*
|
|
1568
1591
|
* Chaque venue applique son mécanisme natif — hyperliquid groupe les enfants sous l'entrée et les
|
|
1569
1592
|
* annule lui-même si elle rate, pacifica les embarque dans l'ordre, aster envoie un lot de
|
|
@@ -1584,7 +1607,27 @@ declare class TradingService {
|
|
|
1584
1607
|
* complète par {@link completeProtection} quand il le constate. Jusqu'en 0.56.0 ces cibles n'étaient
|
|
1585
1608
|
* posées par personne : une position tide à trois paliers sur pacifica partait sans sa dernière.
|
|
1586
1609
|
*/
|
|
1587
|
-
openWithProtection(access: ITradingAccess, input: IEntryWithProtection): Promise<
|
|
1610
|
+
openWithProtection(access: ITradingAccess, input: IEntryWithProtection): Promise<ITradeOrder[]>;
|
|
1611
|
+
/**
|
|
1612
|
+
* LES ORDRES DU TRADE, rangés par rôle — la même liste sur toutes les venues.
|
|
1613
|
+
*
|
|
1614
|
+
* Chaque venue rend ses ordres à sa façon (mesuré sur testnet le 2026-10-01) : hyperliquid en lot `'na'` rend
|
|
1615
|
+
* tous les identifiants, en `normalTpsl` (un seul take-profit) AUCUN pour le stop et la cible (`waitingForFill`,
|
|
1616
|
+
* `waitingForTrigger`) ; pacifica ne rend que l'entrée, le stop et la première cible embarqués naissant au
|
|
1617
|
+
* carnet sans lien (`parent_id: null`) ; aster pose une à une et rend tout.
|
|
1618
|
+
*
|
|
1619
|
+
* On range donc ce qui est revenu : l'entrée est le premier ordre non reduce-only ; le stop et chaque cible
|
|
1620
|
+
* sont reconnus par leur TYPE et leur PRIX DE DÉCLENCHEMENT — ceux qu'XGate vient de calculer, arrondis à la
|
|
1621
|
+
* grille, donc ceux que la venue a posés. Ce qui manque est cherché au CARNET de la paire, par la même
|
|
1622
|
+
* reconnaissance — le procédé que le SDK pacifica applique déjà à ses take-profits (`placeTakeProfit`).
|
|
1623
|
+
*
|
|
1624
|
+
* Un ordre introuvable garde un identifiant VIDE : refusé, ou déjà consommé (un stop touché dans la seconde,
|
|
1625
|
+
* DOT le 2026-10-01). **Mais une position ouverte dont le stop est introuvable lève** : c'est la position
|
|
1626
|
+
* nue que ce service existe pour empêcher, et l'appelant doit le savoir tout de suite.
|
|
1627
|
+
*/
|
|
1628
|
+
private ordresDuTrade;
|
|
1629
|
+
/** Un ordre du trade que la venue n'a pas posé, ou qui n'existe déjà plus — identifiant VIDE. */
|
|
1630
|
+
private ordreAbsent;
|
|
1588
1631
|
/**
|
|
1589
1632
|
* POSE LES CIBLES QUE LA VENUE N'A PAS EMBARQUÉES, une fois l'entrée remplie.
|
|
1590
1633
|
*
|
|
@@ -1661,10 +1704,13 @@ declare class TradingService {
|
|
|
1661
1704
|
* take-profit peut remplir en plusieurs fois. Seul l'historique porte le prix réellement obtenu, et
|
|
1662
1705
|
* c'est lui qui doit servir au calcul du résultat — sinon le PnL affiché est une fiction.
|
|
1663
1706
|
*
|
|
1664
|
-
* On
|
|
1665
|
-
*
|
|
1707
|
+
* On lit les **exécutions** (`trades`), jamais les ordres : le prix d'un ordre est sa LIMITE — chez
|
|
1708
|
+
* hyperliquid, un market est un IOC borné à mark ± glissement, et son « prix » est cette borne, pas le
|
|
1709
|
+
* prix obtenu (BNB, 2026-10-01 : sortie lue à 844,44 pour des fills à 767,69). Le prix de sortie est la
|
|
1710
|
+
* moyenne des exécutions de sortie après `openedAt` — et avant `until` quand il est donné — pondérée par
|
|
1711
|
+
* leur taille : TP partiels et clôture compris, c'est le prix auquel la position est réellement sortie.
|
|
1666
1712
|
*/
|
|
1667
|
-
exitPrice(access: ITradingAccess, symbolXex: string, direction: Direction, openedAt: Date): Promise<string | null>;
|
|
1713
|
+
exitPrice(access: ITradingAccess, symbolXex: string, direction: Direction, openedAt: Date, until?: Date): Promise<string | null>;
|
|
1668
1714
|
/**
|
|
1669
1715
|
* L'HISTORIQUE DES ORDRES du compte — ce qui a été soumis, rempli, annulé ou expiré.
|
|
1670
1716
|
*
|
|
@@ -1960,4 +2006,4 @@ declare function expiryOf(xtras?: Record<string, unknown>): Date | undefined;
|
|
|
1960
2006
|
*/
|
|
1961
2007
|
declare function isDated(xtras?: Record<string, unknown>): boolean;
|
|
1962
2008
|
|
|
1963
|
-
export { type AssetClass, CANDLES_MAX_HISTORY, CANDLES_PER_CALL, CandlesService, CandlesStreamRegistry, CandlesStreamService, CatalogService, type Direction, type IAccountState, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IFundingPaid, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type IPublicTrade, type ITarget, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, REST_THROTTLE_MS, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, SpotPricesService, SpotWsCandlesService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WS_SUBSCRIPTION_LIMITS, WalletService, WsCandlesService, WsTradesService, XGATE_EXCHANGES, XgateEx, XgateModule, assetClassOf, buildLevels, buildProtection, candlesMaxHistoryOf, candlesPerCallOf, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, restThrottleOf, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, subscriptionLimitOf, timeframeMs };
|
|
2009
|
+
export { type AssetClass, CANDLES_MAX_HISTORY, CANDLES_PER_CALL, CandlesService, CandlesStreamRegistry, CandlesStreamService, CatalogService, type Direction, type IAccountState, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IFundingPaid, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type IPublicTrade, type ITarget, type ITrade, type ITradeOrder, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, REST_THROTTLE_MS, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, SpotPricesService, SpotWsCandlesService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, type TradeOrderRole, TradingService, type Unsubscribe, WALLET_XEXES, WS_SUBSCRIPTION_LIMITS, WalletService, WsCandlesService, WsTradesService, XGATE_EXCHANGES, XgateEx, XgateModule, assetClassOf, buildLevels, buildProtection, candlesMaxHistoryOf, candlesPerCallOf, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, restThrottleOf, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, subscriptionLimitOf, timeframeMs };
|
package/dist/index.d.ts
CHANGED
|
@@ -1323,6 +1323,8 @@ interface IOrder {
|
|
|
1323
1323
|
type: OrderType;
|
|
1324
1324
|
/** `null` pour un ordre au marché. */
|
|
1325
1325
|
price: string | null;
|
|
1326
|
+
/** Prix de DÉCLENCHEMENT d'un ordre conditionnel (stop, take-profit) ; `null` pour un ordre ordinaire. */
|
|
1327
|
+
triggerPrice: string | null;
|
|
1326
1328
|
/** Taille demandée, **en unités de base**. */
|
|
1327
1329
|
size: string;
|
|
1328
1330
|
/** Taille déjà exécutée, **en unités de base**. */
|
|
@@ -1335,6 +1337,24 @@ interface IOrder {
|
|
|
1335
1337
|
placedAt: Date;
|
|
1336
1338
|
xtras?: Record<string, unknown>;
|
|
1337
1339
|
}
|
|
1340
|
+
/** Le rôle d'un ordre dans un trade : l'entrée, le stop, ou un take-profit. */
|
|
1341
|
+
type TradeOrderRole = 'entry' | 'sl' | 'tp';
|
|
1342
|
+
/**
|
|
1343
|
+
* UN ORDRE DU TRADE — ce que {@link TradingService.openWithProtection} rend, une ligne par ordre posé.
|
|
1344
|
+
*
|
|
1345
|
+
* La même forme sur toutes les venues : l'appelant garde cette liste avec son trade et la gère sans savoir
|
|
1346
|
+
* comment chaque venue a posé ses protections (hyperliquid en lot, aster une à une, pacifica embarquées dans
|
|
1347
|
+
* l'entrée). C'est XGate qui fait le pont.
|
|
1348
|
+
*
|
|
1349
|
+
* **L'identifiant n'est vide que si l'ordre n'existe pas ou plus** — refusé, ou déjà consommé (un stop touché
|
|
1350
|
+
* dans la seconde) : `status` dit lequel. Une venue qui ne rend pas l'identifiant d'une protection à l'envoi
|
|
1351
|
+
* (hyperliquid `normalTpsl`, pacifica embarqué) le voit retrouvé au carnet par XGate.
|
|
1352
|
+
*/
|
|
1353
|
+
interface ITradeOrder extends IOrder {
|
|
1354
|
+
role: TradeOrderRole;
|
|
1355
|
+
/** La fraction de la position initiale que l'ordre couvre : 1 pour l'entrée et le stop. */
|
|
1356
|
+
part: number;
|
|
1357
|
+
}
|
|
1338
1358
|
/**
|
|
1339
1359
|
* UNE POSITION ouverte.
|
|
1340
1360
|
*
|
|
@@ -1562,8 +1582,11 @@ declare class TradingService {
|
|
|
1562
1582
|
/**
|
|
1563
1583
|
* OUVRE UNE POSITION AVEC SA PROTECTION, en un geste atomique.
|
|
1564
1584
|
*
|
|
1565
|
-
*
|
|
1566
|
-
* l'ordre
|
|
1585
|
+
* **Ce qui revient est la liste des ORDRES DU TRADE** ({@link ITradeOrder}), la même sur toutes les
|
|
1586
|
+
* venues : l'entrée, le stop, puis chaque take-profit dans l'ordre du plan — chacun avec son rôle, sa
|
|
1587
|
+
* part et un identifiant réel, retrouvé au carnet quand la venue ne l'a pas rendu à l'envoi (0.58.0,
|
|
1588
|
+
* cf. `ordresDuTrade`). L'appelant la garde avec son trade : annuler une protection, c'est
|
|
1589
|
+
* {@link cancel} sur son identifiant, quel que soit le rôle.
|
|
1567
1590
|
*
|
|
1568
1591
|
* Chaque venue applique son mécanisme natif — hyperliquid groupe les enfants sous l'entrée et les
|
|
1569
1592
|
* annule lui-même si elle rate, pacifica les embarque dans l'ordre, aster envoie un lot de
|
|
@@ -1584,7 +1607,27 @@ declare class TradingService {
|
|
|
1584
1607
|
* complète par {@link completeProtection} quand il le constate. Jusqu'en 0.56.0 ces cibles n'étaient
|
|
1585
1608
|
* posées par personne : une position tide à trois paliers sur pacifica partait sans sa dernière.
|
|
1586
1609
|
*/
|
|
1587
|
-
openWithProtection(access: ITradingAccess, input: IEntryWithProtection): Promise<
|
|
1610
|
+
openWithProtection(access: ITradingAccess, input: IEntryWithProtection): Promise<ITradeOrder[]>;
|
|
1611
|
+
/**
|
|
1612
|
+
* LES ORDRES DU TRADE, rangés par rôle — la même liste sur toutes les venues.
|
|
1613
|
+
*
|
|
1614
|
+
* Chaque venue rend ses ordres à sa façon (mesuré sur testnet le 2026-10-01) : hyperliquid en lot `'na'` rend
|
|
1615
|
+
* tous les identifiants, en `normalTpsl` (un seul take-profit) AUCUN pour le stop et la cible (`waitingForFill`,
|
|
1616
|
+
* `waitingForTrigger`) ; pacifica ne rend que l'entrée, le stop et la première cible embarqués naissant au
|
|
1617
|
+
* carnet sans lien (`parent_id: null`) ; aster pose une à une et rend tout.
|
|
1618
|
+
*
|
|
1619
|
+
* On range donc ce qui est revenu : l'entrée est le premier ordre non reduce-only ; le stop et chaque cible
|
|
1620
|
+
* sont reconnus par leur TYPE et leur PRIX DE DÉCLENCHEMENT — ceux qu'XGate vient de calculer, arrondis à la
|
|
1621
|
+
* grille, donc ceux que la venue a posés. Ce qui manque est cherché au CARNET de la paire, par la même
|
|
1622
|
+
* reconnaissance — le procédé que le SDK pacifica applique déjà à ses take-profits (`placeTakeProfit`).
|
|
1623
|
+
*
|
|
1624
|
+
* Un ordre introuvable garde un identifiant VIDE : refusé, ou déjà consommé (un stop touché dans la seconde,
|
|
1625
|
+
* DOT le 2026-10-01). **Mais une position ouverte dont le stop est introuvable lève** : c'est la position
|
|
1626
|
+
* nue que ce service existe pour empêcher, et l'appelant doit le savoir tout de suite.
|
|
1627
|
+
*/
|
|
1628
|
+
private ordresDuTrade;
|
|
1629
|
+
/** Un ordre du trade que la venue n'a pas posé, ou qui n'existe déjà plus — identifiant VIDE. */
|
|
1630
|
+
private ordreAbsent;
|
|
1588
1631
|
/**
|
|
1589
1632
|
* POSE LES CIBLES QUE LA VENUE N'A PAS EMBARQUÉES, une fois l'entrée remplie.
|
|
1590
1633
|
*
|
|
@@ -1661,10 +1704,13 @@ declare class TradingService {
|
|
|
1661
1704
|
* take-profit peut remplir en plusieurs fois. Seul l'historique porte le prix réellement obtenu, et
|
|
1662
1705
|
* c'est lui qui doit servir au calcul du résultat — sinon le PnL affiché est une fiction.
|
|
1663
1706
|
*
|
|
1664
|
-
* On
|
|
1665
|
-
*
|
|
1707
|
+
* On lit les **exécutions** (`trades`), jamais les ordres : le prix d'un ordre est sa LIMITE — chez
|
|
1708
|
+
* hyperliquid, un market est un IOC borné à mark ± glissement, et son « prix » est cette borne, pas le
|
|
1709
|
+
* prix obtenu (BNB, 2026-10-01 : sortie lue à 844,44 pour des fills à 767,69). Le prix de sortie est la
|
|
1710
|
+
* moyenne des exécutions de sortie après `openedAt` — et avant `until` quand il est donné — pondérée par
|
|
1711
|
+
* leur taille : TP partiels et clôture compris, c'est le prix auquel la position est réellement sortie.
|
|
1666
1712
|
*/
|
|
1667
|
-
exitPrice(access: ITradingAccess, symbolXex: string, direction: Direction, openedAt: Date): Promise<string | null>;
|
|
1713
|
+
exitPrice(access: ITradingAccess, symbolXex: string, direction: Direction, openedAt: Date, until?: Date): Promise<string | null>;
|
|
1668
1714
|
/**
|
|
1669
1715
|
* L'HISTORIQUE DES ORDRES du compte — ce qui a été soumis, rempli, annulé ou expiré.
|
|
1670
1716
|
*
|
|
@@ -1960,4 +2006,4 @@ declare function expiryOf(xtras?: Record<string, unknown>): Date | undefined;
|
|
|
1960
2006
|
*/
|
|
1961
2007
|
declare function isDated(xtras?: Record<string, unknown>): boolean;
|
|
1962
2008
|
|
|
1963
|
-
export { type AssetClass, CANDLES_MAX_HISTORY, CANDLES_PER_CALL, CandlesService, CandlesStreamRegistry, CandlesStreamService, CatalogService, type Direction, type IAccountState, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IFundingPaid, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type IPublicTrade, type ITarget, type ITrade, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, REST_THROTTLE_MS, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, SpotPricesService, SpotWsCandlesService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, TradingService, type Unsubscribe, WALLET_XEXES, WS_SUBSCRIPTION_LIMITS, WalletService, WsCandlesService, WsTradesService, XGATE_EXCHANGES, XgateEx, XgateModule, assetClassOf, buildLevels, buildProtection, candlesMaxHistoryOf, candlesPerCallOf, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, restThrottleOf, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, subscriptionLimitOf, timeframeMs };
|
|
2009
|
+
export { type AssetClass, CANDLES_MAX_HISTORY, CANDLES_PER_CALL, CandlesService, CandlesStreamRegistry, CandlesStreamService, CatalogService, type Direction, type IAccountState, type IBalance, type ICandle, type ICandlesQuery, type IEntryWithProtection, type IEquityPoint, type IFundingPaid, type IMoveStop, type IMovement, type IOrder, type IPair, type IPosition, type IPrice, type IProtection, type IProtectionInput, type IProtectionLeg, type IPublicTrade, type ITarget, type ITrade, type ITradeOrder, type ITradingAccess, type IWalletAccess, type IXexAccess, type IXexSpotSource, type MarketKind, type MovementKind, type MovementStatus, ORDER_STATUSES, type OrderStatus, type OrderType, PricesService, REST_THROTTLE_MS, SPOT_XEXES, type Side, SpotCandlesService, SpotCatalogService, SpotPricesService, SpotWsCandlesService, TIMEFRAME_MINUTES, type TimeInForce, Timeframe, type TradeOrderRole, TradingService, type Unsubscribe, WALLET_XEXES, WS_SUBSCRIPTION_LIMITS, WalletService, WsCandlesService, WsTradesService, XGATE_EXCHANGES, XgateEx, XgateModule, assetClassOf, buildLevels, buildProtection, candlesMaxHistoryOf, candlesPerCallOf, canonicalFromBase, canonicalFromXex, expiryOf, isDated, multiplierFromXex, restThrottleOf, roundPrice, roundToStep, servesNatively, servesSpot, sourceFor, subscriptionLimitOf, timeframeMs };
|
package/dist/index.js
CHANGED
|
@@ -1250,8 +1250,11 @@ var TradingService = class {
|
|
|
1250
1250
|
/**
|
|
1251
1251
|
* OUVRE UNE POSITION AVEC SA PROTECTION, en un geste atomique.
|
|
1252
1252
|
*
|
|
1253
|
-
*
|
|
1254
|
-
* l'ordre
|
|
1253
|
+
* **Ce qui revient est la liste des ORDRES DU TRADE** ({@link ITradeOrder}), la même sur toutes les
|
|
1254
|
+
* venues : l'entrée, le stop, puis chaque take-profit dans l'ordre du plan — chacun avec son rôle, sa
|
|
1255
|
+
* part et un identifiant réel, retrouvé au carnet quand la venue ne l'a pas rendu à l'envoi (0.58.0,
|
|
1256
|
+
* cf. `ordresDuTrade`). L'appelant la garde avec son trade : annuler une protection, c'est
|
|
1257
|
+
* {@link cancel} sur son identifiant, quel que soit le rôle.
|
|
1255
1258
|
*
|
|
1256
1259
|
* Chaque venue applique son mécanisme natif — hyperliquid groupe les enfants sous l'entrée et les
|
|
1257
1260
|
* annule lui-même si elle rate, pacifica les embarque dans l'ordre, aster envoie un lot de
|
|
@@ -1314,27 +1317,25 @@ var TradingService = class {
|
|
|
1314
1317
|
tps: protection.tps,
|
|
1315
1318
|
clientId: input.clientId
|
|
1316
1319
|
};
|
|
1320
|
+
let posees;
|
|
1317
1321
|
try {
|
|
1318
1322
|
const orders = await perp.createEntryWithProtection(entry, consigne);
|
|
1319
|
-
|
|
1323
|
+
posees = orders.map((order) => this.toOrder(order, access.xex));
|
|
1320
1324
|
const enAttente = this.ciblesEnAttente(perp, consigne);
|
|
1321
|
-
if (enAttente.length ===
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
+
if (enAttente.length > 0 && (tif === "ioc" || tif === "fok")) {
|
|
1326
|
+
const complement = await this.poserCiblesEnAttente(
|
|
1327
|
+
access,
|
|
1328
|
+
perp,
|
|
1329
|
+
input.symbolXex,
|
|
1330
|
+
side,
|
|
1331
|
+
enAttente
|
|
1332
|
+
);
|
|
1333
|
+
posees = [...posees, ...complement];
|
|
1334
|
+
} else if (enAttente.length > 0) {
|
|
1325
1335
|
this.logger.warn(
|
|
1326
1336
|
`openWithProtection(${access.xex}/${input.symbolXex}) : ${enAttente.length} cible(s) que la venue n'embarque pas \u2014 l'entr\xE9e ${tif} peut rester au carnet, appeler completeProtection une fois remplie.`
|
|
1327
1337
|
);
|
|
1328
|
-
return posees;
|
|
1329
1338
|
}
|
|
1330
|
-
const complement = await this.poserCiblesEnAttente(
|
|
1331
|
-
access,
|
|
1332
|
-
perp,
|
|
1333
|
-
input.symbolXex,
|
|
1334
|
-
side,
|
|
1335
|
-
enAttente
|
|
1336
|
-
);
|
|
1337
|
-
return [...posees, ...complement];
|
|
1338
1339
|
} catch (error) {
|
|
1339
1340
|
const message = error instanceof Error ? error.message : String(error);
|
|
1340
1341
|
if (message.includes(ASTER_TIMEOUT) === false) {
|
|
@@ -1343,8 +1344,119 @@ var TradingService = class {
|
|
|
1343
1344
|
this.logger.warn(
|
|
1344
1345
|
`openWithProtection(${access.xex}) : la venue a coup\xE9 avant de r\xE9pondre. Statut inconnu \u2014 relecture de l'\xE9tat r\xE9el.`
|
|
1345
1346
|
);
|
|
1346
|
-
|
|
1347
|
+
posees = await this.etatApres(access, input.symbolXex);
|
|
1347
1348
|
}
|
|
1349
|
+
return await this.ordresDuTrade(access, perp, input, side, protection, posees);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* LES ORDRES DU TRADE, rangés par rôle — la même liste sur toutes les venues.
|
|
1353
|
+
*
|
|
1354
|
+
* Chaque venue rend ses ordres à sa façon (mesuré sur testnet le 2026-10-01) : hyperliquid en lot `'na'` rend
|
|
1355
|
+
* tous les identifiants, en `normalTpsl` (un seul take-profit) AUCUN pour le stop et la cible (`waitingForFill`,
|
|
1356
|
+
* `waitingForTrigger`) ; pacifica ne rend que l'entrée, le stop et la première cible embarqués naissant au
|
|
1357
|
+
* carnet sans lien (`parent_id: null`) ; aster pose une à une et rend tout.
|
|
1358
|
+
*
|
|
1359
|
+
* On range donc ce qui est revenu : l'entrée est le premier ordre non reduce-only ; le stop et chaque cible
|
|
1360
|
+
* sont reconnus par leur TYPE et leur PRIX DE DÉCLENCHEMENT — ceux qu'XGate vient de calculer, arrondis à la
|
|
1361
|
+
* grille, donc ceux que la venue a posés. Ce qui manque est cherché au CARNET de la paire, par la même
|
|
1362
|
+
* reconnaissance — le procédé que le SDK pacifica applique déjà à ses take-profits (`placeTakeProfit`).
|
|
1363
|
+
*
|
|
1364
|
+
* Un ordre introuvable garde un identifiant VIDE : refusé, ou déjà consommé (un stop touché dans la seconde,
|
|
1365
|
+
* DOT le 2026-10-01). **Mais une position ouverte dont le stop est introuvable lève** : c'est la position
|
|
1366
|
+
* nue que ce service existe pour empêcher, et l'appelant doit le savoir tout de suite.
|
|
1367
|
+
*/
|
|
1368
|
+
async ordresDuTrade(access, perp, input, side, protection, posees) {
|
|
1369
|
+
const exit = side === "buy" ? "sell" : "buy";
|
|
1370
|
+
const part = (size) => Number(size) / input.size;
|
|
1371
|
+
const entree = posees.find((order) => order.reduceOnly !== true) ?? null;
|
|
1372
|
+
const rejetee = entree !== null && entree.status === "rejected";
|
|
1373
|
+
const pris = /* @__PURE__ */ new Set();
|
|
1374
|
+
const attendus = [
|
|
1375
|
+
{ role: "sl", leg: protection.sl },
|
|
1376
|
+
...protection.tps.map((leg) => ({ role: "tp", leg }))
|
|
1377
|
+
];
|
|
1378
|
+
const reconnaitre = (pool, role, leg) => pool.find(
|
|
1379
|
+
(order) => order.id !== "" && pris.has(order.id) === false && estDuRole(order.type, role) === true && (order.triggerPrice === null || memePrix(order.triggerPrice, leg.triggerPrice) === true)
|
|
1380
|
+
);
|
|
1381
|
+
let carnet = null;
|
|
1382
|
+
const lireCarnet = async () => {
|
|
1383
|
+
if (carnet === null) {
|
|
1384
|
+
carnet = (await perp.getOpens()).map((order) => this.toOrder(order, access.xex)).filter((order) => order.symbolXex === input.symbolXex && order.reduceOnly === true);
|
|
1385
|
+
}
|
|
1386
|
+
return carnet;
|
|
1387
|
+
};
|
|
1388
|
+
const ordres = [
|
|
1389
|
+
entree === null ? this.ordreAbsent(
|
|
1390
|
+
access,
|
|
1391
|
+
input,
|
|
1392
|
+
"entry",
|
|
1393
|
+
side,
|
|
1394
|
+
"limit",
|
|
1395
|
+
1,
|
|
1396
|
+
null,
|
|
1397
|
+
String(input.size),
|
|
1398
|
+
"other"
|
|
1399
|
+
) : { ...entree, role: "entry", part: 1 }
|
|
1400
|
+
];
|
|
1401
|
+
for (const { role, leg } of attendus) {
|
|
1402
|
+
let trouve = reconnaitre(posees, role, leg);
|
|
1403
|
+
if (trouve === void 0 && rejetee === false) {
|
|
1404
|
+
trouve = reconnaitre(await lireCarnet(), role, leg);
|
|
1405
|
+
}
|
|
1406
|
+
if (trouve !== void 0) {
|
|
1407
|
+
pris.add(trouve.id);
|
|
1408
|
+
ordres.push({ ...trouve, role, part: part(leg.size) });
|
|
1409
|
+
} else {
|
|
1410
|
+
ordres.push(
|
|
1411
|
+
this.ordreAbsent(
|
|
1412
|
+
access,
|
|
1413
|
+
input,
|
|
1414
|
+
role,
|
|
1415
|
+
exit,
|
|
1416
|
+
role === "sl" ? "stopMarket" : "takeProfitMarket",
|
|
1417
|
+
part(leg.size),
|
|
1418
|
+
leg.triggerPrice,
|
|
1419
|
+
leg.size,
|
|
1420
|
+
rejetee === true ? "canceled" : "other"
|
|
1421
|
+
)
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
const stop = ordres.find((order) => order.role === "sl");
|
|
1426
|
+
if (rejetee === false && stop !== void 0 && stop.id === "") {
|
|
1427
|
+
const positions = await perp.getPositions({ name: input.symbolXex });
|
|
1428
|
+
const ouverte = positions.find(
|
|
1429
|
+
(candidate) => candidate.name === input.symbolXex && Number(candidate.size) !== 0
|
|
1430
|
+
);
|
|
1431
|
+
if (ouverte !== void 0) {
|
|
1432
|
+
throw new Error(
|
|
1433
|
+
`openWithProtection(${access.xex}/${input.symbolXex}) : POSITION NUE \u2014 ${ouverte.size} ouvert, stop introuvable au carnet. \xC0 prot\xE9ger imm\xE9diatement.`
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return ordres;
|
|
1438
|
+
}
|
|
1439
|
+
/** Un ordre du trade que la venue n'a pas posé, ou qui n'existe déjà plus — identifiant VIDE. */
|
|
1440
|
+
ordreAbsent(access, input, role, side, type, part, triggerPrice, size, status) {
|
|
1441
|
+
return {
|
|
1442
|
+
xex: access.xex,
|
|
1443
|
+
symbolXex: input.symbolXex,
|
|
1444
|
+
kind: "perp",
|
|
1445
|
+
id: "",
|
|
1446
|
+
clientId: null,
|
|
1447
|
+
side,
|
|
1448
|
+
type,
|
|
1449
|
+
price: null,
|
|
1450
|
+
triggerPrice,
|
|
1451
|
+
size,
|
|
1452
|
+
filled: "0",
|
|
1453
|
+
status,
|
|
1454
|
+
tif: null,
|
|
1455
|
+
reduceOnly: role !== "entry",
|
|
1456
|
+
placedAt: /* @__PURE__ */ new Date(),
|
|
1457
|
+
role,
|
|
1458
|
+
part
|
|
1459
|
+
};
|
|
1348
1460
|
}
|
|
1349
1461
|
/**
|
|
1350
1462
|
* POSE LES CIBLES QUE LA VENUE N'A PAS EMBARQUÉES, une fois l'entrée remplie.
|
|
@@ -1589,15 +1701,25 @@ var TradingService = class {
|
|
|
1589
1701
|
* take-profit peut remplir en plusieurs fois. Seul l'historique porte le prix réellement obtenu, et
|
|
1590
1702
|
* c'est lui qui doit servir au calcul du résultat — sinon le PnL affiché est une fiction.
|
|
1591
1703
|
*
|
|
1592
|
-
* On
|
|
1593
|
-
*
|
|
1704
|
+
* On lit les **exécutions** (`trades`), jamais les ordres : le prix d'un ordre est sa LIMITE — chez
|
|
1705
|
+
* hyperliquid, un market est un IOC borné à mark ± glissement, et son « prix » est cette borne, pas le
|
|
1706
|
+
* prix obtenu (BNB, 2026-10-01 : sortie lue à 844,44 pour des fills à 767,69). Le prix de sortie est la
|
|
1707
|
+
* moyenne des exécutions de sortie après `openedAt` — et avant `until` quand il est donné — pondérée par
|
|
1708
|
+
* leur taille : TP partiels et clôture compris, c'est le prix auquel la position est réellement sortie.
|
|
1594
1709
|
*/
|
|
1595
|
-
async exitPrice(access, symbolXex, direction, openedAt) {
|
|
1710
|
+
async exitPrice(access, symbolXex, direction, openedAt, until) {
|
|
1596
1711
|
const exit = direction === "long" ? "sell" : "buy";
|
|
1597
|
-
const
|
|
1598
|
-
const
|
|
1599
|
-
|
|
1600
|
-
|
|
1712
|
+
const fin = until === void 0 ? Number.POSITIVE_INFINITY : until.getTime();
|
|
1713
|
+
const fills = (await this.trades(access)).filter((fill) => fill.symbolXex === symbolXex && fill.side === exit).filter(
|
|
1714
|
+
(fill) => fill.filledAt.getTime() > openedAt.getTime() && fill.filledAt.getTime() <= fin
|
|
1715
|
+
).filter((fill) => Number(fill.price) > 0 && Number(fill.size) > 0);
|
|
1716
|
+
const taille = fills.reduce((total, fill) => total + Number(fill.size), 0);
|
|
1717
|
+
if (taille === 0) {
|
|
1718
|
+
return null;
|
|
1719
|
+
}
|
|
1720
|
+
const moyenne = fills.reduce((total, fill) => total + Number(fill.price) * Number(fill.size), 0) / taille;
|
|
1721
|
+
const decimales = Math.max(...fills.map((fill) => (fill.price.split(".")[1] ?? "").length));
|
|
1722
|
+
return String(Number(moyenne.toFixed(decimales)));
|
|
1601
1723
|
}
|
|
1602
1724
|
/**
|
|
1603
1725
|
* L'HISTORIQUE DES ORDRES du compte — ce qui a été soumis, rempli, annulé ou expiré.
|
|
@@ -1870,6 +1992,7 @@ var TradingService = class {
|
|
|
1870
1992
|
side: order.side,
|
|
1871
1993
|
type: order.type,
|
|
1872
1994
|
price: order.price,
|
|
1995
|
+
triggerPrice: order.triggerPrice,
|
|
1873
1996
|
size: order.size,
|
|
1874
1997
|
filled: order.filled,
|
|
1875
1998
|
status: this.toStatus(order.status),
|
|
@@ -1883,6 +2006,14 @@ var TradingService = class {
|
|
|
1883
2006
|
TradingService = __decorateClass([
|
|
1884
2007
|
Injectable()
|
|
1885
2008
|
], TradingService);
|
|
2009
|
+
function estDuRole(type, role) {
|
|
2010
|
+
return role === "sl" ? type === "stop" || type === "stopMarket" : type === "takeProfit" || type === "takeProfitMarket";
|
|
2011
|
+
}
|
|
2012
|
+
function memePrix(lu, pose) {
|
|
2013
|
+
const a = Number(lu);
|
|
2014
|
+
const b = Number(pose);
|
|
2015
|
+
return Math.abs(a - b) <= 1e-9 * Math.max(1, Math.abs(b));
|
|
2016
|
+
}
|
|
1886
2017
|
function toXexDate(date) {
|
|
1887
2018
|
return date.toISOString().slice(0, 19).replace("T", " ");
|
|
1888
2019
|
}
|
|
@@ -2261,6 +2392,7 @@ var WsTradesService = class {
|
|
|
2261
2392
|
side: order.side,
|
|
2262
2393
|
type: order.type,
|
|
2263
2394
|
price: order.price,
|
|
2395
|
+
triggerPrice: order.triggerPrice,
|
|
2264
2396
|
size: order.size,
|
|
2265
2397
|
filled: order.filled,
|
|
2266
2398
|
status: ORDER_STATUSES.includes(order.status) ? order.status : "other",
|