openbroker 1.0.80 → 1.0.85
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/bin/cli.ts +22 -1
- package/bin/openbroker.js +4 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/auto/cli.ts +10 -1
- package/scripts/auto/dashboard-forwarder.ts +77 -0
- package/scripts/auto/loader.ts +41 -5
- package/scripts/auto/runtime.ts +18 -6
- package/scripts/core/client.ts +603 -214
- package/scripts/core/config.ts +20 -1
- package/scripts/core/types.ts +1 -0
- package/scripts/core/utils.ts +4 -1
- package/scripts/info/account.ts +12 -8
- package/scripts/info/all-markets.ts +53 -23
- package/scripts/info/candles.ts +16 -2
- package/scripts/info/fees.ts +16 -5
- package/scripts/info/funding-history.ts +16 -2
- package/scripts/info/funding-scan.ts +10 -0
- package/scripts/info/funding.ts +13 -0
- package/scripts/info/markets.ts +12 -0
- package/scripts/info/order-status.ts +11 -2
- package/scripts/info/orders.ts +5 -0
- package/scripts/info/rate-limit.ts +11 -2
- package/scripts/info/search-markets.ts +37 -9
- package/scripts/info/spot.ts +25 -5
- package/scripts/info/trades.ts +16 -2
- package/scripts/setup/onboard.ts +95 -44
package/scripts/core/config.ts
CHANGED
|
@@ -42,6 +42,20 @@ function loadEnvFile(): string | null {
|
|
|
42
42
|
const verbose = process.env.VERBOSE === '1' || process.env.VERBOSE === 'true';
|
|
43
43
|
process.env.DOTENV_CONFIG_QUIET = 'true';
|
|
44
44
|
|
|
45
|
+
// Explicit config path via -c / --config (highest priority — overrides shell env vars)
|
|
46
|
+
const explicitPath = process.env.OPENBROKER_CONFIG;
|
|
47
|
+
if (explicitPath) {
|
|
48
|
+
if (existsSync(explicitPath)) {
|
|
49
|
+
loadDotenv({ path: explicitPath, override: true });
|
|
50
|
+
if (verbose) {
|
|
51
|
+
console.log(`[DEBUG] Loaded config from -c flag: ${explicitPath}`);
|
|
52
|
+
}
|
|
53
|
+
return explicitPath;
|
|
54
|
+
}
|
|
55
|
+
console.error(`Config file not found: ${explicitPath}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
// Check locations in order of priority
|
|
46
60
|
const locations = [
|
|
47
61
|
{ path: LOCAL_ENV_PATH, name: 'local (.env)' },
|
|
@@ -98,7 +112,7 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
98
112
|
// Show warning once
|
|
99
113
|
if (!readOnlyWarningShown) {
|
|
100
114
|
readOnlyWarningShown = true;
|
|
101
|
-
console.
|
|
115
|
+
console.error('\x1b[33m⚠️ Not configured for trading. Run "openbroker setup" to enable trades.\x1b[0m\n');
|
|
102
116
|
}
|
|
103
117
|
}
|
|
104
118
|
|
|
@@ -124,6 +138,10 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
124
138
|
// Determine if this is an API wallet setup
|
|
125
139
|
const isApiWallet = accountAddress !== undefined && accountAddress !== walletAddress;
|
|
126
140
|
|
|
141
|
+
// Vault address — only for vault trading (ERC4626 contracts / native vaults via CoreWriter).
|
|
142
|
+
// Standard API wallets (approved via approveAgent) do NOT need this.
|
|
143
|
+
const vaultAddress = process.env.HYPERLIQUID_VAULT_ADDRESS?.toLowerCase();
|
|
144
|
+
|
|
127
145
|
return {
|
|
128
146
|
baseUrl,
|
|
129
147
|
privateKey: privateKey as `0x${string}`,
|
|
@@ -134,6 +152,7 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
134
152
|
builderAddress,
|
|
135
153
|
builderFee,
|
|
136
154
|
slippageBps,
|
|
155
|
+
vaultAddress,
|
|
137
156
|
};
|
|
138
157
|
}
|
|
139
158
|
|
package/scripts/core/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface OpenBrokerConfig {
|
|
|
12
12
|
builderAddress: string;
|
|
13
13
|
builderFee: number; // tenths of bps (10 = 1 bps)
|
|
14
14
|
slippageBps: number;
|
|
15
|
+
vaultAddress?: string; // Explicit vault address for vault trading (ERC4626 / native vaults via CoreWriter)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// ============ Builder ============
|
package/scripts/core/utils.ts
CHANGED
|
@@ -155,8 +155,11 @@ export function generateCloid(): string {
|
|
|
155
155
|
* Returns true if approved, false if not
|
|
156
156
|
*/
|
|
157
157
|
export async function checkBuilderFeeApproval(
|
|
158
|
-
client: { getMaxBuilderFee: () => Promise<string | null>; builderAddress: string }
|
|
158
|
+
client: { getMaxBuilderFee: () => Promise<string | null>; builderAddress: string; isTestnet: boolean }
|
|
159
159
|
): Promise<boolean> {
|
|
160
|
+
// Skip builder fee check on testnet — builder may not have balance
|
|
161
|
+
if (client.isTestnet) return true;
|
|
162
|
+
|
|
160
163
|
const approval = await client.getMaxBuilderFee();
|
|
161
164
|
if (!approval) {
|
|
162
165
|
console.log('⚠️ Builder fee not approved!');
|
package/scripts/info/account.ts
CHANGED
|
@@ -123,15 +123,19 @@ async function main() {
|
|
|
123
123
|
console.log(`Account Mode: ${modeLabel[accountMode] ?? accountMode}`);
|
|
124
124
|
|
|
125
125
|
if (!isOtherAccount) {
|
|
126
|
-
// Check builder fee approval
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.log(`Builder Fee: ${client.builderFeeBps} bps`);
|
|
130
|
-
if (builderApproval) {
|
|
131
|
-
console.log(`Builder Approved: ✅ Yes (max: ${builderApproval})`);
|
|
126
|
+
// Check builder fee approval (skip on testnet)
|
|
127
|
+
if (client.isTestnet) {
|
|
128
|
+
console.log(`Builder Fee: skipped (testnet)`);
|
|
132
129
|
} else {
|
|
133
|
-
|
|
134
|
-
console.log(
|
|
130
|
+
const builderApproval = await client.getMaxBuilderFee();
|
|
131
|
+
console.log(`Builder Address: ${client.builderAddress}`);
|
|
132
|
+
console.log(`Builder Fee: ${client.builderFeeBps} bps`);
|
|
133
|
+
if (builderApproval) {
|
|
134
|
+
console.log(`Builder Approved: ✅ Yes (max: ${builderApproval})`);
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`Builder Approved: ❌ No`);
|
|
137
|
+
console.log(`\n⚠️ Run: npx tsx scripts/setup/approve-builder.ts`);
|
|
138
|
+
}
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
// Warn if API wallet setup looks misconfigured
|
|
@@ -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,6 +119,9 @@ async function main() {
|
|
|
108
119
|
|
|
109
120
|
// Fetch HIP-3 perps
|
|
110
121
|
if (args.type === 'all' || args.type === 'hip3') {
|
|
122
|
+
if (client.isTestnet && !args.json) {
|
|
123
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use "dexName:COIN" syntax to load a specific dex.\n');
|
|
124
|
+
}
|
|
111
125
|
try {
|
|
112
126
|
const allPerpMetas = await client.getAllPerpMetas();
|
|
113
127
|
// Skip index 0 (main dex), process HIP-3 dexs
|
|
@@ -120,10 +134,13 @@ async function main() {
|
|
|
120
134
|
const ctx = dexData.assetCtxs[i];
|
|
121
135
|
if (!asset || !ctx) continue;
|
|
122
136
|
|
|
137
|
+
let assetId = -1;
|
|
138
|
+
try { assetId = client.getAssetIndex(asset.name); } catch { /* not registered */ }
|
|
123
139
|
allMarkets.push({
|
|
124
140
|
type: 'hip3',
|
|
125
141
|
provider: dexData.dexName || `HIP-3 DEX ${dexIdx}`,
|
|
126
142
|
coin: asset.name,
|
|
143
|
+
assetId,
|
|
127
144
|
price: ctx.markPx,
|
|
128
145
|
volume24h: parseFloat(ctx.dayNtlVlm || '0'),
|
|
129
146
|
funding: ctx.funding,
|
|
@@ -140,15 +157,23 @@ async function main() {
|
|
|
140
157
|
if (args.type === 'all' || args.type === 'spot') {
|
|
141
158
|
try {
|
|
142
159
|
const spotData = await client.getSpotMetaAndAssetCtxs();
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
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;
|
|
147
171
|
|
|
148
172
|
allMarkets.push({
|
|
149
173
|
type: 'spot',
|
|
150
174
|
provider: 'Spot',
|
|
151
175
|
coin: pair.name,
|
|
176
|
+
assetId: 10000 + pair.index,
|
|
152
177
|
price: ctx.markPx,
|
|
153
178
|
volume24h: parseFloat(ctx.dayNtlVlm || '0'),
|
|
154
179
|
});
|
|
@@ -164,6 +189,11 @@ async function main() {
|
|
|
164
189
|
// Apply top filter
|
|
165
190
|
const markets = args.top ? allMarkets.slice(0, args.top) : allMarkets;
|
|
166
191
|
|
|
192
|
+
if (args.json) {
|
|
193
|
+
console.log(JSON.stringify(markets, null, 2));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
167
197
|
// Group by type for display
|
|
168
198
|
const perps = markets.filter((m) => m.type === 'perp');
|
|
169
199
|
const hip3 = markets.filter((m) => m.type === 'hip3');
|
|
@@ -180,11 +210,11 @@ async function main() {
|
|
|
180
210
|
// Print perps
|
|
181
211
|
if (perps.length > 0) {
|
|
182
212
|
console.log('=== Main Perpetuals ===\n');
|
|
183
|
-
console.log('Coin Price 24h Volume Funding (Ann.) Leverage');
|
|
184
|
-
console.log('-'.repeat(
|
|
213
|
+
console.log('Coin AssetID Price 24h Volume Funding (Ann.) Leverage');
|
|
214
|
+
console.log('-'.repeat(87));
|
|
185
215
|
for (const m of perps) {
|
|
186
216
|
console.log(
|
|
187
|
-
`${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)}`
|
|
188
218
|
);
|
|
189
219
|
}
|
|
190
220
|
console.log();
|
|
@@ -193,11 +223,11 @@ async function main() {
|
|
|
193
223
|
// Print HIP-3 markets
|
|
194
224
|
if (hip3.length > 0) {
|
|
195
225
|
console.log('=== HIP-3 Perpetuals ===\n');
|
|
196
|
-
console.log('Coin Provider Price 24h Volume Funding (Ann.)');
|
|
197
|
-
console.log('-'.repeat(
|
|
226
|
+
console.log('Coin Provider AssetID Price 24h Volume Funding (Ann.)');
|
|
227
|
+
console.log('-'.repeat(92));
|
|
198
228
|
for (const m of hip3) {
|
|
199
229
|
console.log(
|
|
200
|
-
`${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)}`
|
|
201
231
|
);
|
|
202
232
|
}
|
|
203
233
|
console.log();
|
|
@@ -206,11 +236,11 @@ async function main() {
|
|
|
206
236
|
// Print spot markets
|
|
207
237
|
if (spots.length > 0) {
|
|
208
238
|
console.log('=== Spot Markets ===\n');
|
|
209
|
-
console.log('Pair Price 24h Volume');
|
|
210
|
-
console.log('-'.repeat(
|
|
239
|
+
console.log('Pair AssetID Price 24h Volume');
|
|
240
|
+
console.log('-'.repeat(62));
|
|
211
241
|
for (const m of spots) {
|
|
212
242
|
console.log(
|
|
213
|
-
`${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)}`
|
|
214
244
|
);
|
|
215
245
|
}
|
|
216
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,19 +60,32 @@ 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)
|
|
69
73
|
await client.getMetaAndAssetCtxs();
|
|
74
|
+
if (client.isTestnet && coin.includes(':')) {
|
|
75
|
+
await client.loadSingleHip3Dex(coin.split(':')[0]);
|
|
76
|
+
}
|
|
70
77
|
|
|
71
78
|
const now = Date.now();
|
|
72
79
|
const startTime = now - (bars * (INTERVAL_MS[interval] || 3_600_000));
|
|
73
80
|
const candles = await client.getCandleSnapshot(normalizeCoin(coin), interval, startTime);
|
|
74
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
|
+
|
|
75
89
|
if (candles.length === 0) {
|
|
76
90
|
console.log('No candle data found');
|
|
77
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,19 +36,32 @@ 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)
|
|
45
49
|
await client.getMetaAndAssetCtxs();
|
|
50
|
+
if (client.isTestnet && coin.includes(':')) {
|
|
51
|
+
await client.loadSingleHip3Dex(coin.split(':')[0]);
|
|
52
|
+
}
|
|
46
53
|
|
|
47
54
|
const now = Date.now();
|
|
48
55
|
const startTime = now - (hours * 3_600_000);
|
|
49
56
|
const history = await client.getFundingHistory(normalizeCoin(coin), startTime);
|
|
50
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
|
+
|
|
51
65
|
if (history.length === 0) {
|
|
52
66
|
console.log('No funding history found');
|
|
53
67
|
return;
|
|
@@ -32,6 +32,7 @@ Examples:
|
|
|
32
32
|
|
|
33
33
|
interface FundingScanResult {
|
|
34
34
|
coin: string;
|
|
35
|
+
assetId: number;
|
|
35
36
|
dex: string;
|
|
36
37
|
hourlyRate: number;
|
|
37
38
|
annualizedPct: number;
|
|
@@ -69,9 +70,14 @@ async function scanFunding(client: ReturnType<typeof getClient>, options: {
|
|
|
69
70
|
|
|
70
71
|
// API returns HIP-3 names already prefixed (e.g., "xyz:CL")
|
|
71
72
|
const coin = asset.name;
|
|
73
|
+
let assetId = isMain ? i : -1;
|
|
74
|
+
if (!isMain) {
|
|
75
|
+
try { assetId = client.getAssetIndex(coin); } catch { /* not registered */ }
|
|
76
|
+
}
|
|
72
77
|
|
|
73
78
|
results.push({
|
|
74
79
|
coin,
|
|
80
|
+
assetId,
|
|
75
81
|
dex: dexData.dexName ?? 'main',
|
|
76
82
|
hourlyRate,
|
|
77
83
|
annualizedPct,
|
|
@@ -184,6 +190,10 @@ async function main() {
|
|
|
184
190
|
console.log('==================================\n');
|
|
185
191
|
console.log(`Threshold: ${threshold}% annualized | Scope: ${mainOnly ? 'main only' : hip3Only ? 'HIP-3 only' : 'all dexes'}\n`);
|
|
186
192
|
|
|
193
|
+
if (client.isTestnet && !mainOnly) {
|
|
194
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Only main perps will be scanned.\n');
|
|
195
|
+
}
|
|
196
|
+
|
|
187
197
|
const options = { threshold, mainOnly, hip3Only, topN };
|
|
188
198
|
|
|
189
199
|
const runScan = async () => {
|
package/scripts/info/funding.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { formatPercent, annualizeFundingRate, parseArgs } from '../core/utils.js
|
|
|
6
6
|
|
|
7
7
|
interface FundingDisplay {
|
|
8
8
|
coin: string;
|
|
9
|
+
assetId: number;
|
|
9
10
|
hourlyRate: number;
|
|
10
11
|
annualizedRate: number;
|
|
11
12
|
premium: number;
|
|
@@ -47,6 +48,7 @@ async function main() {
|
|
|
47
48
|
|
|
48
49
|
fundingData.push({
|
|
49
50
|
coin: asset.name,
|
|
51
|
+
assetId: i,
|
|
50
52
|
hourlyRate,
|
|
51
53
|
annualizedRate,
|
|
52
54
|
premium,
|
|
@@ -56,7 +58,14 @@ async function main() {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
// Include HIP-3 dex assets
|
|
61
|
+
if (client.isTestnet && (includeHip3 || showAll) && !(filterCoin?.includes(':'))) {
|
|
62
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use --coin dexName:COIN to view a specific HIP-3 asset.\n');
|
|
63
|
+
}
|
|
59
64
|
if (includeHip3 || showAll || (filterCoin && filterCoin.includes(':'))) {
|
|
65
|
+
// On testnet, load the specific dex on demand if user specified dex:COIN
|
|
66
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
67
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
68
|
+
}
|
|
60
69
|
const allPerps = await client.getAllPerpMetas();
|
|
61
70
|
for (const dexData of allPerps) {
|
|
62
71
|
if (!dexData.dexName) continue; // Skip main dex (already loaded)
|
|
@@ -77,8 +86,12 @@ async function main() {
|
|
|
77
86
|
|
|
78
87
|
if (!showAll && openInterest < 1000) continue;
|
|
79
88
|
|
|
89
|
+
let assetId = -1;
|
|
90
|
+
try { assetId = client.getAssetIndex(coinName); } catch { /* not registered */ }
|
|
91
|
+
|
|
80
92
|
fundingData.push({
|
|
81
93
|
coin: coinName,
|
|
94
|
+
assetId,
|
|
82
95
|
hourlyRate,
|
|
83
96
|
annualizedRate,
|
|
84
97
|
premium: 0,
|
package/scripts/info/markets.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { formatUsd, parseArgs } from '../core/utils.js';
|
|
|
6
6
|
|
|
7
7
|
interface MarketDisplay {
|
|
8
8
|
coin: string;
|
|
9
|
+
assetId: number;
|
|
9
10
|
markPx: number;
|
|
10
11
|
oraclePx: number;
|
|
11
12
|
prevDayPx: number;
|
|
@@ -47,6 +48,7 @@ async function main() {
|
|
|
47
48
|
|
|
48
49
|
markets.push({
|
|
49
50
|
coin: asset.name,
|
|
51
|
+
assetId: i,
|
|
50
52
|
markPx,
|
|
51
53
|
oraclePx,
|
|
52
54
|
prevDayPx,
|
|
@@ -59,7 +61,14 @@ async function main() {
|
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
// Include HIP-3 markets
|
|
64
|
+
if (client.isTestnet && includeHip3 && !(filterCoin?.includes(':'))) {
|
|
65
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use --coin dexName:COIN to view a specific HIP-3 asset.\n');
|
|
66
|
+
}
|
|
62
67
|
if (includeHip3 || (filterCoin && filterCoin.includes(':'))) {
|
|
68
|
+
// On testnet, load the specific dex on demand if user specified dex:COIN
|
|
69
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
70
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
71
|
+
}
|
|
63
72
|
const allPerps = await client.getAllPerpMetas();
|
|
64
73
|
for (const dexData of allPerps) {
|
|
65
74
|
if (!dexData.dexName) continue;
|
|
@@ -79,9 +88,12 @@ async function main() {
|
|
|
79
88
|
const volume24h = parseFloat(ctx.dayNtlVlm);
|
|
80
89
|
const openInterest = parseFloat(ctx.openInterest);
|
|
81
90
|
const change24h = prevDayPx > 0 ? (markPx - prevDayPx) / prevDayPx : 0;
|
|
91
|
+
let assetId = -1;
|
|
92
|
+
try { assetId = client.getAssetIndex(coinName); } catch { /* not registered */ }
|
|
82
93
|
|
|
83
94
|
markets.push({
|
|
84
95
|
coin: coinName,
|
|
96
|
+
assetId,
|
|
85
97
|
markPx,
|
|
86
98
|
oraclePx,
|
|
87
99
|
prevDayPx,
|
|
@@ -11,6 +11,7 @@ Usage: openbroker order-status --oid <order-id>
|
|
|
11
11
|
Options:
|
|
12
12
|
--oid <id> Order ID (number) or client order ID (hex string) — required
|
|
13
13
|
--address <0x...> Look up order on another account
|
|
14
|
+
--json Output as JSON (machine-readable)
|
|
14
15
|
--help, -h Show this help
|
|
15
16
|
|
|
16
17
|
Examples:
|
|
@@ -37,15 +38,23 @@ async function main() {
|
|
|
37
38
|
|
|
38
39
|
const oid = oidArg.startsWith('0x') ? oidArg : parseInt(oidArg);
|
|
39
40
|
const targetAddress = args.address as string | undefined;
|
|
41
|
+
const jsonOutput = args.json as boolean;
|
|
40
42
|
const client = getClient();
|
|
41
43
|
const lookupAddress = targetAddress?.toLowerCase();
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
if (!jsonOutput) {
|
|
46
|
+
console.log('Open Broker - Order Status');
|
|
47
|
+
console.log('=========================\n');
|
|
48
|
+
}
|
|
45
49
|
|
|
46
50
|
try {
|
|
47
51
|
const result = await client.getOrderStatus(oid, lookupAddress);
|
|
48
52
|
|
|
53
|
+
if (jsonOutput) {
|
|
54
|
+
console.log(JSON.stringify(result, null, 2));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
if (result.status === 'unknownOid') {
|
|
50
59
|
console.log(`Order ${oidArg} not found`);
|
|
51
60
|
return;
|
package/scripts/info/orders.ts
CHANGED
|
@@ -45,6 +45,11 @@ async function main() {
|
|
|
45
45
|
const lookupAddress = targetAddress?.toLowerCase();
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
|
+
// On testnet, load specific HIP-3 dex on demand if filtering by dex:COIN
|
|
49
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
50
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
if (openOnly) {
|
|
49
54
|
// Use the dedicated open orders endpoint
|
|
50
55
|
let openOrders = await client.getOpenOrders(lookupAddress);
|
|
@@ -9,6 +9,7 @@ function printUsage() {
|
|
|
9
9
|
Usage: openbroker rate-limit [options]
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
+
--json Output as JSON (machine-readable)
|
|
12
13
|
--help, -h Show this help
|
|
13
14
|
|
|
14
15
|
Examples:
|
|
@@ -24,14 +25,22 @@ async function main() {
|
|
|
24
25
|
process.exit(0);
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
const jsonOutput = args.json as boolean;
|
|
27
29
|
const client = getClient();
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
if (!jsonOutput) {
|
|
32
|
+
console.log('Open Broker - API Rate Limit');
|
|
33
|
+
console.log('===========================\n');
|
|
34
|
+
}
|
|
31
35
|
|
|
32
36
|
try {
|
|
33
37
|
const rl = await client.getUserRateLimit();
|
|
34
38
|
|
|
39
|
+
if (jsonOutput) {
|
|
40
|
+
console.log(JSON.stringify(rl, null, 2));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
const used = rl.nRequestsUsed;
|
|
36
45
|
const cap = rl.nRequestsCap;
|
|
37
46
|
const surplus = rl.nRequestsSurplus;
|