perp-cli 0.9.8 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +30 -25
  2. package/dist/_stderr-filter.d.ts +0 -0
  3. package/dist/_stderr-filter.js +30 -0
  4. package/dist/agent-wallet/expiry.d.ts +15 -0
  5. package/dist/agent-wallet/expiry.js +26 -0
  6. package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
  7. package/dist/agent-wallet/lighter-keystore.js +232 -0
  8. package/dist/agent-wallet/passphrase.d.ts +13 -0
  9. package/dist/agent-wallet/passphrase.js +54 -0
  10. package/dist/agent-wallet/signing-strategy.d.ts +67 -0
  11. package/dist/agent-wallet/signing-strategy.js +51 -0
  12. package/dist/agent-wallet/store.d.ts +37 -0
  13. package/dist/agent-wallet/store.js +172 -0
  14. package/dist/api/public/_http.d.ts +15 -0
  15. package/dist/api/public/_http.js +24 -0
  16. package/dist/api/public/hyperliquid.js +36 -24
  17. package/dist/api/public/lighter.d.ts +7 -1
  18. package/dist/api/public/lighter.js +85 -37
  19. package/dist/api/public/pacifica.js +47 -18
  20. package/dist/bot/engine.js +23 -4
  21. package/dist/bot/presets.js +3 -3
  22. package/dist/bot/strategies/funding-arb-strategy.d.ts +0 -23
  23. package/dist/bot/strategies/funding-arb-strategy.js +41 -176
  24. package/dist/bot/strategies/funding-auto.js +2 -2
  25. package/dist/bridge-engine.js +102 -49
  26. package/dist/cli-spec.js +1 -2
  27. package/dist/commands/account.js +2 -500
  28. package/dist/commands/agent.d.ts +8 -2
  29. package/dist/commands/agent.js +1903 -366
  30. package/dist/commands/alerts.js +3 -1
  31. package/dist/commands/arb/index.js +37 -7
  32. package/dist/commands/arb-auto.js +44 -11
  33. package/dist/commands/arb.js +83 -34
  34. package/dist/commands/bot.d.ts +1 -1
  35. package/dist/commands/bot.js +20 -16
  36. package/dist/commands/bridge.d.ts +1 -1
  37. package/dist/commands/bridge.js +4 -4
  38. package/dist/commands/funds.d.ts +1 -1
  39. package/dist/commands/funds.js +18 -68
  40. package/dist/commands/jobs.d.ts +1 -1
  41. package/dist/commands/jobs.js +2 -2
  42. package/dist/commands/manage.d.ts +1 -1
  43. package/dist/commands/manage.js +12 -84
  44. package/dist/commands/plan.d.ts +1 -1
  45. package/dist/commands/plan.js +2 -2
  46. package/dist/commands/portfolio.d.ts +19 -3
  47. package/dist/commands/portfolio.js +747 -163
  48. package/dist/commands/rebalance.d.ts +1 -1
  49. package/dist/commands/rebalance.js +18 -12
  50. package/dist/commands/risk.js +2 -6
  51. package/dist/commands/settings.js +30 -53
  52. package/dist/commands/trade.js +16 -15
  53. package/dist/commands/wallet.d.ts +2 -1
  54. package/dist/commands/wallet.js +1122 -176
  55. package/dist/config.d.ts +9 -0
  56. package/dist/config.js +55 -9
  57. package/dist/errors.d.ts +64 -0
  58. package/dist/errors.js +82 -2
  59. package/dist/event-stream.js +12 -1
  60. package/dist/exchanges/aster-typed-data.d.ts +156 -0
  61. package/dist/exchanges/aster-typed-data.js +189 -0
  62. package/dist/exchanges/aster.d.ts +60 -11
  63. package/dist/exchanges/aster.js +278 -136
  64. package/dist/exchanges/hyperliquid.d.ts +14 -0
  65. package/dist/exchanges/hyperliquid.js +88 -34
  66. package/dist/exchanges/lighter-spot.js +19 -5
  67. package/dist/exchanges/lighter.d.ts +66 -0
  68. package/dist/exchanges/lighter.js +213 -31
  69. package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
  70. package/dist/exchanges/pacifica-typed-data.js +74 -0
  71. package/dist/exchanges/pacifica.d.ts +29 -1
  72. package/dist/exchanges/pacifica.js +94 -6
  73. package/dist/funding/rates.js +59 -21
  74. package/dist/guardrail/contracts.d.ts +14 -0
  75. package/dist/guardrail/contracts.js +72 -0
  76. package/dist/guardrail/perp-guardrail.d.ts +21 -0
  77. package/dist/guardrail/perp-guardrail.js +145 -0
  78. package/dist/index.d.ts +1 -1
  79. package/dist/index.js +307 -362
  80. package/dist/jobs.js +3 -1
  81. package/dist/mcp-server.js +92 -88
  82. package/dist/position-history.js +47 -4
  83. package/dist/settings.d.ts +69 -3
  84. package/dist/settings.js +3 -2
  85. package/dist/signer/index.d.ts +2 -0
  86. package/dist/signer/index.js +2 -0
  87. package/dist/signer/ows-evm.d.ts +32 -0
  88. package/dist/signer/ows-evm.js +86 -0
  89. package/dist/signer/ows-loader.d.ts +2 -0
  90. package/dist/signer/ows-loader.js +6 -0
  91. package/dist/signer/ows-solana.d.ts +25 -0
  92. package/dist/signer/ows-solana.js +56 -0
  93. package/dist/smart-order.d.ts +8 -2
  94. package/dist/smart-order.js +46 -11
  95. package/dist/strategies/funding-arb.js +29 -8
  96. package/dist/trade-validator.js +30 -5
  97. package/dist/utils.d.ts +10 -0
  98. package/dist/utils.js +19 -0
  99. package/package.json +4 -2
  100. package/skills/perp-cli/SKILL.md +23 -14
  101. package/skills/perp-cli/references/agent-operations.md +6 -4
  102. package/skills/perp-cli/references/commands.md +55 -34
  103. package/skills/perp-cli/scripts/preflight.sh +4 -4
  104. package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
  105. package/dist/__tests__/alert-logic.test.d.ts +0 -1
  106. package/dist/__tests__/alert-logic.test.js +0 -107
  107. package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
  108. package/dist/__tests__/arb-auto-3dex.test.js +0 -397
  109. package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
  110. package/dist/__tests__/arb-history-stats.test.js +0 -176
  111. package/dist/__tests__/arb-logic.test.d.ts +0 -1
  112. package/dist/__tests__/arb-logic.test.js +0 -84
  113. package/dist/__tests__/arb-manage.test.d.ts +0 -1
  114. package/dist/__tests__/arb-manage.test.js +0 -253
  115. package/dist/__tests__/arb-new-features.test.d.ts +0 -1
  116. package/dist/__tests__/arb-new-features.test.js +0 -457
  117. package/dist/__tests__/arb-sizing.test.d.ts +0 -1
  118. package/dist/__tests__/arb-sizing.test.js +0 -48
  119. package/dist/__tests__/arb-state.test.d.ts +0 -1
  120. package/dist/__tests__/arb-state.test.js +0 -284
  121. package/dist/__tests__/arb-userflow.test.d.ts +0 -1
  122. package/dist/__tests__/arb-userflow.test.js +0 -945
  123. package/dist/__tests__/arb-utils.test.d.ts +0 -1
  124. package/dist/__tests__/arb-utils.test.js +0 -264
  125. package/dist/__tests__/bot-conditions.test.d.ts +0 -1
  126. package/dist/__tests__/bot-conditions.test.js +0 -341
  127. package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
  128. package/dist/__tests__/client-id-tracker.test.js +0 -137
  129. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
  130. package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
  131. package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
  132. package/dist/__tests__/commands/order-intent.test.js +0 -600
  133. package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
  134. package/dist/__tests__/commands/trade-commands.test.js +0 -821
  135. package/dist/__tests__/config.test.d.ts +0 -1
  136. package/dist/__tests__/config.test.js +0 -86
  137. package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
  138. package/dist/__tests__/cross-chain-margin.test.js +0 -287
  139. package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
  140. package/dist/__tests__/dex-asset-map.test.js +0 -191
  141. package/dist/__tests__/errors.test.d.ts +0 -1
  142. package/dist/__tests__/errors.test.js +0 -110
  143. package/dist/__tests__/event-stream.test.d.ts +0 -1
  144. package/dist/__tests__/event-stream.test.js +0 -276
  145. package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
  146. package/dist/__tests__/exchanges/interface.test.js +0 -132
  147. package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
  148. package/dist/__tests__/exchanges/mock-adapter.js +0 -137
  149. package/dist/__tests__/execution-log.test.d.ts +0 -1
  150. package/dist/__tests__/execution-log.test.js +0 -106
  151. package/dist/__tests__/funding-calc.test.d.ts +0 -1
  152. package/dist/__tests__/funding-calc.test.js +0 -71
  153. package/dist/__tests__/funding-history.test.d.ts +0 -1
  154. package/dist/__tests__/funding-history.test.js +0 -343
  155. package/dist/__tests__/funding-rates.test.d.ts +0 -1
  156. package/dist/__tests__/funding-rates.test.js +0 -342
  157. package/dist/__tests__/funding.test.d.ts +0 -1
  158. package/dist/__tests__/funding.test.js +0 -173
  159. package/dist/__tests__/gap-logic.test.d.ts +0 -1
  160. package/dist/__tests__/gap-logic.test.js +0 -43
  161. package/dist/__tests__/hip3-dex.test.d.ts +0 -1
  162. package/dist/__tests__/hip3-dex.test.js +0 -234
  163. package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
  164. package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
  165. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
  166. package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
  167. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
  168. package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
  169. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
  170. package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
  171. package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
  172. package/dist/__tests__/integration/bridge.integration.test.js +0 -309
  173. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
  174. package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
  175. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
  176. package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
  177. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
  178. package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
  179. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
  180. package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
  181. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
  182. package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
  183. package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
  184. package/dist/__tests__/integration/lighter.integration.test.js +0 -53
  185. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
  186. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
  187. package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
  188. package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
  189. package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
  190. package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
  191. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
  192. package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
  193. package/dist/__tests__/liquidity.test.d.ts +0 -1
  194. package/dist/__tests__/liquidity.test.js +0 -225
  195. package/dist/__tests__/plan-executor.test.d.ts +0 -1
  196. package/dist/__tests__/plan-executor.test.js +0 -314
  197. package/dist/__tests__/position-history.test.d.ts +0 -1
  198. package/dist/__tests__/position-history.test.js +0 -367
  199. package/dist/__tests__/retry.test.d.ts +0 -1
  200. package/dist/__tests__/retry.test.js +0 -310
  201. package/dist/__tests__/risk-assessment.test.d.ts +0 -1
  202. package/dist/__tests__/risk-assessment.test.js +0 -288
  203. package/dist/__tests__/security-adversarial.test.d.ts +0 -1
  204. package/dist/__tests__/security-adversarial.test.js +0 -574
  205. package/dist/__tests__/strategies.test.d.ts +0 -1
  206. package/dist/__tests__/strategies.test.js +0 -539
  207. package/dist/__tests__/trade-execution.test.d.ts +0 -1
  208. package/dist/__tests__/trade-execution.test.js +0 -129
  209. package/dist/__tests__/trade-validator.test.d.ts +0 -1
  210. package/dist/__tests__/trade-validator.test.js +0 -655
  211. package/dist/__tests__/utils.test.d.ts +0 -1
  212. package/dist/__tests__/utils.test.js +0 -76
  213. package/dist/arb/reconcile.d.ts +0 -28
  214. package/dist/arb/reconcile.js +0 -162
  215. package/dist/commands/alert.d.ts +0 -2
  216. package/dist/commands/alert.js +0 -421
  217. package/dist/commands/analytics.d.ts +0 -3
  218. package/dist/commands/analytics.js +0 -311
  219. package/dist/commands/arb-manage.d.ts +0 -5
  220. package/dist/commands/arb-manage.js +0 -5
  221. package/dist/commands/dashboard.d.ts +0 -3
  222. package/dist/commands/dashboard.js +0 -176
  223. package/dist/commands/deposit.d.ts +0 -4
  224. package/dist/commands/deposit.js +0 -573
  225. package/dist/commands/dex.d.ts +0 -3
  226. package/dist/commands/dex.js +0 -120
  227. package/dist/commands/env.d.ts +0 -2
  228. package/dist/commands/env.js +0 -136
  229. package/dist/commands/funding.d.ts +0 -3
  230. package/dist/commands/funding.js +0 -554
  231. package/dist/commands/gap.d.ts +0 -2
  232. package/dist/commands/gap.js +0 -305
  233. package/dist/commands/health.d.ts +0 -2
  234. package/dist/commands/health.js +0 -67
  235. package/dist/commands/multileg.d.ts +0 -16
  236. package/dist/commands/multileg.js +0 -174
  237. package/dist/commands/pnl.d.ts +0 -12
  238. package/dist/commands/pnl.js +0 -243
  239. package/dist/commands/run.d.ts +0 -3
  240. package/dist/commands/run.js +0 -202
  241. package/dist/commands/stream.d.ts +0 -5
  242. package/dist/commands/stream.js +0 -200
  243. package/dist/commands/withdraw.d.ts +0 -3
  244. package/dist/commands/withdraw.js +0 -187
  245. package/dist/exchanges/edgex.d.ts +0 -62
  246. package/dist/exchanges/edgex.js +0 -552
  247. package/dist/exchanges/nado.d.ts +0 -52
  248. package/dist/exchanges/nado.js +0 -223
  249. package/dist/funding/index.d.ts +0 -3
  250. package/dist/funding/index.js +0 -3
  251. package/dist/retry.d.ts +0 -74
  252. package/dist/retry.js +0 -129
  253. package/dist/ws/market-feed.d.ts +0 -74
  254. package/dist/ws/market-feed.js +0 -227
@@ -1,99 +1,7 @@
1
- import { makeTable, formatUsd, formatPnl, printJson, jsonOk, jsonError, symbolMatch, withJsonErrors } from "../utils.js";
1
+ import { makeTable, formatUsd, formatPnl, printJson, jsonOk, jsonError, symbolMatch } from "../utils.js";
2
2
  import chalk from "chalk";
3
- import { assessRisk } from "../risk.js";
4
3
  import { hasPacificaSdk, isDexCapable } from "../exchanges/capabilities.js";
5
4
  const EXCHANGES = ["pacifica", "hyperliquid", "lighter", "aster"];
6
- async function fetchExchangeSnapshot(name, getAdapter) {
7
- try {
8
- const adapter = await getAdapter(name);
9
- const [balance, positions, orders] = await Promise.all([
10
- adapter.getBalance(),
11
- adapter.getPositions(),
12
- adapter.getOpenOrders(),
13
- ]);
14
- return {
15
- exchange: name,
16
- connected: true,
17
- balance,
18
- positions,
19
- openOrders: orders.length,
20
- };
21
- }
22
- catch (err) {
23
- return {
24
- exchange: name,
25
- connected: false,
26
- balance: null,
27
- positions: [],
28
- openOrders: 0,
29
- error: err instanceof Error ? err.message : String(err),
30
- };
31
- }
32
- }
33
- function buildSummary(snapshots) {
34
- let totalEquity = 0;
35
- let totalAvailable = 0;
36
- let totalMarginUsed = 0;
37
- let totalUnrealizedPnl = 0;
38
- let totalPositions = 0;
39
- let totalOpenOrders = 0;
40
- const allPositions = [];
41
- for (const snap of snapshots) {
42
- if (snap.balance) {
43
- totalEquity += Number(snap.balance.equity);
44
- totalAvailable += Number(snap.balance.available);
45
- totalMarginUsed += Number(snap.balance.marginUsed);
46
- totalUnrealizedPnl += Number(snap.balance.unrealizedPnl);
47
- }
48
- totalPositions += snap.positions.length;
49
- totalOpenOrders += snap.openOrders;
50
- for (const pos of snap.positions) {
51
- allPositions.push({ ...pos, exchange: snap.exchange });
52
- }
53
- }
54
- // Risk metrics
55
- const marginUtilization = totalEquity > 0 ? (totalMarginUsed / totalEquity) * 100 : 0;
56
- let largestPosition = null;
57
- for (const pos of allPositions) {
58
- const notional = Math.abs(Number(pos.size) * Number(pos.markPrice));
59
- if (!largestPosition || notional > largestPosition.notional) {
60
- largestPosition = { symbol: pos.symbol, exchange: pos.exchange, notional };
61
- }
62
- }
63
- const exchangeConcentration = snapshots
64
- .filter(s => s.balance && Number(s.balance.equity) > 0)
65
- .map(s => ({
66
- exchange: s.exchange,
67
- pct: totalEquity > 0 ? (Number(s.balance.equity) / totalEquity) * 100 : 0,
68
- }))
69
- .sort((a, b) => b.pct - a.pct);
70
- // Full risk assessment (reuses same data, no extra API calls)
71
- const riskBalances = snapshots
72
- .filter(s => s.balance)
73
- .map(s => ({ exchange: s.exchange, balance: s.balance }));
74
- const riskPositions = allPositions.map(p => ({ exchange: p.exchange, position: p }));
75
- const assessment = assessRisk(riskBalances, riskPositions);
76
- return {
77
- totalEquity,
78
- totalAvailable,
79
- totalMarginUsed,
80
- totalUnrealizedPnl,
81
- totalPositions,
82
- totalOpenOrders,
83
- exchanges: snapshots,
84
- positions: allPositions,
85
- riskMetrics: {
86
- marginUtilization,
87
- largestPosition,
88
- exchangeConcentration,
89
- },
90
- risk: {
91
- level: assessment.level,
92
- canTrade: assessment.canTrade,
93
- violations: assessment.violations,
94
- },
95
- };
96
- }
97
5
  function pac(adapter) {
98
6
  if (!hasPacificaSdk(adapter))
99
7
  throw new Error("Market settings are only available on Pacifica.");
@@ -101,231 +9,7 @@ function pac(adapter) {
101
9
  }
102
10
  export function registerAccountCommands(program, getAdapter, isJson, getAdapterForExchange) {
103
11
  const account = program.command("account").description("Account commands");
104
- async function fetchExchangeBalance(exName, adapter) {
105
- const name = exName.toLowerCase();
106
- const [bal, fundingPayments] = await Promise.all([
107
- adapter.getBalance(),
108
- adapter.getFundingPayments(200).catch(() => []),
109
- ]);
110
- let spotBalances = [];
111
- try {
112
- if (name === "hyperliquid") {
113
- const { HyperliquidSpotAdapter } = await import("../exchanges/hyperliquid-spot.js");
114
- const { HyperliquidAdapter } = await import("../exchanges/hyperliquid.js");
115
- const hlSpot = new HyperliquidSpotAdapter(adapter);
116
- await hlSpot.init();
117
- const raw = await hlSpot.getSpotBalances();
118
- const markets = await hlSpot.getSpotMarkets();
119
- const priceMap = new Map(markets.map(m => [m.baseToken.toUpperCase(), Number(m.markPrice)]));
120
- const stripSuffix = (t) => t.replace(/-SPOT$/i, "").toUpperCase();
121
- spotBalances = raw
122
- .filter(b => Number(b.total) > 0)
123
- .map(b => {
124
- const base = stripSuffix(b.token);
125
- return {
126
- ...b,
127
- valueUsd: base === "USDC" ? Number(b.total) : (priceMap.get(base) ?? 0) * Number(b.total),
128
- };
129
- });
130
- }
131
- else if (name === "lighter") {
132
- const { LighterAdapter } = await import("../exchanges/lighter.js");
133
- const { LighterSpotAdapter } = await import("../exchanges/lighter-spot.js");
134
- const ltSpot = new LighterSpotAdapter(adapter);
135
- await ltSpot.init();
136
- const raw = await ltSpot.getSpotBalances();
137
- const markets = await ltSpot.getSpotMarkets();
138
- const priceMap = new Map(markets.map(m => [m.baseToken.toUpperCase(), Number(m.markPrice)]));
139
- spotBalances = raw
140
- .filter(b => Number(b.total) > 0)
141
- .map(b => ({
142
- ...b,
143
- valueUsd: b.token === "USDC" || b.token === "USDC_SPOT"
144
- ? Number(b.total)
145
- : (priceMap.get(b.token.toUpperCase()) ?? 0) * Number(b.total),
146
- }));
147
- }
148
- }
149
- catch { /* spot not available */ }
150
- // HL unified account: perp equity already includes spot USDC balance.
151
- // Detect by checking if adapter is HL without a dex override.
152
- const isUnified = name === "hyperliquid" && (!isDexCapable(adapter) || !adapter.dex);
153
- const cutoff24h = Date.now() - 24 * 60 * 60 * 1000;
154
- const recent = fundingPayments.filter(f => f.time >= cutoff24h);
155
- const funding24h = recent.reduce((sum, f) => sum + Number(f.payment), 0);
156
- const totalSpotUsd = spotBalances.reduce((s, b) => s + (b.valueUsd ?? 0), 0);
157
- // For unified accounts, equity already contains USDC spot value.
158
- // Only add non-USDC spot token values to avoid double-counting.
159
- const nonUsdcSpotUsd = isUnified
160
- ? spotBalances
161
- .filter(b => {
162
- const tk = b.token.replace(/-SPOT$/i, "").toUpperCase();
163
- return tk !== "USDC";
164
- })
165
- .reduce((s, b) => s + (b.valueUsd ?? 0), 0)
166
- : totalSpotUsd;
167
- return {
168
- exchange: name,
169
- perp: bal,
170
- spot: spotBalances,
171
- funding24h,
172
- fundingPayments24h: recent.length,
173
- totalSpotValueUsd: totalSpotUsd,
174
- totalAccountValueUsd: Number(bal.equity) + nonUsdcSpotUsd,
175
- unifiedAccount: isUnified,
176
- };
177
- }
178
- function printSingleBalance(exName, r) {
179
- console.log(chalk.cyan.bold(`\n ${exName.toUpperCase()} Account Balance`));
180
- if (r.unifiedAccount)
181
- console.log(chalk.gray(" (Unified account — perp & spot share single balance)\n"));
182
- else
183
- console.log();
184
- console.log(chalk.white.bold(r.unifiedAccount ? " Account" : " Perp Account"));
185
- console.log(` Equity: $${formatUsd(r.perp.equity)}`);
186
- console.log(` Available: $${formatUsd(r.perp.available)}`);
187
- console.log(` Margin Used: $${formatUsd(r.perp.marginUsed)}`);
188
- console.log(` Unreal. PnL: ${formatPnl(r.perp.unrealizedPnl)}`);
189
- // For unified, show non-USDC spot holdings (USDC is already in equity)
190
- const displaySpot = r.unifiedAccount
191
- ? r.spot.filter(b => b.token.replace(/-SPOT$/i, "").toUpperCase() !== "USDC")
192
- : r.spot;
193
- if (displaySpot.length > 0) {
194
- console.log(chalk.white.bold("\n Spot Holdings"));
195
- const rows = displaySpot.map(b => [
196
- chalk.white.bold(b.token),
197
- b.total,
198
- b.available,
199
- b.held !== "0" ? b.held : chalk.gray("-"),
200
- b.valueUsd ? `$${formatUsd(b.valueUsd)}` : chalk.gray("-"),
201
- ]);
202
- console.log(makeTable(["Token", "Total", "Available", "Held", "Value"], rows));
203
- }
204
- console.log(chalk.white.bold(" Funding (24h)"));
205
- console.log(` Payments: ${r.fundingPayments24h}`);
206
- console.log(` Total: ${formatPnl(r.funding24h)}`);
207
- // Only show Total breakdown when there are non-USDC spot tokens adding value
208
- const nonUsdcSpotValue = displaySpot.reduce((s, b) => s + (b.valueUsd ?? 0), 0);
209
- if (nonUsdcSpotValue > 0) {
210
- console.log(chalk.white.bold("\n Total"));
211
- console.log(` Equity: $${formatUsd(r.perp.equity)}`);
212
- console.log(` Spot (other): $${formatUsd(nonUsdcSpotValue)}`);
213
- console.log(` Total Value: $${formatUsd(r.totalAccountValueUsd)}`);
214
- }
215
- }
216
- account
217
- .command("balance")
218
- .description("Account overview: perp balance, spot holdings, and 24h funding")
219
- .action(async () => {
220
- // Detect if user explicitly set -e (single exchange mode)
221
- const explicitExchange = program.getOptionValueSource?.("exchange") === "cli";
222
- // Multi-exchange mode: show all exchanges
223
- if (!explicitExchange && getAdapterForExchange) {
224
- const results = [];
225
- const errors = {};
226
- await Promise.all(EXCHANGES.map(async (ex) => {
227
- try {
228
- const adapter = await getAdapterForExchange(ex);
229
- results.push(await fetchExchangeBalance(ex, adapter));
230
- }
231
- catch (err) {
232
- errors[ex] = err instanceof Error ? err.message : String(err);
233
- }
234
- }));
235
- // Sort by equity desc
236
- results.sort((a, b) => b.totalAccountValueUsd - a.totalAccountValueUsd);
237
- if (isJson()) {
238
- const grandTotal = results.reduce((s, r) => s + r.totalAccountValueUsd, 0);
239
- return printJson(jsonOk({
240
- exchanges: results,
241
- errors: Object.keys(errors).length > 0 ? errors : undefined,
242
- totalEquity: results.reduce((s, r) => s + Number(r.perp.equity), 0),
243
- totalSpotValueUsd: results.reduce((s, r) => {
244
- if (r.unifiedAccount) {
245
- return s + r.spot
246
- .filter(b => b.token.replace(/-SPOT$/i, "").toUpperCase() !== "USDC")
247
- .reduce((ss, b) => ss + (b.valueUsd ?? 0), 0);
248
- }
249
- return s + r.totalSpotValueUsd;
250
- }, 0),
251
- totalAccountValueUsd: grandTotal,
252
- totalFunding24h: results.reduce((s, r) => s + r.funding24h, 0),
253
- }));
254
- }
255
- console.log(chalk.cyan.bold("\n All Exchanges — Account Balance\n"));
256
- // Summary table
257
- // For unified accounts, Spot column shows non-USDC only (USDC already in Equity)
258
- const balRows = results.map(r => {
259
- const nonUsdcSpot = r.unifiedAccount
260
- ? r.spot
261
- .filter(b => b.token.replace(/-SPOT$/i, "").toUpperCase() !== "USDC")
262
- .reduce((s, b) => s + (b.valueUsd ?? 0), 0)
263
- : r.totalSpotValueUsd;
264
- return [
265
- chalk.white.bold(r.exchange) + (r.unifiedAccount ? chalk.gray(" ★") : ""),
266
- `$${formatUsd(r.perp.equity)}`,
267
- `$${formatUsd(r.perp.available)}`,
268
- `$${formatUsd(r.perp.marginUsed)}`,
269
- nonUsdcSpot > 0 ? `$${formatUsd(nonUsdcSpot)}` : chalk.gray("-"),
270
- formatPnl(r.funding24h),
271
- `$${formatUsd(r.totalAccountValueUsd)}`,
272
- ];
273
- });
274
- const grandEquity = results.reduce((s, r) => s + Number(r.perp.equity), 0);
275
- const grandSpot = results.reduce((s, r) => {
276
- if (r.unifiedAccount) {
277
- return s + r.spot
278
- .filter(b => b.token.replace(/-SPOT$/i, "").toUpperCase() !== "USDC")
279
- .reduce((ss, b) => ss + (b.valueUsd ?? 0), 0);
280
- }
281
- return s + r.totalSpotValueUsd;
282
- }, 0);
283
- const grandFunding = results.reduce((s, r) => s + r.funding24h, 0);
284
- const grandTotal = results.reduce((s, r) => s + r.totalAccountValueUsd, 0);
285
- balRows.push([
286
- chalk.cyan.bold("TOTAL"),
287
- chalk.cyan.bold(`$${formatUsd(grandEquity)}`),
288
- "",
289
- "",
290
- grandSpot > 0 ? chalk.cyan.bold(`$${formatUsd(grandSpot)}`) : "",
291
- formatPnl(grandFunding),
292
- chalk.cyan.bold(`$${formatUsd(grandTotal)}`),
293
- ]);
294
- console.log(makeTable(["Exchange", "Equity", "Available", "Margin", "Spot", "Funding 24h", "Total"], balRows));
295
- if (results.some(r => r.unifiedAccount)) {
296
- console.log(chalk.gray(" ★ Unified account — Equity includes spot USDC, Spot shows non-USDC tokens only\n"));
297
- }
298
- // Spot details per exchange (for unified, skip USDC as it's in equity)
299
- for (const r of results) {
300
- const displaySpot = r.unifiedAccount
301
- ? r.spot.filter(b => b.token.replace(/-SPOT$/i, "").toUpperCase() !== "USDC")
302
- : r.spot;
303
- if (displaySpot.length > 0) {
304
- console.log(chalk.white.bold(` ${r.exchange.toUpperCase()} Spot Holdings`));
305
- const rows = displaySpot.map(b => [
306
- chalk.white.bold(b.token),
307
- b.total,
308
- b.valueUsd ? `$${formatUsd(b.valueUsd)}` : chalk.gray("-"),
309
- ]);
310
- console.log(makeTable(["Token", "Total", "Value"], rows));
311
- }
312
- }
313
- // Errors
314
- for (const [ex, msg] of Object.entries(errors)) {
315
- console.log(chalk.gray(` ${ex}: ${msg}`));
316
- }
317
- console.log();
318
- return;
319
- }
320
- // Single exchange mode (explicit -e or no getAdapterForExchange)
321
- const adapter = await getAdapter();
322
- const exName = adapter.name.toLowerCase();
323
- const result = await fetchExchangeBalance(exName, adapter);
324
- if (isJson())
325
- return printJson(jsonOk(result));
326
- printSingleBalance(exName, result);
327
- console.log();
328
- });
12
+ // ── Single exchange balance fetch (used by both single + multi mode) ──
329
13
  // ── HIP-3 dex helper: fetch data from all deployed dexes in parallel ──
330
14
  async function fetchHip3Data(hlAdapter, fetcher) {
331
15
  const dexes = await hlAdapter.listDeployedDexes();
@@ -932,186 +616,4 @@ export function registerAccountCommands(program, getAdapter, isJson, getAdapterF
932
616
  ]);
933
617
  console.log(makeTable(["ID", "Symbol", "Side", "Size", "Filled", "Duration"], rows));
934
618
  });
935
- // ── Portfolio (top-level command) ──
936
- if (getAdapterForExchange) {
937
- program
938
- .command("portfolio")
939
- .description("Cross-exchange portfolio overview with risk metrics")
940
- .option("--exchange <exchanges>", "Comma-separated exchanges to include (default: all)")
941
- .action(async (opts) => {
942
- await withJsonErrors(isJson(), async () => {
943
- const exchanges = opts.exchange
944
- ? opts.exchange.split(",").map(e => e.trim())
945
- : [...EXCHANGES];
946
- // Fetch snapshots + funding + spot ALL in parallel
947
- const fundingByExSym = new Map();
948
- const spotRows = [];
949
- let totalSpotValue = 0;
950
- const [snapshots] = await Promise.all([
951
- // Phase 1: balance + positions + orders
952
- Promise.all(exchanges.map(ex => fetchExchangeSnapshot(ex, getAdapterForExchange))),
953
- // Phase 2: funding payments (parallel with Phase 1)
954
- Promise.allSettled(exchanges.map(async (exName) => {
955
- try {
956
- const adapter = await getAdapterForExchange(exName);
957
- const payments = await adapter.getFundingPayments(200);
958
- for (const p of payments) {
959
- const sym = p.symbol.replace("-PERP", "").toUpperCase();
960
- const key = `${exName}:${sym}`;
961
- fundingByExSym.set(key, (fundingByExSym.get(key) ?? 0) + Number(p.payment));
962
- }
963
- }
964
- catch { /* skip */ }
965
- })),
966
- // Phase 3: spot balances (parallel with Phase 1+2)
967
- Promise.allSettled(exchanges.map(async (exName) => {
968
- try {
969
- const adapter = await getAdapterForExchange(exName);
970
- if (exName === "hyperliquid") {
971
- const { HyperliquidSpotAdapter } = await import("../exchanges/hyperliquid-spot.js");
972
- const { HyperliquidAdapter } = await import("../exchanges/hyperliquid.js");
973
- const hlSpot = new HyperliquidSpotAdapter(adapter);
974
- await hlSpot.init();
975
- const bals = await hlSpot.getSpotBalances();
976
- const markets = await hlSpot.getSpotMarkets();
977
- const priceMap = new Map(markets.map(m => [m.baseToken.toUpperCase(), Number(m.markPrice)]));
978
- for (const b of bals) {
979
- const base = b.token.replace(/-SPOT$/i, "").toUpperCase();
980
- if (base === "USDC" || Number(b.total) <= 0)
981
- continue;
982
- const price = priceMap.get(base) ?? 0;
983
- const value = price * Number(b.total);
984
- if (value < 1)
985
- continue;
986
- totalSpotValue += value;
987
- spotRows.push([chalk.white.bold(base), chalk.gray("hyperliquid"), b.total, `$${formatUsd(price)}`, `$${formatUsd(value)}`]);
988
- }
989
- }
990
- else if (exName === "lighter") {
991
- const { LighterAdapter } = await import("../exchanges/lighter.js");
992
- const { LighterSpotAdapter } = await import("../exchanges/lighter-spot.js");
993
- const ltSpot = new LighterSpotAdapter(adapter);
994
- await ltSpot.init();
995
- const bals = await ltSpot.getSpotBalances();
996
- const markets = await ltSpot.getSpotMarkets();
997
- const priceMap = new Map(markets.map(m => [m.baseToken.toUpperCase(), Number(m.markPrice)]));
998
- for (const b of bals) {
999
- if (b.token === "USDC" || b.token === "USDC_SPOT" || Number(b.total) <= 0)
1000
- continue;
1001
- const price = priceMap.get(b.token.toUpperCase()) ?? 0;
1002
- const value = price * Number(b.total);
1003
- if (value < 1)
1004
- continue;
1005
- totalSpotValue += value;
1006
- spotRows.push([chalk.white.bold(b.token), chalk.gray("lighter"), b.total, `$${formatUsd(price)}`, `$${formatUsd(value)}`]);
1007
- }
1008
- }
1009
- }
1010
- catch { /* spot not available */ }
1011
- })),
1012
- ]);
1013
- const summary = buildSummary(snapshots);
1014
- if (isJson()) {
1015
- return printJson(jsonOk(summary));
1016
- }
1017
- // ── Header ──
1018
- console.log(chalk.cyan.bold("\n Cross-Exchange Portfolio\n"));
1019
- // ── Balance Summary ──
1020
- console.log(chalk.white.bold(" Balances"));
1021
- const balRows = snapshots.map(s => {
1022
- if (!s.connected) {
1023
- return [chalk.white(s.exchange), chalk.red("disconnected"), "-", "-", "-", chalk.gray(s.error ?? "")];
1024
- }
1025
- const b = s.balance;
1026
- return [
1027
- chalk.white.bold(s.exchange),
1028
- `$${formatUsd(b.equity)}`,
1029
- `$${formatUsd(b.available)}`,
1030
- `$${formatUsd(b.marginUsed)}`,
1031
- formatPnl(b.unrealizedPnl),
1032
- chalk.green("connected"),
1033
- ];
1034
- });
1035
- balRows.push([
1036
- chalk.cyan.bold("TOTAL"),
1037
- chalk.cyan.bold(`$${formatUsd(summary.totalEquity)}`),
1038
- chalk.cyan.bold(`$${formatUsd(summary.totalAvailable)}`),
1039
- chalk.cyan.bold(`$${formatUsd(summary.totalMarginUsed)}`),
1040
- formatPnl(summary.totalUnrealizedPnl),
1041
- "",
1042
- ]);
1043
- console.log(makeTable(["Exchange", "Equity", "Available", "Margin Used", "uPnL", "Status"], balRows));
1044
- if (summary.positions.length > 0) {
1045
- console.log(chalk.white.bold("\n Open Positions"));
1046
- const posRows = summary.positions.map(p => {
1047
- const sideColor = p.side === "long" ? chalk.green : chalk.red;
1048
- const notional = Math.abs(Number(p.size) * Number(p.markPrice));
1049
- const sym = p.symbol.replace("-PERP", "").toUpperCase();
1050
- const funding = fundingByExSym.get(`${p.exchange}:${sym}`) ?? 0;
1051
- return [
1052
- chalk.white.bold(p.symbol),
1053
- chalk.gray(p.exchange),
1054
- sideColor(p.side.toUpperCase()),
1055
- p.size,
1056
- `$${formatUsd(p.entryPrice)}`,
1057
- `$${formatUsd(p.markPrice)}`,
1058
- formatPnl(p.unrealizedPnl),
1059
- formatPnl(funding),
1060
- `$${formatUsd(notional)}`,
1061
- `${p.leverage}x`,
1062
- ];
1063
- });
1064
- console.log(makeTable(["Symbol", "Exchange", "Side", "Size", "Entry", "Mark", "uPnL", "Funding", "Notional", "Lev"], posRows));
1065
- }
1066
- else {
1067
- console.log(chalk.gray("\n No open positions.\n"));
1068
- }
1069
- // ── Spot Holdings (already fetched in parallel above) ──
1070
- if (spotRows.length > 0) {
1071
- console.log(chalk.white.bold("\n Spot Holdings"));
1072
- console.log(makeTable(["Token", "Exchange", "Amount", "Price", "Value"], spotRows));
1073
- }
1074
- // ── Total (perp + spot) ──
1075
- if (totalSpotValue > 0) {
1076
- const totalValue = summary.totalEquity + totalSpotValue;
1077
- console.log(chalk.white.bold(" Total Account Value"));
1078
- console.log(` Perp Equity: $${formatUsd(summary.totalEquity)}`);
1079
- console.log(` Spot Value: $${formatUsd(totalSpotValue)}`);
1080
- console.log(` ${chalk.cyan.bold(`Total: $${formatUsd(totalValue)}`)}`);
1081
- }
1082
- // ── Risk Metrics ──
1083
- console.log(chalk.white.bold("\n Risk Metrics"));
1084
- const levelColor = {
1085
- low: chalk.green,
1086
- medium: chalk.yellow,
1087
- high: chalk.red,
1088
- critical: chalk.bgRed.white,
1089
- }[summary.risk.level];
1090
- console.log(` Risk Level: ${levelColor(summary.risk.level.toUpperCase())}`);
1091
- console.log(` Can Trade: ${summary.risk.canTrade ? chalk.green("YES") : chalk.red("NO")}`);
1092
- const mu = summary.riskMetrics.marginUtilization;
1093
- const muColor = mu < 30 ? chalk.green : mu < 60 ? chalk.yellow : chalk.red;
1094
- console.log(` Margin Utilization: ${muColor(`${mu.toFixed(1)}%`)}`);
1095
- if (summary.riskMetrics.largestPosition) {
1096
- const lp = summary.riskMetrics.largestPosition;
1097
- console.log(` Largest Position: ${lp.symbol} on ${lp.exchange} ($${formatUsd(lp.notional)})`);
1098
- }
1099
- if (summary.riskMetrics.exchangeConcentration.length > 0) {
1100
- console.log(` Exchange Allocation:`);
1101
- for (const ec of summary.riskMetrics.exchangeConcentration) {
1102
- const bar = "\u2588".repeat(Math.round(ec.pct / 5)) + "\u2591".repeat(20 - Math.round(ec.pct / 5));
1103
- console.log(` ${ec.exchange.padEnd(12)} ${bar} ${ec.pct.toFixed(1)}%`);
1104
- }
1105
- }
1106
- if (summary.risk.violations.length > 0) {
1107
- console.log(chalk.red.bold("\n Risk Violations"));
1108
- for (const v of summary.risk.violations) {
1109
- const sevColor = { low: chalk.green, medium: chalk.yellow, high: chalk.red, critical: chalk.bgRed.white }[v.severity];
1110
- console.log(` ${sevColor(v.severity.toUpperCase().padEnd(8))} ${v.message}`);
1111
- }
1112
- }
1113
- console.log();
1114
- });
1115
- });
1116
- }
1117
619
  }
@@ -1,3 +1,9 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/index.js";
3
- export declare function registerAgentCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean): void;
2
+ /**
3
+ * Register the per-DEX agent-wallet management subtree (`approve` /
4
+ * `list` / `revoke` / `rotate` / `verify`) under the supplied parent
5
+ * `wallet` command. The function previously registered `agent` as a
6
+ * top-level command on `program`; the hard-cut consolidation in v0.11+
7
+ * relocates it to `wallet agent ...`.
8
+ */
9
+ export declare function registerWalletAgentCommands(walletCmd: Command, isJson: () => boolean): void;