perp-cli 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/__tests__/alert-logic.test.d.ts +1 -0
- package/dist/__tests__/alert-logic.test.js +107 -0
- package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
- package/dist/__tests__/arb-auto-3dex.test.js +397 -0
- package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
- package/dist/__tests__/arb-history-stats.test.js +176 -0
- package/dist/__tests__/arb-logic.test.d.ts +1 -0
- package/dist/__tests__/arb-logic.test.js +84 -0
- package/dist/__tests__/arb-manage.test.d.ts +1 -0
- package/dist/__tests__/arb-manage.test.js +253 -0
- package/dist/__tests__/arb-new-features.test.d.ts +1 -0
- package/dist/__tests__/arb-new-features.test.js +457 -0
- package/dist/__tests__/arb-sizing.test.d.ts +1 -0
- package/dist/__tests__/arb-sizing.test.js +48 -0
- package/dist/__tests__/arb-state.test.d.ts +1 -0
- package/dist/__tests__/arb-state.test.js +284 -0
- package/dist/__tests__/arb-userflow.test.d.ts +1 -0
- package/dist/__tests__/arb-userflow.test.js +945 -0
- package/dist/__tests__/arb-utils.test.d.ts +1 -0
- package/dist/__tests__/arb-utils.test.js +264 -0
- package/dist/__tests__/bot-conditions.test.d.ts +1 -0
- package/dist/__tests__/bot-conditions.test.js +341 -0
- package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
- package/dist/__tests__/client-id-tracker.test.js +137 -0
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
- package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
- package/dist/__tests__/commands/order-intent.test.js +600 -0
- package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/trade-commands.test.js +821 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
- package/dist/__tests__/cross-chain-margin.test.js +287 -0
- package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
- package/dist/__tests__/dex-asset-map.test.js +191 -0
- package/dist/__tests__/errors.test.d.ts +1 -0
- package/dist/__tests__/errors.test.js +110 -0
- package/dist/__tests__/event-stream.test.d.ts +1 -0
- package/dist/__tests__/event-stream.test.js +276 -0
- package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
- package/dist/__tests__/exchanges/interface.test.js +132 -0
- package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
- package/dist/__tests__/exchanges/mock-adapter.js +137 -0
- package/dist/__tests__/execution-log.test.d.ts +1 -0
- package/dist/__tests__/execution-log.test.js +106 -0
- package/dist/__tests__/funding-calc.test.d.ts +1 -0
- package/dist/__tests__/funding-calc.test.js +71 -0
- package/dist/__tests__/funding-history.test.d.ts +1 -0
- package/dist/__tests__/funding-history.test.js +343 -0
- package/dist/__tests__/funding-rates.test.d.ts +1 -0
- package/dist/__tests__/funding-rates.test.js +342 -0
- package/dist/__tests__/funding.test.d.ts +1 -0
- package/dist/__tests__/funding.test.js +173 -0
- package/dist/__tests__/gap-logic.test.d.ts +1 -0
- package/dist/__tests__/gap-logic.test.js +43 -0
- package/dist/__tests__/hip3-dex.test.d.ts +1 -0
- package/dist/__tests__/hip3-dex.test.js +234 -0
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
- package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge.integration.test.js +309 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
- package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/lighter.integration.test.js +53 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
- package/dist/__tests__/liquidity.test.d.ts +1 -0
- package/dist/__tests__/liquidity.test.js +225 -0
- package/dist/__tests__/plan-executor.test.d.ts +1 -0
- package/dist/__tests__/plan-executor.test.js +314 -0
- package/dist/__tests__/position-history.test.d.ts +1 -0
- package/dist/__tests__/position-history.test.js +367 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +310 -0
- package/dist/__tests__/risk-assessment.test.d.ts +1 -0
- package/dist/__tests__/risk-assessment.test.js +145 -0
- package/dist/__tests__/security-adversarial.test.d.ts +1 -0
- package/dist/__tests__/security-adversarial.test.js +574 -0
- package/dist/__tests__/strategies.test.d.ts +1 -0
- package/dist/__tests__/strategies.test.js +539 -0
- package/dist/__tests__/trade-execution.test.d.ts +1 -0
- package/dist/__tests__/trade-execution.test.js +129 -0
- package/dist/__tests__/trade-validator.test.d.ts +1 -0
- package/dist/__tests__/trade-validator.test.js +655 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +76 -0
- package/dist/api/public/hyperliquid.d.ts +18 -0
- package/dist/api/public/hyperliquid.js +82 -0
- package/dist/api/public/index.d.ts +8 -0
- package/dist/api/public/index.js +8 -0
- package/dist/api/public/lighter.d.ts +24 -0
- package/dist/api/public/lighter.js +100 -0
- package/dist/api/public/pacifica.d.ts +17 -0
- package/dist/api/public/pacifica.js +54 -0
- package/dist/api/public/urls.d.ts +12 -0
- package/dist/api/public/urls.js +33 -0
- package/dist/arb/history-stats.d.ts +44 -0
- package/dist/arb/history-stats.js +135 -0
- package/dist/arb/index.d.ts +4 -0
- package/dist/arb/index.js +4 -0
- package/dist/arb/sizing.d.ts +23 -0
- package/dist/arb/sizing.js +96 -0
- package/dist/arb/state.d.ts +51 -0
- package/dist/arb/state.js +112 -0
- package/dist/arb/utils.d.ts +81 -0
- package/dist/arb/utils.js +267 -0
- package/dist/arb-history-stats.d.ts +5 -0
- package/dist/arb-history-stats.js +5 -0
- package/dist/arb-sizing.d.ts +5 -0
- package/dist/arb-sizing.js +5 -0
- package/dist/arb-state.d.ts +5 -0
- package/dist/arb-state.js +5 -0
- package/dist/arb-utils.d.ts +5 -0
- package/dist/arb-utils.js +5 -0
- package/dist/bot/conditions.d.ts +32 -0
- package/dist/bot/conditions.js +141 -0
- package/dist/bot/config.d.ts +76 -0
- package/dist/bot/config.js +160 -0
- package/dist/bot/engine.d.ts +8 -0
- package/dist/bot/engine.js +519 -0
- package/dist/bot/presets.d.ts +11 -0
- package/dist/bot/presets.js +296 -0
- package/dist/bridge-engine.d.ts +133 -0
- package/dist/bridge-engine.js +1487 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +99 -0
- package/dist/cli-spec.d.ts +50 -0
- package/dist/cli-spec.js +75 -0
- package/dist/client-id-tracker.d.ts +25 -0
- package/dist/client-id-tracker.js +76 -0
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +425 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +386 -0
- package/dist/commands/alert.d.ts +2 -0
- package/dist/commands/alert.js +421 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +311 -0
- package/dist/commands/arb/index.d.ts +3 -0
- package/dist/commands/arb/index.js +921 -0
- package/dist/commands/arb-auto.d.ts +54 -0
- package/dist/commands/arb-auto.js +1328 -0
- package/dist/commands/arb-manage.d.ts +5 -0
- package/dist/commands/arb-manage.js +5 -0
- package/dist/commands/arb.d.ts +2 -0
- package/dist/commands/arb.js +347 -0
- package/dist/commands/backtest.d.ts +2 -0
- package/dist/commands/backtest.js +327 -0
- package/dist/commands/bot.d.ts +3 -0
- package/dist/commands/bot.js +412 -0
- package/dist/commands/bridge.d.ts +2 -0
- package/dist/commands/bridge.js +396 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +176 -0
- package/dist/commands/deposit.d.ts +4 -0
- package/dist/commands/deposit.js +573 -0
- package/dist/commands/dex.d.ts +3 -0
- package/dist/commands/dex.js +114 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/funding.d.ts +2 -0
- package/dist/commands/funding.js +347 -0
- package/dist/commands/gap.d.ts +2 -0
- package/dist/commands/gap.js +305 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +67 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +235 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +266 -0
- package/dist/commands/jobs.d.ts +2 -0
- package/dist/commands/jobs.js +133 -0
- package/dist/commands/manage.d.ts +4 -0
- package/dist/commands/manage.js +309 -0
- package/dist/commands/market.d.ts +3 -0
- package/dist/commands/market.js +225 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +95 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.js +169 -0
- package/dist/commands/rebalance.d.ts +3 -0
- package/dist/commands/rebalance.js +293 -0
- package/dist/commands/risk.d.ts +3 -0
- package/dist/commands/risk.js +169 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +102 -0
- package/dist/commands/stream.d.ts +5 -0
- package/dist/commands/stream.js +123 -0
- package/dist/commands/trade.d.ts +3 -0
- package/dist/commands/trade.js +1273 -0
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +602 -0
- package/dist/commands/withdraw.d.ts +3 -0
- package/dist/commands/withdraw.js +187 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +68 -0
- package/dist/cross-chain-margin.d.ts +46 -0
- package/dist/cross-chain-margin.js +107 -0
- package/dist/dashboard/server.d.ts +80 -0
- package/dist/dashboard/server.js +340 -0
- package/dist/dashboard/ui.d.ts +4 -0
- package/dist/dashboard/ui.js +538 -0
- package/dist/dashboard/ws-feeds.d.ts +29 -0
- package/dist/dashboard/ws-feeds.js +660 -0
- package/dist/dex-asset-map.d.ts +80 -0
- package/dist/dex-asset-map.js +201 -0
- package/dist/errors.d.ts +109 -0
- package/dist/errors.js +84 -0
- package/dist/event-stream.d.ts +25 -0
- package/dist/event-stream.js +168 -0
- package/dist/exchanges/hyperliquid.d.ts +212 -0
- package/dist/exchanges/hyperliquid.js +931 -0
- package/dist/exchanges/interface.d.ts +95 -0
- package/dist/exchanges/interface.js +5 -0
- package/dist/exchanges/lighter.d.ts +159 -0
- package/dist/exchanges/lighter.js +793 -0
- package/dist/exchanges/pacifica.d.ts +51 -0
- package/dist/exchanges/pacifica.js +248 -0
- package/dist/execution-log.d.ts +36 -0
- package/dist/execution-log.js +102 -0
- package/dist/funding/history.d.ts +63 -0
- package/dist/funding/history.js +266 -0
- package/dist/funding/index.d.ts +3 -0
- package/dist/funding/index.js +3 -0
- package/dist/funding/normalize.d.ts +39 -0
- package/dist/funding/normalize.js +66 -0
- package/dist/funding/rates.d.ts +45 -0
- package/dist/funding/rates.js +172 -0
- package/dist/funding-history.d.ts +5 -0
- package/dist/funding-history.js +5 -0
- package/dist/funding-rates.d.ts +5 -0
- package/dist/funding-rates.js +5 -0
- package/dist/funding.d.ts +5 -0
- package/dist/funding.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +458 -0
- package/dist/jobs.d.ts +37 -0
- package/dist/jobs.js +152 -0
- package/dist/liquidity.d.ts +34 -0
- package/dist/liquidity.js +100 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.js +1206 -0
- package/dist/pacifica/client.d.ts +111 -0
- package/dist/pacifica/client.js +310 -0
- package/dist/pacifica/constants.d.ts +27 -0
- package/dist/pacifica/constants.js +47 -0
- package/dist/pacifica/deposit.d.ts +14 -0
- package/dist/pacifica/deposit.js +78 -0
- package/dist/pacifica/index.d.ts +6 -0
- package/dist/pacifica/index.js +11 -0
- package/dist/pacifica/signing.d.ts +49 -0
- package/dist/pacifica/signing.js +97 -0
- package/dist/pacifica/types/account.d.ts +42 -0
- package/dist/pacifica/types/account.js +1 -0
- package/dist/pacifica/types/index.d.ts +6 -0
- package/dist/pacifica/types/index.js +6 -0
- package/dist/pacifica/types/lake.d.ts +18 -0
- package/dist/pacifica/types/lake.js +1 -0
- package/dist/pacifica/types/market.d.ts +64 -0
- package/dist/pacifica/types/market.js +1 -0
- package/dist/pacifica/types/order.d.ts +92 -0
- package/dist/pacifica/types/order.js +1 -0
- package/dist/pacifica/types/position.d.ts +25 -0
- package/dist/pacifica/types/position.js +1 -0
- package/dist/pacifica/types/ws.d.ts +34 -0
- package/dist/pacifica/types/ws.js +41 -0
- package/dist/pacifica/ws-client.d.ts +42 -0
- package/dist/pacifica/ws-client.js +180 -0
- package/dist/plan-executor.d.ts +48 -0
- package/dist/plan-executor.js +280 -0
- package/dist/position-history.d.ts +68 -0
- package/dist/position-history.js +222 -0
- package/dist/rebalance.d.ts +64 -0
- package/dist/rebalance.js +142 -0
- package/dist/retry.d.ts +74 -0
- package/dist/retry.js +129 -0
- package/dist/risk.d.ts +48 -0
- package/dist/risk.js +156 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +45 -0
- package/dist/shared-api.d.ts +5 -0
- package/dist/shared-api.js +5 -0
- package/dist/strategies/dca.d.ts +25 -0
- package/dist/strategies/dca.js +114 -0
- package/dist/strategies/funding-arb.d.ts +15 -0
- package/dist/strategies/funding-arb.js +281 -0
- package/dist/strategies/grid.d.ts +34 -0
- package/dist/strategies/grid.js +185 -0
- package/dist/strategies/trailing-stop.d.ts +17 -0
- package/dist/strategies/trailing-stop.js +121 -0
- package/dist/strategies/twap.d.ts +20 -0
- package/dist/strategies/twap.js +78 -0
- package/dist/trade-validator.d.ts +39 -0
- package/dist/trade-validator.js +154 -0
- package/dist/utils.d.ts +38 -0
- package/dist/utils.js +110 -0
- package/package.json +63 -0
- package/skills/perp-cli/SKILL.md +149 -0
- package/skills/perp-cli/references/commands.md +143 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { printJson, formatUsd, jsonOk } from "../utils.js";
|
|
3
|
+
import { PacificaAdapter } from "../exchanges/pacifica.js";
|
|
4
|
+
import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
|
|
5
|
+
import { logExecution } from "../execution-log.js";
|
|
6
|
+
export function registerWithdrawCommands(program, getAdapter, isJson) {
|
|
7
|
+
const withdraw = program.command("withdraw").description("Withdraw funds from exchange accounts");
|
|
8
|
+
// ── Pacifica ──
|
|
9
|
+
withdraw
|
|
10
|
+
.command("pacifica <amount>")
|
|
11
|
+
.description("Withdraw USDC from Pacifica to your Solana wallet")
|
|
12
|
+
.option("--to <address>", "Destination Solana address (default: your wallet)")
|
|
13
|
+
.action(async (amount, opts) => {
|
|
14
|
+
const amountNum = parseFloat(amount);
|
|
15
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
16
|
+
throw new Error("Invalid amount");
|
|
17
|
+
const adapter = await getAdapter();
|
|
18
|
+
if (!(adapter instanceof PacificaAdapter))
|
|
19
|
+
throw new Error("Requires --exchange pacifica");
|
|
20
|
+
const dest = opts.to || adapter.publicKey;
|
|
21
|
+
if (!isJson()) {
|
|
22
|
+
console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Pacifica...\n`));
|
|
23
|
+
console.log(` Destination: ${dest}`);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const result = await adapter.sdk.withdraw({ amount: String(amountNum), dest_address: dest }, adapter.publicKey, adapter.signer);
|
|
27
|
+
logExecution({
|
|
28
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "withdraw",
|
|
29
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
30
|
+
meta: { action: "withdraw", destination: dest },
|
|
31
|
+
});
|
|
32
|
+
if (isJson())
|
|
33
|
+
return printJson(jsonOk(result));
|
|
34
|
+
console.log(chalk.green(`\n Withdrawal submitted!`));
|
|
35
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
36
|
+
console.log(chalk.gray(`\n Funds arrive in your Solana wallet shortly.\n`));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
logExecution({
|
|
40
|
+
type: "bridge", exchange: "pacifica", symbol: "USDC", side: "withdraw",
|
|
41
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
42
|
+
error: err instanceof Error ? err.message : String(err),
|
|
43
|
+
meta: { action: "withdraw", destination: dest },
|
|
44
|
+
});
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
// ── Hyperliquid ──
|
|
49
|
+
withdraw
|
|
50
|
+
.command("hyperliquid <amount>")
|
|
51
|
+
.description("Withdraw USDC from Hyperliquid to your Arbitrum wallet")
|
|
52
|
+
.option("--to <address>", "Destination EVM address (default: your wallet)")
|
|
53
|
+
.action(async (amount, opts) => {
|
|
54
|
+
const amountNum = parseFloat(amount);
|
|
55
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
56
|
+
throw new Error("Invalid amount");
|
|
57
|
+
const adapter = await getAdapter();
|
|
58
|
+
if (!(adapter instanceof HyperliquidAdapter))
|
|
59
|
+
throw new Error("Requires --exchange hyperliquid");
|
|
60
|
+
const dest = opts.to || adapter.address;
|
|
61
|
+
if (!isJson()) {
|
|
62
|
+
console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Hyperliquid...\n`));
|
|
63
|
+
console.log(` Destination: ${dest}`);
|
|
64
|
+
}
|
|
65
|
+
// Check withdrawable balance
|
|
66
|
+
const balance = await adapter.getBalance();
|
|
67
|
+
const available = Number(balance.available);
|
|
68
|
+
if (amountNum > available) {
|
|
69
|
+
console.error(chalk.red(` Insufficient withdrawable balance. Available: $${formatUsd(available)}`));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const result = await adapter.withdraw(String(amountNum), dest);
|
|
74
|
+
logExecution({
|
|
75
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "withdraw",
|
|
76
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
77
|
+
meta: { action: "withdraw", destination: dest },
|
|
78
|
+
});
|
|
79
|
+
if (isJson())
|
|
80
|
+
return printJson(jsonOk(result));
|
|
81
|
+
console.log(chalk.green(`\n Withdrawal submitted!`));
|
|
82
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
83
|
+
console.log(chalk.gray(`\n Processing may take a few minutes.\n`));
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
logExecution({
|
|
87
|
+
type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "withdraw",
|
|
88
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
89
|
+
error: err instanceof Error ? err.message : String(err),
|
|
90
|
+
meta: { action: "withdraw", destination: dest },
|
|
91
|
+
});
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
// ── Lighter ──
|
|
96
|
+
withdraw
|
|
97
|
+
.command("lighter <amount>")
|
|
98
|
+
.description("Withdraw USDC from Lighter to your Ethereum L1 wallet")
|
|
99
|
+
.option("--asset-id <id>", "Asset ID (default: 2 = USDC)", "2")
|
|
100
|
+
.option("--route <type>", "Route type: 0=perp, 1=spot (default: 0)", "0")
|
|
101
|
+
.action(async (amount, opts) => {
|
|
102
|
+
const amountNum = parseFloat(amount);
|
|
103
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
104
|
+
throw new Error("Invalid amount");
|
|
105
|
+
const adapter = await getAdapter();
|
|
106
|
+
// Lazy-import to check instance
|
|
107
|
+
const { LighterAdapter } = await import("../exchanges/lighter.js");
|
|
108
|
+
if (!(adapter instanceof LighterAdapter))
|
|
109
|
+
throw new Error("Requires --exchange lighter");
|
|
110
|
+
if (!isJson()) {
|
|
111
|
+
console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Lighter...\n`));
|
|
112
|
+
console.log(` Account Index: ${adapter.accountIndex}`);
|
|
113
|
+
console.log(` Address: ${adapter.address}`);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const result = await adapter.withdraw(amountNum, parseInt(opts.assetId), parseInt(opts.route));
|
|
117
|
+
logExecution({
|
|
118
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "withdraw",
|
|
119
|
+
size: String(amountNum), status: "success", dryRun: false,
|
|
120
|
+
meta: { action: "withdraw" },
|
|
121
|
+
});
|
|
122
|
+
if (isJson())
|
|
123
|
+
return printJson(jsonOk(result));
|
|
124
|
+
console.log(chalk.green(`\n Withdrawal submitted!`));
|
|
125
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
126
|
+
console.log(chalk.gray(`\n Standard withdrawal takes ~12 hours. Use Lighter web for fast withdrawal.\n`));
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
logExecution({
|
|
130
|
+
type: "bridge", exchange: "lighter", symbol: "USDC", side: "withdraw",
|
|
131
|
+
size: String(amountNum), status: "failed", dryRun: false,
|
|
132
|
+
error: err instanceof Error ? err.message : String(err),
|
|
133
|
+
meta: { action: "withdraw" },
|
|
134
|
+
});
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// ── Transfer (HL internal) ──
|
|
139
|
+
withdraw
|
|
140
|
+
.command("transfer <amount> <destination>")
|
|
141
|
+
.description("Transfer USDC between Hyperliquid accounts (internal, instant)")
|
|
142
|
+
.action(async (amount, destination) => {
|
|
143
|
+
const amountNum = parseFloat(amount);
|
|
144
|
+
if (isNaN(amountNum) || amountNum <= 0)
|
|
145
|
+
throw new Error("Invalid amount");
|
|
146
|
+
const adapter = await getAdapter();
|
|
147
|
+
if (!(adapter instanceof HyperliquidAdapter))
|
|
148
|
+
throw new Error("Requires --exchange hyperliquid");
|
|
149
|
+
if (!isJson())
|
|
150
|
+
console.log(chalk.cyan(`\n Transferring $${formatUsd(amountNum)} USDC to ${destination}...\n`));
|
|
151
|
+
const result = await adapter.usdTransfer(amountNum, destination);
|
|
152
|
+
if (isJson())
|
|
153
|
+
return printJson(jsonOk(result));
|
|
154
|
+
console.log(chalk.green(` Transfer complete!`));
|
|
155
|
+
console.log(` Amount: $${formatUsd(amountNum)} USDC`);
|
|
156
|
+
console.log(` To: ${destination}\n`);
|
|
157
|
+
});
|
|
158
|
+
// ── Info ──
|
|
159
|
+
withdraw
|
|
160
|
+
.command("info")
|
|
161
|
+
.description("Show withdrawal info & limits")
|
|
162
|
+
.action(async () => {
|
|
163
|
+
if (isJson()) {
|
|
164
|
+
return printJson(jsonOk({
|
|
165
|
+
exchanges: {
|
|
166
|
+
pacifica: { chain: "Solana", token: "USDC", speed: "~10s", fee: "none" },
|
|
167
|
+
hyperliquid: { chain: "Arbitrum", token: "USDC", speed: "minutes", fee: "$1" },
|
|
168
|
+
lighter: { chain: "Ethereum L1", token: "USDC", speed: "~12 hours (standard), minutes (fast)", fee: "varies" },
|
|
169
|
+
},
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
console.log(chalk.cyan.bold("\n Withdrawal Info\n"));
|
|
173
|
+
console.log(chalk.white.bold(" Pacifica") + chalk.gray(" → Solana"));
|
|
174
|
+
console.log(` Speed: ~10s | Fee: none`);
|
|
175
|
+
console.log(` Command: ${chalk.green("perp withdraw pacifica <amount>")}`);
|
|
176
|
+
console.log(chalk.white.bold("\n Hyperliquid") + chalk.gray(" → Arbitrum"));
|
|
177
|
+
console.log(` Speed: minutes | Fee: ~$1`);
|
|
178
|
+
console.log(` Command: ${chalk.green("perp -e hyperliquid withdraw hyperliquid <amount>")}`);
|
|
179
|
+
console.log(chalk.white.bold("\n Lighter") + chalk.gray(" → Ethereum L1"));
|
|
180
|
+
console.log(` Speed: ~12h (standard) | Fee: varies`);
|
|
181
|
+
console.log(` Command: ${chalk.green("perp -e lighter withdraw lighter <amount>")}`);
|
|
182
|
+
console.log(chalk.white.bold("\n Internal Transfer") + chalk.gray(" (Hyperliquid → Hyperliquid)"));
|
|
183
|
+
console.log(` Speed: instant | Fee: none`);
|
|
184
|
+
console.log(` Command: ${chalk.green("perp -e hyperliquid withdraw transfer <amount> <address>")}`);
|
|
185
|
+
console.log();
|
|
186
|
+
});
|
|
187
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Keypair } from "@solana/web3.js";
|
|
2
|
+
export type Exchange = "pacifica" | "hyperliquid" | "lighter";
|
|
3
|
+
export declare function loadPrivateKey(exchange: Exchange, pkOverride?: string): Promise<string>;
|
|
4
|
+
export declare function parseSolanaKeypair(input: string): Keypair;
|
|
5
|
+
export declare function isEvmPrivateKey(input: string): boolean;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Keypair } from "@solana/web3.js";
|
|
2
|
+
import bs58 from "bs58";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { resolve } from "path";
|
|
5
|
+
import { existsSync, readFileSync } from "fs";
|
|
6
|
+
export async function loadPrivateKey(exchange, pkOverride) {
|
|
7
|
+
// 1. CLI flag
|
|
8
|
+
if (pkOverride)
|
|
9
|
+
return pkOverride;
|
|
10
|
+
// 2. Exchange-specific env vars
|
|
11
|
+
const envMap = {
|
|
12
|
+
pacifica: ["PACIFICA_PRIVATE_KEY", "pk"],
|
|
13
|
+
hyperliquid: ["HYPERLIQUID_PRIVATE_KEY", "HL_PRIVATE_KEY"],
|
|
14
|
+
lighter: ["LIGHTER_PRIVATE_KEY"],
|
|
15
|
+
};
|
|
16
|
+
for (const envVar of envMap[exchange]) {
|
|
17
|
+
if (process.env[envVar])
|
|
18
|
+
return process.env[envVar];
|
|
19
|
+
}
|
|
20
|
+
// 3. Generic fallback
|
|
21
|
+
if (process.env.PRIVATE_KEY)
|
|
22
|
+
return process.env.PRIVATE_KEY;
|
|
23
|
+
// 4. Active wallet from wallets.json
|
|
24
|
+
try {
|
|
25
|
+
const { getActiveWalletKey } = await import("./commands/wallet.js");
|
|
26
|
+
const walletKey = getActiveWalletKey(exchange);
|
|
27
|
+
if (walletKey)
|
|
28
|
+
return walletKey;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// wallet module not available, skip
|
|
32
|
+
}
|
|
33
|
+
// 5. Legacy key file (~/.perp/<exchange>.key)
|
|
34
|
+
const keyFile = resolve(process.env.HOME || "~", ".perp", `${exchange}.key`);
|
|
35
|
+
if (existsSync(keyFile)) {
|
|
36
|
+
return readFileSync(keyFile, "utf-8").trim();
|
|
37
|
+
}
|
|
38
|
+
throw new Error(`No private key configured for ${exchange}.\n\n` +
|
|
39
|
+
` Quick start: ${chalk.cyan("perp init")}\n\n` +
|
|
40
|
+
` Or manually:\n` +
|
|
41
|
+
` perp wallet set ${exchange} <key>\n` +
|
|
42
|
+
` ${envMap[exchange][0]}=<key> (env var)\n` +
|
|
43
|
+
` --private-key <key> (per-command)`);
|
|
44
|
+
}
|
|
45
|
+
export function parseSolanaKeypair(input) {
|
|
46
|
+
// Try base58 first
|
|
47
|
+
try {
|
|
48
|
+
const bytes = bs58.decode(input);
|
|
49
|
+
return Keypair.fromSecretKey(bytes);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// noop
|
|
53
|
+
}
|
|
54
|
+
// Try JSON array (Solana CLI format)
|
|
55
|
+
try {
|
|
56
|
+
const arr = JSON.parse(input);
|
|
57
|
+
if (Array.isArray(arr)) {
|
|
58
|
+
return Keypair.fromSecretKey(Uint8Array.from(arr));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// noop
|
|
63
|
+
}
|
|
64
|
+
throw new Error("Invalid Solana private key. Expected base58 or JSON byte array.");
|
|
65
|
+
}
|
|
66
|
+
export function isEvmPrivateKey(input) {
|
|
67
|
+
return input.startsWith("0x") && input.length === 66;
|
|
68
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-chain margin monitoring and dynamic auto-sizing.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Per-exchange margin status checking
|
|
6
|
+
* - Auto-size computation based on orderbook depth and margin limits
|
|
7
|
+
*/
|
|
8
|
+
import type { ExchangeAdapter } from "./exchanges/interface.js";
|
|
9
|
+
export interface ChainMarginStatus {
|
|
10
|
+
exchange: string;
|
|
11
|
+
chain: string;
|
|
12
|
+
equity: number;
|
|
13
|
+
usedMargin: number;
|
|
14
|
+
freeMargin: number;
|
|
15
|
+
marginRatio: number;
|
|
16
|
+
belowThreshold: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check margin status across all configured exchanges.
|
|
20
|
+
*
|
|
21
|
+
* @param adapters - Map of exchange name to adapter
|
|
22
|
+
* @param minMarginPct - Warn when free margin / equity falls below this (default: 30%)
|
|
23
|
+
* @returns Per-exchange margin status array
|
|
24
|
+
*/
|
|
25
|
+
export declare function checkChainMargins(adapters: Map<string, ExchangeAdapter>, minMarginPct?: number): Promise<ChainMarginStatus[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a specific exchange has critically low margin (below 15%).
|
|
28
|
+
*/
|
|
29
|
+
export declare function isCriticalMargin(status: ChainMarginStatus): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Check if a specific exchange should block new entries (below threshold).
|
|
32
|
+
*/
|
|
33
|
+
export declare function shouldBlockEntries(status: ChainMarginStatus, minMarginPct?: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Compute the optimal position size automatically based on:
|
|
36
|
+
* 1. Orderbook depth on both sides
|
|
37
|
+
* 2. Free margin constraints (max 50% per trade)
|
|
38
|
+
* 3. Risk config maxPositionUsd
|
|
39
|
+
*
|
|
40
|
+
* @param longAdapter - Adapter for the long side exchange
|
|
41
|
+
* @param shortAdapter - Adapter for the short side exchange
|
|
42
|
+
* @param symbol - Trading symbol
|
|
43
|
+
* @param maxSlippagePct - Max acceptable slippage (default: 0.3%)
|
|
44
|
+
* @returns USD size per leg, or 0 if not viable
|
|
45
|
+
*/
|
|
46
|
+
export declare function computeAutoSize(longAdapter: ExchangeAdapter, shortAdapter: ExchangeAdapter, symbol: string, maxSlippagePct?: number): Promise<number>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-chain margin monitoring and dynamic auto-sizing.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Per-exchange margin status checking
|
|
6
|
+
* - Auto-size computation based on orderbook depth and margin limits
|
|
7
|
+
*/
|
|
8
|
+
import { computeExecutableSize } from "./liquidity.js";
|
|
9
|
+
import { loadRiskLimits } from "./risk.js";
|
|
10
|
+
const EXCHANGE_CHAIN_MAP = {
|
|
11
|
+
lighter: "arbitrum",
|
|
12
|
+
pacifica: "solana",
|
|
13
|
+
hyperliquid: "hyperliquid",
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Check margin status across all configured exchanges.
|
|
17
|
+
*
|
|
18
|
+
* @param adapters - Map of exchange name to adapter
|
|
19
|
+
* @param minMarginPct - Warn when free margin / equity falls below this (default: 30%)
|
|
20
|
+
* @returns Per-exchange margin status array
|
|
21
|
+
*/
|
|
22
|
+
export async function checkChainMargins(adapters, minMarginPct = 30) {
|
|
23
|
+
const entries = [...adapters.entries()];
|
|
24
|
+
const results = await Promise.allSettled(entries.map(async ([name, adapter]) => {
|
|
25
|
+
const bal = await adapter.getBalance();
|
|
26
|
+
const equity = Number(bal.equity);
|
|
27
|
+
const usedMargin = Number(bal.marginUsed);
|
|
28
|
+
const freeMargin = equity - usedMargin;
|
|
29
|
+
// marginRatio = (freeMargin / equity) * 100
|
|
30
|
+
// If equity is 0, ratio is 0 (critical). If no margin used, ratio is 100.
|
|
31
|
+
const marginRatio = equity > 0 ? (freeMargin / equity) * 100 : 0;
|
|
32
|
+
return {
|
|
33
|
+
exchange: name,
|
|
34
|
+
chain: EXCHANGE_CHAIN_MAP[name.toLowerCase()] ?? "unknown",
|
|
35
|
+
equity,
|
|
36
|
+
usedMargin,
|
|
37
|
+
freeMargin,
|
|
38
|
+
marginRatio,
|
|
39
|
+
belowThreshold: marginRatio < minMarginPct,
|
|
40
|
+
};
|
|
41
|
+
}));
|
|
42
|
+
return results
|
|
43
|
+
.filter((r) => r.status === "fulfilled")
|
|
44
|
+
.map((r) => r.value);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Check if a specific exchange has critically low margin (below 15%).
|
|
48
|
+
*/
|
|
49
|
+
export function isCriticalMargin(status) {
|
|
50
|
+
return status.marginRatio < 15;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if a specific exchange should block new entries (below threshold).
|
|
54
|
+
*/
|
|
55
|
+
export function shouldBlockEntries(status, minMarginPct = 30) {
|
|
56
|
+
return status.marginRatio < minMarginPct;
|
|
57
|
+
}
|
|
58
|
+
// ── Dynamic Auto-Sizing ──
|
|
59
|
+
/**
|
|
60
|
+
* Compute the optimal position size automatically based on:
|
|
61
|
+
* 1. Orderbook depth on both sides
|
|
62
|
+
* 2. Free margin constraints (max 50% per trade)
|
|
63
|
+
* 3. Risk config maxPositionUsd
|
|
64
|
+
*
|
|
65
|
+
* @param longAdapter - Adapter for the long side exchange
|
|
66
|
+
* @param shortAdapter - Adapter for the short side exchange
|
|
67
|
+
* @param symbol - Trading symbol
|
|
68
|
+
* @param maxSlippagePct - Max acceptable slippage (default: 0.3%)
|
|
69
|
+
* @returns USD size per leg, or 0 if not viable
|
|
70
|
+
*/
|
|
71
|
+
export async function computeAutoSize(longAdapter, shortAdapter, symbol, maxSlippagePct = 0.3) {
|
|
72
|
+
// Step 1: Get orderbooks from both sides
|
|
73
|
+
const [longOB, shortOB, longBal, shortBal] = await Promise.all([
|
|
74
|
+
longAdapter.getOrderbook(symbol),
|
|
75
|
+
shortAdapter.getOrderbook(symbol),
|
|
76
|
+
longAdapter.getBalance(),
|
|
77
|
+
shortAdapter.getBalance(),
|
|
78
|
+
]);
|
|
79
|
+
// Step 2: Compute executable size on each side
|
|
80
|
+
// For long entry we consume asks, for short entry we consume bids
|
|
81
|
+
// Use a large requested size to discover the full depth available
|
|
82
|
+
const probeSize = 1_000_000; // $1M probe to find max depth
|
|
83
|
+
const longCheck = computeExecutableSize(longOB.asks, probeSize, maxSlippagePct);
|
|
84
|
+
const shortCheck = computeExecutableSize(shortOB.bids, probeSize, maxSlippagePct);
|
|
85
|
+
// Step 3: Take the minimum of both sides (in USD)
|
|
86
|
+
const longMaxUsd = longCheck.maxSize * longCheck.avgFillPrice;
|
|
87
|
+
const shortMaxUsd = shortCheck.maxSize * shortCheck.avgFillPrice;
|
|
88
|
+
let sizeUsd = Math.min(longMaxUsd, shortMaxUsd);
|
|
89
|
+
if (sizeUsd <= 0)
|
|
90
|
+
return 0;
|
|
91
|
+
// Step 4: Cap at 50% of free margin on either exchange
|
|
92
|
+
const longEquity = Number(longBal.equity);
|
|
93
|
+
const longUsed = Number(longBal.marginUsed);
|
|
94
|
+
const longFree = longEquity - longUsed;
|
|
95
|
+
const shortEquity = Number(shortBal.equity);
|
|
96
|
+
const shortUsed = Number(shortBal.marginUsed);
|
|
97
|
+
const shortFree = shortEquity - shortUsed;
|
|
98
|
+
const maxByMargin = Math.min(longFree, shortFree) * 0.5;
|
|
99
|
+
if (maxByMargin > 0) {
|
|
100
|
+
sizeUsd = Math.min(sizeUsd, maxByMargin);
|
|
101
|
+
}
|
|
102
|
+
// Step 5: Cap at maxPositionUsd from risk config
|
|
103
|
+
const riskLimits = loadRiskLimits();
|
|
104
|
+
sizeUsd = Math.min(sizeUsd, riskLimits.maxPositionUsd);
|
|
105
|
+
// Round to nearest dollar
|
|
106
|
+
return Math.floor(sizeUsd);
|
|
107
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Dashboard Server — HTTP + WebSocket for real-time portfolio monitoring.
|
|
3
|
+
*
|
|
4
|
+
* Polls all configured exchange adapters and pushes updates to connected clients.
|
|
5
|
+
* Includes cross-exchange arb data: funding rate comparison + dex arb scan.
|
|
6
|
+
*/
|
|
7
|
+
import type { ExchangeAdapter, ExchangeBalance, ExchangePosition, ExchangeOrder, ExchangeMarketInfo } from "../exchanges/interface.js";
|
|
8
|
+
export interface DashboardExchange {
|
|
9
|
+
name: string;
|
|
10
|
+
adapter: ExchangeAdapter;
|
|
11
|
+
}
|
|
12
|
+
export interface ArbOpportunity {
|
|
13
|
+
symbol: string;
|
|
14
|
+
longExchange: string;
|
|
15
|
+
shortExchange: string;
|
|
16
|
+
spreadAnnual: number;
|
|
17
|
+
estHourlyUsd: number;
|
|
18
|
+
rates: {
|
|
19
|
+
exchange: string;
|
|
20
|
+
annualizedPct: number;
|
|
21
|
+
hourlyRate: number;
|
|
22
|
+
markPrice: number;
|
|
23
|
+
}[];
|
|
24
|
+
}
|
|
25
|
+
export interface DexArbOpportunity {
|
|
26
|
+
underlying: string;
|
|
27
|
+
longDex: string;
|
|
28
|
+
shortDex: string;
|
|
29
|
+
annualSpread: number;
|
|
30
|
+
priceGapPct: number;
|
|
31
|
+
viability: string;
|
|
32
|
+
}
|
|
33
|
+
export interface DexAssetRow {
|
|
34
|
+
base: string;
|
|
35
|
+
dexes: {
|
|
36
|
+
dex: string;
|
|
37
|
+
rate: number;
|
|
38
|
+
annualizedPct: number;
|
|
39
|
+
markPrice: number;
|
|
40
|
+
oi: number;
|
|
41
|
+
}[];
|
|
42
|
+
}
|
|
43
|
+
export interface DashboardSnapshot {
|
|
44
|
+
timestamp: string;
|
|
45
|
+
exchanges: {
|
|
46
|
+
name: string;
|
|
47
|
+
balance: ExchangeBalance;
|
|
48
|
+
positions: ExchangePosition[];
|
|
49
|
+
orders: ExchangeOrder[];
|
|
50
|
+
topMarkets: ExchangeMarketInfo[];
|
|
51
|
+
}[];
|
|
52
|
+
totals: {
|
|
53
|
+
equity: number;
|
|
54
|
+
available: number;
|
|
55
|
+
marginUsed: number;
|
|
56
|
+
unrealizedPnl: number;
|
|
57
|
+
positionCount: number;
|
|
58
|
+
orderCount: number;
|
|
59
|
+
};
|
|
60
|
+
arb: {
|
|
61
|
+
opportunities: ArbOpportunity[];
|
|
62
|
+
dexArb: DexArbOpportunity[];
|
|
63
|
+
dexAssets: DexAssetRow[];
|
|
64
|
+
dexNames: string[];
|
|
65
|
+
exchangeStatus: Record<string, string>;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface DashboardOpts {
|
|
69
|
+
port?: number;
|
|
70
|
+
pollInterval?: number;
|
|
71
|
+
arbInterval?: number;
|
|
72
|
+
signal?: AbortSignal;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Start the dashboard HTTP + WebSocket server.
|
|
76
|
+
*/
|
|
77
|
+
export declare function startDashboard(exchanges: DashboardExchange[], opts?: DashboardOpts): Promise<{
|
|
78
|
+
port: number;
|
|
79
|
+
close: () => void;
|
|
80
|
+
}>;
|