openbroker 1.0.82 → 1.0.87
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/SKILL.md +79 -7
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/auto/cli.ts +1 -0
- package/scripts/auto/dashboard-forwarder.ts +77 -0
- package/scripts/auto/loader.ts +4 -2
- package/scripts/auto/report.ts +2 -2
- package/scripts/auto/runtime.ts +42 -14
- package/scripts/auto/types.ts +17 -1
- package/scripts/core/client.ts +167 -23
- package/scripts/core/config.ts +1 -1
- package/scripts/core/types.ts +13 -4
- package/scripts/info/all-markets.ts +51 -24
- package/scripts/info/candles.ts +13 -2
- package/scripts/info/fees.ts +16 -5
- package/scripts/info/funding-history.ts +13 -2
- package/scripts/info/funding-scan.ts +6 -0
- package/scripts/info/funding.ts +7 -1
- package/scripts/info/markets.ts +5 -0
- package/scripts/info/order-status.ts +11 -2
- package/scripts/info/rate-limit.ts +11 -2
- package/scripts/info/search-markets.ts +30 -10
- package/scripts/info/spot.ts +25 -5
- package/scripts/info/trades.ts +13 -2
- package/scripts/operations/limit-order.ts +1 -1
- package/scripts/operations/twap-cancel.ts +4 -2
- package/scripts/operations/twap.ts +8 -10
- package/scripts/plugin/tools.ts +15 -19
- package/scripts/setup/onboard.ts +8 -2
package/scripts/core/client.ts
CHANGED
|
@@ -9,6 +9,8 @@ import type {
|
|
|
9
9
|
OrderResponse,
|
|
10
10
|
CancelResponse,
|
|
11
11
|
MetaAndAssetCtxs,
|
|
12
|
+
AssetMeta,
|
|
13
|
+
AssetCtx,
|
|
12
14
|
ClearinghouseState,
|
|
13
15
|
OpenOrder,
|
|
14
16
|
} from './types.js';
|
|
@@ -256,13 +258,16 @@ export class HyperliquidClient {
|
|
|
256
258
|
}
|
|
257
259
|
this.log('metaAndAssetCtxs response:', JSON.stringify(response, null, 2).slice(0, 500) + '...');
|
|
258
260
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
261
|
+
// Build the narrowed value locally so neither narrowing nor the `| null`
|
|
262
|
+
// field type can be lost across `await` / method-call boundaries below.
|
|
263
|
+
const meta: MetaAndAssetCtxs = {
|
|
264
|
+
meta: { universe: response[0].universe as AssetMeta[] },
|
|
265
|
+
assetCtxs: response[1] as AssetCtx[],
|
|
262
266
|
};
|
|
267
|
+
this.meta = meta;
|
|
263
268
|
|
|
264
269
|
// Build lookup maps for main dex
|
|
265
|
-
|
|
270
|
+
meta.meta.universe.forEach((asset, index) => {
|
|
266
271
|
this.assetMap.set(asset.name, index);
|
|
267
272
|
this.szDecimalsMap.set(asset.name, asset.szDecimals);
|
|
268
273
|
this.coinDexMap.set(asset.name, { dexName: null, dexIdx: 0, localName: asset.name });
|
|
@@ -274,7 +279,7 @@ export class HyperliquidClient {
|
|
|
274
279
|
this.hip3Loaded = true;
|
|
275
280
|
}
|
|
276
281
|
|
|
277
|
-
return
|
|
282
|
+
return meta;
|
|
278
283
|
}
|
|
279
284
|
|
|
280
285
|
/**
|
|
@@ -782,7 +787,19 @@ export class HyperliquidClient {
|
|
|
782
787
|
tokenId,
|
|
783
788
|
}),
|
|
784
789
|
});
|
|
785
|
-
const data = await response.json()
|
|
790
|
+
const data = await response.json() as {
|
|
791
|
+
name: string;
|
|
792
|
+
maxSupply: string;
|
|
793
|
+
totalSupply: string;
|
|
794
|
+
circulatingSupply: string;
|
|
795
|
+
szDecimals: number;
|
|
796
|
+
weiDecimals: number;
|
|
797
|
+
midPx: string;
|
|
798
|
+
markPx: string;
|
|
799
|
+
prevDayPx: string;
|
|
800
|
+
deployer: string;
|
|
801
|
+
deployTime: string;
|
|
802
|
+
} | null;
|
|
786
803
|
this.log('tokenDetails response:', JSON.stringify(data).slice(0, 500));
|
|
787
804
|
return data;
|
|
788
805
|
} catch {
|
|
@@ -807,7 +824,10 @@ export class HyperliquidClient {
|
|
|
807
824
|
headers: { 'Content-Type': 'application/json' },
|
|
808
825
|
body: JSON.stringify({ type: 'predictedFundings' }),
|
|
809
826
|
});
|
|
810
|
-
const data = await response.json()
|
|
827
|
+
const data = await response.json() as Array<[
|
|
828
|
+
string,
|
|
829
|
+
Array<[string, { fundingRate: string; nextFundingTime: number }]>
|
|
830
|
+
]>;
|
|
811
831
|
this.log('predictedFundings response length:', data?.length);
|
|
812
832
|
return data;
|
|
813
833
|
}
|
|
@@ -827,7 +847,7 @@ export class HyperliquidClient {
|
|
|
827
847
|
}> {
|
|
828
848
|
this.log('Fetching l2Book for:', coin);
|
|
829
849
|
// API accepts prefixed names directly (e.g., "xyz:CL")
|
|
830
|
-
let response
|
|
850
|
+
let response: Awaited<ReturnType<typeof this.info.l2Book>>;
|
|
831
851
|
try {
|
|
832
852
|
response = await this.info.l2Book({ coin });
|
|
833
853
|
} catch (error) {
|
|
@@ -838,8 +858,12 @@ export class HyperliquidClient {
|
|
|
838
858
|
throw new Error(`l2Book(${coin}) failed: ${this.describeError(error)}`);
|
|
839
859
|
}
|
|
840
860
|
|
|
841
|
-
|
|
842
|
-
|
|
861
|
+
if (!response || !Array.isArray(response.levels)) {
|
|
862
|
+
throw new Error(`l2Book(${coin}) returned empty/malformed payload.`);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const bids = (response.levels[0] ?? []) as Array<{ px: string; sz: string; n: number }>;
|
|
866
|
+
const asks = (response.levels[1] ?? []) as Array<{ px: string; sz: string; n: number }>;
|
|
843
867
|
|
|
844
868
|
const bestBid = bids.length > 0 ? parseFloat(bids[0].px) : 0;
|
|
845
869
|
const bestAsk = asks.length > 0 ? parseFloat(asks[0].px) : 0;
|
|
@@ -1015,7 +1039,7 @@ export class HyperliquidClient {
|
|
|
1015
1039
|
user: user ?? this.address,
|
|
1016
1040
|
}),
|
|
1017
1041
|
});
|
|
1018
|
-
const data = await response.json();
|
|
1042
|
+
const data = await response.json() as string | { abstraction?: string; mode?: string } | null;
|
|
1019
1043
|
this.log('userAbstraction response:', JSON.stringify(data));
|
|
1020
1044
|
|
|
1021
1045
|
// API may return a bare string or an object. Normalize to string for matching.
|
|
@@ -1194,10 +1218,13 @@ export class HyperliquidClient {
|
|
|
1194
1218
|
}
|
|
1195
1219
|
|
|
1196
1220
|
try {
|
|
1221
|
+
// approveBuilderFee is a wallet-level authorization (not a vault action)
|
|
1222
|
+
// and the SDK's `opts` parameter only carries an AbortSignal. Do not pass
|
|
1223
|
+
// vaultParam here — the signature is { signal? }, not a vault wrapper.
|
|
1197
1224
|
const response = await this.exchange.approveBuilderFee({
|
|
1198
1225
|
builder: targetBuilder as `0x${string}`,
|
|
1199
1226
|
maxFeeRate,
|
|
1200
|
-
}
|
|
1227
|
+
});
|
|
1201
1228
|
this.log('approveBuilderFee response:', response);
|
|
1202
1229
|
return { status: 'ok', response };
|
|
1203
1230
|
} catch (error) {
|
|
@@ -1242,7 +1269,18 @@ export class HyperliquidClient {
|
|
|
1242
1269
|
headers: { 'Content-Type': 'application/json' },
|
|
1243
1270
|
body: JSON.stringify(body),
|
|
1244
1271
|
});
|
|
1245
|
-
const data = await response.json()
|
|
1272
|
+
const data = await response.json() as Array<{
|
|
1273
|
+
time: number;
|
|
1274
|
+
hash: string;
|
|
1275
|
+
delta: {
|
|
1276
|
+
type: 'funding';
|
|
1277
|
+
coin: string;
|
|
1278
|
+
usdc: string;
|
|
1279
|
+
szi: string;
|
|
1280
|
+
fundingRate: string;
|
|
1281
|
+
nSamples: number | null;
|
|
1282
|
+
};
|
|
1283
|
+
}>;
|
|
1246
1284
|
this.log('userFunding response length:', data?.length);
|
|
1247
1285
|
return data;
|
|
1248
1286
|
}
|
|
@@ -1285,7 +1323,25 @@ export class HyperliquidClient {
|
|
|
1285
1323
|
headers: { 'Content-Type': 'application/json' },
|
|
1286
1324
|
body: JSON.stringify(body),
|
|
1287
1325
|
});
|
|
1288
|
-
const data = await response.json()
|
|
1326
|
+
const data = await response.json() as Array<{
|
|
1327
|
+
coin: string;
|
|
1328
|
+
px: string;
|
|
1329
|
+
sz: string;
|
|
1330
|
+
side: 'B' | 'A';
|
|
1331
|
+
time: number;
|
|
1332
|
+
startPosition: string;
|
|
1333
|
+
dir: string;
|
|
1334
|
+
closedPnl: string;
|
|
1335
|
+
fee: string;
|
|
1336
|
+
hash: string;
|
|
1337
|
+
oid: number;
|
|
1338
|
+
tid: number;
|
|
1339
|
+
crossed: boolean;
|
|
1340
|
+
feeToken: string;
|
|
1341
|
+
twapId: number | null;
|
|
1342
|
+
cloid: string | null;
|
|
1343
|
+
builderFee: string | null;
|
|
1344
|
+
}>;
|
|
1289
1345
|
this.log('userFills response length:', data?.length);
|
|
1290
1346
|
return data;
|
|
1291
1347
|
}
|
|
@@ -1328,7 +1384,28 @@ export class HyperliquidClient {
|
|
|
1328
1384
|
user: user ?? this.address,
|
|
1329
1385
|
}),
|
|
1330
1386
|
});
|
|
1331
|
-
const data = await response.json()
|
|
1387
|
+
const data = await response.json() as Array<{
|
|
1388
|
+
order: {
|
|
1389
|
+
coin: string;
|
|
1390
|
+
side: string;
|
|
1391
|
+
limitPx: string;
|
|
1392
|
+
sz: string;
|
|
1393
|
+
origSz: string;
|
|
1394
|
+
oid: number;
|
|
1395
|
+
timestamp: number;
|
|
1396
|
+
orderType: string;
|
|
1397
|
+
tif: string | null;
|
|
1398
|
+
cloid: string | null;
|
|
1399
|
+
triggerCondition: string;
|
|
1400
|
+
triggerPx: string;
|
|
1401
|
+
isTrigger: boolean;
|
|
1402
|
+
isPositionTpsl: boolean;
|
|
1403
|
+
reduceOnly: boolean;
|
|
1404
|
+
children: unknown[];
|
|
1405
|
+
};
|
|
1406
|
+
status: string;
|
|
1407
|
+
statusTimestamp: number;
|
|
1408
|
+
}>;
|
|
1332
1409
|
this.log('historicalOrders response length:', data?.length);
|
|
1333
1410
|
return data;
|
|
1334
1411
|
}
|
|
@@ -1374,7 +1451,30 @@ export class HyperliquidClient {
|
|
|
1374
1451
|
oid: typeof oid === 'string' ? oid : oid,
|
|
1375
1452
|
}),
|
|
1376
1453
|
});
|
|
1377
|
-
const data = await response.json()
|
|
1454
|
+
const data = await response.json() as {
|
|
1455
|
+
status: string;
|
|
1456
|
+
order?: {
|
|
1457
|
+
order: {
|
|
1458
|
+
coin: string;
|
|
1459
|
+
side: string;
|
|
1460
|
+
limitPx: string;
|
|
1461
|
+
sz: string;
|
|
1462
|
+
origSz: string;
|
|
1463
|
+
oid: number;
|
|
1464
|
+
timestamp: number;
|
|
1465
|
+
orderType: string;
|
|
1466
|
+
tif: string | null;
|
|
1467
|
+
cloid: string | null;
|
|
1468
|
+
triggerCondition: string;
|
|
1469
|
+
triggerPx: string;
|
|
1470
|
+
isTrigger: boolean;
|
|
1471
|
+
isPositionTpsl: boolean;
|
|
1472
|
+
reduceOnly: boolean;
|
|
1473
|
+
};
|
|
1474
|
+
status: string;
|
|
1475
|
+
statusTimestamp: number;
|
|
1476
|
+
};
|
|
1477
|
+
};
|
|
1378
1478
|
this.log('orderStatus response:', JSON.stringify(data).slice(0, 500));
|
|
1379
1479
|
return data;
|
|
1380
1480
|
}
|
|
@@ -1409,7 +1509,20 @@ export class HyperliquidClient {
|
|
|
1409
1509
|
user: user ?? this.address,
|
|
1410
1510
|
}),
|
|
1411
1511
|
});
|
|
1412
|
-
const data = await response.json()
|
|
1512
|
+
const data = await response.json() as {
|
|
1513
|
+
dailyUserVlm: Array<{ date: string; exchange: string; userCross: string; userAdd: string }>;
|
|
1514
|
+
feeSchedule: Record<string, unknown>;
|
|
1515
|
+
userCrossRate: string;
|
|
1516
|
+
userAddRate: string;
|
|
1517
|
+
userSpotCrossRate: string;
|
|
1518
|
+
userSpotAddRate: string;
|
|
1519
|
+
activeReferralDiscount: string;
|
|
1520
|
+
trial: unknown;
|
|
1521
|
+
feeTrialEscrow: string;
|
|
1522
|
+
nextTrialAvailableTimestamp: unknown;
|
|
1523
|
+
stakingLink: { stakingUser: string; status: string } | null;
|
|
1524
|
+
activeStakingDiscount: { basisPoints: number; discountRate: string } | null;
|
|
1525
|
+
};
|
|
1413
1526
|
this.log('userFees response:', JSON.stringify(data).slice(0, 500));
|
|
1414
1527
|
return data;
|
|
1415
1528
|
}
|
|
@@ -1448,7 +1561,18 @@ export class HyperliquidClient {
|
|
|
1448
1561
|
headers: { 'Content-Type': 'application/json' },
|
|
1449
1562
|
body: JSON.stringify({ type: 'candleSnapshot', req }),
|
|
1450
1563
|
});
|
|
1451
|
-
const data = await response.json()
|
|
1564
|
+
const data = await response.json() as Array<{
|
|
1565
|
+
t: number;
|
|
1566
|
+
T: number;
|
|
1567
|
+
s: string;
|
|
1568
|
+
i: string;
|
|
1569
|
+
o: string;
|
|
1570
|
+
c: string;
|
|
1571
|
+
h: string;
|
|
1572
|
+
l: string;
|
|
1573
|
+
v: string;
|
|
1574
|
+
n: number;
|
|
1575
|
+
}>;
|
|
1452
1576
|
this.log('candleSnapshot response length:', data?.length);
|
|
1453
1577
|
return data;
|
|
1454
1578
|
}
|
|
@@ -1480,7 +1604,12 @@ export class HyperliquidClient {
|
|
|
1480
1604
|
headers: { 'Content-Type': 'application/json' },
|
|
1481
1605
|
body: JSON.stringify(body),
|
|
1482
1606
|
});
|
|
1483
|
-
const data = await response.json()
|
|
1607
|
+
const data = await response.json() as Array<{
|
|
1608
|
+
coin: string;
|
|
1609
|
+
fundingRate: string;
|
|
1610
|
+
premium: string;
|
|
1611
|
+
time: number;
|
|
1612
|
+
}>;
|
|
1484
1613
|
this.log('fundingHistory response length:', data?.length);
|
|
1485
1614
|
return data;
|
|
1486
1615
|
}
|
|
@@ -1510,7 +1639,15 @@ export class HyperliquidClient {
|
|
|
1510
1639
|
headers: { 'Content-Type': 'application/json' },
|
|
1511
1640
|
body: JSON.stringify(body),
|
|
1512
1641
|
});
|
|
1513
|
-
const data = await response.json()
|
|
1642
|
+
const data = await response.json() as Array<{
|
|
1643
|
+
coin: string;
|
|
1644
|
+
side: 'B' | 'A';
|
|
1645
|
+
px: string;
|
|
1646
|
+
sz: string;
|
|
1647
|
+
time: number;
|
|
1648
|
+
hash: string;
|
|
1649
|
+
tid: number;
|
|
1650
|
+
}>;
|
|
1514
1651
|
this.log('recentTrades response length:', data?.length);
|
|
1515
1652
|
return data;
|
|
1516
1653
|
}
|
|
@@ -1537,7 +1674,12 @@ export class HyperliquidClient {
|
|
|
1537
1674
|
user: user ?? this.address,
|
|
1538
1675
|
}),
|
|
1539
1676
|
});
|
|
1540
|
-
const data = await response.json()
|
|
1677
|
+
const data = await response.json() as {
|
|
1678
|
+
cumVlm: string;
|
|
1679
|
+
nRequestsUsed: number;
|
|
1680
|
+
nRequestsCap: number;
|
|
1681
|
+
nRequestsSurplus: number;
|
|
1682
|
+
};
|
|
1541
1683
|
this.log('userRateLimit response:', JSON.stringify(data));
|
|
1542
1684
|
return data;
|
|
1543
1685
|
}
|
|
@@ -2137,7 +2279,9 @@ export class HyperliquidClient {
|
|
|
2137
2279
|
// Use the exact spot market key from spotMeta (e.g. "@230", "PURR/USDC").
|
|
2138
2280
|
// On testnet the tradable asset id and displayed market key can diverge.
|
|
2139
2281
|
const mids = await this.getAllMids();
|
|
2140
|
-
|
|
2282
|
+
// Record<string, string> lookup returns `string` under TS defaults but
|
|
2283
|
+
// is runtime-undefined when the key is absent — hence the explicit union.
|
|
2284
|
+
let midStr: string | undefined = mids[spotCoinKey];
|
|
2141
2285
|
|
|
2142
2286
|
// Fallback: allMids may omit spot pairs (especially on testnet).
|
|
2143
2287
|
// Try spotMetaAndAssetCtxs which returns markPx directly.
|
|
@@ -2295,7 +2439,7 @@ export class HyperliquidClient {
|
|
|
2295
2439
|
await this.getMetaAndAssetCtxs();
|
|
2296
2440
|
|
|
2297
2441
|
if (leverage) {
|
|
2298
|
-
await this.
|
|
2442
|
+
await this.updateLeverage(coin, leverage);
|
|
2299
2443
|
}
|
|
2300
2444
|
|
|
2301
2445
|
const assetIndex = this.getAssetIndex(coin);
|
package/scripts/core/config.ts
CHANGED
|
@@ -112,7 +112,7 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
112
112
|
// Show warning once
|
|
113
113
|
if (!readOnlyWarningShown) {
|
|
114
114
|
readOnlyWarningShown = true;
|
|
115
|
-
console.
|
|
115
|
+
console.error('\x1b[33m⚠️ Not configured for trading. Run "openbroker setup" to enable trades.\x1b[0m\n');
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
package/scripts/core/types.ts
CHANGED
|
@@ -81,11 +81,18 @@ export interface CancelResponse {
|
|
|
81
81
|
|
|
82
82
|
// ============ Market Data Types ============
|
|
83
83
|
|
|
84
|
+
// Shape matches the upstream @nktkas/hyperliquid SDK response. Fields like
|
|
85
|
+
// `onlyIsolated` and `premium` are absent-or-null in practice for some assets,
|
|
86
|
+
// so they're modelled as optional / nullable. Index signature absorbs any
|
|
87
|
+
// additional SDK fields we don't consume here (marginTableId, growthMode, …).
|
|
84
88
|
export interface AssetMeta {
|
|
85
89
|
name: string;
|
|
86
90
|
szDecimals: number;
|
|
87
91
|
maxLeverage: number;
|
|
88
|
-
onlyIsolated
|
|
92
|
+
onlyIsolated?: boolean;
|
|
93
|
+
isDelisted?: boolean;
|
|
94
|
+
marginTableId?: number;
|
|
95
|
+
[key: string]: unknown;
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
export interface AssetCtx {
|
|
@@ -93,11 +100,13 @@ export interface AssetCtx {
|
|
|
93
100
|
openInterest: string;
|
|
94
101
|
prevDayPx: string;
|
|
95
102
|
dayNtlVlm: string;
|
|
96
|
-
premium: string;
|
|
103
|
+
premium: string | null;
|
|
97
104
|
oraclePx: string;
|
|
98
105
|
markPx: string;
|
|
99
|
-
midPx?: string;
|
|
100
|
-
impactPxs?: [string, string];
|
|
106
|
+
midPx?: string | null;
|
|
107
|
+
impactPxs?: [string, string] | string[] | null;
|
|
108
|
+
dayBaseVlm?: string;
|
|
109
|
+
[key: string]: unknown;
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
export interface Meta {
|
|
@@ -7,6 +7,7 @@ interface Args {
|
|
|
7
7
|
type?: 'perp' | 'spot' | 'hip3' | 'all';
|
|
8
8
|
top?: number;
|
|
9
9
|
verbose?: boolean;
|
|
10
|
+
json?: boolean;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
function parseArgs(): Args {
|
|
@@ -23,6 +24,8 @@ function parseArgs(): Args {
|
|
|
23
24
|
args.top = parseInt(process.argv[++i], 10);
|
|
24
25
|
} else if (arg === '--verbose') {
|
|
25
26
|
args.verbose = true;
|
|
27
|
+
} else if (arg === '--json') {
|
|
28
|
+
args.json = true;
|
|
26
29
|
} else if (arg === '--help' || arg === '-h') {
|
|
27
30
|
console.log(`
|
|
28
31
|
All Markets - View all available markets on Hyperliquid
|
|
@@ -32,6 +35,7 @@ Usage: npx tsx scripts/info/all-markets.ts [options]
|
|
|
32
35
|
Options:
|
|
33
36
|
--type <type> Market type: perp, spot, hip3, or all (default: all)
|
|
34
37
|
--top <n> Show only top N markets by volume
|
|
38
|
+
--json Output as JSON (machine-readable)
|
|
35
39
|
--verbose Show detailed output
|
|
36
40
|
--help Show this help
|
|
37
41
|
|
|
@@ -41,6 +45,7 @@ Examples:
|
|
|
41
45
|
npx tsx scripts/info/all-markets.ts --type hip3 # Show only HIP-3 perps
|
|
42
46
|
npx tsx scripts/info/all-markets.ts --type spot # Show only spot markets
|
|
43
47
|
npx tsx scripts/info/all-markets.ts --top 20 # Show top 20 by volume
|
|
48
|
+
npx tsx scripts/info/all-markets.ts --json # JSON output
|
|
44
49
|
`);
|
|
45
50
|
process.exit(0);
|
|
46
51
|
}
|
|
@@ -71,22 +76,27 @@ function formatFunding(rate: string): string {
|
|
|
71
76
|
return `${sign}${annualized.toFixed(2)}%`;
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
interface MarketRow {
|
|
80
|
+
type: 'perp' | 'spot' | 'hip3';
|
|
81
|
+
provider: string;
|
|
82
|
+
coin: string;
|
|
83
|
+
assetId: number;
|
|
84
|
+
price: string;
|
|
85
|
+
volume24h: number;
|
|
86
|
+
funding?: string;
|
|
87
|
+
maxLeverage?: number;
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
async function main() {
|
|
75
91
|
const args = parseArgs();
|
|
76
92
|
const client = getClient();
|
|
77
93
|
client.verbose = args.verbose ?? false;
|
|
78
94
|
|
|
79
|
-
|
|
95
|
+
if (!args.json) {
|
|
96
|
+
console.log('Fetching market data...\n');
|
|
97
|
+
}
|
|
80
98
|
|
|
81
|
-
const allMarkets:
|
|
82
|
-
type: 'perp' | 'spot' | 'hip3';
|
|
83
|
-
provider: string;
|
|
84
|
-
coin: string;
|
|
85
|
-
price: string;
|
|
86
|
-
volume24h: number;
|
|
87
|
-
funding?: string;
|
|
88
|
-
maxLeverage?: number;
|
|
89
|
-
}> = [];
|
|
99
|
+
const allMarkets: MarketRow[] = [];
|
|
90
100
|
|
|
91
101
|
// Fetch main perps
|
|
92
102
|
if (args.type === 'all' || args.type === 'perp') {
|
|
@@ -98,6 +108,7 @@ async function main() {
|
|
|
98
108
|
type: 'perp',
|
|
99
109
|
provider: 'Hyperliquid',
|
|
100
110
|
coin: asset.name,
|
|
111
|
+
assetId: i,
|
|
101
112
|
price: ctx.markPx,
|
|
102
113
|
volume24h: parseFloat(ctx.dayNtlVlm),
|
|
103
114
|
funding: ctx.funding,
|
|
@@ -108,7 +119,7 @@ async function main() {
|
|
|
108
119
|
|
|
109
120
|
// Fetch HIP-3 perps
|
|
110
121
|
if (args.type === 'all' || args.type === 'hip3') {
|
|
111
|
-
if (client.isTestnet) {
|
|
122
|
+
if (client.isTestnet && !args.json) {
|
|
112
123
|
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use "dexName:COIN" syntax to load a specific dex.\n');
|
|
113
124
|
}
|
|
114
125
|
try {
|
|
@@ -123,10 +134,13 @@ async function main() {
|
|
|
123
134
|
const ctx = dexData.assetCtxs[i];
|
|
124
135
|
if (!asset || !ctx) continue;
|
|
125
136
|
|
|
137
|
+
let assetId = -1;
|
|
138
|
+
try { assetId = client.getAssetIndex(asset.name); } catch { /* not registered */ }
|
|
126
139
|
allMarkets.push({
|
|
127
140
|
type: 'hip3',
|
|
128
141
|
provider: dexData.dexName || `HIP-3 DEX ${dexIdx}`,
|
|
129
142
|
coin: asset.name,
|
|
143
|
+
assetId,
|
|
130
144
|
price: ctx.markPx,
|
|
131
145
|
volume24h: parseFloat(ctx.dayNtlVlm || '0'),
|
|
132
146
|
funding: ctx.funding,
|
|
@@ -143,15 +157,23 @@ async function main() {
|
|
|
143
157
|
if (args.type === 'all' || args.type === 'spot') {
|
|
144
158
|
try {
|
|
145
159
|
const spotData = await client.getSpotMetaAndAssetCtxs();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (
|
|
160
|
+
// contexts carry coin field that matches pair.name; not necessarily aligned by index
|
|
161
|
+
const ctxMap = new Map<string, (typeof spotData.assetCtxs)[number]>();
|
|
162
|
+
for (const ctx of spotData.assetCtxs) {
|
|
163
|
+
if ((ctx as Record<string, unknown>).coin) {
|
|
164
|
+
ctxMap.set((ctx as Record<string, unknown>).coin as string, ctx);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
for (const pair of spotData.meta.universe) {
|
|
168
|
+
if (!pair) continue;
|
|
169
|
+
const ctx = ctxMap.get(pair.name);
|
|
170
|
+
if (!ctx) continue;
|
|
150
171
|
|
|
151
172
|
allMarkets.push({
|
|
152
173
|
type: 'spot',
|
|
153
174
|
provider: 'Spot',
|
|
154
175
|
coin: pair.name,
|
|
176
|
+
assetId: 10000 + pair.index,
|
|
155
177
|
price: ctx.markPx,
|
|
156
178
|
volume24h: parseFloat(ctx.dayNtlVlm || '0'),
|
|
157
179
|
});
|
|
@@ -167,6 +189,11 @@ async function main() {
|
|
|
167
189
|
// Apply top filter
|
|
168
190
|
const markets = args.top ? allMarkets.slice(0, args.top) : allMarkets;
|
|
169
191
|
|
|
192
|
+
if (args.json) {
|
|
193
|
+
console.log(JSON.stringify(markets, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
170
197
|
// Group by type for display
|
|
171
198
|
const perps = markets.filter((m) => m.type === 'perp');
|
|
172
199
|
const hip3 = markets.filter((m) => m.type === 'hip3');
|
|
@@ -183,11 +210,11 @@ async function main() {
|
|
|
183
210
|
// Print perps
|
|
184
211
|
if (perps.length > 0) {
|
|
185
212
|
console.log('=== Main Perpetuals ===\n');
|
|
186
|
-
console.log('Coin Price 24h Volume Funding (Ann.) Leverage');
|
|
187
|
-
console.log('-'.repeat(
|
|
213
|
+
console.log('Coin AssetID Price 24h Volume Funding (Ann.) Leverage');
|
|
214
|
+
console.log('-'.repeat(87));
|
|
188
215
|
for (const m of perps) {
|
|
189
216
|
console.log(
|
|
190
|
-
`${m.coin.padEnd(14)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)} ${(m.maxLeverage ? `${m.maxLeverage}x` : '-').padStart(9)}`
|
|
217
|
+
`${m.coin.padEnd(14)} ${String(m.assetId).padStart(7)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)} ${(m.maxLeverage ? `${m.maxLeverage}x` : '-').padStart(9)}`
|
|
191
218
|
);
|
|
192
219
|
}
|
|
193
220
|
console.log();
|
|
@@ -196,11 +223,11 @@ async function main() {
|
|
|
196
223
|
// Print HIP-3 markets
|
|
197
224
|
if (hip3.length > 0) {
|
|
198
225
|
console.log('=== HIP-3 Perpetuals ===\n');
|
|
199
|
-
console.log('Coin Provider Price 24h Volume Funding (Ann.)');
|
|
200
|
-
console.log('-'.repeat(
|
|
226
|
+
console.log('Coin Provider AssetID Price 24h Volume Funding (Ann.)');
|
|
227
|
+
console.log('-'.repeat(92));
|
|
201
228
|
for (const m of hip3) {
|
|
202
229
|
console.log(
|
|
203
|
-
`${m.coin.padEnd(14)} ${m.provider.padEnd(16)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)}`
|
|
230
|
+
`${m.coin.padEnd(14)} ${m.provider.padEnd(16)} ${String(m.assetId).padStart(7)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)}`
|
|
204
231
|
);
|
|
205
232
|
}
|
|
206
233
|
console.log();
|
|
@@ -209,11 +236,11 @@ async function main() {
|
|
|
209
236
|
// Print spot markets
|
|
210
237
|
if (spots.length > 0) {
|
|
211
238
|
console.log('=== Spot Markets ===\n');
|
|
212
|
-
console.log('Pair Price 24h Volume');
|
|
213
|
-
console.log('-'.repeat(
|
|
239
|
+
console.log('Pair AssetID Price 24h Volume');
|
|
240
|
+
console.log('-'.repeat(62));
|
|
214
241
|
for (const m of spots) {
|
|
215
242
|
console.log(
|
|
216
|
-
`${m.coin.padEnd(14)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)}`
|
|
243
|
+
`${m.coin.padEnd(14)} ${String(m.assetId).padStart(7)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)}`
|
|
217
244
|
);
|
|
218
245
|
}
|
|
219
246
|
console.log();
|
package/scripts/info/candles.ts
CHANGED
|
@@ -21,6 +21,7 @@ Options:
|
|
|
21
21
|
--interval <interval> Candle interval (default: 1h)
|
|
22
22
|
Valid: ${VALID_INTERVALS.join(', ')}
|
|
23
23
|
--bars <n> Number of bars to fetch (default: 24)
|
|
24
|
+
--json Output as JSON (machine-readable)
|
|
24
25
|
--help, -h Show this help
|
|
25
26
|
|
|
26
27
|
Examples:
|
|
@@ -59,10 +60,13 @@ async function main() {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
const bars = parseInt(args.bars as string) || 24;
|
|
63
|
+
const jsonOutput = args.json as boolean;
|
|
62
64
|
const client = getClient();
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
if (!jsonOutput) {
|
|
67
|
+
console.log(`Open Broker - ${normalizeCoin(coin)} Candles (${interval})`);
|
|
68
|
+
console.log('='.repeat(40) + '\n');
|
|
69
|
+
}
|
|
66
70
|
|
|
67
71
|
try {
|
|
68
72
|
// Load metadata (needed for HIP-3 coin resolution)
|
|
@@ -75,6 +79,13 @@ async function main() {
|
|
|
75
79
|
const startTime = now - (bars * (INTERVAL_MS[interval] || 3_600_000));
|
|
76
80
|
const candles = await client.getCandleSnapshot(normalizeCoin(coin), interval, startTime);
|
|
77
81
|
|
|
82
|
+
if (jsonOutput) {
|
|
83
|
+
let assetId = -1;
|
|
84
|
+
try { assetId = client.getAssetIndex(normalizeCoin(coin)); } catch { /* noop */ }
|
|
85
|
+
console.log(JSON.stringify({ coin: normalizeCoin(coin), assetId, interval, candles }, null, 2));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
if (candles.length === 0) {
|
|
79
90
|
console.log('No candle data found');
|
|
80
91
|
return;
|
package/scripts/info/fees.ts
CHANGED
|
@@ -9,10 +9,13 @@ function printUsage() {
|
|
|
9
9
|
Usage: openbroker fees [options]
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
-
--
|
|
12
|
+
--address <0x...> Look up another account's fees
|
|
13
|
+
--json Output as JSON (machine-readable)
|
|
14
|
+
--help, -h Show this help
|
|
13
15
|
|
|
14
16
|
Examples:
|
|
15
17
|
openbroker fees
|
|
18
|
+
openbroker fees --json
|
|
16
19
|
`);
|
|
17
20
|
}
|
|
18
21
|
|
|
@@ -25,19 +28,27 @@ async function main() {
|
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
const targetAddress = args.address as string | undefined;
|
|
31
|
+
const jsonOutput = args.json as boolean;
|
|
28
32
|
const client = getClient();
|
|
29
33
|
const lookupAddress = targetAddress?.toLowerCase();
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
if (!jsonOutput) {
|
|
36
|
+
console.log('Open Broker - Fee Schedule');
|
|
37
|
+
console.log('=========================\n');
|
|
33
38
|
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
if (targetAddress) {
|
|
40
|
+
console.log(`Lookup: ${lookupAddress}\n`);
|
|
41
|
+
}
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
try {
|
|
39
45
|
const fees = await client.getUserFees(lookupAddress);
|
|
40
46
|
|
|
47
|
+
if (jsonOutput) {
|
|
48
|
+
console.log(JSON.stringify(fees, null, 2));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
// Fee rates
|
|
42
53
|
console.log('Fee Rates');
|
|
43
54
|
console.log('─'.repeat(40));
|
|
@@ -11,6 +11,7 @@ Usage: openbroker funding-history --coin <symbol> [options]
|
|
|
11
11
|
Options:
|
|
12
12
|
--coin <symbol> Asset symbol (required, e.g. ETH, BTC)
|
|
13
13
|
--hours <n> Hours of history to fetch (default: 24)
|
|
14
|
+
--json Output as JSON (machine-readable)
|
|
14
15
|
--help, -h Show this help
|
|
15
16
|
|
|
16
17
|
Examples:
|
|
@@ -35,10 +36,13 @@ async function main() {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const hours = parseInt(args.hours as string) || 24;
|
|
39
|
+
const jsonOutput = args.json as boolean;
|
|
38
40
|
const client = getClient();
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
if (!jsonOutput) {
|
|
43
|
+
console.log(`Open Broker - ${normalizeCoin(coin)} Funding History (${hours}h)`);
|
|
44
|
+
console.log('='.repeat(40) + '\n');
|
|
45
|
+
}
|
|
42
46
|
|
|
43
47
|
try {
|
|
44
48
|
// Load metadata (needed for HIP-3 coin resolution)
|
|
@@ -51,6 +55,13 @@ async function main() {
|
|
|
51
55
|
const startTime = now - (hours * 3_600_000);
|
|
52
56
|
const history = await client.getFundingHistory(normalizeCoin(coin), startTime);
|
|
53
57
|
|
|
58
|
+
if (jsonOutput) {
|
|
59
|
+
let assetId = -1;
|
|
60
|
+
try { assetId = client.getAssetIndex(normalizeCoin(coin)); } catch { /* noop */ }
|
|
61
|
+
console.log(JSON.stringify({ coin: normalizeCoin(coin), assetId, history }, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
54
65
|
if (history.length === 0) {
|
|
55
66
|
console.log('No funding history found');
|
|
56
67
|
return;
|