openbroker 1.9.3 → 1.9.5
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/CHANGELOG.md +17 -0
- package/README.md +31 -0
- package/SKILL.md +21 -4
- package/bin/cli.ts +4 -2
- package/dist/auto/cli.js +1 -1
- package/dist/auto/examples/price-alert.js +1 -1
- package/dist/auto/realtime.d.ts +45 -0
- package/dist/auto/realtime.d.ts.map +1 -0
- package/dist/auto/realtime.js +177 -0
- package/dist/auto/realtime.test.d.ts +2 -0
- package/dist/auto/realtime.test.d.ts.map +1 -0
- package/dist/auto/realtime.test.js +73 -0
- package/dist/auto/runtime.d.ts +3 -3
- package/dist/auto/runtime.d.ts.map +1 -1
- package/dist/auto/runtime.js +117 -45
- package/dist/auto/types.d.ts +2 -1
- package/dist/auto/types.d.ts.map +1 -1
- package/dist/core/client.d.ts +66 -1
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/client.js +141 -4
- package/dist/core/ws.d.ts +14 -2
- package/dist/core/ws.d.ts.map +1 -1
- package/dist/core/ws.js +53 -7
- package/dist/setup/install.js +100 -3
- package/dist/setup/package-catalog.d.ts +12 -0
- package/dist/setup/package-catalog.d.ts.map +1 -0
- package/dist/setup/package-catalog.js +36 -0
- package/dist/setup/package-catalog.test.d.ts +2 -0
- package/dist/setup/package-catalog.test.d.ts.map +1 -0
- package/dist/setup/package-catalog.test.js +31 -0
- package/package.json +5 -3
- package/scripts/auto/cli.ts +1 -1
- package/scripts/auto/examples/price-alert.ts +1 -1
- package/scripts/auto/realtime.test.ts +84 -0
- package/scripts/auto/realtime.ts +194 -0
- package/scripts/auto/runtime.ts +125 -47
- package/scripts/auto/types.ts +2 -1
- package/scripts/core/client.ts +175 -4
- package/scripts/core/ws.ts +57 -8
- package/scripts/setup/install.ts +110 -3
- package/scripts/setup/package-catalog.test.ts +50 -0
- package/scripts/setup/package-catalog.ts +49 -0
package/dist/core/client.js
CHANGED
|
@@ -15,6 +15,8 @@ export class HyperliquidClient {
|
|
|
15
15
|
szDecimalsMap = new Map();
|
|
16
16
|
/** Maps coin name → dex info for HIP-3 assets. Main dex assets have dexName=null */
|
|
17
17
|
coinDexMap = new Map();
|
|
18
|
+
/** Static positional universe per dex, used to join allDexsAssetCtxs WS pushes. */
|
|
19
|
+
dexUniverseMap = new Map();
|
|
18
20
|
/** Cache of perpDexs list */
|
|
19
21
|
perpDexsCache = null;
|
|
20
22
|
/** Whether HIP-3 assets have been loaded into maps */
|
|
@@ -45,6 +47,13 @@ export class HyperliquidClient {
|
|
|
45
47
|
spotMetaLoaded = false;
|
|
46
48
|
/** HIP-4 outcome metadata cache */
|
|
47
49
|
outcomeMeta = null;
|
|
50
|
+
/** Static main-dex universe retained while live asset contexts arrive over WebSocket. */
|
|
51
|
+
staticMeta = null;
|
|
52
|
+
/** Runtime-owned live data cache. CLI calls leave this detached and remain REST-only. */
|
|
53
|
+
realtime = null;
|
|
54
|
+
predictedFundingsCache = null;
|
|
55
|
+
predictedFundingsInFlight = null;
|
|
56
|
+
spotMetaCache = null;
|
|
48
57
|
verbose = false;
|
|
49
58
|
constructor(config) {
|
|
50
59
|
this.config = config ?? loadConfig();
|
|
@@ -184,6 +193,10 @@ export class HyperliquidClient {
|
|
|
184
193
|
get isTestnet() {
|
|
185
194
|
return !isMainnet();
|
|
186
195
|
}
|
|
196
|
+
/** Attach/detach the automation runtime's WebSocket-first data cache. */
|
|
197
|
+
setRealtimeDataProvider(provider) {
|
|
198
|
+
this.realtime = provider;
|
|
199
|
+
}
|
|
187
200
|
/**
|
|
188
201
|
* Returns vaultAddress param for SDK exchange calls.
|
|
189
202
|
* Only used for vault trading (HYPERLIQUID_VAULT_ADDRESS set explicitly).
|
|
@@ -208,6 +221,10 @@ export class HyperliquidClient {
|
|
|
208
221
|
}
|
|
209
222
|
// ============ Market Data ============
|
|
210
223
|
async getMetaAndAssetCtxs() {
|
|
224
|
+
const liveCtxs = this.realtime?.connected ? this.realtime.getMainAssetCtxs() : null;
|
|
225
|
+
if (this.staticMeta && liveCtxs) {
|
|
226
|
+
return { meta: this.staticMeta, assetCtxs: liveCtxs };
|
|
227
|
+
}
|
|
211
228
|
if (this.meta)
|
|
212
229
|
return this.meta;
|
|
213
230
|
this.log('Fetching metaAndAssetCtxs...');
|
|
@@ -230,6 +247,8 @@ export class HyperliquidClient {
|
|
|
230
247
|
assetCtxs: response[1],
|
|
231
248
|
};
|
|
232
249
|
this.meta = meta;
|
|
250
|
+
this.staticMeta = meta.meta;
|
|
251
|
+
this.dexUniverseMap.set('', meta.meta.universe.map((asset) => asset.name));
|
|
233
252
|
// Build lookup maps for main dex
|
|
234
253
|
meta.meta.universe.forEach((asset, index) => {
|
|
235
254
|
this.assetMap.set(asset.name, index);
|
|
@@ -243,6 +262,29 @@ export class HyperliquidClient {
|
|
|
243
262
|
}
|
|
244
263
|
return meta;
|
|
245
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* Warm only the native/main static universe for an automation runtime.
|
|
267
|
+
* Dynamic contexts arrive via allDexsAssetCtxs, and HIP-3 metadata is loaded
|
|
268
|
+
* on demand when a strategy references a prefixed market. This avoids the
|
|
269
|
+
* old startup burst that fetched every HIP-3 dex before the socket opened.
|
|
270
|
+
*/
|
|
271
|
+
async initializeRealtimeMetadata() {
|
|
272
|
+
if (this.staticMeta)
|
|
273
|
+
return;
|
|
274
|
+
const response = await this.withRetry(() => this.info.metaAndAssetCtxs(), 'metaAndAssetCtxs(realtime-init)');
|
|
275
|
+
const meta = {
|
|
276
|
+
meta: { universe: response[0].universe },
|
|
277
|
+
assetCtxs: response[1],
|
|
278
|
+
};
|
|
279
|
+
this.meta = meta;
|
|
280
|
+
this.staticMeta = meta.meta;
|
|
281
|
+
this.dexUniverseMap.set('', meta.meta.universe.map((asset) => asset.name));
|
|
282
|
+
meta.meta.universe.forEach((asset, index) => {
|
|
283
|
+
this.assetMap.set(asset.name, index);
|
|
284
|
+
this.szDecimalsMap.set(asset.name, asset.szDecimals);
|
|
285
|
+
this.coinDexMap.set(asset.name, { dexName: null, dexIdx: 0, localName: asset.name });
|
|
286
|
+
});
|
|
287
|
+
}
|
|
246
288
|
/**
|
|
247
289
|
* Load HIP-3 perp dex assets into the asset/szDecimals maps.
|
|
248
290
|
* Asset index formula: 100000 + dexIdx * 10000 + assetIdx
|
|
@@ -356,10 +398,14 @@ export class HyperliquidClient {
|
|
|
356
398
|
if (asset.maxLeverage)
|
|
357
399
|
this.hip3MaxLeverageMap.set(coinName, asset.maxLeverage);
|
|
358
400
|
});
|
|
401
|
+
this.dexUniverseMap.set(dexName, universe.map((asset) => (asset.name.startsWith(`${dexName}:`) ? asset.name : `${dexName}:${asset.name}`)));
|
|
359
402
|
}
|
|
360
403
|
this.loadedHip3Dexes.add(dexName);
|
|
361
404
|
}
|
|
362
405
|
async getAllMids() {
|
|
406
|
+
const live = this.realtime?.connected ? this.realtime.getAllMids() : null;
|
|
407
|
+
if (live)
|
|
408
|
+
return { ...live };
|
|
363
409
|
this.log('Fetching allMids...');
|
|
364
410
|
let response;
|
|
365
411
|
try {
|
|
@@ -449,8 +495,11 @@ export class HyperliquidClient {
|
|
|
449
495
|
* Get spot market metadata
|
|
450
496
|
*/
|
|
451
497
|
async getSpotMeta() {
|
|
498
|
+
if (this.spotMetaCache)
|
|
499
|
+
return this.spotMetaCache;
|
|
452
500
|
this.log('Fetching spotMeta...');
|
|
453
501
|
const data = await this.postInfo({ type: 'spotMeta' }, 'spotMeta');
|
|
502
|
+
this.spotMetaCache = data;
|
|
454
503
|
this.log('spotMeta response:', JSON.stringify(data).slice(0, 500));
|
|
455
504
|
return data;
|
|
456
505
|
}
|
|
@@ -458,6 +507,23 @@ export class HyperliquidClient {
|
|
|
458
507
|
* Get spot metadata with asset contexts (prices, volumes)
|
|
459
508
|
*/
|
|
460
509
|
async getSpotMetaAndAssetCtxs() {
|
|
510
|
+
const liveMids = this.realtime?.connected ? this.realtime.getAllMids() : null;
|
|
511
|
+
if (liveMids) {
|
|
512
|
+
const meta = await this.getSpotMeta();
|
|
513
|
+
return {
|
|
514
|
+
meta,
|
|
515
|
+
assetCtxs: meta.universe.map((pair) => {
|
|
516
|
+
const price = liveMids[pair.name] ?? '0';
|
|
517
|
+
return {
|
|
518
|
+
coin: pair.name,
|
|
519
|
+
dayNtlVlm: '0',
|
|
520
|
+
markPx: price,
|
|
521
|
+
midPx: price,
|
|
522
|
+
prevDayPx: price,
|
|
523
|
+
};
|
|
524
|
+
}),
|
|
525
|
+
};
|
|
526
|
+
}
|
|
461
527
|
this.log('Fetching spotMetaAndAssetCtxs...');
|
|
462
528
|
const data = await this.postInfo({ type: 'spotMetaAndAssetCtxs' }, 'spotMetaAndAssetCtxs');
|
|
463
529
|
this.log('spotMetaAndAssetCtxs response:', JSON.stringify(data).slice(0, 500));
|
|
@@ -767,6 +833,10 @@ export class HyperliquidClient {
|
|
|
767
833
|
* Get user's spot token balances
|
|
768
834
|
*/
|
|
769
835
|
async getSpotBalances(user) {
|
|
836
|
+
const target = user ?? this.address;
|
|
837
|
+
const live = this.realtime?.connected ? this.realtime.getSpotBalances(target) : null;
|
|
838
|
+
if (live)
|
|
839
|
+
return live;
|
|
770
840
|
this.log('Fetching spotClearinghouseState for:', user ?? this.address);
|
|
771
841
|
const data = await this.postInfo({
|
|
772
842
|
type: 'spotClearinghouseState',
|
|
@@ -804,6 +874,30 @@ export class HyperliquidClient {
|
|
|
804
874
|
* Get predicted funding rates across venues
|
|
805
875
|
*/
|
|
806
876
|
async getPredictedFundings() {
|
|
877
|
+
const ttlMs = 60_000;
|
|
878
|
+
if (this.predictedFundingsCache && Date.now() - this.predictedFundingsCache.timestamp < ttlMs) {
|
|
879
|
+
return this.predictedFundingsCache.value;
|
|
880
|
+
}
|
|
881
|
+
if (this.predictedFundingsInFlight)
|
|
882
|
+
return this.predictedFundingsInFlight;
|
|
883
|
+
this.predictedFundingsInFlight = this.fetchPredictedFundings();
|
|
884
|
+
try {
|
|
885
|
+
const value = await this.predictedFundingsInFlight;
|
|
886
|
+
this.predictedFundingsCache = { value, timestamp: Date.now() };
|
|
887
|
+
return value;
|
|
888
|
+
}
|
|
889
|
+
catch (error) {
|
|
890
|
+
if (this.predictedFundingsCache) {
|
|
891
|
+
this.log('predictedFundings refresh failed; using stale cache:', this.describeError(error));
|
|
892
|
+
return this.predictedFundingsCache.value;
|
|
893
|
+
}
|
|
894
|
+
throw error;
|
|
895
|
+
}
|
|
896
|
+
finally {
|
|
897
|
+
this.predictedFundingsInFlight = null;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
async fetchPredictedFundings() {
|
|
807
901
|
this.log('Fetching predictedFundings...');
|
|
808
902
|
const baseUrl = isMainnet()
|
|
809
903
|
? 'https://api.hyperliquid.xyz'
|
|
@@ -817,8 +911,13 @@ export class HyperliquidClient {
|
|
|
817
911
|
},
|
|
818
912
|
body: JSON.stringify({ type: 'predictedFundings' }),
|
|
819
913
|
});
|
|
914
|
+
if (!response.ok) {
|
|
915
|
+
throw new Error(`predictedFundings failed: HTTP ${response.status} ${response.statusText}`);
|
|
916
|
+
}
|
|
820
917
|
const data = await response.json();
|
|
821
|
-
|
|
918
|
+
if (!Array.isArray(data))
|
|
919
|
+
throw new Error('predictedFundings returned malformed payload');
|
|
920
|
+
this.log('predictedFundings response length:', data.length);
|
|
822
921
|
return data;
|
|
823
922
|
}
|
|
824
923
|
/**
|
|
@@ -826,6 +925,9 @@ export class HyperliquidClient {
|
|
|
826
925
|
* Returns best bid/ask and depth
|
|
827
926
|
*/
|
|
828
927
|
async getL2Book(coin) {
|
|
928
|
+
const live = this.realtime?.connected ? await this.realtime.getL2Book(coin) : null;
|
|
929
|
+
if (live)
|
|
930
|
+
return this.normalizeL2Book(live.levels);
|
|
829
931
|
this.log('Fetching l2Book for:', coin);
|
|
830
932
|
// API accepts prefixed names directly (e.g., "xyz:CL")
|
|
831
933
|
let response;
|
|
@@ -842,8 +944,11 @@ export class HyperliquidClient {
|
|
|
842
944
|
if (!response || !Array.isArray(response.levels)) {
|
|
843
945
|
throw new Error(`l2Book(${coin}) returned empty/malformed payload.`);
|
|
844
946
|
}
|
|
845
|
-
|
|
846
|
-
|
|
947
|
+
return this.normalizeL2Book(response.levels);
|
|
948
|
+
}
|
|
949
|
+
normalizeL2Book(levels) {
|
|
950
|
+
const bids = levels[0] ?? [];
|
|
951
|
+
const asks = levels[1] ?? [];
|
|
847
952
|
const bestBid = bids.length > 0 ? parseFloat(bids[0].px) : 0;
|
|
848
953
|
const bestAsk = asks.length > 0 ? parseFloat(asks[0].px) : 0;
|
|
849
954
|
const midPrice = (bestBid + bestAsk) / 2;
|
|
@@ -959,6 +1064,26 @@ export class HyperliquidClient {
|
|
|
959
1064
|
this.meta = null;
|
|
960
1065
|
// Keep the asset/szDecimals/coinDex maps - they don't change
|
|
961
1066
|
}
|
|
1067
|
+
/** Join an allDexsAssetCtxs WebSocket payload with the cached static universes. */
|
|
1068
|
+
fundingRatesFromWs(ctxs) {
|
|
1069
|
+
const rates = new Map();
|
|
1070
|
+
for (const [dexName, values] of ctxs) {
|
|
1071
|
+
const universe = this.dexUniverseMap.get(dexName || '');
|
|
1072
|
+
if (!universe)
|
|
1073
|
+
continue;
|
|
1074
|
+
for (let index = 0; index < Math.min(universe.length, values.length); index++) {
|
|
1075
|
+
const coin = universe[index];
|
|
1076
|
+
const value = values[index];
|
|
1077
|
+
if (!coin || !value)
|
|
1078
|
+
continue;
|
|
1079
|
+
rates.set(coin, {
|
|
1080
|
+
rate: Number(value.funding ?? 0),
|
|
1081
|
+
premium: Number(value.premium ?? 0),
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
return rates;
|
|
1086
|
+
}
|
|
962
1087
|
/**
|
|
963
1088
|
* Get all loaded asset names (main + HIP-3)
|
|
964
1089
|
*/
|
|
@@ -1362,6 +1487,10 @@ export class HyperliquidClient {
|
|
|
1362
1487
|
return data;
|
|
1363
1488
|
}
|
|
1364
1489
|
async getUserState(user, dex) {
|
|
1490
|
+
const target = user ?? this.address;
|
|
1491
|
+
const live = this.realtime?.connected ? this.realtime.getUserState(target, dex) : null;
|
|
1492
|
+
if (live)
|
|
1493
|
+
return live;
|
|
1365
1494
|
this.log('Fetching clearinghouseState for:', user ?? this.address, dex ? `dex: ${dex}` : '');
|
|
1366
1495
|
const params = { user: user ?? this.address };
|
|
1367
1496
|
if (dex !== undefined)
|
|
@@ -1504,6 +1633,10 @@ export class HyperliquidClient {
|
|
|
1504
1633
|
* For standard accounts: aggregates margin summaries from each dex.
|
|
1505
1634
|
*/
|
|
1506
1635
|
async getUserStateAll(user) {
|
|
1636
|
+
const target = user ?? this.address;
|
|
1637
|
+
const live = this.realtime?.connected ? this.realtime.getUserStateAll(target) : null;
|
|
1638
|
+
if (live)
|
|
1639
|
+
return live;
|
|
1507
1640
|
await this.getMetaAndAssetCtxs(); // Ensure HIP-3 dex list is loaded
|
|
1508
1641
|
const unified = await this.isUnifiedAccount(user);
|
|
1509
1642
|
const mainState = await this.getUserState(user);
|
|
@@ -1539,7 +1672,11 @@ export class HyperliquidClient {
|
|
|
1539
1672
|
return this.mergeUserStateAll(perDex, unified, spotBalances);
|
|
1540
1673
|
}
|
|
1541
1674
|
async getOpenOrders(user) {
|
|
1542
|
-
|
|
1675
|
+
const target = user ?? this.address;
|
|
1676
|
+
const live = this.realtime?.connected ? this.realtime.getOpenOrders(target) : null;
|
|
1677
|
+
if (live)
|
|
1678
|
+
return live;
|
|
1679
|
+
this.log('Fetching openOrders for:', target);
|
|
1543
1680
|
await this.getMetaAndAssetCtxs(); // Ensure HIP-3 dex list is loaded
|
|
1544
1681
|
// Fetch main dex orders
|
|
1545
1682
|
const orders = await this.withRetry(() => this.info.openOrders({ user: user ?? this.address }), 'openOrders');
|
package/dist/core/ws.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ISubscription } from '@nktkas/hyperliquid';
|
|
2
2
|
import type { UserEventsWsEvent } from '@nktkas/hyperliquid';
|
|
3
|
-
import type { ClearinghouseState } from './types.js';
|
|
3
|
+
import type { ClearinghouseState, OpenOrder } from './types.js';
|
|
4
4
|
export interface WsEventMap {
|
|
5
5
|
/** L2 order book snapshot for a specific coin */
|
|
6
6
|
l2Book: {
|
|
@@ -69,6 +69,12 @@ export interface WsEventMap {
|
|
|
69
69
|
entryNtl?: string;
|
|
70
70
|
}>;
|
|
71
71
|
};
|
|
72
|
+
/** Complete open-order snapshot for one user + dex (empty dex = native/main). */
|
|
73
|
+
openOrders: {
|
|
74
|
+
user: string;
|
|
75
|
+
dex: string;
|
|
76
|
+
orders: OpenOrder[];
|
|
77
|
+
};
|
|
72
78
|
/** Order status changed (filled, canceled, rejected, etc.) */
|
|
73
79
|
orderUpdate: {
|
|
74
80
|
order: {
|
|
@@ -134,6 +140,7 @@ export declare class WebSocketManager {
|
|
|
134
140
|
private subscriptions;
|
|
135
141
|
private handlers;
|
|
136
142
|
private _connected;
|
|
143
|
+
private closing;
|
|
137
144
|
private verbose;
|
|
138
145
|
constructor(verbose?: boolean);
|
|
139
146
|
private log;
|
|
@@ -172,6 +179,8 @@ export declare class WebSocketManager {
|
|
|
172
179
|
* `getSpotBalances` REST polling in the collateral gate.
|
|
173
180
|
*/
|
|
174
181
|
subscribeSpotState(user: `0x${string}`): Promise<ISubscription>;
|
|
182
|
+
/** Subscribe to complete open-order snapshots for one dex. */
|
|
183
|
+
subscribeOpenOrders(user: `0x${string}`, dex?: string): Promise<ISubscription>;
|
|
175
184
|
/**
|
|
176
185
|
* Subscribe to order lifecycle events (fill, cancel, reject, etc.).
|
|
177
186
|
* This is the most important subscription for trading automations.
|
|
@@ -189,11 +198,14 @@ export declare class WebSocketManager {
|
|
|
189
198
|
/**
|
|
190
199
|
* Start all subscriptions needed for the automation runtime:
|
|
191
200
|
* - allMids (price feed)
|
|
201
|
+
* - allDexsAssetCtxs (funding / mark / oracle contexts)
|
|
202
|
+
* - allDexsClearinghouseState + spotState (positions, margin, balances)
|
|
203
|
+
* - openOrders for main + requested HIP-3 dexes
|
|
192
204
|
* - orderUpdates (order lifecycle)
|
|
193
205
|
* - userFills (trade fills)
|
|
194
206
|
* - userEvents (liquidations, funding payments, system cancels)
|
|
195
207
|
*/
|
|
196
|
-
subscribeAll(user: `0x${string}
|
|
208
|
+
subscribeAll(user: `0x${string}`, dexNames?: string[]): Promise<void>;
|
|
197
209
|
}
|
|
198
210
|
export declare function getWebSocket(verbose?: boolean): WebSocketManager;
|
|
199
211
|
export declare function resetWebSocket(): void;
|
package/dist/core/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../scripts/core/ws.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAKV,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../scripts/core/ws.ts"],"names":[],"mappings":"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"}
|
package/dist/core/ws.js
CHANGED
|
@@ -9,6 +9,7 @@ export class WebSocketManager {
|
|
|
9
9
|
subscriptions = [];
|
|
10
10
|
handlers = new Map();
|
|
11
11
|
_connected = false;
|
|
12
|
+
closing = false;
|
|
12
13
|
verbose;
|
|
13
14
|
constructor(verbose = false) {
|
|
14
15
|
this.verbose = verbose;
|
|
@@ -27,14 +28,36 @@ export class WebSocketManager {
|
|
|
27
28
|
this.transport = new WebSocketTransport({
|
|
28
29
|
isTestnet: !isMainnet(),
|
|
29
30
|
resubscribe: true, // auto-resubscribe on reconnect
|
|
31
|
+
reconnect: { maxRetries: Infinity },
|
|
32
|
+
});
|
|
33
|
+
const socket = this.transport.socket;
|
|
34
|
+
socket.addEventListener('open', () => {
|
|
35
|
+
if (this._connected)
|
|
36
|
+
return;
|
|
37
|
+
this._connected = true;
|
|
38
|
+
this.emit('connected', undefined);
|
|
39
|
+
this.log('Connected to', isMainnet() ? 'mainnet' : 'testnet');
|
|
40
|
+
});
|
|
41
|
+
socket.addEventListener('close', () => {
|
|
42
|
+
if (!this._connected)
|
|
43
|
+
return;
|
|
44
|
+
this._connected = false;
|
|
45
|
+
this.emit('disconnected', { reason: this.closing ? 'manual close' : 'socket closed' });
|
|
46
|
+
this.log(this.closing ? 'Closed' : 'Disconnected; transport will reconnect');
|
|
47
|
+
});
|
|
48
|
+
socket.addEventListener('error', () => {
|
|
49
|
+
this.emit('error', { error: new Error('Hyperliquid WebSocket transport error') });
|
|
30
50
|
});
|
|
31
51
|
this.client = new SubscriptionClient({ transport: this.transport });
|
|
32
52
|
await this.transport.ready();
|
|
33
|
-
this._connected
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
if (!this._connected) {
|
|
54
|
+
this._connected = true;
|
|
55
|
+
this.emit('connected', undefined);
|
|
56
|
+
this.log('Connected to', isMainnet() ? 'mainnet' : 'testnet');
|
|
57
|
+
}
|
|
36
58
|
}
|
|
37
59
|
async close() {
|
|
60
|
+
this.closing = true;
|
|
38
61
|
for (const sub of this.subscriptions) {
|
|
39
62
|
try {
|
|
40
63
|
await sub.unsubscribe();
|
|
@@ -50,9 +73,12 @@ export class WebSocketManager {
|
|
|
50
73
|
this.transport = null;
|
|
51
74
|
this.client = null;
|
|
52
75
|
}
|
|
53
|
-
this._connected
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
if (this._connected) {
|
|
77
|
+
this._connected = false;
|
|
78
|
+
this.emit('disconnected', { reason: 'manual close' });
|
|
79
|
+
this.log('Closed');
|
|
80
|
+
}
|
|
81
|
+
this.closing = false;
|
|
56
82
|
}
|
|
57
83
|
// ── Event system ───────────────────────────────────────────────
|
|
58
84
|
on(event, handler) {
|
|
@@ -162,6 +188,18 @@ export class WebSocketManager {
|
|
|
162
188
|
});
|
|
163
189
|
return this.trackSub(sub);
|
|
164
190
|
}
|
|
191
|
+
/** Subscribe to complete open-order snapshots for one dex. */
|
|
192
|
+
async subscribeOpenOrders(user, dex = '') {
|
|
193
|
+
const client = this.ensureClient();
|
|
194
|
+
const sub = await client.openOrders({ user, dex }, (data) => {
|
|
195
|
+
this.emit('openOrders', {
|
|
196
|
+
user: data.user,
|
|
197
|
+
dex: data.dex,
|
|
198
|
+
orders: data.orders,
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
return this.trackSub(sub);
|
|
202
|
+
}
|
|
165
203
|
/**
|
|
166
204
|
* Subscribe to order lifecycle events (fill, cancel, reject, etc.).
|
|
167
205
|
* This is the most important subscription for trading automations.
|
|
@@ -232,15 +270,23 @@ export class WebSocketManager {
|
|
|
232
270
|
/**
|
|
233
271
|
* Start all subscriptions needed for the automation runtime:
|
|
234
272
|
* - allMids (price feed)
|
|
273
|
+
* - allDexsAssetCtxs (funding / mark / oracle contexts)
|
|
274
|
+
* - allDexsClearinghouseState + spotState (positions, margin, balances)
|
|
275
|
+
* - openOrders for main + requested HIP-3 dexes
|
|
235
276
|
* - orderUpdates (order lifecycle)
|
|
236
277
|
* - userFills (trade fills)
|
|
237
278
|
* - userEvents (liquidations, funding payments, system cancels)
|
|
238
279
|
*/
|
|
239
|
-
async subscribeAll(user) {
|
|
280
|
+
async subscribeAll(user, dexNames = []) {
|
|
240
281
|
await this.connect();
|
|
241
282
|
this.log('Subscribing to all feeds for', user);
|
|
242
283
|
await Promise.all([
|
|
243
284
|
this.subscribeAllMids(),
|
|
285
|
+
this.subscribeAllDexsAssetCtxs(),
|
|
286
|
+
this.subscribeAllDexsClearinghouseState(user),
|
|
287
|
+
this.subscribeSpotState(user),
|
|
288
|
+
this.subscribeOpenOrders(user),
|
|
289
|
+
...dexNames.map((dex) => this.subscribeOpenOrders(user, dex)),
|
|
244
290
|
this.subscribeOrderUpdates(user),
|
|
245
291
|
this.subscribeUserFills(user),
|
|
246
292
|
this.subscribeUserEvents(user),
|
package/dist/setup/install.js
CHANGED
|
@@ -5,19 +5,54 @@ import * as path from 'path';
|
|
|
5
5
|
import { homedir } from 'os';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { spawnSync } from 'child_process';
|
|
8
|
+
import { INSTALLABLE_PACKAGES, packageSpec, resolveInstallablePackage, } from './package-catalog.js';
|
|
8
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
10
|
const __dirname = path.dirname(__filename);
|
|
10
11
|
const packageRoot = path.resolve(__dirname, '../..');
|
|
11
|
-
const
|
|
12
|
+
const rawArgs = process.argv.slice(2);
|
|
13
|
+
const args = new Set(rawArgs);
|
|
14
|
+
function positionalArgs() {
|
|
15
|
+
const positionals = [];
|
|
16
|
+
for (let index = 0; index < rawArgs.length; index++) {
|
|
17
|
+
const arg = rawArgs[index];
|
|
18
|
+
if (arg === '--tag') {
|
|
19
|
+
index++;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (!arg.startsWith('-'))
|
|
23
|
+
positionals.push(arg);
|
|
24
|
+
}
|
|
25
|
+
return positionals;
|
|
26
|
+
}
|
|
27
|
+
function optionValue(flag) {
|
|
28
|
+
const index = rawArgs.indexOf(flag);
|
|
29
|
+
if (index < 0)
|
|
30
|
+
return null;
|
|
31
|
+
const value = rawArgs[index + 1];
|
|
32
|
+
if (!value || value.startsWith('-'))
|
|
33
|
+
fail(`${flag} requires a value`);
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
12
36
|
function printUsage() {
|
|
13
37
|
console.log(`
|
|
14
|
-
OpenBroker
|
|
15
|
-
|
|
38
|
+
OpenBroker Installer
|
|
39
|
+
====================
|
|
16
40
|
|
|
17
41
|
Usage:
|
|
42
|
+
openbroker install <package> [--tag <version>] [--dry]
|
|
43
|
+
openbroker install --list
|
|
18
44
|
openbroker install --codex [options]
|
|
19
45
|
npx openbroker@latest install --codex [options]
|
|
20
46
|
|
|
47
|
+
Companion packages:
|
|
48
|
+
monitoring Install the local automation dashboard
|
|
49
|
+
extended Install the Extended Exchange CLI
|
|
50
|
+
|
|
51
|
+
Package options:
|
|
52
|
+
--tag <tag> Install a release tag or exact version (default: latest)
|
|
53
|
+
--dry Print the npm command without installing
|
|
54
|
+
--list List supported companion packages
|
|
55
|
+
|
|
21
56
|
Harnesses:
|
|
22
57
|
--codex Install the OpenBroker skill for Codex
|
|
23
58
|
|
|
@@ -74,6 +109,55 @@ function installGlobalCli() {
|
|
|
74
109
|
fail('global CLI installation failed. Fix the npm permission error, then rerun with --skip-cli.');
|
|
75
110
|
}
|
|
76
111
|
}
|
|
112
|
+
function printInstallablePackages() {
|
|
113
|
+
console.log('Installable OpenBroker packages:\n');
|
|
114
|
+
for (const entry of INSTALLABLE_PACKAGES) {
|
|
115
|
+
console.log(` ${entry.key.padEnd(12)} ${entry.packageName.padEnd(26)} ${entry.description}`);
|
|
116
|
+
}
|
|
117
|
+
console.log('\nInstall or upgrade with: openbroker install <package>');
|
|
118
|
+
}
|
|
119
|
+
function installCompanionPackage(target) {
|
|
120
|
+
const entry = resolveInstallablePackage(target);
|
|
121
|
+
if (!entry) {
|
|
122
|
+
printInstallablePackages();
|
|
123
|
+
fail(`unknown installable package: ${target}`);
|
|
124
|
+
}
|
|
125
|
+
const allowedFlags = new Set(['--tag', '--dry']);
|
|
126
|
+
const unsupported = rawArgs.filter((arg, index) => (arg.startsWith('-')
|
|
127
|
+
&& !allowedFlags.has(arg)
|
|
128
|
+
&& rawArgs[index - 1] !== '--tag'));
|
|
129
|
+
if (unsupported.length > 0)
|
|
130
|
+
fail(`unsupported package option: ${unsupported[0]}`);
|
|
131
|
+
const tag = optionValue('--tag') ?? 'latest';
|
|
132
|
+
let spec;
|
|
133
|
+
try {
|
|
134
|
+
spec = packageSpec(entry, tag);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
138
|
+
}
|
|
139
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
140
|
+
const npmArgs = ['install', '--global', spec];
|
|
141
|
+
console.log(`OpenBroker — Install ${entry.key}`);
|
|
142
|
+
console.log('================================\n');
|
|
143
|
+
console.log(`Package: ${spec}`);
|
|
144
|
+
console.log(`Command: ${npmCommand} ${npmArgs.join(' ')}`);
|
|
145
|
+
if (args.has('--dry')) {
|
|
146
|
+
console.log('\nDry run only; nothing was installed.');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const result = spawnSync(npmCommand, npmArgs, { stdio: 'inherit' });
|
|
150
|
+
if (result.error)
|
|
151
|
+
fail(`could not start npm: ${result.error.message}`);
|
|
152
|
+
if (result.status !== 0) {
|
|
153
|
+
fail(`installation failed for ${entry.packageName}; resolve the npm error and retry`);
|
|
154
|
+
}
|
|
155
|
+
console.log(`\n✅ ${entry.packageName} installed successfully.`);
|
|
156
|
+
console.log(`Available command: ${entry.command}`);
|
|
157
|
+
console.log('\nNext steps:');
|
|
158
|
+
for (const step of entry.nextSteps)
|
|
159
|
+
console.log(` ${step}`);
|
|
160
|
+
}
|
|
77
161
|
function runApiWalletSetup() {
|
|
78
162
|
const onboardPath = path.join(packageRoot, 'scripts', 'setup', 'onboard.ts');
|
|
79
163
|
console.log('\nStarting restricted API-wallet onboarding...\n');
|
|
@@ -94,6 +178,19 @@ function main() {
|
|
|
94
178
|
printUsage();
|
|
95
179
|
return;
|
|
96
180
|
}
|
|
181
|
+
if (args.has('--list')) {
|
|
182
|
+
printInstallablePackages();
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const positionals = positionalArgs();
|
|
186
|
+
if (positionals.length > 0) {
|
|
187
|
+
if (positionals.length > 1)
|
|
188
|
+
fail(`expected one package name, received: ${positionals.join(' ')}`);
|
|
189
|
+
if (args.has('--codex'))
|
|
190
|
+
fail('choose either a companion package or the --codex harness installer');
|
|
191
|
+
installCompanionPackage(positionals[0]);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
97
194
|
if (!args.has('--codex')) {
|
|
98
195
|
printUsage();
|
|
99
196
|
fail('choose a supported harness flag such as --codex');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface InstallablePackage {
|
|
2
|
+
key: string;
|
|
3
|
+
aliases: string[];
|
|
4
|
+
packageName: string;
|
|
5
|
+
command: string;
|
|
6
|
+
description: string;
|
|
7
|
+
nextSteps: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare const INSTALLABLE_PACKAGES: InstallablePackage[];
|
|
10
|
+
export declare function resolveInstallablePackage(input: string): InstallablePackage | null;
|
|
11
|
+
export declare function packageSpec(entry: InstallablePackage, tag?: string): string;
|
|
12
|
+
//# sourceMappingURL=package-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-catalog.d.ts","sourceRoot":"","sources":["../../scripts/setup/package-catalog.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,eAAO,MAAM,oBAAoB,EAAE,kBAAkB,EAuBpD,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAOlF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,SAAW,GAAG,MAAM,CAK7E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const INSTALLABLE_PACKAGES = [
|
|
2
|
+
{
|
|
3
|
+
key: 'monitoring',
|
|
4
|
+
aliases: ['openbroker-monitoring'],
|
|
5
|
+
packageName: 'openbroker-monitoring',
|
|
6
|
+
command: 'openbroker-monitoring',
|
|
7
|
+
description: 'Local automation dashboard and optional audit observer',
|
|
8
|
+
nextSteps: [
|
|
9
|
+
'openbroker-monitoring serve --host 127.0.0.1 --port 3001',
|
|
10
|
+
'Open http://127.0.0.1:3001',
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
key: 'extended',
|
|
15
|
+
aliases: ['openbroker-extended'],
|
|
16
|
+
packageName: 'openbroker-extended',
|
|
17
|
+
command: 'openbroker-ex',
|
|
18
|
+
description: 'Extended Exchange trading CLI and library',
|
|
19
|
+
nextSteps: [
|
|
20
|
+
'openbroker-ex --help',
|
|
21
|
+
'openbroker-ex setup',
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
export function resolveInstallablePackage(input) {
|
|
26
|
+
const normalized = input.trim().toLowerCase();
|
|
27
|
+
return INSTALLABLE_PACKAGES.find((entry) => (entry.key === normalized
|
|
28
|
+
|| entry.packageName === normalized
|
|
29
|
+
|| entry.aliases.includes(normalized))) ?? null;
|
|
30
|
+
}
|
|
31
|
+
export function packageSpec(entry, tag = 'latest') {
|
|
32
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(tag)) {
|
|
33
|
+
throw new Error(`invalid package tag or version: ${tag}`);
|
|
34
|
+
}
|
|
35
|
+
return `${entry.packageName}@${tag}`;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-catalog.test.d.ts","sourceRoot":"","sources":["../../scripts/setup/package-catalog.test.ts"],"names":[],"mappings":""}
|