perp-cli 0.6.4 → 0.7.1

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.
Files changed (177) hide show
  1. package/README.md +7 -2
  2. package/dist/api/public/hyperliquid.d.ts +2 -2
  3. package/dist/api/public/hyperliquid.js +0 -10
  4. package/dist/api/public/lighter.d.ts +2 -1
  5. package/dist/api/public/pacifica.d.ts +2 -1
  6. package/dist/arb/index.d.ts +7 -4
  7. package/dist/arb/index.js +5 -4
  8. package/dist/arb/sizing.d.ts +1 -1
  9. package/dist/arb/state.d.ts +1 -1
  10. package/dist/arb/state.js +4 -3
  11. package/dist/arb/utils.d.ts +0 -10
  12. package/dist/arb/utils.js +0 -42
  13. package/dist/bot/apex/guard.d.ts +26 -0
  14. package/dist/bot/apex/guard.js +86 -0
  15. package/dist/bot/apex/index.d.ts +6 -0
  16. package/dist/bot/apex/index.js +4 -0
  17. package/dist/bot/apex/pulse.d.ts +22 -0
  18. package/dist/bot/apex/pulse.js +107 -0
  19. package/dist/bot/apex/radar.d.ts +17 -0
  20. package/dist/bot/apex/radar.js +150 -0
  21. package/dist/bot/conditions.d.ts +1 -1
  22. package/dist/bot/config.d.ts +5 -1
  23. package/dist/bot/config.js +1 -1
  24. package/dist/bot/engine.d.ts +23 -2
  25. package/dist/bot/engine.js +296 -351
  26. package/dist/bot/index.d.ts +16 -0
  27. package/dist/bot/index.js +10 -0
  28. package/dist/bot/indicators.d.ts +29 -0
  29. package/dist/bot/indicators.js +144 -0
  30. package/dist/bot/reflect.d.ts +34 -0
  31. package/dist/bot/reflect.js +155 -0
  32. package/dist/bot/strategies/aggressive-taker.d.ts +36 -0
  33. package/dist/bot/strategies/aggressive-taker.js +125 -0
  34. package/dist/bot/strategies/apex-strategy.d.ts +36 -0
  35. package/dist/bot/strategies/apex-strategy.js +185 -0
  36. package/dist/bot/strategies/avellaneda-mm.d.ts +27 -0
  37. package/dist/bot/strategies/avellaneda-mm.js +111 -0
  38. package/dist/bot/strategies/basis-arb.d.ts +27 -0
  39. package/dist/bot/strategies/basis-arb.js +97 -0
  40. package/dist/bot/strategies/claude-agent.d.ts +31 -0
  41. package/dist/bot/strategies/claude-agent.js +155 -0
  42. package/dist/bot/strategies/dca-strategy.d.ts +25 -0
  43. package/dist/bot/strategies/dca-strategy.js +59 -0
  44. package/dist/bot/strategies/engine-mm.d.ts +29 -0
  45. package/dist/bot/strategies/engine-mm.js +141 -0
  46. package/dist/bot/strategies/funding-arb-strategy.d.ts +32 -0
  47. package/dist/bot/strategies/funding-arb-strategy.js +168 -0
  48. package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
  49. package/dist/bot/strategies/funding-arb-v2.js +222 -0
  50. package/dist/bot/strategies/funding-auto.d.ts +1 -0
  51. package/dist/bot/strategies/funding-auto.js +572 -0
  52. package/dist/bot/strategies/grid-mm.d.ts +25 -0
  53. package/dist/bot/strategies/grid-mm.js +99 -0
  54. package/dist/bot/strategies/grid-strategy.d.ts +39 -0
  55. package/dist/bot/strategies/grid-strategy.js +176 -0
  56. package/dist/bot/strategies/hedge-agent.d.ts +25 -0
  57. package/dist/bot/strategies/hedge-agent.js +79 -0
  58. package/dist/bot/strategies/liquidation-mm.d.ts +25 -0
  59. package/dist/bot/strategies/liquidation-mm.js +108 -0
  60. package/dist/bot/strategies/mean-reversion.d.ts +35 -0
  61. package/dist/bot/strategies/mean-reversion.js +118 -0
  62. package/dist/bot/strategies/momentum-breakout.d.ts +36 -0
  63. package/dist/bot/strategies/momentum-breakout.js +159 -0
  64. package/dist/bot/strategies/regime-mm.d.ts +25 -0
  65. package/dist/bot/strategies/regime-mm.js +111 -0
  66. package/dist/bot/strategies/rfq-agent.d.ts +25 -0
  67. package/dist/bot/strategies/rfq-agent.js +71 -0
  68. package/dist/bot/strategies/simple-mm.d.ts +25 -0
  69. package/dist/bot/strategies/simple-mm.js +76 -0
  70. package/dist/bot/strategy-registry.d.ts +9 -0
  71. package/dist/bot/strategy-registry.js +14 -0
  72. package/dist/bot/strategy-types.d.ts +64 -0
  73. package/dist/bot/strategy-types.js +5 -0
  74. package/dist/bot/trade-journal.d.ts +27 -0
  75. package/dist/bot/trade-journal.js +53 -0
  76. package/dist/bot/tui/BotDashboard.d.ts +49 -0
  77. package/dist/bot/tui/BotDashboard.js +93 -0
  78. package/dist/bot/tui/index.d.ts +6 -0
  79. package/dist/bot/tui/index.js +8 -0
  80. package/dist/bridge-engine.d.ts +0 -1
  81. package/dist/bridge-engine.js +0 -1
  82. package/dist/cache.d.ts +0 -1
  83. package/dist/cache.js +0 -1
  84. package/dist/commands/account.d.ts +1 -1
  85. package/dist/commands/account.js +94 -91
  86. package/dist/commands/agent.d.ts +1 -1
  87. package/dist/commands/arb/index.d.ts +1 -1
  88. package/dist/commands/arb/index.js +1 -1
  89. package/dist/commands/arb-auto.d.ts +1 -1
  90. package/dist/commands/arb-auto.js +1 -1
  91. package/dist/commands/arb.d.ts +1 -1
  92. package/dist/commands/bot.d.ts +1 -1
  93. package/dist/commands/bot.js +198 -14
  94. package/dist/commands/dashboard.d.ts +1 -1
  95. package/dist/commands/dashboard.js +1 -1
  96. package/dist/commands/funds.d.ts +1 -1
  97. package/dist/commands/funds.js +33 -20
  98. package/dist/commands/history.d.ts +1 -1
  99. package/dist/commands/history.js +5 -4
  100. package/dist/commands/manage.d.ts +2 -3
  101. package/dist/commands/manage.js +11 -18
  102. package/dist/commands/market.d.ts +1 -1
  103. package/dist/commands/market.js +7 -7
  104. package/dist/commands/plan.d.ts +1 -1
  105. package/dist/commands/rebalance.d.ts +1 -1
  106. package/dist/commands/rebalance.js +8 -9
  107. package/dist/commands/risk.d.ts +1 -1
  108. package/dist/commands/settings.d.ts +1 -1
  109. package/dist/commands/settings.js +3 -1
  110. package/dist/commands/trade.d.ts +1 -1
  111. package/dist/commands/trade.js +39 -52
  112. package/dist/commands/wallet.d.ts +0 -2
  113. package/dist/commands/wallet.js +0 -5
  114. package/dist/constants.d.ts +0 -3
  115. package/dist/constants.js +0 -4
  116. package/dist/cross-chain-margin.d.ts +1 -1
  117. package/dist/dashboard/index.d.ts +4 -0
  118. package/dist/dashboard/index.js +3 -0
  119. package/dist/dashboard/server.d.ts +1 -1
  120. package/dist/dashboard/server.js +1 -1
  121. package/dist/dashboard/ws-feeds.d.ts +1 -1
  122. package/dist/event-stream.d.ts +1 -1
  123. package/dist/exchanges/aster.d.ts +58 -0
  124. package/dist/exchanges/aster.js +409 -0
  125. package/dist/exchanges/capabilities.d.ts +74 -0
  126. package/dist/exchanges/capabilities.js +44 -0
  127. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  128. package/dist/exchanges/hyperliquid-spot.js +20 -12
  129. package/dist/exchanges/hyperliquid.d.ts +8 -2
  130. package/dist/exchanges/hyperliquid.js +21 -6
  131. package/dist/exchanges/index.d.ts +11 -0
  132. package/dist/exchanges/index.js +10 -0
  133. package/dist/exchanges/interface.d.ts +3 -0
  134. package/dist/exchanges/lighter-spot.d.ts +13 -0
  135. package/dist/exchanges/lighter-spot.js +98 -26
  136. package/dist/exchanges/lighter.d.ts +9 -3
  137. package/dist/exchanges/lighter.js +18 -8
  138. package/dist/exchanges/nado.d.ts +52 -0
  139. package/dist/exchanges/nado.js +223 -0
  140. package/dist/exchanges/pacifica.d.ts +7 -1
  141. package/dist/exchanges/pacifica.js +17 -2
  142. package/dist/exchanges/registry.d.ts +13 -0
  143. package/dist/exchanges/registry.js +37 -0
  144. package/dist/exchanges/spot-interface.d.ts +9 -0
  145. package/dist/funding/rates.js +1 -1
  146. package/dist/index.js +2 -6
  147. package/dist/jobs.js +1 -1
  148. package/dist/liquidity.d.ts +1 -2
  149. package/dist/liquidity.js +12 -5
  150. package/dist/mcp-server.js +4 -2
  151. package/dist/pacifica/constants.d.ts +0 -3
  152. package/dist/pacifica/constants.js +0 -4
  153. package/dist/pacifica/types/account.d.ts +0 -8
  154. package/dist/pacifica/types/lake.d.ts +0 -6
  155. package/dist/pacifica/types/position.d.ts +0 -12
  156. package/dist/pacifica/types/ws.d.ts +0 -24
  157. package/dist/pacifica/types/ws.js +1 -41
  158. package/dist/plan-executor.d.ts +1 -1
  159. package/dist/rebalance.d.ts +1 -7
  160. package/dist/rebalance.js +0 -6
  161. package/dist/risk.d.ts +1 -1
  162. package/dist/risk.js +3 -1
  163. package/dist/smart-order.d.ts +1 -1
  164. package/dist/strategies/dca.d.ts +1 -1
  165. package/dist/strategies/funding-arb.d.ts +1 -1
  166. package/dist/strategies/funding-arb.js +193 -181
  167. package/dist/strategies/grid.d.ts +1 -1
  168. package/dist/strategies/grid.js +1 -1
  169. package/dist/strategies/index.d.ts +12 -0
  170. package/dist/strategies/index.js +7 -0
  171. package/dist/strategies/split-order.d.ts +1 -1
  172. package/dist/strategies/trailing-stop.d.ts +1 -1
  173. package/dist/strategies/twap.d.ts +1 -1
  174. package/dist/trade-validator.d.ts +1 -1
  175. package/package.json +7 -1
  176. package/skills/perp-cli/SKILL.md +20 -0
  177. package/skills/perp-cli/references/commands.md +22 -8
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerDashboardCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean, getHLAdapterForDex?: (dex: string) => Promise<ExchangeAdapter>): void;
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { startDashboard } from "../dashboard/server.js";
2
+ import { startDashboard } from "../dashboard/index.js";
3
3
  const KNOWN_HIP3_DEXES = ["xyz", "flx", "hyna", "km", "cash", "vntl"];
4
4
  const HL_INFO_URL = "https://api.hyperliquid.xyz/info";
5
5
  /** Lightweight check: query HL info API directly for dex positions (no SDK/WebSocket needed) */
@@ -1,4 +1,4 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  import type { Network } from "../pacifica/index.js";
4
4
  export declare function registerFundsCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getNetwork: () => Network): void;
@@ -1,8 +1,7 @@
1
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";
2
+ import { printJson, formatUsd, jsonOk, jsonError } from "../utils.js";
5
3
  import { logExecution } from "../execution-log.js";
4
+ import { hasPacificaSdk, hasEvmAddress, isWithdrawCapable, isUsdTransferCapable, hasLighterAccount } from "../exchanges/capabilities.js";
6
5
  const DEFAULT_RELAYER = "http://localhost:3100";
7
6
  function getRelayerUrl() {
8
7
  return process.env.PERP_RELAYER_URL || DEFAULT_RELAYER;
@@ -38,14 +37,16 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
38
37
  if (!isJson())
39
38
  console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} via relayer (gasless)...\n`));
40
39
  const adapter = await getAdapter();
41
- if (!(adapter instanceof PacificaAdapter))
40
+ if (adapter.name !== "pacifica")
42
41
  throw new Error("Requires --exchange pacifica");
42
+ const { PacificaAdapter } = await import("../exchanges/pacifica.js");
43
+ const pacAdapter = adapter;
43
44
  try {
44
45
  const buildRes = await fetch(`${getRelayerUrl()}/deposit/pacifica/build`, {
45
46
  method: "POST",
46
47
  headers: { "Content-Type": "application/json" },
47
48
  body: JSON.stringify({
48
- userPubkey: adapter.keypair.publicKey.toBase58(),
49
+ userPubkey: pacAdapter.keypair.publicKey.toBase58(),
49
50
  amount: amountNum,
50
51
  testnet: getNetwork() === "testnet",
51
52
  }),
@@ -61,7 +62,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
61
62
  }
62
63
  const { Transaction } = await import("@solana/web3.js");
63
64
  const tx = Transaction.from(Buffer.from(transaction, "base64"));
64
- tx.partialSign(adapter.keypair);
65
+ tx.partialSign(pacAdapter.keypair);
65
66
  const signed = tx.serialize().toString("base64");
66
67
  const submitRes = await fetch(`${getRelayerUrl()}/deposit/pacifica/submit`, {
67
68
  method: "POST",
@@ -104,15 +105,17 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
104
105
  console.log(chalk.gray(" Requires: SOL for gas (~0.005 SOL) + USDC on Solana\n"));
105
106
  }
106
107
  const adapter = await getAdapter();
107
- if (!(adapter instanceof PacificaAdapter))
108
+ if (adapter.name !== "pacifica")
108
109
  throw new Error("Requires --exchange pacifica");
110
+ const { PacificaAdapter: PacAdapterCls } = await import("../exchanges/pacifica.js");
111
+ const pacDirect = adapter;
109
112
  try {
110
113
  const { Connection, Transaction } = await import("@solana/web3.js");
111
114
  const { buildDepositInstruction } = await import("../pacifica/deposit.js");
112
115
  const network = getNetwork();
113
116
  const rpcUrl = network === "testnet" ? "https://api.devnet.solana.com" : "https://api.mainnet-beta.solana.com";
114
117
  const connection = new Connection(rpcUrl, "confirmed");
115
- const solBalance = await connection.getBalance(adapter.keypair.publicKey);
118
+ const solBalance = await connection.getBalance(pacDirect.keypair.publicKey);
116
119
  if (solBalance < 5_000_000) {
117
120
  console.error(chalk.red(" Insufficient SOL for gas. Need ~0.005 SOL."));
118
121
  console.error(chalk.gray(" Tip: Use relayer for gasless deposits (start relayer server)\n"));
@@ -120,7 +123,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
120
123
  }
121
124
  const { PublicKey } = await import("@solana/web3.js");
122
125
  const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
123
- const tokenAccounts = await connection.getParsedTokenAccountsByOwner(adapter.keypair.publicKey, { mint: USDC_MINT });
126
+ const tokenAccounts = await connection.getParsedTokenAccountsByOwner(pacDirect.keypair.publicKey, { mint: USDC_MINT });
124
127
  const usdcBalance = tokenAccounts.value.length > 0
125
128
  ? tokenAccounts.value[0].account.data.parsed.info.tokenAmount.uiAmount ?? 0
126
129
  : 0;
@@ -128,11 +131,11 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
128
131
  console.error(chalk.red(` Insufficient USDC on Solana. Have: $${formatUsd(usdcBalance)}, need: $${formatUsd(amountNum)}`));
129
132
  process.exit(1);
130
133
  }
131
- const ix = await buildDepositInstruction(adapter.keypair.publicKey, amountNum, network);
134
+ const ix = await buildDepositInstruction(pacDirect.keypair.publicKey, amountNum, network);
132
135
  const tx = new Transaction().add(ix);
133
- tx.feePayer = adapter.keypair.publicKey;
136
+ tx.feePayer = pacDirect.keypair.publicKey;
134
137
  tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
135
- tx.sign(adapter.keypair);
138
+ tx.sign(pacDirect.keypair);
136
139
  const sig = await connection.sendRawTransaction(tx.serialize());
137
140
  await connection.confirmTransaction(sig, "confirmed");
138
141
  logExecution({
@@ -173,7 +176,13 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
173
176
  console.log(chalk.cyan(`\n Depositing $${formatUsd(amountNum)} via relayer (gasless)...\n`));
174
177
  const { ethers } = await import("ethers");
175
178
  const { loadPrivateKey } = await import("../config.js");
176
- const pk = await loadPrivateKey("hyperliquid");
179
+ let pk;
180
+ try {
181
+ pk = await loadPrivateKey("hyperliquid");
182
+ }
183
+ catch (err) {
184
+ throw new Error(`Private key not configured for Hyperliquid. Run: perp init`);
185
+ }
177
186
  const wallet = new ethers.Wallet(pk);
178
187
  try {
179
188
  const res = await fetch(`${getRelayerUrl()}/deposit/hyperliquid`, {
@@ -475,7 +484,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
475
484
  if (isNaN(amountNum) || amountNum <= 0)
476
485
  throw new Error("Invalid amount");
477
486
  const adapter = await getAdapter();
478
- if (!(adapter instanceof PacificaAdapter))
487
+ if (!hasPacificaSdk(adapter) || !isWithdrawCapable(adapter))
479
488
  throw new Error("Requires --exchange pacifica");
480
489
  const dest = opts.to || adapter.publicKey;
481
490
  if (!isJson()) {
@@ -483,7 +492,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
483
492
  console.log(` Destination: ${dest}`);
484
493
  }
485
494
  try {
486
- const result = await adapter.sdk.withdraw({ amount: String(amountNum), dest_address: dest }, adapter.publicKey, adapter.signer);
495
+ const result = await adapter.withdraw(String(amountNum), dest);
487
496
  logExecution({
488
497
  type: "bridge", exchange: "pacifica", symbol: "USDC", side: "withdraw",
489
498
  size: String(amountNum), status: "success", dryRun: false,
@@ -515,7 +524,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
515
524
  if (isNaN(amountNum) || amountNum <= 0)
516
525
  throw new Error("Invalid amount");
517
526
  const adapter = await getAdapter();
518
- if (!(adapter instanceof HyperliquidAdapter))
527
+ if (adapter.name !== "hyperliquid" || !hasEvmAddress(adapter) || !isWithdrawCapable(adapter))
519
528
  throw new Error("Requires --exchange hyperliquid");
520
529
  const dest = opts.to || adapter.address;
521
530
  if (!isJson()) {
@@ -562,8 +571,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
562
571
  if (isNaN(amountNum) || amountNum <= 0)
563
572
  throw new Error("Invalid amount");
564
573
  const adapter = await getAdapter();
565
- const { LighterAdapter } = await import("../exchanges/lighter.js");
566
- if (!(adapter instanceof LighterAdapter))
574
+ if (!hasLighterAccount(adapter) || !isWithdrawCapable(adapter))
567
575
  throw new Error("Requires --exchange lighter");
568
576
  if (!isJson()) {
569
577
  console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Lighter...\n`));
@@ -571,7 +579,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
571
579
  console.log(` Address: ${adapter.address}`);
572
580
  }
573
581
  try {
574
- const result = await adapter.withdraw(amountNum, parseInt(opts.assetId), parseInt(opts.route));
582
+ const result = await adapter.withdraw(String(amountNum), "", { assetId: parseInt(opts.assetId), routeType: parseInt(opts.route) });
575
583
  logExecution({
576
584
  type: "bridge", exchange: "lighter", symbol: "USDC", side: "withdraw",
577
585
  size: String(amountNum), status: "success", dryRun: false,
@@ -604,7 +612,7 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
604
612
  if (isNaN(amountNum) || amountNum <= 0)
605
613
  throw new Error("Invalid amount");
606
614
  const adapter = await getAdapter();
607
- if (!(adapter instanceof HyperliquidAdapter))
615
+ if (!isUsdTransferCapable(adapter))
608
616
  throw new Error("Requires --exchange hyperliquid");
609
617
  if (!isJson())
610
618
  console.log(chalk.cyan(`\n Transferring $${formatUsd(amountNum)} USDC to ${destination}...\n`));
@@ -654,8 +662,13 @@ export function registerFundsCommands(program, getAdapter, isJson, getNetwork) {
654
662
  console.log(chalk.gray(` Check: perp funds bridge-status --hash ${result.messageHash}\n`));
655
663
  }
656
664
  else {
665
+ if (isJson()) {
666
+ printJson(jsonError("RELAYER_UNAVAILABLE", "CCTP bridge requires relayer server. Start: cd packages/relayer && pnpm start"));
667
+ process.exit(1);
668
+ }
657
669
  console.error(chalk.red("\n CCTP bridge requires relayer server."));
658
670
  console.error(chalk.gray(" Start: cd packages/relayer && pnpm start\n"));
671
+ process.exit(1);
659
672
  }
660
673
  });
661
674
  funds
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerHistoryCommands(program: Command, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { printJson, jsonOk, jsonError, makeTable, formatUsd, formatPnl, withJsonErrors } from "../utils.js";
3
3
  import { readExecutionLog, getExecutionStats, pruneExecutionLog } from "../execution-log.js";
4
- import { loadArbState } from "../arb/state.js";
4
+ import { loadArbState } from "../arb/index.js";
5
5
  import { readPositionHistory, getPositionStats } from "../position-history.js";
6
6
  import { saveEquitySnapshot, readEquityHistory, computePnlMetrics, computeDailyPnl, aggregateWeekly, } from "../equity-tracker.js";
7
7
  const EXCHANGES = ["pacifica", "hyperliquid", "lighter"];
@@ -899,8 +899,8 @@ function registerPnlSubcommands(parent, getAdapterForExchange, isJson) {
899
899
  process.on("SIGINT", () => controller.abort());
900
900
  // Initial snapshot
901
901
  const snaps = await takeSnapshot(getAdapterForExchange, exchanges);
902
- const total = snaps.reduce((s, sn) => s + sn.equity, 0);
903
- console.log(`${chalk.gray(new Date().toLocaleTimeString())} Total equity: $${formatUsd(total)}`);
902
+ let prevTotal = snaps.reduce((s, sn) => s + sn.equity, 0);
903
+ console.log(`${chalk.gray(new Date().toLocaleTimeString())} Total equity: $${formatUsd(prevTotal)}`);
904
904
  while (!controller.signal.aborted) {
905
905
  await new Promise((resolve) => {
906
906
  const timer = setTimeout(resolve, intervalMs);
@@ -911,9 +911,10 @@ function registerPnlSubcommands(parent, getAdapterForExchange, isJson) {
911
911
  try {
912
912
  const s = await takeSnapshot(getAdapterForExchange, exchanges);
913
913
  const t = s.reduce((sum, sn) => sum + sn.equity, 0);
914
- const delta = t - total;
914
+ const delta = t - prevTotal;
915
915
  console.log(`${chalk.gray(new Date().toLocaleTimeString())} ` +
916
916
  `Total: $${formatUsd(t)} ${formatPnl(delta)}`);
917
+ prevTotal = t;
917
918
  }
918
919
  catch (err) {
919
920
  console.error(chalk.red(`Snapshot error: ${err instanceof Error ? err.message : err}`));
@@ -1,4 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
3
- import { PacificaAdapter } from "../exchanges/pacifica.js";
4
- export declare function registerManageCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getPacificaAdapter: () => PacificaAdapter): void;
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
+ export declare function registerManageCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getPacificaAdapter: () => unknown): void;
@@ -1,15 +1,16 @@
1
- import { PacificaAdapter } from "../exchanges/pacifica.js";
2
1
  import { printJson, jsonOk } from "../utils.js";
3
2
  import { setEnvVar } from "./init.js";
4
3
  import chalk from "chalk";
4
+ import { hasPacificaSdk, hasLighterAccount } from "../exchanges/capabilities.js";
5
5
  export function registerManageCommands(program, getAdapter, isJson, getPacificaAdapter) {
6
6
  const manage = program.command("manage").description("Exchange account settings (margin, subaccount, API keys, builder)");
7
7
  // Ensure adapter is initialized before accessing PacificaAdapter
8
8
  async function pac() {
9
9
  const adapter = await getAdapter();
10
- if (!(adapter instanceof PacificaAdapter)) {
10
+ if (!hasPacificaSdk(adapter)) {
11
11
  throw new Error("This command requires --exchange pacifica");
12
12
  }
13
+ // Cast sdk to any to allow calling Pacifica SDK methods without importing the concrete type
13
14
  return adapter;
14
15
  }
15
16
  manage
@@ -30,13 +31,8 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
30
31
  const withdrawCmd = manage
31
32
  .command("withdraw <amount> <address>")
32
33
  .description("Use 'perp withdraw pacifica <amount>'")
33
- .action(async (amount, address) => {
34
+ .action(async (_amount, _address) => {
34
35
  console.log(chalk.yellow("\n Use 'perp withdraw pacifica <amount>' instead.\n"));
35
- const a = await pac();
36
- const result = await a.sdk.withdraw({ amount, dest_address: address }, a.publicKey, a.signer);
37
- if (isJson())
38
- return printJson(jsonOk(result));
39
- console.log(chalk.green(`\n Withdrawal of $${amount} to ${address} submitted.\n`));
40
36
  });
41
37
  withdrawCmd._hidden = true;
42
38
  // Subaccounts
@@ -59,7 +55,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
59
55
  const result = await a.sdk.listSubaccounts(a.publicKey, a.signer);
60
56
  if (isJson())
61
57
  return printJson(jsonOk(result));
62
- printJson(jsonOk(result));
58
+ console.log(JSON.stringify(result, null, 2));
63
59
  });
64
60
  sub
65
61
  .command("transfer <from> <to> <amount>")
@@ -91,7 +87,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
91
87
  const result = await a.sdk.listAgentWallets(a.publicKey, a.signer);
92
88
  if (isJson())
93
89
  return printJson(jsonOk(result));
94
- printJson(jsonOk(result));
90
+ console.log(JSON.stringify(result, null, 2));
95
91
  });
96
92
  agent
97
93
  .command("revoke <wallet>")
@@ -124,7 +120,6 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
124
120
  if (isJson())
125
121
  return printJson(jsonOk(result));
126
122
  console.log(chalk.green(`\n Lake created for ${symbol.toUpperCase()} with $${amount}.\n`));
127
- printJson(jsonOk(result));
128
123
  });
129
124
  lake
130
125
  .command("deposit <lakeId> <amount>")
@@ -195,7 +190,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
195
190
  const result = await a.sdk.getBuilderOverview(a.publicKey);
196
191
  if (isJson())
197
192
  return printJson(jsonOk(result));
198
- printJson(jsonOk(result));
193
+ console.log(JSON.stringify(result, null, 2));
199
194
  });
200
195
  builder
201
196
  .command("trades <code>")
@@ -205,7 +200,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
205
200
  const result = await a.sdk.getBuilderTrades(code);
206
201
  if (isJson())
207
202
  return printJson(jsonOk(result));
208
- printJson(jsonOk(result));
203
+ console.log(JSON.stringify(result, null, 2));
209
204
  });
210
205
  builder
211
206
  .command("leaderboard <code>")
@@ -215,7 +210,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
215
210
  const result = await a.sdk.getBuilderLeaderboard(code);
216
211
  if (isJson())
217
212
  return printJson(jsonOk(result));
218
- printJson(jsonOk(result));
213
+ console.log(JSON.stringify(result, null, 2));
219
214
  });
220
215
  builder
221
216
  .command("update-fee <code> <feeRate>")
@@ -250,7 +245,6 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
250
245
  if (isJson())
251
246
  return printJson(jsonOk(result));
252
247
  console.log(chalk.green(`\n API key "${name}" created.\n`));
253
- printJson(jsonOk(result));
254
248
  });
255
249
  apikey
256
250
  .command("list")
@@ -260,7 +254,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
260
254
  const result = await a.sdk.listApiKeys(a.publicKey, a.signer);
261
255
  if (isJson())
262
256
  return printJson(jsonOk(result));
263
- printJson(jsonOk(result));
257
+ console.log(JSON.stringify(result, null, 2));
264
258
  });
265
259
  apikey
266
260
  .command("revoke <key>")
@@ -279,8 +273,7 @@ export function registerManageCommands(program, getAdapter, isJson, getPacificaA
279
273
  .option("--key-index <n>", "API key index (4-254, default: 4)", "4")
280
274
  .action(async (opts) => {
281
275
  const adapter = await getAdapter();
282
- const { LighterAdapter } = await import("../exchanges/lighter.js");
283
- if (!(adapter instanceof LighterAdapter)) {
276
+ if (!hasLighterAccount(adapter)) {
284
277
  throw new Error("This command requires --exchange lighter");
285
278
  }
286
279
  const keyIndex = parseInt(opts.keyIndex);
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerMarketCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;
@@ -1,7 +1,6 @@
1
- import { PacificaAdapter } from "../exchanges/pacifica.js";
2
- import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
3
1
  import { makeTable, formatUsd, formatPercent, printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
4
2
  import chalk from "chalk";
3
+ import { hasPacificaSdk, isDexCapable } from "../exchanges/capabilities.js";
5
4
  const EXCHANGES = ["pacifica", "hyperliquid", "lighter"];
6
5
  export function registerMarketCommands(program, getAdapter, isJson, getAdapterForExchange) {
7
6
  const market = program.command("market").description("Market data commands");
@@ -30,7 +29,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
30
29
  const adapter = await getAdapterForExchange(ex);
31
30
  grouped[ex] = await adapter.getMarkets();
32
31
  // --hip3: fetch HIP-3 dex markets
33
- if (opts.hip3 && ex === "hyperliquid" && adapter instanceof HyperliquidAdapter) {
32
+ if (opts.hip3 && ex === "hyperliquid" && isDexCapable(adapter)) {
34
33
  const dexes = await adapter.listDeployedDexes();
35
34
  await Promise.allSettled(dexes.map(async (d) => {
36
35
  const dexAdapter = Object.create(adapter);
@@ -67,7 +66,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
67
66
  const markets = await adapter.getMarkets();
68
67
  // --hip3: fetch HIP-3 dex markets
69
68
  const hip3 = {};
70
- if (opts.hip3 && adapter instanceof HyperliquidAdapter) {
69
+ if (opts.hip3 && isDexCapable(adapter)) {
71
70
  const dexes = await adapter.listDeployedDexes();
72
71
  await Promise.allSettled(dexes.map(async (d) => {
73
72
  const dexAdapter = Object.create(adapter);
@@ -127,8 +126,9 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
127
126
  return;
128
127
  }
129
128
  const adapter = await getAdapter();
130
- if (adapter instanceof PacificaAdapter) {
131
- const prices = await adapter.sdk.getPrices();
129
+ if (hasPacificaSdk(adapter)) {
130
+ const sdk = adapter.sdk;
131
+ const prices = await sdk.getPrices();
132
132
  if (isJson())
133
133
  return printJson(jsonOk(prices));
134
134
  const rows = prices.map((p) => [
@@ -351,7 +351,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
351
351
  .action(async () => {
352
352
  await withJsonErrors(isJson(), async () => {
353
353
  const adapter = await getAdapter();
354
- if (!(adapter instanceof HyperliquidAdapter)) {
354
+ if (!(isDexCapable(adapter))) {
355
355
  if (isJson())
356
356
  return printJson(jsonError("INVALID_EXCHANGE", "HIP-3 dexes are only available on Hyperliquid. Use -e hyperliquid."));
357
357
  console.error(chalk.red("\n HIP-3 dexes are only available on Hyperliquid. Use -e hyperliquid.\n"));
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerPlanCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean): void;
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerRebalanceCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
@@ -229,29 +229,28 @@ export function registerRebalanceCommands(program, getAdapterForExchange, isJson
229
229
  * Execute a withdrawal from a source exchange.
230
230
  */
231
231
  async function executeWithdraw(move, adapters) {
232
+ const { hasPacificaSdk, isWithdrawCapable, hasEvmAddress } = await import("../exchanges/capabilities.js");
232
233
  const adapter = adapters.get(move.from);
233
234
  if (!adapter)
234
235
  throw new Error(`No adapter for ${move.from}`);
235
- switch (move.from) {
236
+ switch (adapter.name) {
236
237
  case "pacifica": {
237
- const { PacificaAdapter } = await import("../exchanges/pacifica.js");
238
- if (!(adapter instanceof PacificaAdapter))
238
+ if (!hasPacificaSdk(adapter))
239
239
  throw new Error("Invalid adapter");
240
- await adapter.sdk.withdraw({ amount: String(move.amount), dest_address: adapter.publicKey }, adapter.publicKey, adapter.signer);
240
+ const sdk = adapter.sdk;
241
+ await sdk.withdraw({ amount: String(move.amount), dest_address: adapter.publicKey }, adapter.publicKey, adapter.signer);
241
242
  break;
242
243
  }
243
244
  case "hyperliquid": {
244
- const { HyperliquidAdapter } = await import("../exchanges/hyperliquid.js");
245
- if (!(adapter instanceof HyperliquidAdapter))
245
+ if (!isWithdrawCapable(adapter) || !hasEvmAddress(adapter))
246
246
  throw new Error("Invalid adapter");
247
247
  await adapter.withdraw(String(move.amount), adapter.address);
248
248
  break;
249
249
  }
250
250
  case "lighter": {
251
- const { LighterAdapter } = await import("../exchanges/lighter.js");
252
- if (!(adapter instanceof LighterAdapter))
251
+ if (!isWithdrawCapable(adapter))
253
252
  throw new Error("Invalid adapter");
254
- await adapter.withdraw(move.amount);
253
+ await adapter.withdraw(String(move.amount), "");
255
254
  break;
256
255
  }
257
256
  default:
@@ -1,5 +1,5 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  /** Run health check and return results + render. Exported for use by status --health. */
4
4
  export declare function runHealthCheck(isJson: () => boolean): Promise<void>;
5
5
  export declare function registerRiskCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerSettingsCommands(program: Command, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;
@@ -1,5 +1,5 @@
1
1
  import chalk from "chalk";
2
- import { printJson, jsonOk, withJsonErrors } from "../utils.js";
2
+ import { printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
3
3
  import { loadSettings, saveSettings } from "../settings.js";
4
4
  import { ENV_FILE, loadEnvFile, setEnvVar, EXCHANGE_ENV_MAP, validateKey } from "./init.js";
5
5
  // Resolve exchange alias → env var key
@@ -80,6 +80,8 @@ export function registerSettingsCommands(program, isJson, getAdapterForExchange)
80
80
  console.log(chalk.yellow("\n Referrals disabled. No codes will be sent.\n"));
81
81
  }
82
82
  else {
83
+ if (isJson())
84
+ return printJson(jsonError("INVALID_ARGS", `Invalid action "${action}". Usage: perp settings referrals <on|off>`));
83
85
  console.error(chalk.red(`\n Usage: perp settings referrals <on|off>\n`));
84
86
  }
85
87
  });
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerTradeCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, isDryRun?: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;