openbroker 1.3.1 → 1.4.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/CHANGELOG.md +12 -0
- package/SKILL.md +7 -4
- package/dist/auto/audit.d.ts +57 -0
- package/dist/auto/audit.d.ts.map +1 -0
- package/dist/auto/audit.js +407 -0
- package/dist/auto/cli.d.ts +2 -0
- package/dist/auto/cli.d.ts.map +1 -0
- package/dist/auto/cli.js +423 -0
- package/dist/auto/events.d.ts +11 -0
- package/dist/auto/events.d.ts.map +1 -0
- package/dist/auto/events.js +36 -0
- package/dist/auto/examples/dca.d.ts +4 -0
- package/dist/auto/examples/dca.d.ts.map +1 -0
- package/dist/auto/examples/dca.js +60 -0
- package/dist/auto/examples/funding-arb.d.ts +4 -0
- package/dist/auto/examples/funding-arb.d.ts.map +1 -0
- package/dist/auto/examples/funding-arb.js +81 -0
- package/dist/auto/examples/grid.d.ts +4 -0
- package/dist/auto/examples/grid.d.ts.map +1 -0
- package/dist/auto/examples/grid.js +114 -0
- package/dist/auto/examples/mm-maker.d.ts +4 -0
- package/dist/auto/examples/mm-maker.d.ts.map +1 -0
- package/dist/auto/examples/mm-maker.js +131 -0
- package/dist/auto/examples/mm-spread.d.ts +4 -0
- package/dist/auto/examples/mm-spread.d.ts.map +1 -0
- package/dist/auto/examples/mm-spread.js +119 -0
- package/dist/auto/examples/price-alert.d.ts +4 -0
- package/dist/auto/examples/price-alert.d.ts.map +1 -0
- package/dist/auto/examples/price-alert.js +85 -0
- package/dist/auto/keep-awake.d.ts +11 -0
- package/dist/auto/keep-awake.d.ts.map +1 -0
- package/dist/auto/keep-awake.js +70 -0
- package/dist/auto/loader.d.ts +22 -0
- package/dist/auto/loader.d.ts.map +1 -0
- package/dist/auto/loader.js +127 -0
- package/dist/auto/prune.d.ts +40 -0
- package/dist/auto/prune.d.ts.map +1 -0
- package/dist/auto/prune.js +204 -0
- package/dist/auto/registry.d.ts +24 -0
- package/dist/auto/registry.d.ts.map +1 -0
- package/dist/auto/registry.js +93 -0
- package/dist/auto/report.d.ts +3 -0
- package/dist/auto/report.d.ts.map +1 -0
- package/dist/auto/report.js +385 -0
- package/dist/auto/runtime.d.ts +33 -0
- package/dist/auto/runtime.d.ts.map +1 -0
- package/dist/auto/runtime.js +844 -0
- package/dist/auto/types.d.ts +236 -0
- package/dist/auto/types.d.ts.map +1 -0
- package/dist/auto/types.js +3 -0
- package/dist/core/client.d.ts +684 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +2040 -0
- package/dist/core/config.d.ts +22 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +143 -0
- package/dist/core/types.d.ts +221 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/utils.d.ts +61 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/core/utils.js +142 -0
- package/dist/core/ws.d.ts +121 -0
- package/dist/core/ws.d.ts.map +1 -0
- package/dist/core/ws.js +222 -0
- package/dist/info/account.d.ts +3 -0
- package/dist/info/account.d.ts.map +1 -0
- package/dist/info/account.js +198 -0
- package/dist/info/all-markets.d.ts +3 -0
- package/dist/info/all-markets.d.ts.map +1 -0
- package/dist/info/all-markets.js +272 -0
- package/dist/info/candles.d.ts +3 -0
- package/dist/info/candles.d.ts.map +1 -0
- package/dist/info/candles.js +120 -0
- package/dist/info/fees.d.ts +3 -0
- package/dist/info/fees.d.ts.map +1 -0
- package/dist/info/fees.js +87 -0
- package/dist/info/fills.d.ts +3 -0
- package/dist/info/fills.d.ts.map +1 -0
- package/dist/info/fills.js +105 -0
- package/dist/info/funding-history.d.ts +3 -0
- package/dist/info/funding-history.d.ts.map +1 -0
- package/dist/info/funding-history.js +98 -0
- package/dist/info/funding-scan.d.ts +3 -0
- package/dist/info/funding-scan.d.ts.map +1 -0
- package/dist/info/funding-scan.js +178 -0
- package/dist/info/funding.d.ts +3 -0
- package/dist/info/funding.d.ts.map +1 -0
- package/dist/info/funding.js +158 -0
- package/dist/info/markets.d.ts +3 -0
- package/dist/info/markets.d.ts.map +1 -0
- package/dist/info/markets.js +178 -0
- package/dist/info/order-status.d.ts +3 -0
- package/dist/info/order-status.d.ts.map +1 -0
- package/dist/info/order-status.js +85 -0
- package/dist/info/orders.d.ts +3 -0
- package/dist/info/orders.d.ts.map +1 -0
- package/dist/info/orders.js +162 -0
- package/dist/info/outcomes.d.ts +3 -0
- package/dist/info/outcomes.d.ts.map +1 -0
- package/dist/info/outcomes.js +175 -0
- package/dist/info/positions.d.ts +3 -0
- package/dist/info/positions.d.ts.map +1 -0
- package/dist/info/positions.js +127 -0
- package/dist/info/rate-limit.d.ts +3 -0
- package/dist/info/rate-limit.d.ts.map +1 -0
- package/dist/info/rate-limit.js +58 -0
- package/dist/info/search-markets.d.ts +3 -0
- package/dist/info/search-markets.d.ts.map +1 -0
- package/dist/info/search-markets.js +296 -0
- package/dist/info/spot.d.ts +3 -0
- package/dist/info/spot.d.ts.map +1 -0
- package/dist/info/spot.js +192 -0
- package/dist/info/trades.d.ts +3 -0
- package/dist/info/trades.d.ts.map +1 -0
- package/dist/info/trades.js +97 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +17 -0
- package/dist/operations/bracket.d.ts +28 -0
- package/dist/operations/bracket.d.ts.map +1 -0
- package/dist/operations/bracket.js +266 -0
- package/dist/operations/cancel.d.ts +3 -0
- package/dist/operations/cancel.d.ts.map +1 -0
- package/dist/operations/cancel.js +107 -0
- package/dist/operations/chase.d.ts +25 -0
- package/dist/operations/chase.d.ts.map +1 -0
- package/dist/operations/chase.js +215 -0
- package/dist/operations/limit-order.d.ts +3 -0
- package/dist/operations/limit-order.d.ts.map +1 -0
- package/dist/operations/limit-order.js +144 -0
- package/dist/operations/market-order.d.ts +3 -0
- package/dist/operations/market-order.d.ts.map +1 -0
- package/dist/operations/market-order.js +153 -0
- package/dist/operations/outcome-order.d.ts +3 -0
- package/dist/operations/outcome-order.d.ts.map +1 -0
- package/dist/operations/outcome-order.js +171 -0
- package/dist/operations/scale.d.ts +3 -0
- package/dist/operations/scale.d.ts.map +1 -0
- package/dist/operations/scale.js +212 -0
- package/dist/operations/set-tpsl.d.ts +3 -0
- package/dist/operations/set-tpsl.d.ts.map +1 -0
- package/dist/operations/set-tpsl.js +277 -0
- package/dist/operations/spot-order.d.ts +3 -0
- package/dist/operations/spot-order.d.ts.map +1 -0
- package/dist/operations/spot-order.js +173 -0
- package/dist/operations/trigger-order.d.ts +3 -0
- package/dist/operations/trigger-order.d.ts.map +1 -0
- package/dist/operations/trigger-order.js +177 -0
- package/dist/operations/twap-cancel.d.ts +3 -0
- package/dist/operations/twap-cancel.d.ts.map +1 -0
- package/dist/operations/twap-cancel.js +57 -0
- package/dist/operations/twap-status.d.ts +3 -0
- package/dist/operations/twap-status.d.ts.map +1 -0
- package/dist/operations/twap-status.js +81 -0
- package/dist/operations/twap.d.ts +3 -0
- package/dist/operations/twap.d.ts.map +1 -0
- package/dist/operations/twap.js +124 -0
- package/dist/setup/approve-builder.d.ts +3 -0
- package/dist/setup/approve-builder.d.ts.map +1 -0
- package/dist/setup/approve-builder.js +155 -0
- package/dist/setup/env.d.ts +4 -0
- package/dist/setup/env.d.ts.map +1 -0
- package/dist/setup/env.js +8 -0
- package/dist/setup/onboard.d.ts +10 -0
- package/dist/setup/onboard.d.ts.map +1 -0
- package/dist/setup/onboard.js +462 -0
- package/package.json +10 -4
- package/scripts/core/client.ts +13 -3
- package/scripts/info/all-markets.ts +18 -2
- package/scripts/info/search-markets.ts +18 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ISubscription } from '@nktkas/hyperliquid';
|
|
2
|
+
import type { UserEventsWsEvent } from '@nktkas/hyperliquid';
|
|
3
|
+
export interface WsEventMap {
|
|
4
|
+
/** L2 order book snapshot for a specific coin */
|
|
5
|
+
l2Book: {
|
|
6
|
+
coin: string;
|
|
7
|
+
time: number;
|
|
8
|
+
levels: [
|
|
9
|
+
Array<{
|
|
10
|
+
px: string;
|
|
11
|
+
sz: string;
|
|
12
|
+
n: number;
|
|
13
|
+
}>,
|
|
14
|
+
Array<{
|
|
15
|
+
px: string;
|
|
16
|
+
sz: string;
|
|
17
|
+
n: number;
|
|
18
|
+
}>
|
|
19
|
+
];
|
|
20
|
+
};
|
|
21
|
+
/** Mid prices for all assets updated */
|
|
22
|
+
allMids: {
|
|
23
|
+
mids: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
/** Order status changed (filled, canceled, rejected, etc.) */
|
|
26
|
+
orderUpdate: {
|
|
27
|
+
order: {
|
|
28
|
+
coin: string;
|
|
29
|
+
side: 'B' | 'A';
|
|
30
|
+
limitPx: string;
|
|
31
|
+
sz: string;
|
|
32
|
+
oid: number;
|
|
33
|
+
timestamp: number;
|
|
34
|
+
origSz: string;
|
|
35
|
+
cloid?: string;
|
|
36
|
+
reduceOnly?: boolean;
|
|
37
|
+
};
|
|
38
|
+
status: string;
|
|
39
|
+
statusTimestamp: number;
|
|
40
|
+
};
|
|
41
|
+
/** Trade fill received */
|
|
42
|
+
userFill: {
|
|
43
|
+
coin: string;
|
|
44
|
+
px: string;
|
|
45
|
+
sz: string;
|
|
46
|
+
side: 'B' | 'A';
|
|
47
|
+
time: number;
|
|
48
|
+
closedPnl: string;
|
|
49
|
+
fee: string;
|
|
50
|
+
/** Token the fee is denominated in. Spot buys typically pay fee in the base asset (e.g. "HYPE") rather than "USDC"; consumers must convert using `px` to get a USD value. */
|
|
51
|
+
feeToken: string;
|
|
52
|
+
oid: number;
|
|
53
|
+
crossed: boolean;
|
|
54
|
+
};
|
|
55
|
+
/** User event (fills, funding, liquidation, non-user cancels) */
|
|
56
|
+
userEvent: UserEventsWsEvent;
|
|
57
|
+
/** WebSocket connected */
|
|
58
|
+
connected: undefined;
|
|
59
|
+
/** WebSocket disconnected */
|
|
60
|
+
disconnected: {
|
|
61
|
+
reason?: string;
|
|
62
|
+
};
|
|
63
|
+
/** WebSocket error */
|
|
64
|
+
error: {
|
|
65
|
+
error: Error;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export type WsEventType = keyof WsEventMap;
|
|
69
|
+
export type WsEventHandler<E extends WsEventType> = (data: WsEventMap[E]) => void;
|
|
70
|
+
export declare class WebSocketManager {
|
|
71
|
+
private transport;
|
|
72
|
+
private client;
|
|
73
|
+
private subscriptions;
|
|
74
|
+
private handlers;
|
|
75
|
+
private _connected;
|
|
76
|
+
private verbose;
|
|
77
|
+
constructor(verbose?: boolean);
|
|
78
|
+
private log;
|
|
79
|
+
get connected(): boolean;
|
|
80
|
+
connect(): Promise<void>;
|
|
81
|
+
close(): Promise<void>;
|
|
82
|
+
on<E extends WsEventType>(event: E, handler: WsEventHandler<E>): void;
|
|
83
|
+
off<E extends WsEventType>(event: E, handler: WsEventHandler<E>): void;
|
|
84
|
+
private emit;
|
|
85
|
+
removeAllListeners(): void;
|
|
86
|
+
private ensureClient;
|
|
87
|
+
private trackSub;
|
|
88
|
+
/**
|
|
89
|
+
* Subscribe to mid prices for all assets. Fires on every price update.
|
|
90
|
+
*/
|
|
91
|
+
subscribeAllMids(): Promise<ISubscription>;
|
|
92
|
+
/**
|
|
93
|
+
* Subscribe to L2 order book snapshots for a specific coin.
|
|
94
|
+
*/
|
|
95
|
+
subscribeL2Book(coin: string): Promise<ISubscription>;
|
|
96
|
+
/**
|
|
97
|
+
* Subscribe to order lifecycle events (fill, cancel, reject, etc.).
|
|
98
|
+
* This is the most important subscription for trading automations.
|
|
99
|
+
*/
|
|
100
|
+
subscribeOrderUpdates(user: `0x${string}`): Promise<ISubscription>;
|
|
101
|
+
/**
|
|
102
|
+
* Subscribe to trade fills for a user.
|
|
103
|
+
*/
|
|
104
|
+
subscribeUserFills(user: `0x${string}`): Promise<ISubscription>;
|
|
105
|
+
/**
|
|
106
|
+
* Subscribe to all user events (fills, funding, liquidations, non-user cancels).
|
|
107
|
+
* This is the only way to get liquidation alerts.
|
|
108
|
+
*/
|
|
109
|
+
subscribeUserEvents(user: `0x${string}`): Promise<ISubscription>;
|
|
110
|
+
/**
|
|
111
|
+
* Start all subscriptions needed for the automation runtime:
|
|
112
|
+
* - allMids (price feed)
|
|
113
|
+
* - orderUpdates (order lifecycle)
|
|
114
|
+
* - userFills (trade fills)
|
|
115
|
+
* - userEvents (liquidations, funding payments, system cancels)
|
|
116
|
+
*/
|
|
117
|
+
subscribeAll(user: `0x${string}`): Promise<void>;
|
|
118
|
+
}
|
|
119
|
+
export declare function getWebSocket(verbose?: boolean): WebSocketManager;
|
|
120
|
+
export declare function resetWebSocket(): void;
|
|
121
|
+
//# sourceMappingURL=ws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../scripts/core/ws.ts"],"names":[],"mappings":"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"}
|
package/dist/core/ws.js
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// WebSocket Manager for Hyperliquid real-time data
|
|
2
|
+
// Wraps @nktkas/hyperliquid SubscriptionClient with event-driven API
|
|
3
|
+
import { WebSocketTransport, SubscriptionClient } from '@nktkas/hyperliquid';
|
|
4
|
+
import { isMainnet } from './config.js';
|
|
5
|
+
// ── Manager ────────────────────────────────────────────────────────
|
|
6
|
+
export class WebSocketManager {
|
|
7
|
+
transport = null;
|
|
8
|
+
client = null;
|
|
9
|
+
subscriptions = [];
|
|
10
|
+
handlers = new Map();
|
|
11
|
+
_connected = false;
|
|
12
|
+
verbose;
|
|
13
|
+
constructor(verbose = false) {
|
|
14
|
+
this.verbose = verbose;
|
|
15
|
+
}
|
|
16
|
+
log(...args) {
|
|
17
|
+
if (this.verbose)
|
|
18
|
+
console.log('[WS]', ...args);
|
|
19
|
+
}
|
|
20
|
+
get connected() {
|
|
21
|
+
return this._connected;
|
|
22
|
+
}
|
|
23
|
+
// ── Connection management ──────────────────────────────────────
|
|
24
|
+
async connect() {
|
|
25
|
+
if (this.transport)
|
|
26
|
+
return; // already connected
|
|
27
|
+
this.transport = new WebSocketTransport({
|
|
28
|
+
isTestnet: !isMainnet(),
|
|
29
|
+
resubscribe: true, // auto-resubscribe on reconnect
|
|
30
|
+
});
|
|
31
|
+
this.client = new SubscriptionClient({ transport: this.transport });
|
|
32
|
+
await this.transport.ready();
|
|
33
|
+
this._connected = true;
|
|
34
|
+
this.emit('connected', undefined);
|
|
35
|
+
this.log('Connected to', isMainnet() ? 'mainnet' : 'testnet');
|
|
36
|
+
}
|
|
37
|
+
async close() {
|
|
38
|
+
for (const sub of this.subscriptions) {
|
|
39
|
+
try {
|
|
40
|
+
await sub.unsubscribe();
|
|
41
|
+
}
|
|
42
|
+
catch { /* ignore */ }
|
|
43
|
+
}
|
|
44
|
+
this.subscriptions = [];
|
|
45
|
+
if (this.transport) {
|
|
46
|
+
try {
|
|
47
|
+
await this.transport.close();
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore */ }
|
|
50
|
+
this.transport = null;
|
|
51
|
+
this.client = null;
|
|
52
|
+
}
|
|
53
|
+
this._connected = false;
|
|
54
|
+
this.emit('disconnected', { reason: 'manual close' });
|
|
55
|
+
this.log('Closed');
|
|
56
|
+
}
|
|
57
|
+
// ── Event system ───────────────────────────────────────────────
|
|
58
|
+
on(event, handler) {
|
|
59
|
+
if (!this.handlers.has(event)) {
|
|
60
|
+
this.handlers.set(event, new Set());
|
|
61
|
+
}
|
|
62
|
+
this.handlers.get(event).add(handler);
|
|
63
|
+
}
|
|
64
|
+
off(event, handler) {
|
|
65
|
+
this.handlers.get(event)?.delete(handler);
|
|
66
|
+
}
|
|
67
|
+
emit(event, data) {
|
|
68
|
+
const set = this.handlers.get(event);
|
|
69
|
+
if (!set)
|
|
70
|
+
return;
|
|
71
|
+
for (const handler of set) {
|
|
72
|
+
try {
|
|
73
|
+
handler(data);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this.log('Handler error:', err instanceof Error ? err.message : String(err));
|
|
77
|
+
this.emit('error', { error: err instanceof Error ? err : new Error(String(err)) });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
removeAllListeners() {
|
|
82
|
+
this.handlers.clear();
|
|
83
|
+
}
|
|
84
|
+
// ── Subscription helpers ───────────────────────────────────────
|
|
85
|
+
ensureClient() {
|
|
86
|
+
if (!this.client)
|
|
87
|
+
throw new Error('WebSocket not connected. Call connect() first.');
|
|
88
|
+
return this.client;
|
|
89
|
+
}
|
|
90
|
+
trackSub(sub) {
|
|
91
|
+
this.subscriptions.push(sub);
|
|
92
|
+
sub.failureSignal.addEventListener('abort', () => {
|
|
93
|
+
this.log('Subscription failed, removing from tracked list');
|
|
94
|
+
const idx = this.subscriptions.indexOf(sub);
|
|
95
|
+
if (idx >= 0)
|
|
96
|
+
this.subscriptions.splice(idx, 1);
|
|
97
|
+
});
|
|
98
|
+
return sub;
|
|
99
|
+
}
|
|
100
|
+
// ── Market data subscriptions ──────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Subscribe to mid prices for all assets. Fires on every price update.
|
|
103
|
+
*/
|
|
104
|
+
async subscribeAllMids() {
|
|
105
|
+
const client = this.ensureClient();
|
|
106
|
+
const sub = await client.allMids((data) => {
|
|
107
|
+
this.emit('allMids', { mids: data.mids });
|
|
108
|
+
});
|
|
109
|
+
return this.trackSub(sub);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Subscribe to L2 order book snapshots for a specific coin.
|
|
113
|
+
*/
|
|
114
|
+
async subscribeL2Book(coin) {
|
|
115
|
+
const client = this.ensureClient();
|
|
116
|
+
const sub = await client.l2Book({ coin }, (data) => {
|
|
117
|
+
this.emit('l2Book', {
|
|
118
|
+
coin: data.coin,
|
|
119
|
+
time: data.time,
|
|
120
|
+
levels: data.levels,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
return this.trackSub(sub);
|
|
124
|
+
}
|
|
125
|
+
// ── User subscriptions ────────────────────────────────────────
|
|
126
|
+
/**
|
|
127
|
+
* Subscribe to order lifecycle events (fill, cancel, reject, etc.).
|
|
128
|
+
* This is the most important subscription for trading automations.
|
|
129
|
+
*/
|
|
130
|
+
async subscribeOrderUpdates(user) {
|
|
131
|
+
const client = this.ensureClient();
|
|
132
|
+
const sub = await client.orderUpdates({ user }, (data) => {
|
|
133
|
+
for (const update of data) {
|
|
134
|
+
this.emit('orderUpdate', {
|
|
135
|
+
order: {
|
|
136
|
+
coin: update.order.coin,
|
|
137
|
+
side: update.order.side,
|
|
138
|
+
limitPx: update.order.limitPx,
|
|
139
|
+
sz: update.order.sz,
|
|
140
|
+
oid: update.order.oid,
|
|
141
|
+
timestamp: update.order.timestamp,
|
|
142
|
+
origSz: update.order.origSz,
|
|
143
|
+
cloid: update.order.cloid,
|
|
144
|
+
reduceOnly: update.order.reduceOnly,
|
|
145
|
+
},
|
|
146
|
+
status: update.status,
|
|
147
|
+
statusTimestamp: update.statusTimestamp,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return this.trackSub(sub);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Subscribe to trade fills for a user.
|
|
155
|
+
*/
|
|
156
|
+
async subscribeUserFills(user) {
|
|
157
|
+
const client = this.ensureClient();
|
|
158
|
+
const sub = await client.userFills({ user }, (data) => {
|
|
159
|
+
if (data.isSnapshot)
|
|
160
|
+
return; // skip initial snapshot
|
|
161
|
+
for (const fill of data.fills) {
|
|
162
|
+
this.emit('userFill', {
|
|
163
|
+
coin: fill.coin,
|
|
164
|
+
px: fill.px,
|
|
165
|
+
sz: fill.sz,
|
|
166
|
+
side: fill.side,
|
|
167
|
+
time: fill.time,
|
|
168
|
+
closedPnl: fill.closedPnl,
|
|
169
|
+
fee: fill.fee,
|
|
170
|
+
feeToken: fill.feeToken,
|
|
171
|
+
oid: fill.oid,
|
|
172
|
+
crossed: fill.crossed,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
return this.trackSub(sub);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Subscribe to all user events (fills, funding, liquidations, non-user cancels).
|
|
180
|
+
* This is the only way to get liquidation alerts.
|
|
181
|
+
*/
|
|
182
|
+
async subscribeUserEvents(user) {
|
|
183
|
+
const client = this.ensureClient();
|
|
184
|
+
const sub = await client.userEvents({ user }, (data) => {
|
|
185
|
+
this.emit('userEvent', data);
|
|
186
|
+
});
|
|
187
|
+
return this.trackSub(sub);
|
|
188
|
+
}
|
|
189
|
+
// ── Convenience: subscribe to all relevant feeds for an automation ──
|
|
190
|
+
/**
|
|
191
|
+
* Start all subscriptions needed for the automation runtime:
|
|
192
|
+
* - allMids (price feed)
|
|
193
|
+
* - orderUpdates (order lifecycle)
|
|
194
|
+
* - userFills (trade fills)
|
|
195
|
+
* - userEvents (liquidations, funding payments, system cancels)
|
|
196
|
+
*/
|
|
197
|
+
async subscribeAll(user) {
|
|
198
|
+
await this.connect();
|
|
199
|
+
this.log('Subscribing to all feeds for', user);
|
|
200
|
+
await Promise.all([
|
|
201
|
+
this.subscribeAllMids(),
|
|
202
|
+
this.subscribeOrderUpdates(user),
|
|
203
|
+
this.subscribeUserFills(user),
|
|
204
|
+
this.subscribeUserEvents(user),
|
|
205
|
+
]);
|
|
206
|
+
this.log('All subscriptions active');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ── Singleton ──────────────────────────────────────────────────────
|
|
210
|
+
let wsInstance = null;
|
|
211
|
+
export function getWebSocket(verbose = false) {
|
|
212
|
+
if (!wsInstance) {
|
|
213
|
+
wsInstance = new WebSocketManager(verbose);
|
|
214
|
+
}
|
|
215
|
+
return wsInstance;
|
|
216
|
+
}
|
|
217
|
+
export function resetWebSocket() {
|
|
218
|
+
if (wsInstance) {
|
|
219
|
+
wsInstance.close().catch(() => { });
|
|
220
|
+
wsInstance = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../scripts/info/account.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
// Get account info from Hyperliquid
|
|
3
|
+
import { getClient } from '../core/client.js';
|
|
4
|
+
import { formatUsd, formatPercent, parseArgs } from '../core/utils.js';
|
|
5
|
+
async function main() {
|
|
6
|
+
const args = parseArgs(process.argv.slice(2));
|
|
7
|
+
const jsonOutput = args.json;
|
|
8
|
+
const targetAddress = args.address;
|
|
9
|
+
const client = getClient();
|
|
10
|
+
if (args.verbose) {
|
|
11
|
+
client.verbose = true;
|
|
12
|
+
}
|
|
13
|
+
const lookupAddress = targetAddress?.toLowerCase() ?? client.address;
|
|
14
|
+
const isOtherAccount = !!targetAddress;
|
|
15
|
+
const accountMode = await client.getAccountMode(isOtherAccount ? lookupAddress : undefined);
|
|
16
|
+
try {
|
|
17
|
+
const state = await client.getUserStateAll(isOtherAccount ? lookupAddress : undefined);
|
|
18
|
+
const margin = state.crossMarginSummary;
|
|
19
|
+
const accountValue = parseFloat(margin.accountValue);
|
|
20
|
+
const totalMarginUsed = parseFloat(margin.totalMarginUsed);
|
|
21
|
+
const withdrawable = parseFloat(margin.withdrawable);
|
|
22
|
+
const totalNotional = parseFloat(margin.totalNtlPos);
|
|
23
|
+
const positions = state.assetPositions
|
|
24
|
+
.filter(ap => Math.abs(parseFloat(ap.position.szi)) >= 0.0001)
|
|
25
|
+
.map(ap => {
|
|
26
|
+
const pos = ap.position;
|
|
27
|
+
const size = parseFloat(pos.szi);
|
|
28
|
+
const entryPx = parseFloat(pos.entryPx);
|
|
29
|
+
const notional = parseFloat(pos.positionValue);
|
|
30
|
+
const markPx = Math.abs(notional / size);
|
|
31
|
+
const pnl = parseFloat(pos.unrealizedPnl);
|
|
32
|
+
return {
|
|
33
|
+
coin: pos.coin,
|
|
34
|
+
side: size > 0 ? 'long' : 'short',
|
|
35
|
+
size: pos.szi,
|
|
36
|
+
entryPrice: pos.entryPx,
|
|
37
|
+
markPrice: markPx,
|
|
38
|
+
notional: Math.abs(notional),
|
|
39
|
+
unrealizedPnl: pnl,
|
|
40
|
+
leverage: `${pos.leverage.value}x ${pos.leverage.type}`,
|
|
41
|
+
liquidationPx: pos.liquidationPx,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
const totalPnl = positions.reduce((sum, p) => sum + p.unrealizedPnl, 0);
|
|
45
|
+
// Fetch spot balances
|
|
46
|
+
const userParam = isOtherAccount ? lookupAddress : undefined;
|
|
47
|
+
const spotState = await client.getSpotBalances(userParam);
|
|
48
|
+
const spotBalances = (spotState?.balances ?? []).filter(b => parseFloat(b.total) > 0);
|
|
49
|
+
// JSON output
|
|
50
|
+
if (jsonOutput) {
|
|
51
|
+
const result = {
|
|
52
|
+
address: lookupAddress,
|
|
53
|
+
...(isOtherAccount ? {} : {
|
|
54
|
+
signingWallet: client.walletAddress,
|
|
55
|
+
walletType: client.isApiWallet ? 'api' : 'main',
|
|
56
|
+
}),
|
|
57
|
+
accountMode,
|
|
58
|
+
equity: accountValue,
|
|
59
|
+
totalNotional,
|
|
60
|
+
totalMarginUsed,
|
|
61
|
+
withdrawable,
|
|
62
|
+
marginRatio: totalMarginUsed > 0 && accountValue > 0 ? totalMarginUsed / accountValue : 0,
|
|
63
|
+
totalUnrealizedPnl: totalPnl,
|
|
64
|
+
positions,
|
|
65
|
+
spotBalances: spotBalances.map(b => ({
|
|
66
|
+
coin: b.coin,
|
|
67
|
+
total: b.total,
|
|
68
|
+
hold: b.hold,
|
|
69
|
+
entryNtl: b.entryNtl,
|
|
70
|
+
})),
|
|
71
|
+
};
|
|
72
|
+
if (args.orders) {
|
|
73
|
+
const orders = await client.getOpenOrders(isOtherAccount ? lookupAddress : undefined);
|
|
74
|
+
result.openOrders = orders.map(o => ({
|
|
75
|
+
coin: o.coin,
|
|
76
|
+
oid: o.oid,
|
|
77
|
+
side: o.side === 'B' ? 'buy' : 'sell',
|
|
78
|
+
size: o.sz,
|
|
79
|
+
price: o.limitPx,
|
|
80
|
+
orderType: o.orderType,
|
|
81
|
+
timestamp: o.timestamp,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
console.log(JSON.stringify(result, null, 2));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Human-readable output
|
|
88
|
+
console.log('Open Broker - Account Info');
|
|
89
|
+
console.log('==========================\n');
|
|
90
|
+
if (isOtherAccount) {
|
|
91
|
+
console.log('Lookup Address');
|
|
92
|
+
console.log('--------------------');
|
|
93
|
+
console.log(`Address: ${lookupAddress}`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log('Wallet Configuration');
|
|
97
|
+
console.log('--------------------');
|
|
98
|
+
console.log(`Trading Account: ${client.address}`);
|
|
99
|
+
console.log(`Signing Wallet: ${client.walletAddress}`);
|
|
100
|
+
console.log(`Wallet Type: ${client.isApiWallet ? 'API Wallet' : 'Main Wallet'}`);
|
|
101
|
+
}
|
|
102
|
+
const modeLabel = {
|
|
103
|
+
standard: 'Standard (separate balances per dex)',
|
|
104
|
+
unified: 'Unified Account (shared USDC across all dexes)',
|
|
105
|
+
portfolio: 'Portfolio Margin',
|
|
106
|
+
dexAbstraction: 'DEX Abstraction (deprecated)',
|
|
107
|
+
};
|
|
108
|
+
console.log(`Account Mode: ${modeLabel[accountMode] ?? accountMode}`);
|
|
109
|
+
if (!isOtherAccount) {
|
|
110
|
+
// Check builder fee approval (skip on testnet)
|
|
111
|
+
if (client.isTestnet) {
|
|
112
|
+
console.log(`Builder Fee: skipped (testnet)`);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const builderApproval = await client.getMaxBuilderFee();
|
|
116
|
+
console.log(`Builder Address: ${client.builderAddress}`);
|
|
117
|
+
console.log(`Builder Fee: ${client.builderFeeBps} bps`);
|
|
118
|
+
if (builderApproval) {
|
|
119
|
+
console.log(`Builder Approved: ✅ Yes (max: ${builderApproval})`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.log(`Builder Approved: ❌ No`);
|
|
123
|
+
console.log(`\n⚠️ Run: npx tsx scripts/setup/approve-builder.ts`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Warn if API wallet setup looks misconfigured
|
|
127
|
+
if (!client.isApiWallet && accountValue === 0 && positions.length === 0) {
|
|
128
|
+
console.log('\n⚠️ No positions and $0 equity.');
|
|
129
|
+
console.log(' If this account is traded via an API wallet, set HYPERLIQUID_ACCOUNT_ADDRESS');
|
|
130
|
+
console.log(' in ~/.openbroker/.env to the master account address (the wallet that holds funds).');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
console.log('');
|
|
134
|
+
console.log('Margin Summary');
|
|
135
|
+
console.log('--------------');
|
|
136
|
+
console.log(`Account Value: ${formatUsd(accountValue)}`);
|
|
137
|
+
console.log(`Total Notional: ${formatUsd(totalNotional)}`);
|
|
138
|
+
console.log(`Margin Used: ${formatUsd(totalMarginUsed)}`);
|
|
139
|
+
console.log(`Withdrawable: ${formatUsd(withdrawable)}`);
|
|
140
|
+
if (totalMarginUsed > 0) {
|
|
141
|
+
const marginRatio = totalMarginUsed / accountValue;
|
|
142
|
+
console.log(`Margin Ratio: ${formatPercent(marginRatio)}`);
|
|
143
|
+
}
|
|
144
|
+
console.log('\nPositions Summary');
|
|
145
|
+
console.log('-----------------');
|
|
146
|
+
if (positions.length === 0) {
|
|
147
|
+
console.log('No open positions');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log('Coin | Size | Entry | Mark | PnL | Leverage');
|
|
151
|
+
console.log('---------|------------|------------|------------|------------|----------');
|
|
152
|
+
for (const p of positions) {
|
|
153
|
+
const side = p.side === 'long' ? 'L' : 'S';
|
|
154
|
+
console.log(`${p.coin.padEnd(8)} | ${side} ${Math.abs(parseFloat(p.size)).toFixed(4).padStart(8)} | ` +
|
|
155
|
+
`${formatUsd(parseFloat(p.entryPrice)).padStart(10)} | ${formatUsd(p.markPrice).padStart(10)} | ` +
|
|
156
|
+
`${formatUsd(p.unrealizedPnl).padStart(10)} | ${p.leverage}`);
|
|
157
|
+
}
|
|
158
|
+
console.log('---------|------------|------------|------------|------------|----------');
|
|
159
|
+
console.log(`Total Unrealized PnL: ${formatUsd(totalPnl)}`);
|
|
160
|
+
}
|
|
161
|
+
// Show spot balances
|
|
162
|
+
if (spotBalances.length > 0) {
|
|
163
|
+
console.log('\nSpot Balances');
|
|
164
|
+
console.log('-------------');
|
|
165
|
+
console.log('Token | Total | Hold | Entry Value');
|
|
166
|
+
console.log('-------------|--------------------|--------------------|------------');
|
|
167
|
+
for (const b of spotBalances) {
|
|
168
|
+
const total = parseFloat(b.total);
|
|
169
|
+
const hold = parseFloat(b.hold);
|
|
170
|
+
const entry = parseFloat(b.entryNtl);
|
|
171
|
+
console.log(`${b.coin.padEnd(12)} | ${total.toFixed(6).padStart(18)} | ${hold.toFixed(6).padStart(18)} | ${formatUsd(entry)}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Show open orders if requested
|
|
175
|
+
if (args.orders) {
|
|
176
|
+
console.log('\nOpen Orders');
|
|
177
|
+
console.log('-----------');
|
|
178
|
+
const orders = await client.getOpenOrders(isOtherAccount ? lookupAddress : undefined);
|
|
179
|
+
if (orders.length === 0) {
|
|
180
|
+
console.log('No open orders');
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.log('Coin | Side | Size | Price | Type');
|
|
184
|
+
console.log('---------|------|------------|------------|------');
|
|
185
|
+
for (const order of orders) {
|
|
186
|
+
const side = order.side === 'B' ? 'BUY ' : 'SELL';
|
|
187
|
+
console.log(`${order.coin.padEnd(8)} | ${side} | ${parseFloat(order.sz).toFixed(4).padStart(10)} | ` +
|
|
188
|
+
`${formatUsd(parseFloat(order.limitPx)).padStart(10)} | ${order.orderType}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
console.error('Error fetching account info:', error);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"all-markets.d.ts","sourceRoot":"","sources":["../../scripts/info/all-markets.ts"],"names":[],"mappings":""}
|