@syldel/hl-shared-types 0.0.22 → 0.0.24

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.MD CHANGED
@@ -1,5 +1,5 @@
1
- Copyright (c) 2026, Syl-Studio Inc. c/o Syl. D.
2
-
3
- Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
-
1
+ Copyright (c) 2026, Syl-Studio Inc. c/o Syl. D.
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
5
  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -119,10 +119,48 @@ git push origin main --follow-tags
119
119
 
120
120
  ---
121
121
 
122
+ ## 🔍 Avant de déclarer un champ : d'où vient l'information
123
+
124
+ Chaque champ de ce paquet est une **affirmation sur ce que l'exchange rend**, et les trois
125
+ dépôts la croient. Une affirmation fausse ne se voit pas : elle se paie plus loin, sur un
126
+ dimensionnement ou un affichage. Avant d'ajouter, de resserrer ou d'élargir un type, lire
127
+ [`nest-hyperliquid-gateway/docs/sources.md`](https://github.com/Syldel/nest-hyperliquid-gateway/blob/main/docs/sources.md).
128
+
129
+ En résumé de ce document :
130
+
131
+ * **La mesure** (captures du gateway, lectures `/hyperliquid/info/*`) prouve qu'une forme
132
+ existe, jamais qu'elle soit la seule. Un `null` vu une fois clôt la nullité d'un champ ;
133
+ mille valeurs présentes ne closent rien. Et un échantillon se décrit honnêtement — 22
134
+ instantanés d'une même position ne sont pas 22 observations.
135
+ * **La doc officielle** fait autorité sur les noms, et tient sur **un exemple par
136
+ endpoint** : elle est donc muette sur ce qui varie. Un champ absent d'un exemple n'est ni
137
+ absent de l'API ni optionnel.
138
+ * **Le SDK Python officiel**
139
+ ([hyperliquid-dex/hyperliquid-python-sdk](https://github.com/hyperliquid-dex/hyperliquid-python-sdk))
140
+ est de première partie **et typé** : il tranche les unions, les champs propres à une
141
+ variante et les valeurs énumérées que la prose laisse en suspens. Mais il ne type pas
142
+ tout, et son silence n'est pas une réponse.
143
+ * **Les SDK communautaires ne concluent pas.** La page d'API en recommande plusieurs — un en
144
+ Rust, deux en TypeScript, plus CCXT — et le dit : ils sont écrits par la communauté. Être
145
+ lié par la doc officielle donne de la visibilité, pas de l'autorité ; ils lisent la même
146
+ API que nous et peuvent s'être trompés pareil. Ils servent à repérer une question, jamais
147
+ à la clore — et le piège est celui en TypeScript, dont on a envie de recopier une union
148
+ toute faite. Il n'existe **aucun SDK TypeScript de première partie**.
149
+
150
+ `HLPerpLeverage` est le cas d'école : ni les captures (que du `cross`) ni l'exemple de la
151
+ doc (que de l'`isolated`) ne pouvaient trancher, et `hyperliquid/utils/types.py` déclare les
152
+ deux branches. Les types portent leur source et leur date en commentaire — c'est ce qui
153
+ permet de les contredire plus tard au lieu de devoir tout remesurer.
154
+
155
+ ---
156
+
122
157
  ## 📝 Conventions de code
123
158
 
124
159
  * **Sauts de ligne** : Une ligne vide est automatiquement insérée entre chaque `interface` pour une meilleure lisibilité.
125
160
  * **Naming** : Toutes les interfaces commencent par `HL`.
126
161
  * **Types stricts** : Usage de `DecimalString` pour la précision financière.
162
+ * **Fins de ligne** : LF partout, imposé par `.gitattributes` et non par la configuration de
163
+ la machine — le paquet publié embarque du code généré par `tsc`, dont les fins de ligne
164
+ suivent celles des sources.
127
165
 
128
166
  ---
@@ -1,15 +1,58 @@
1
1
  import { DecimalString, Timestamp } from '../common';
2
2
  /**
3
3
  * Leverage configuration for a perpetual position.
4
+ *
5
+ * ============================================================================
6
+ * UNE UNION, PARCE QUE LES DEUX MODES NE PORTENT PAS LES MÊMES CHAMPS
7
+ *
8
+ * `rawUsd` était déclaré **obligatoire** pour les deux modes, et le faux
9
+ * gateway devait donc mentir par une assertion de type pour construire une
10
+ * position cross plausible — exactement ce qu'un double n'a pas le droit de
11
+ * faire.
12
+ *
13
+ * La forme vient du **SDK Python officiel** (`hyperliquid-dex/
14
+ * hyperliquid-python-sdk`, `hyperliquid/utils/types.py`), qui déclare la même
15
+ * union, discriminée sur `type` :
16
+ *
17
+ * CrossLeverage = {"type": Literal["cross"], "value": int}
18
+ * IsolatedLeverage = {"type": Literal["isolated"], "value": int,
19
+ * "rawUsd": str}
20
+ * Leverage = Union[CrossLeverage, IsolatedLeverage]
21
+ *
22
+ * Deux relevés la recoupent, chacun pour une branche : les captures du gateway
23
+ * du 2026-09-22 n'ont que des leviers `cross`, tous en `{ type, value }` nus ;
24
+ * l'exemple de la doc de `clearinghouseState` n'a qu'une position isolée, qui
25
+ * porte `rawUsd: "-95.059824"`. Aucun des deux ne couvre l'autre branche — le
26
+ * SDK, lui, les couvre toutes les deux.
27
+ *
28
+ * ⚠️ Ce « officiel » ne se déduit **pas** du nom de l'organisation GitHub, qui
29
+ * n'est d'ailleurs pas vérifiée (`is_verified: false`). Il repose sur la page
30
+ * d'API de la documentation, qui distingue ce SDK des bibliothèques
31
+ * communautaires, et sur PyPI, qui le publie sous `hello@hyperliquid.xyz`. La
32
+ * chaîne et ses limites : `nest-hyperliquid-gateway/docs/sources.md`.
33
+ *
34
+ * Pourquoi l'union et non `rawUsd?: DecimalString` : l'optionnel rendrait
35
+ * `lev.rawUsd` lisible sans vérifier `type`, et `Number(undefined)` rend `NaN`
36
+ * sans un mot. L'union en fait une erreur de compilation. Et si un cross
37
+ * portait un jour `rawUsd`, le coût de s'être trompé ici est nul — personne ne
38
+ * lit ce champ, il serait simplement invisible — là où le coût du `NaN`
39
+ * silencieux se paierait sur un dimensionnement.
40
+ * ============================================================================
4
41
  */
5
- export interface HLPerpLeverage {
6
- /** Notional value in USD used for leverage calculation. */
7
- rawUsd: DecimalString;
42
+ export type HLPerpLeverage = {
8
43
  /** Margin mode used for the position. */
9
- type: 'isolated' | 'cross';
44
+ type: 'cross';
10
45
  /** Current leverage multiplier. */
11
46
  value: number;
12
- }
47
+ } | {
48
+ type: 'isolated';
49
+ value: number;
50
+ /**
51
+ * Notional value in USD used for leverage calculation. Propre à l'isolé,
52
+ * où la marge est cantonnée à la position.
53
+ */
54
+ rawUsd: DecimalString;
55
+ };
13
56
  /**
14
57
  * Funding payments accumulated for a perpetual position.
15
58
  */
@@ -33,8 +76,25 @@ export interface HLPerpPositionDetail {
33
76
  entryPx: DecimalString;
34
77
  /** Leverage configuration. */
35
78
  leverage: HLPerpLeverage;
36
- /** Estimated liquidation price. */
37
- liquidationPx: DecimalString;
79
+ /**
80
+ * Estimated liquidation price, **`null`** quand l'exchange n'en publie pas.
81
+ *
82
+ * Relevé le 2026-09-22 dans les captures réelles du gateway : sur 6 états
83
+ * distincts d'une même paire (`xyz:MU`, cross), les 3 **longs** rendent
84
+ * `null` et les 2 **shorts** rendent une chaîne. Un seul coin sur un seul
85
+ * dex : cette corrélation avec le sens de la position n'explique rien, et je
86
+ * ne connais pas la règle.
87
+ *
88
+ * Ce qui est établi suffit pourtant, et c'est asymétrique : une nullité se
89
+ * prouve par un seul contre-exemple, alors que la non-nullité demanderait de
90
+ * l'avoir épuisée. Trois `null` mesurés closent la question du type ; ils ne
91
+ * disent rien de *quand*.
92
+ *
93
+ * Conséquence pour un consommateur : le `null` s'écarte, il ne se convertit
94
+ * pas. `Number(null)` rend `0`, et un prix de liquidation à zéro est le plus
95
+ * rassurant des mensonges — il paraît infiniment loin du prix courant.
96
+ */
97
+ liquidationPx: DecimalString | null;
38
98
  /** Margin currently used by the position. */
39
99
  marginUsed: DecimalString;
40
100
  /** Maximum allowed leverage for this market. */
@@ -52,7 +112,17 @@ export interface HLPerpPositionDetail {
52
112
  * Wrapper describing a perpetual asset position.
53
113
  */
54
114
  export interface HLPerpAssetPosition {
55
- /** Position details (null if no active position). */
115
+ /**
116
+ * Position details (null if no active position).
117
+ *
118
+ * ⚠️ Laissé nullable bien qu'aucune des entrées relevées le 2026-09-22 ne
119
+ * soit nulle : des observations d'une valeur présente ne prouvent pas qu'elle
120
+ * le soit toujours — et celles-ci portent sur une seule paire —, la doc nomme
121
+ * le cas, et deux des trois lecteurs du bot le gardaient déjà. Le troisième
122
+ * ne le gardait pas, et levait un `Cannot read properties of null` juste
123
+ * après une entrée remplie : c'est ce défaut qui a fait garder ce `| null`
124
+ * plutôt que le resserrer.
125
+ */
56
126
  position: HLPerpPositionDetail | null;
57
127
  /** Position mode configuration. */
58
128
  type: 'oneWay' | 'hedged';
@@ -0,0 +1,115 @@
1
+ import { DecimalString, Timestamp } from '../common';
2
+ /**
3
+ * ============================================================================
4
+ * L'HISTORIQUE DE FUNDING — LE SEUL CHAMP DE CONTEXTE QUI AIT UNE VRAIE SÉRIE
5
+ *
6
+ * `fundingHistory` est, au 2026-10-06, le **seul** endpoint d'information qui
7
+ * rende une série temporelle d'un champ de contexte de marché. Il n'existe
8
+ * aucun endpoint documenté pour l'historique de l'intérêt ouvert, ni pour celui
9
+ * des prix d'impact : ces valeurs ne sont lisibles qu'à l'instant courant.
10
+ *
11
+ * C'est ce qui décide de ce qu'un moteur de règles peut en faire : une
12
+ * transformation glissante (z-score, pente, percentile) a besoin d'une série,
13
+ * donc elle n'est légitime que sur `fundingRate` et `premium`.
14
+ *
15
+ * ## Ce qui a été mesuré, le 2026-10-06
16
+ *
17
+ * Deux appels, sur six heures, sur `api.hyperliquid.xyz` :
18
+ *
19
+ * - **BTC** (vivant) : 6 entrées pour 6 heures — la cadence est bien horaire,
20
+ * ce que la documentation confirme (« funding is paid every hour at one
21
+ * eighth of the computed rate »). Quatre clés, toutes en chaîne sauf `time`.
22
+ * **Aucun nul**, sur aucun champ.
23
+ * - **MATIC** (délisté) : 6 entrées également, avec `fundingRate: "0.0"` et
24
+ * `premium: "0.0"`.
25
+ * - **xyz:XYZ100** (HIP-3, vivant) : 6 entrées, écarts de 3 600 000 ms ± 20 ms,
26
+ * et le `coin` rendu est identique à celui demandé, préfixe compris.
27
+ *
28
+ * ⚠️ Les horodatages ne tombent **pas exactement** sur l'heure : les restes
29
+ * modulo une heure relevés valaient 37, 39, 27, 7, 46 et 24 ms. Aligner une
30
+ * fenêtre de requête sur l'heure pleine reste correct — `startTime` est
31
+ * inclusif, donc arrondir vers le bas attrape l'entrée — mais comparer un
32
+ * horodatage à une frontière d'heure par égalité ne marcherait pas.
33
+ *
34
+ * ## ⚠️ Trois pièges que ces mesures révèlent
35
+ *
36
+ * **1. `"0.0"` a trois significations différentes, et rien ne les distingue
37
+ * dans cette réponse.** Mesuré le 2026-10-06 via `perpDexs` :
38
+ *
39
+ * - un **marché délisté** rend `"0.0"` (MATIC) ;
40
+ * - un **marché vivant et sain** peut rendre `"0.0"` en permanence, parce que
41
+ * son dex déclare un multiplicateur de funding nul : `assetToFundingMultiplier`
42
+ * vaut `0.0` sur **tous** les actifs de `flx` et de `vntl` ;
43
+ * - et un financement réellement nul à cette heure-là.
44
+ *
45
+ * Une règle du genre `funding <= 0` serait donc vraie en permanence sur des
46
+ * marchés parfaitement vivants. Un consommateur doit croiser cette série avec
47
+ * `isDelisted` (voir `HLPerpMarketInfo`) **et** avec le multiplicateur du dex
48
+ * (voir `HLPerpDex.assetToFundingMultiplier`). L'API fournit ici elle-même la
49
+ * valeur de repli plausible et fausse que ce dépôt refuse d'habitude de
50
+ * fabriquer.
51
+ *
52
+ * **2. Un seuil de funding absolu n'est pas transposable d'un marché à
53
+ * l'autre.** Les multiplicateurs relevés vont de `0.0` (`flx`, `vntl`) à
54
+ * `0.000001` (`cash`), `0.01` à `1.0` (`hyna`, qui les fait varier **par
55
+ * actif** dans un même dex), `0.1` à `0.6` (`para`), `0.125` à `0.5` (`io`),
56
+ * `0.5` partout sur `xyz`. Trois dex n'en déclarent aucun. Un `funding >
57
+ * 0.00001` serait donc courant sur l'un et impossible sur l'autre : ce champ
58
+ * se compare **relativement** (z-score, percentile) ou normalisé par le
59
+ * multiplicateur, jamais à une constante écrite en dur.
60
+ *
61
+ * **3. `fundingRate` est souvent constant.** Sur BTC, les trois heures relevées
62
+ * valaient toutes `0.0000125`, soit exactement le plancher (un huitième de
63
+ * 0,01 %) ; sur `xyz:XYZ100`, toutes `0.00000625`, soit ce même plancher
64
+ * multiplié par le `0.5` du dex. Une transformation glissante sur un taux au
65
+ * plancher voit une série plate : son écart-type est nul, et un z-score n'y a
66
+ * aucun sens. `premium`, lui, varie en continu sur les deux marchés — c'est la
67
+ * série informative des deux.
68
+ *
69
+ * ## `premium` ici n'est pas `premium` dans `metaAndAssetCtxs`
70
+ *
71
+ * La documentation dit que « the premium is sampled **every 5 seconds and
72
+ * averaged over the hour** », et que `premium = impact_price_difference /
73
+ * oracle_price`. Le champ rendu ici est donc une **moyenne horaire** sur
74
+ * environ 720 échantillons, là où le `premium` d'un contexte d'actif est la
75
+ * lecture de l'instant. Les deux portent le même nom et ne sont pas la même
76
+ * quantité.
77
+ * ============================================================================
78
+ */
79
+ /**
80
+ * Une entrée de l'historique de funding, telle que `fundingHistory` la rend.
81
+ *
82
+ * Aucun champ n'est optionnel ni nullable : mesuré sur un marché vivant **et**
83
+ * sur un marché délisté, l'endpoint rend toujours les quatre, et rend `"0.0"`
84
+ * plutôt que `null` quand il n'y a rien à payer.
85
+ */
86
+ export interface HLFundingHistoryEntry {
87
+ /** Le marché concerné, tel que demandé (ex. `BTC`, `xyz:XYZ100`). */
88
+ coin: string;
89
+ /**
90
+ * Le taux **horaire** réellement appliqué, soit un huitième du taux calculé
91
+ * sur 8 heures. `"0.0"` sur un marché délisté — voir l'en-tête.
92
+ */
93
+ fundingRate: DecimalString;
94
+ /**
95
+ * La prime moyenne de l'heure : `impact_price_difference / oracle_price`,
96
+ * échantillonnée toutes les 5 secondes et moyennée. Peut être négative.
97
+ */
98
+ premium: DecimalString;
99
+ /** Début de l'heure de funding, en millisecondes. */
100
+ time: Timestamp;
101
+ }
102
+ /**
103
+ * Les paramètres de `fundingHistory`.
104
+ *
105
+ * ⚠️ À plat dans le corps de la requête, contrairement à `candleSnapshot` qui
106
+ * imbrique les siens sous une clé `req`. Deux endpoints voisins, deux formes :
107
+ * c'est le genre d'écart qui se découvre par un appel qui échoue.
108
+ */
109
+ export interface HLFundingHistoryRequest {
110
+ coin: string;
111
+ /** Inclusif, en millisecondes. Requis. */
112
+ startTime: Timestamp;
113
+ /** Inclusif. Par défaut, l'instant courant. */
114
+ endTime?: Timestamp;
115
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,3 +1,4 @@
1
1
  export * from './balance.interfaces';
2
2
  export * from './meta.interfaces';
3
3
  export * from './dexs.interfaces';
4
+ export * from './funding.interfaces';
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./balance.interfaces"), exports);
18
18
  __exportStar(require("./meta.interfaces"), exports);
19
19
  __exportStar(require("./dexs.interfaces"), exports);
20
+ __exportStar(require("./funding.interfaces"), exports);
package/package.json CHANGED
@@ -1,54 +1,54 @@
1
- {
2
- "name": "@syldel/hl-shared-types",
3
- "version": "0.0.22",
4
- "description": "Shared TypeScript interfaces and types for Hyperliquid integration.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/Syldel/hl-shared-types.git"
10
- },
11
- "bugs": {
12
- "url": "https://github.com/Syldel/hl-shared-types/issues"
13
- },
14
- "homepage": "https://github.com/Syldel/hl-shared-types#readme",
15
- "files": [
16
- "dist"
17
- ],
18
- "scripts": {
19
- "test": "jest",
20
- "lint": "eslint .",
21
- "lint:fix": "eslint . --fix",
22
- "clean": "rm -rf dist",
23
- "build": "npm run lint && npm run test && npm run clean && tsc",
24
- "prepublishOnly": "npm run build",
25
- "release": "npm run build && npm version patch && npm publish"
26
- },
27
- "publishConfig": {
28
- "access": "public"
29
- },
30
- "keywords": [
31
- "hyperliquid",
32
- "trading",
33
- "types",
34
- "interfaces",
35
- "crypto",
36
- "exchange",
37
- "defi"
38
- ],
39
- "author": "Syl. D.",
40
- "license": "ISC",
41
- "devDependencies": {
42
- "@types/jest": "^30.0.0",
43
- "eslint": "^9.39.2",
44
- "eslint-config-prettier": "^10.1.8",
45
- "eslint-plugin-jsonc": "^2.21.0",
46
- "eslint-plugin-prettier": "^5.5.5",
47
- "jest": "^30.5.2",
48
- "jsonc-eslint-parser": "^2.4.2",
49
- "prettier": "^3.8.0",
50
- "ts-jest": "^29.4.13",
51
- "typescript": "^5.3.3",
52
- "typescript-eslint": "^8.53.0"
53
- }
54
- }
1
+ {
2
+ "name": "@syldel/hl-shared-types",
3
+ "version": "0.0.24",
4
+ "description": "Shared TypeScript interfaces and types for Hyperliquid integration.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Syldel/hl-shared-types.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/Syldel/hl-shared-types/issues"
13
+ },
14
+ "homepage": "https://github.com/Syldel/hl-shared-types#readme",
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "test": "jest",
20
+ "lint": "eslint .",
21
+ "lint:fix": "eslint . --fix",
22
+ "clean": "rm -rf dist",
23
+ "build": "npm run lint && npm run test && npm run clean && tsc",
24
+ "prepublishOnly": "npm run build",
25
+ "release": "npm run build && npm version patch && npm publish"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "keywords": [
31
+ "hyperliquid",
32
+ "trading",
33
+ "types",
34
+ "interfaces",
35
+ "crypto",
36
+ "exchange",
37
+ "defi"
38
+ ],
39
+ "author": "Syl. D.",
40
+ "license": "ISC",
41
+ "devDependencies": {
42
+ "@types/jest": "^30.0.0",
43
+ "eslint": "^9.39.2",
44
+ "eslint-config-prettier": "^10.1.8",
45
+ "eslint-plugin-jsonc": "^2.21.0",
46
+ "eslint-plugin-prettier": "^5.5.5",
47
+ "jest": "^30.5.2",
48
+ "jsonc-eslint-parser": "^2.4.2",
49
+ "prettier": "^3.8.0",
50
+ "ts-jest": "^29.4.13",
51
+ "typescript": "^5.3.3",
52
+ "typescript-eslint": "^8.53.0"
53
+ }
54
+ }