suzi-cli 0.1.15 → 0.1.17

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.
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.renderPortfolioDisplay = renderPortfolioDisplay;
6
7
  exports.registerPortfolioCommand = registerPortfolioCommand;
7
8
  const chalk_1 = __importDefault(require("chalk"));
8
9
  const cli_table3_1 = __importDefault(require("cli-table3"));
@@ -26,15 +27,366 @@ function getTokenSymbol(mintOrSymbol) {
26
27
  // Return as-is (full mint address or symbol)
27
28
  return mintOrSymbol;
28
29
  }
30
+ function formatUsdSuffix(valueUsd) {
31
+ if (valueUsd == null || !Number.isFinite(valueUsd))
32
+ return '';
33
+ if (valueUsd > 0 && valueUsd < 0.005)
34
+ return ' (~<$0.01)';
35
+ return ` (~${(0, ui_1.formatUsd)(valueUsd)})`;
36
+ }
37
+ const BALANCE_DISPLAY_THRESHOLDS = {
38
+ usdValue: 0.01,
39
+ };
40
+ function shouldShowBalance(balance, showAllBalances, valueUsd) {
41
+ if (!Number.isFinite(balance) || balance <= 0)
42
+ return false;
43
+ if (showAllBalances)
44
+ return true;
45
+ if (valueUsd != null && Number.isFinite(valueUsd)) {
46
+ return valueUsd > BALANCE_DISPLAY_THRESHOLDS.usdValue;
47
+ }
48
+ // Hide unpriced assets by default; users can opt-in with --all-balances.
49
+ return false;
50
+ }
51
+ function renderPortfolioDisplay(portfolio, options = {}) {
52
+ const showAllBalances = options.showAllBalances ?? false;
53
+ // Display portfolio
54
+ (0, ui_1.header)('Portfolio');
55
+ console.log();
56
+ // Top-level summary
57
+ (0, ui_1.label)('Total Exposure', (0, ui_1.formatUsd)(portfolio.totals.totalExposure));
58
+ (0, ui_1.label)('Available Balance', (0, ui_1.formatUsd)(portfolio.totals.availableBalance));
59
+ (0, ui_1.label)('Open Positions', portfolio.totals.totalPositions.toString());
60
+ console.log();
61
+ // Protocol breakdown
62
+ console.log(ui_1.colors.muted(' Protocol Breakdown:'));
63
+ const protocolTable = new cli_table3_1.default({
64
+ head: [chalk_1.default.gray('Protocol'), chalk_1.default.gray('Value'), chalk_1.default.gray('Positions')],
65
+ style: { head: [], border: ['gray'] },
66
+ colWidths: [20, 18, 12],
67
+ });
68
+ if (portfolio.protocols.hyperliquid.totalEquity > 0 || portfolio.protocols.hyperliquid.totalPositions > 0) {
69
+ protocolTable.push([
70
+ ui_1.colors.evm('Hyperliquid'),
71
+ (0, ui_1.formatUsd)(portfolio.protocols.hyperliquid.totalEquity),
72
+ portfolio.protocols.hyperliquid.totalPositions.toString(),
73
+ ]);
74
+ }
75
+ if (portfolio.protocols.polymarket.totalValue > 0 || portfolio.protocols.polymarket.totalPositions > 0) {
76
+ protocolTable.push([
77
+ ui_1.colors.secondary('Polymarket'),
78
+ (0, ui_1.formatUsd)(portfolio.protocols.polymarket.totalValue),
79
+ portfolio.protocols.polymarket.totalPositions.toString(),
80
+ ]);
81
+ }
82
+ if (portfolio.protocols.kamino.netValueUsd > 0 || portfolio.protocols.kamino.positions.length > 0) {
83
+ protocolTable.push([
84
+ ui_1.colors.sol('Kamino'),
85
+ (0, ui_1.formatUsd)(portfolio.protocols.kamino.netValueUsd),
86
+ portfolio.protocols.kamino.positions.length.toString(),
87
+ ]);
88
+ }
89
+ if (portfolio.protocols.meteora.totalEquity > 0 || portfolio.protocols.meteora.totalPositions > 0) {
90
+ protocolTable.push([
91
+ ui_1.colors.sol('Meteora'),
92
+ (0, ui_1.formatUsd)(portfolio.protocols.meteora.totalEquity),
93
+ portfolio.protocols.meteora.totalPositions.toString(),
94
+ ]);
95
+ }
96
+ if (protocolTable.length > 0) {
97
+ console.log(protocolTable.toString());
98
+ console.log();
99
+ }
100
+ (0, ui_1.divider)();
101
+ // === Spot Balances Section ===
102
+ const balanceTable = new cli_table3_1.default({
103
+ head: [chalk_1.default.gray('Asset'), chalk_1.default.gray('Balance'), chalk_1.default.gray('Chain')],
104
+ style: { head: [], border: ['gray'] },
105
+ colWidths: [46, 24, 16],
106
+ });
107
+ let hasBalances = false;
108
+ // SOL native balance
109
+ if (shouldShowBalance(portfolio.balances.solana.nativeBalance, showAllBalances, portfolio.balances.solana.nativeValueUsd)) {
110
+ balanceTable.push([
111
+ ui_1.colors.sol('SOL'),
112
+ chalk_1.default.bold(`${portfolio.balances.solana.nativeBalance.toFixed(4)}${formatUsdSuffix(portfolio.balances.solana.nativeValueUsd)}`),
113
+ ui_1.colors.muted('(Solana)'),
114
+ ]);
115
+ hasBalances = true;
116
+ }
117
+ // All SPL tokens (dynamic)
118
+ for (const token of portfolio.balances.solana.tokenBalances) {
119
+ if (!shouldShowBalance(token.balance, showAllBalances, token.valueUsd))
120
+ continue;
121
+ const symbol = getTokenSymbol(token.token);
122
+ balanceTable.push([
123
+ chalk_1.default.white(symbol),
124
+ chalk_1.default.bold(`${token.balance.toFixed(2)}${formatUsdSuffix(token.valueUsd)}`),
125
+ ui_1.colors.muted('(Solana)'),
126
+ ]);
127
+ hasBalances = true;
128
+ }
129
+ // Hyperliquid spot balances
130
+ for (const spot of portfolio.protocols.hyperliquid.spotBalances || []) {
131
+ if (!shouldShowBalance(spot.balance, showAllBalances, spot.valueUsd))
132
+ continue;
133
+ balanceTable.push([
134
+ chalk_1.default.green(spot.token),
135
+ chalk_1.default.bold(`${spot.balance.toFixed(2)}${formatUsdSuffix(spot.valueUsd)}`),
136
+ ui_1.colors.muted('(Hyperliquid)'),
137
+ ]);
138
+ hasBalances = true;
139
+ }
140
+ // EVM chain balances (native + ERC-20)
141
+ const evmChains = [
142
+ { key: 'ethereum', display: 'Ethereum', color: chalk_1.default.blue },
143
+ { key: 'arbitrum', display: 'Arbitrum', color: chalk_1.default.cyan },
144
+ { key: 'polygon', display: 'Polygon', color: chalk_1.default.magenta },
145
+ { key: 'base', display: 'Base', color: chalk_1.default.blueBright },
146
+ { key: 'optimism', display: 'Optimism', color: chalk_1.default.red },
147
+ ];
148
+ for (const { key, display, color } of evmChains) {
149
+ const chainData = portfolio.balances.evm[key];
150
+ if (!chainData)
151
+ continue;
152
+ const nativeBalance = Number.parseFloat(chainData.nativeBalance);
153
+ if (shouldShowBalance(nativeBalance, showAllBalances, chainData.nativeValueUsd)) {
154
+ balanceTable.push([
155
+ color(chainData.nativeAsset),
156
+ chalk_1.default.bold(`${nativeBalance.toFixed(4)}${formatUsdSuffix(chainData.nativeValueUsd)}`),
157
+ ui_1.colors.muted(`(${display})`),
158
+ ]);
159
+ hasBalances = true;
160
+ }
161
+ for (const token of chainData.tokenBalances) {
162
+ const tokenBalance = Number.parseFloat(token.balance);
163
+ if (!shouldShowBalance(tokenBalance, showAllBalances, token.valueUsd))
164
+ continue;
165
+ balanceTable.push([
166
+ chalk_1.default.white(token.symbol || 'UNKNOWN'),
167
+ chalk_1.default.bold(`${tokenBalance.toFixed(2)}${formatUsdSuffix(token.valueUsd)}`),
168
+ ui_1.colors.muted(`(${display})`),
169
+ ]);
170
+ hasBalances = true;
171
+ }
172
+ }
173
+ if (hasBalances) {
174
+ console.log(balanceTable.toString());
175
+ }
176
+ else {
177
+ (0, ui_1.info)('No spot balances found.');
178
+ }
179
+ if (portfolio.balances.evm.errors && Object.keys(portfolio.balances.evm.errors).length > 0) {
180
+ console.log();
181
+ (0, ui_1.warn)('Some EVM chains failed to load:');
182
+ for (const [chain, errorMsg] of Object.entries(portfolio.balances.evm.errors)) {
183
+ console.log(` ${chalk_1.default.gray('•')} ${chain}: ${chalk_1.default.red(errorMsg)}`);
184
+ }
185
+ }
186
+ // === Hyperliquid Overview ===
187
+ console.log();
188
+ console.log(ui_1.colors.evm.bold(' Hyperliquid'));
189
+ const marketMakers = portfolio.protocols.hyperliquid.marketMakers;
190
+ // Market Maker Summary Table
191
+ const mmEntries = [
192
+ ['main', marketMakers.main],
193
+ ['xyz', marketMakers.xyz],
194
+ ['flx', marketMakers.flx],
195
+ ['vntl', marketMakers.vntl],
196
+ ['hyna', marketMakers.hyna],
197
+ ['km', marketMakers.km],
198
+ ['cash', marketMakers.cash],
199
+ ];
200
+ const mmSummaryTable = new cli_table3_1.default({
201
+ head: [
202
+ chalk_1.default.gray('Market Maker'),
203
+ chalk_1.default.gray('Equity'),
204
+ chalk_1.default.gray('Available'),
205
+ chalk_1.default.gray('Positions'),
206
+ ],
207
+ style: { head: [], border: ['gray'] },
208
+ });
209
+ let hasActiveMarketMakers = false;
210
+ for (const [mm, data] of mmEntries) {
211
+ if (data && (data.equity > 0 || data.positions.length > 0)) {
212
+ mmSummaryTable.push([
213
+ mm === 'main' ? chalk_1.default.white(mm) : chalk_1.default.cyan(mm),
214
+ (0, ui_1.formatUsd)(data.equity),
215
+ (0, ui_1.formatUsd)(data.availableBalance),
216
+ data.positions.length.toString(),
217
+ ]);
218
+ hasActiveMarketMakers = true;
219
+ }
220
+ }
221
+ if (hasActiveMarketMakers) {
222
+ console.log(mmSummaryTable.toString());
223
+ }
224
+ // Aggregate all positions
225
+ const allHlPositions = [];
226
+ for (const [mm, data] of mmEntries) {
227
+ if (data?.positions) {
228
+ for (const posWrapper of data.positions) {
229
+ allHlPositions.push({ ...posWrapper.position, marketMaker: mm });
230
+ }
231
+ }
232
+ }
233
+ // Positions Detail Table
234
+ if (allHlPositions.length > 0) {
235
+ console.log();
236
+ console.log(ui_1.colors.muted(' Open Positions:'));
237
+ const hlTable = new cli_table3_1.default({
238
+ head: [
239
+ chalk_1.default.gray('Market'),
240
+ chalk_1.default.gray('Side'),
241
+ chalk_1.default.gray('Size'),
242
+ chalk_1.default.gray('Entry'),
243
+ chalk_1.default.gray('Value'),
244
+ chalk_1.default.gray('PnL'),
245
+ chalk_1.default.gray('MM'),
246
+ ],
247
+ style: { head: [], border: ['gray'] },
248
+ });
249
+ for (const pos of allHlPositions) {
250
+ const pnl = parseFloat(pos.unrealizedPnl || '0');
251
+ const size = parseFloat(pos.szi || '0');
252
+ hlTable.push([
253
+ pos.coin || 'N/A',
254
+ size > 0 ? chalk_1.default.green('LONG') : chalk_1.default.red('SHORT'),
255
+ Math.abs(size).toFixed(4),
256
+ (0, ui_1.formatUsd)(parseFloat(pos.entryPx || '0')),
257
+ (0, ui_1.formatUsd)(parseFloat(pos.positionValue || '0')),
258
+ pnl >= 0
259
+ ? chalk_1.default.green(`+$${pnl.toFixed(2)}`)
260
+ : chalk_1.default.red(`-$${Math.abs(pnl).toFixed(2)}`),
261
+ ui_1.colors.muted(pos.marketMaker),
262
+ ]);
263
+ }
264
+ console.log(hlTable.toString());
265
+ // Show aggregate P&L
266
+ const totalPnl = allHlPositions.reduce((sum, pos) => sum + parseFloat(pos.unrealizedPnl || '0'), 0);
267
+ (0, ui_1.info)(` Total: ${(0, ui_1.formatUsd)(portfolio.protocols.hyperliquid.totalEquity)} | ` +
268
+ `Unrealized P&L: ${totalPnl >= 0
269
+ ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(totalPnl)}`)
270
+ : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(totalPnl))}`)}`);
271
+ }
272
+ else {
273
+ console.log();
274
+ (0, ui_1.info)(' No open positions.');
275
+ }
276
+ // === Polymarket Positions ===
277
+ console.log();
278
+ console.log(ui_1.colors.secondary.bold(' Polymarket Positions'));
279
+ if (portfolio.protocols.polymarket.totalPositions > 0) {
280
+ const pmTable = new cli_table3_1.default({
281
+ head: [
282
+ chalk_1.default.gray('Market'),
283
+ chalk_1.default.gray('Outcome'),
284
+ chalk_1.default.gray('Size'),
285
+ chalk_1.default.gray('Avg'),
286
+ chalk_1.default.gray('Cur'),
287
+ chalk_1.default.gray('P&L'),
288
+ ],
289
+ style: { head: [], border: ['gray'] },
290
+ });
291
+ for (const pos of portfolio.protocols.polymarket.positions) {
292
+ const pnl = pos.cashPnl;
293
+ pmTable.push([
294
+ pos.title,
295
+ pos.outcome === 'Yes' ? chalk_1.default.green('Yes') : chalk_1.default.red('No'),
296
+ pos.size.toFixed(0),
297
+ (0, ui_1.formatUsd)(pos.avgPrice),
298
+ (0, ui_1.formatUsd)(pos.curPrice),
299
+ pnl >= 0
300
+ ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(pnl)}`)
301
+ : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(pnl))}`),
302
+ ]);
303
+ }
304
+ console.log(pmTable.toString());
305
+ const totalPnl = portfolio.protocols.polymarket.totalPnl;
306
+ (0, ui_1.info)(` Total: ${(0, ui_1.formatUsd)(portfolio.protocols.polymarket.totalValue)} | ` +
307
+ `P&L: ${totalPnl >= 0
308
+ ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(totalPnl)}`)
309
+ : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(totalPnl))}`)}`);
310
+ }
311
+ else {
312
+ (0, ui_1.info)(' No Polymarket positions.');
313
+ }
314
+ // === Kamino Lending ===
315
+ console.log();
316
+ console.log(ui_1.colors.sol.bold(' Kamino Lending'));
317
+ if (portfolio.protocols.kamino.positions.length > 0) {
318
+ const kaminoTable = new cli_table3_1.default({
319
+ head: [
320
+ chalk_1.default.gray('Type'),
321
+ chalk_1.default.gray('Asset'),
322
+ chalk_1.default.gray('Amount'),
323
+ chalk_1.default.gray('USD Value'),
324
+ ],
325
+ style: { head: [], border: ['gray'] },
326
+ });
327
+ for (const pos of portfolio.protocols.kamino.positions) {
328
+ kaminoTable.push([
329
+ pos.type === 'deposit' ? chalk_1.default.green('Deposit') : chalk_1.default.yellow('Borrow'),
330
+ pos.symbol,
331
+ parseFloat(pos.amount).toFixed(4),
332
+ (0, ui_1.formatUsd)(parseFloat(pos.amountUsd)),
333
+ ]);
334
+ }
335
+ console.log(kaminoTable.toString());
336
+ (0, ui_1.info)(` Deposits: ${(0, ui_1.formatUsd)(portfolio.protocols.kamino.totalDepositsUsd)} | ` +
337
+ `Borrows: ${(0, ui_1.formatUsd)(portfolio.protocols.kamino.totalBorrowsUsd)} | ` +
338
+ `Net: ${(0, ui_1.formatUsd)(portfolio.protocols.kamino.netValueUsd)}`);
339
+ }
340
+ else {
341
+ (0, ui_1.info)(' No Kamino positions.');
342
+ }
343
+ // === Meteora LP ===
344
+ console.log();
345
+ console.log(ui_1.colors.sol.bold(' Meteora LP Positions'));
346
+ if (portfolio.protocols.meteora.totalPositions > 0) {
347
+ const metTable = new cli_table3_1.default({
348
+ head: [
349
+ chalk_1.default.gray('Pool'),
350
+ chalk_1.default.gray('Token X'),
351
+ chalk_1.default.gray('Token Y'),
352
+ chalk_1.default.gray('USD Value'),
353
+ ],
354
+ style: { head: [], border: ['gray'] },
355
+ });
356
+ for (const pool of portfolio.protocols.meteora.positions || []) {
357
+ const shortPool = pool.poolAddress.slice(0, 4) + '…' + pool.poolAddress.slice(-4);
358
+ pool.positions.forEach((pos, idx) => {
359
+ const xAmt = parseFloat(pos.totalXAmount || '0');
360
+ const yAmt = parseFloat(pos.totalYAmount || '0');
361
+ metTable.push([
362
+ shortPool,
363
+ chalk_1.default.bold(xAmt.toFixed(4)),
364
+ chalk_1.default.bold(yAmt.toFixed(2)),
365
+ // Only show USD value for the first position in the pool
366
+ idx === 0 ? chalk_1.default.green((0, ui_1.formatUsd)(pool.estimatedValueUsd)) : '',
367
+ ]);
368
+ });
369
+ }
370
+ console.log(metTable.toString());
371
+ (0, ui_1.info)(` Total Value: ${(0, ui_1.formatUsd)(portfolio.protocols.meteora.totalEquity)}`);
372
+ }
373
+ else {
374
+ (0, ui_1.info)(' No Meteora positions.');
375
+ }
376
+ console.log();
377
+ (0, ui_1.divider)();
378
+ }
29
379
  function registerPortfolioCommand(program) {
30
380
  program
31
381
  .command('portfolio')
32
382
  .description('View your complete portfolio — balances, positions, P&L across all protocols')
33
383
  .option('--json', 'Output as JSON')
384
+ .option('--all-balances', 'Show low/dust spot balances (default hides tiny balances)')
34
385
  .action(async (opts) => {
35
386
  if (!(0, ui_1.requireAuth)())
36
387
  return;
37
388
  const jsonMode = opts?.json || false;
389
+ const showAllBalances = opts?.allBalances || false;
38
390
  const account = (0, config_1.getActiveAccount)();
39
391
  if (!account) {
40
392
  const msg = 'No active account.';
@@ -69,295 +421,13 @@ function registerPortfolioCommand(program) {
69
421
  (0, tty_1.outputJson)({ success: true, data: portfolio });
70
422
  return;
71
423
  }
72
- // Display portfolio
73
- (0, ui_1.header)('Portfolio');
74
- console.log();
75
- // Top-level summary
76
- (0, ui_1.label)('Total Equity', (0, ui_1.formatUsd)(portfolio.totals.totalEquity));
77
- (0, ui_1.label)('Available Balance', (0, ui_1.formatUsd)(portfolio.totals.availableBalance));
78
- (0, ui_1.label)('Open Positions', portfolio.totals.totalPositions.toString());
79
- console.log();
80
- // Protocol breakdown
81
- console.log(ui_1.colors.muted(' Protocol Breakdown:'));
82
- const protocolTable = new cli_table3_1.default({
83
- head: [chalk_1.default.gray('Protocol'), chalk_1.default.gray('Value'), chalk_1.default.gray('Positions')],
84
- style: { head: [], border: ['gray'] },
85
- colWidths: [20, 18, 12],
86
- });
87
- if (portfolio.protocols.hyperliquid.totalEquity > 0 || portfolio.protocols.hyperliquid.totalPositions > 0) {
88
- protocolTable.push([
89
- ui_1.colors.evm('Hyperliquid'),
90
- (0, ui_1.formatUsd)(portfolio.protocols.hyperliquid.totalEquity),
91
- portfolio.protocols.hyperliquid.totalPositions.toString(),
92
- ]);
93
- }
94
- if (portfolio.protocols.polymarket.totalValue > 0 || portfolio.protocols.polymarket.totalPositions > 0) {
95
- protocolTable.push([
96
- ui_1.colors.secondary('Polymarket'),
97
- (0, ui_1.formatUsd)(portfolio.protocols.polymarket.totalValue),
98
- portfolio.protocols.polymarket.totalPositions.toString(),
99
- ]);
100
- }
101
- if (portfolio.protocols.kamino.netValueUsd > 0 || portfolio.protocols.kamino.positions.length > 0) {
102
- protocolTable.push([
103
- ui_1.colors.sol('Kamino'),
104
- (0, ui_1.formatUsd)(portfolio.protocols.kamino.netValueUsd),
105
- portfolio.protocols.kamino.positions.length.toString(),
106
- ]);
107
- }
108
- if (portfolio.protocols.meteora.totalEquity > 0 || portfolio.protocols.meteora.totalPositions > 0) {
109
- protocolTable.push([
110
- ui_1.colors.sol('Meteora'),
111
- (0, ui_1.formatUsd)(portfolio.protocols.meteora.totalEquity),
112
- portfolio.protocols.meteora.totalPositions.toString(),
113
- ]);
114
- }
115
- if (protocolTable.length > 0) {
116
- console.log(protocolTable.toString());
117
- console.log();
118
- }
119
- (0, ui_1.divider)();
120
- // === Spot Balances Section ===
121
- const balanceTable = new cli_table3_1.default({
122
- head: [chalk_1.default.gray('Asset'), chalk_1.default.gray('Balance'), chalk_1.default.gray('Chain')],
123
- style: { head: [], border: ['gray'] },
124
- colWidths: [46, 18, 16],
125
- });
126
- let hasBalances = false;
127
- // SOL native balance
128
- if (portfolio.balances.solana.nativeBalance > 0) {
129
- balanceTable.push([
130
- ui_1.colors.sol('SOL'),
131
- chalk_1.default.bold(portfolio.balances.solana.nativeBalance.toFixed(4)),
132
- ui_1.colors.muted('(Solana)'),
133
- ]);
134
- hasBalances = true;
135
- }
136
- // All SPL tokens (dynamic)
137
- for (const token of portfolio.balances.solana.tokenBalances) {
138
- const symbol = getTokenSymbol(token.token);
139
- balanceTable.push([
140
- chalk_1.default.white(symbol),
141
- chalk_1.default.bold(token.balance.toFixed(2)),
142
- ui_1.colors.muted('(Solana)'),
143
- ]);
144
- hasBalances = true;
145
- }
146
- // Hyperliquid spot balances
147
- for (const spot of portfolio.protocols.hyperliquid.spotBalances || []) {
148
- if (spot.balance > 0) {
149
- balanceTable.push([
150
- chalk_1.default.green(spot.token),
151
- chalk_1.default.bold(spot.balance.toFixed(2)),
152
- ui_1.colors.muted('(Hyperliquid)'),
153
- ]);
154
- hasBalances = true;
155
- }
156
- }
157
- if (hasBalances) {
158
- console.log(balanceTable.toString());
159
- }
160
- else {
161
- (0, ui_1.info)('No spot balances found.');
162
- }
163
- // === Hyperliquid Overview ===
164
- console.log();
165
- console.log(ui_1.colors.evm.bold(' Hyperliquid'));
166
- const marketMakers = portfolio.protocols.hyperliquid.marketMakers;
167
- // Market Maker Summary Table
168
- const mmEntries = [
169
- ['main', marketMakers.main],
170
- ['xyz', marketMakers.xyz],
171
- ['flx', marketMakers.flx],
172
- ['vntl', marketMakers.vntl],
173
- ['hyna', marketMakers.hyna],
174
- ['km', marketMakers.km],
175
- ['cash', marketMakers.cash],
176
- ];
177
- const mmSummaryTable = new cli_table3_1.default({
178
- head: [
179
- chalk_1.default.gray('Market Maker'),
180
- chalk_1.default.gray('Equity'),
181
- chalk_1.default.gray('Available'),
182
- chalk_1.default.gray('Positions'),
183
- ],
184
- style: { head: [], border: ['gray'] },
185
- });
186
- let hasActiveMarketMakers = false;
187
- for (const [mm, data] of mmEntries) {
188
- if (data && (data.equity > 0 || data.positions.length > 0)) {
189
- mmSummaryTable.push([
190
- mm === 'main' ? chalk_1.default.white(mm) : chalk_1.default.cyan(mm),
191
- (0, ui_1.formatUsd)(data.equity),
192
- (0, ui_1.formatUsd)(data.availableBalance),
193
- data.positions.length.toString(),
194
- ]);
195
- hasActiveMarketMakers = true;
196
- }
197
- }
198
- if (hasActiveMarketMakers) {
199
- console.log(mmSummaryTable.toString());
200
- }
201
- // Aggregate all positions
202
- const allHlPositions = [];
203
- for (const [mm, data] of mmEntries) {
204
- if (data?.positions) {
205
- for (const posWrapper of data.positions) {
206
- allHlPositions.push({ ...posWrapper.position, marketMaker: mm });
207
- }
208
- }
209
- }
210
- // Positions Detail Table
211
- if (allHlPositions.length > 0) {
212
- console.log();
213
- console.log(ui_1.colors.muted(' Open Positions:'));
214
- const hlTable = new cli_table3_1.default({
215
- head: [
216
- chalk_1.default.gray('Market'),
217
- chalk_1.default.gray('Side'),
218
- chalk_1.default.gray('Size'),
219
- chalk_1.default.gray('Entry'),
220
- chalk_1.default.gray('Value'),
221
- chalk_1.default.gray('PnL'),
222
- chalk_1.default.gray('MM'),
223
- ],
224
- style: { head: [], border: ['gray'] },
225
- });
226
- for (const pos of allHlPositions) {
227
- const pnl = parseFloat(pos.unrealizedPnl || '0');
228
- const size = parseFloat(pos.szi || '0');
229
- hlTable.push([
230
- pos.coin || 'N/A',
231
- size > 0 ? chalk_1.default.green('LONG') : chalk_1.default.red('SHORT'),
232
- Math.abs(size).toFixed(4),
233
- (0, ui_1.formatUsd)(parseFloat(pos.entryPx || '0')),
234
- (0, ui_1.formatUsd)(parseFloat(pos.positionValue || '0')),
235
- pnl >= 0
236
- ? chalk_1.default.green(`+$${pnl.toFixed(2)}`)
237
- : chalk_1.default.red(`-$${Math.abs(pnl).toFixed(2)}`),
238
- ui_1.colors.muted(pos.marketMaker === 'undefined' ? 'main' : pos.marketMaker),
239
- ]);
240
- }
241
- console.log(hlTable.toString());
242
- // Show aggregate P&L
243
- const totalPnl = allHlPositions.reduce((sum, pos) => sum + parseFloat(pos.unrealizedPnl || '0'), 0);
244
- (0, ui_1.info)(` Total: ${(0, ui_1.formatUsd)(portfolio.protocols.hyperliquid.totalEquity)} | ` +
245
- `Unrealized P&L: ${totalPnl >= 0
246
- ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(totalPnl)}`)
247
- : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(totalPnl))}`)}`);
248
- }
249
- else {
250
- console.log();
251
- (0, ui_1.info)(' No open positions.');
252
- }
253
- // === Polymarket Positions ===
254
- console.log();
255
- console.log(ui_1.colors.secondary.bold(' Polymarket Positions'));
256
- if (portfolio.protocols.polymarket.totalPositions > 0) {
257
- const pmTable = new cli_table3_1.default({
258
- head: [
259
- chalk_1.default.gray('Market'),
260
- chalk_1.default.gray('Outcome'),
261
- chalk_1.default.gray('Size'),
262
- chalk_1.default.gray('Avg'),
263
- chalk_1.default.gray('Cur'),
264
- chalk_1.default.gray('P&L'),
265
- ],
266
- style: { head: [], border: ['gray'] },
267
- });
268
- for (const pos of portfolio.protocols.polymarket.positions) {
269
- const pnl = pos.cashPnl;
270
- pmTable.push([
271
- pos.title,
272
- pos.outcome === 'Yes' ? chalk_1.default.green('Yes') : chalk_1.default.red('No'),
273
- pos.size.toFixed(0),
274
- (0, ui_1.formatUsd)(pos.avgPrice),
275
- (0, ui_1.formatUsd)(pos.curPrice),
276
- pnl >= 0
277
- ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(pnl)}`)
278
- : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(pnl))}`),
279
- ]);
280
- }
281
- console.log(pmTable.toString());
282
- const totalPnl = portfolio.protocols.polymarket.totalPnl;
283
- (0, ui_1.info)(` Total: ${(0, ui_1.formatUsd)(portfolio.protocols.polymarket.totalValue)} | ` +
284
- `P&L: ${totalPnl >= 0
285
- ? chalk_1.default.green(`+${(0, ui_1.formatUsd)(totalPnl)}`)
286
- : chalk_1.default.red(`-${(0, ui_1.formatUsd)(Math.abs(totalPnl))}`)}`);
287
- }
288
- else {
289
- (0, ui_1.info)(' No Polymarket positions.');
290
- }
291
- // === Kamino Lending ===
292
- console.log();
293
- console.log(ui_1.colors.sol.bold(' Kamino Lending'));
294
- if (portfolio.protocols.kamino.positions.length > 0) {
295
- const kaminoTable = new cli_table3_1.default({
296
- head: [
297
- chalk_1.default.gray('Type'),
298
- chalk_1.default.gray('Asset'),
299
- chalk_1.default.gray('Amount'),
300
- chalk_1.default.gray('USD Value'),
301
- ],
302
- style: { head: [], border: ['gray'] },
303
- });
304
- for (const pos of portfolio.protocols.kamino.positions) {
305
- kaminoTable.push([
306
- pos.type === 'deposit' ? chalk_1.default.green('Deposit') : chalk_1.default.yellow('Borrow'),
307
- pos.symbol,
308
- parseFloat(pos.amount).toFixed(4),
309
- `$${parseFloat(pos.amountUsd).toFixed(2)}`,
310
- ]);
311
- }
312
- console.log(kaminoTable.toString());
313
- (0, ui_1.info)(` Deposits: $${portfolio.protocols.kamino.totalDepositsUsd.toFixed(2)} | ` +
314
- `Borrows: $${portfolio.protocols.kamino.totalBorrowsUsd.toFixed(2)} | ` +
315
- `Net: $${portfolio.protocols.kamino.netValueUsd.toFixed(2)}`);
316
- }
317
- else {
318
- (0, ui_1.info)(' No Kamino positions.');
319
- }
320
- // === Meteora LP ===
321
- console.log();
322
- console.log(ui_1.colors.sol.bold(' Meteora LP Positions'));
323
- if (portfolio.protocols.meteora.totalPositions > 0) {
324
- const metTable = new cli_table3_1.default({
325
- head: [
326
- chalk_1.default.gray('Pool'),
327
- chalk_1.default.gray('Token X'),
328
- chalk_1.default.gray('Token Y'),
329
- chalk_1.default.gray('USD Value'),
330
- ],
331
- style: { head: [], border: ['gray'] },
332
- });
333
- for (const pool of portfolio.protocols.meteora.positions || []) {
334
- const shortPool = pool.poolAddress.slice(0, 4) + '…' + pool.poolAddress.slice(-4);
335
- pool.positions.forEach((pos, idx) => {
336
- const xAmt = parseFloat(pos.totalXAmount || '0');
337
- const yAmt = parseFloat(pos.totalYAmount || '0');
338
- metTable.push([
339
- shortPool,
340
- chalk_1.default.bold(xAmt.toFixed(4)),
341
- chalk_1.default.bold(yAmt.toFixed(2)),
342
- // Only show USD value for the first position in the pool
343
- idx === 0 ? chalk_1.default.green(`$${pool.estimatedValueUsd.toFixed(2)}`) : '',
344
- ]);
345
- });
346
- }
347
- console.log(metTable.toString());
348
- (0, ui_1.info)(` Total Value: $${portfolio.protocols.meteora.totalEquity.toFixed(2)}`);
349
- }
350
- else {
351
- (0, ui_1.info)(' No Meteora positions.');
352
- }
424
+ renderPortfolioDisplay(portfolio, { showAllBalances });
353
425
  }
354
426
  catch (err) {
355
427
  spinner?.stop();
356
428
  const errorMessage = err instanceof Error ? err.message : 'Unknown error';
357
429
  (0, ui_1.error)(`Failed to load portfolio: ${errorMessage}`);
358
430
  }
359
- console.log();
360
- (0, ui_1.divider)();
361
431
  });
362
432
  }
363
433
  //# sourceMappingURL=portfolio.js.map