prab-cli 1.2.4 → 1.2.5

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.
@@ -0,0 +1,508 @@
1
+ "use strict";
2
+ /**
3
+ * Whale Activity Tracker
4
+ * Monitors large cryptocurrency transactions and exchange flows
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.displayWhaleActivity = displayWhaleActivity;
11
+ exports.runWhaleTracker = runWhaleTracker;
12
+ /* global fetch */
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const ora_1 = __importDefault(require("ora"));
15
+ // Known exchange addresses (simplified list)
16
+ const EXCHANGE_ADDRESSES = {
17
+ BTC: [
18
+ "bc1qgdjqv0av3q56jvd82tkdjpy7gdp9ut8tlqmgrpmv24sq90ecnvqqjwvw97", // Binance
19
+ "3FupZp77ySr7jwoLYEJ9mwzJpvoNBXsBnE", // Binance
20
+ "1NDyJtNTjmwk5xPNhjgAMu4HDHigtobu1s", // Binance
21
+ "bc1qm34lsc65zpw79lxes69zkqmk6ee3ewf0j77s3h", // Binance
22
+ "3Kzh9qAqVWQhEsfQz7zEQL1EuSx5tyNLNS", // Coinbase
23
+ "3JZq4atUahhuA9rLhXLMhhTo133J9rF97j", // Coinbase
24
+ "1Pzaqw98PeRfyHypfqyEgg5yycJRsENrE7", // Bitfinex
25
+ ],
26
+ ETH: [
27
+ "0x28c6c06298d514db089934071355e5743bf21d60", // Binance
28
+ "0x21a31ee1afc51d94c2efccaa2092ad1028285549", // Binance
29
+ "0xdfd5293d8e347dfe59e90efd55b2956a1343963d", // Binance
30
+ "0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43", // Coinbase
31
+ "0x503828976d22510aad0201ac7ec88293211d23da", // Coinbase
32
+ "0x77134cbc06cb00b66f4c7e623d5fdbf6777635ec", // Kraken
33
+ ],
34
+ };
35
+ // ============================================
36
+ // BLOCKCHAIN DATA FETCHING
37
+ // ============================================
38
+ /**
39
+ * Fetch recent large BTC transactions from Blockchain.info
40
+ */
41
+ async function fetchBTCWhaleTransactions(minAmount = 100) {
42
+ const transactions = [];
43
+ try {
44
+ // Fetch latest unconfirmed transactions (more reliable than blocks)
45
+ const response = await fetch("https://blockchain.info/unconfirmed-transactions?format=json");
46
+ const data = await response.json();
47
+ if (!data || !data.txs || !Array.isArray(data.txs)) {
48
+ // Fallback: try fetching latest block
49
+ const blocksRes = await fetch("https://blockchain.info/latestblock");
50
+ const latestBlock = await blocksRes.json();
51
+ if (latestBlock && latestBlock.hash) {
52
+ const blockRes = await fetch(`https://blockchain.info/rawblock/${latestBlock.hash}?format=json`);
53
+ const blockData = await blockRes.json();
54
+ if (blockData && blockData.tx) {
55
+ for (const tx of blockData.tx.slice(0, 30)) {
56
+ processTransaction(tx, latestBlock.height, minAmount, transactions);
57
+ }
58
+ }
59
+ }
60
+ return transactions;
61
+ }
62
+ // Process unconfirmed transactions
63
+ for (const tx of data.txs.slice(0, 50)) {
64
+ processTransaction(tx, 0, minAmount, transactions);
65
+ }
66
+ }
67
+ catch (error) {
68
+ // Silently handle errors - whale data is supplementary
69
+ }
70
+ return transactions;
71
+ }
72
+ /**
73
+ * Process a single transaction and add to list if it meets criteria
74
+ */
75
+ function processTransaction(tx, blockHeight, minAmount, transactions) {
76
+ try {
77
+ if (!tx || !tx.out)
78
+ return;
79
+ const totalOutput = tx.out.reduce((sum, out) => sum + (out.value || 0), 0);
80
+ const btcAmount = totalOutput / 100000000; // Convert satoshis to BTC
81
+ if (btcAmount >= minAmount) {
82
+ const fromAddr = tx.inputs?.[0]?.prev_out?.addr || "unknown";
83
+ const toAddr = tx.out?.[0]?.addr || "unknown";
84
+ const fromType = isExchangeAddress(fromAddr, "BTC") ? "exchange" : "wallet";
85
+ const toType = isExchangeAddress(toAddr, "BTC") ? "exchange" : "wallet";
86
+ let flowType = "unknown";
87
+ if (fromType === "wallet" && toType === "exchange")
88
+ flowType = "exchange_inflow";
89
+ else if (fromType === "exchange" && toType === "wallet")
90
+ flowType = "exchange_outflow";
91
+ else if (fromType === "wallet" && toType === "wallet")
92
+ flowType = "wallet_transfer";
93
+ transactions.push({
94
+ hash: tx.hash || "unknown",
95
+ coin: "BTC",
96
+ amount: btcAmount,
97
+ amountUSD: 0,
98
+ from: fromAddr,
99
+ to: toAddr,
100
+ fromType,
101
+ toType,
102
+ flowType,
103
+ timestamp: (tx.time || Date.now() / 1000) * 1000,
104
+ blockHeight,
105
+ });
106
+ }
107
+ }
108
+ catch {
109
+ // Skip invalid transactions
110
+ }
111
+ }
112
+ /**
113
+ * Fetch recent large ETH transactions from Etherscan (public API - limited)
114
+ */
115
+ async function fetchETHWhaleTransactions(minAmount = 1000) {
116
+ const transactions = [];
117
+ try {
118
+ // Use public Etherscan API (limited but no key needed for basic queries)
119
+ // Fetch internal transactions of known whale addresses or latest blocks
120
+ const response = await fetch(`https://api.etherscan.io/api?module=account&action=txlist&address=0x28c6c06298d514db089934071355e5743bf21d60&startblock=0&endblock=99999999&page=1&offset=20&sort=desc`);
121
+ const data = await response.json();
122
+ if (data.status !== "1" || !data.result)
123
+ return transactions;
124
+ for (const tx of data.result) {
125
+ const ethAmount = parseFloat(tx.value) / 1e18;
126
+ if (ethAmount >= minAmount) {
127
+ const fromType = isExchangeAddress(tx.from, "ETH") ? "exchange" : "wallet";
128
+ const toType = isExchangeAddress(tx.to, "ETH") ? "exchange" : "wallet";
129
+ let flowType = "unknown";
130
+ if (fromType === "wallet" && toType === "exchange")
131
+ flowType = "exchange_inflow";
132
+ else if (fromType === "exchange" && toType === "wallet")
133
+ flowType = "exchange_outflow";
134
+ else if (fromType === "wallet" && toType === "wallet")
135
+ flowType = "wallet_transfer";
136
+ transactions.push({
137
+ hash: tx.hash,
138
+ coin: "ETH",
139
+ amount: ethAmount,
140
+ amountUSD: 0,
141
+ from: tx.from,
142
+ to: tx.to,
143
+ fromType,
144
+ toType,
145
+ flowType,
146
+ timestamp: parseInt(tx.timeStamp) * 1000,
147
+ blockHeight: parseInt(tx.blockNumber),
148
+ });
149
+ }
150
+ }
151
+ }
152
+ catch (error) {
153
+ console.error("Error fetching ETH whale transactions:", error);
154
+ }
155
+ return transactions;
156
+ }
157
+ /**
158
+ * Check if address is a known exchange address
159
+ */
160
+ function isExchangeAddress(address, coin) {
161
+ const addresses = EXCHANGE_ADDRESSES[coin] || [];
162
+ return addresses.some((a) => a.toLowerCase() === address.toLowerCase());
163
+ }
164
+ /**
165
+ * Fetch current prices for USD conversion
166
+ */
167
+ async function fetchPrices() {
168
+ try {
169
+ const response = await fetch("https://api.binance.com/api/v3/ticker/price?symbols=[%22BTCUSDT%22,%22ETHUSDT%22]");
170
+ const data = await response.json();
171
+ const prices = {};
172
+ for (const item of data) {
173
+ const symbol = item.symbol.replace("USDT", "");
174
+ prices[symbol] = parseFloat(item.price);
175
+ }
176
+ return prices;
177
+ }
178
+ catch {
179
+ return { BTC: 85000, ETH: 2800 }; // Fallback prices
180
+ }
181
+ }
182
+ // ============================================
183
+ // WHALE ACTIVITY ANALYSIS
184
+ // ============================================
185
+ /**
186
+ * Analyze whale activity and generate summary
187
+ */
188
+ function analyzeWhaleActivity(transactions, coin) {
189
+ const coinTxs = transactions.filter((tx) => tx.coin === coin);
190
+ let totalInflow = 0;
191
+ let totalOutflow = 0;
192
+ for (const tx of coinTxs) {
193
+ if (tx.flowType === "exchange_inflow") {
194
+ totalInflow += tx.amountUSD;
195
+ }
196
+ else if (tx.flowType === "exchange_outflow") {
197
+ totalOutflow += tx.amountUSD;
198
+ }
199
+ }
200
+ const netFlow = totalOutflow - totalInflow;
201
+ // Determine sentiment based on net flow
202
+ // Outflow > Inflow = Bullish (coins leaving exchanges = less sell pressure)
203
+ // Inflow > Outflow = Bearish (coins entering exchanges = potential sell pressure)
204
+ let sentiment = "neutral";
205
+ if (netFlow > 1000000)
206
+ sentiment = "bullish";
207
+ else if (netFlow < -1000000)
208
+ sentiment = "bearish";
209
+ // Determine activity level
210
+ let whaleActivity = "low";
211
+ if (coinTxs.length >= 10)
212
+ whaleActivity = "high";
213
+ else if (coinTxs.length >= 5)
214
+ whaleActivity = "medium";
215
+ return {
216
+ coin,
217
+ totalInflow,
218
+ totalOutflow,
219
+ netFlow,
220
+ largeTransactions: coinTxs.length,
221
+ sentiment,
222
+ whaleActivity,
223
+ recentTransactions: coinTxs.slice(0, 5),
224
+ };
225
+ }
226
+ // ============================================
227
+ // DISPLAY FUNCTIONS
228
+ // ============================================
229
+ function formatAmount(amount, coin) {
230
+ if (amount >= 1000) {
231
+ return `${(amount / 1000).toFixed(1)}K ${coin}`;
232
+ }
233
+ return `${amount.toFixed(2)} ${coin}`;
234
+ }
235
+ function formatUSD(amount) {
236
+ if (amount >= 1000000000) {
237
+ return `$${(amount / 1000000000).toFixed(2)}B`;
238
+ }
239
+ if (amount >= 1000000) {
240
+ return `$${(amount / 1000000).toFixed(2)}M`;
241
+ }
242
+ if (amount >= 1000) {
243
+ return `$${(amount / 1000).toFixed(1)}K`;
244
+ }
245
+ return `$${amount.toFixed(0)}`;
246
+ }
247
+ function truncateAddress(addr) {
248
+ if (addr.length <= 16)
249
+ return addr;
250
+ return `${addr.slice(0, 8)}...${addr.slice(-6)}`;
251
+ }
252
+ function formatTimeAgo(timestamp) {
253
+ const diff = Date.now() - timestamp;
254
+ const minutes = Math.floor(diff / 60000);
255
+ const hours = Math.floor(diff / 3600000);
256
+ if (hours > 0)
257
+ return `${hours}h ago`;
258
+ if (minutes > 0)
259
+ return `${minutes}m ago`;
260
+ return "just now";
261
+ }
262
+ function displayWhaleActivity(summaries) {
263
+ console.log("");
264
+ console.log(chalk_1.default.bold.cyan(" ═══════════════════════════════════════════════════════"));
265
+ console.log(chalk_1.default.bold.cyan(" 🐋 WHALE ACTIVITY MONITOR "));
266
+ console.log(chalk_1.default.bold.cyan(" ═══════════════════════════════════════════════════════"));
267
+ console.log("");
268
+ for (const summary of summaries) {
269
+ const sentimentColor = summary.sentiment === "bullish"
270
+ ? chalk_1.default.green
271
+ : summary.sentiment === "bearish"
272
+ ? chalk_1.default.red
273
+ : chalk_1.default.yellow;
274
+ const activityColor = summary.whaleActivity === "high"
275
+ ? chalk_1.default.red
276
+ : summary.whaleActivity === "medium"
277
+ ? chalk_1.default.yellow
278
+ : chalk_1.default.gray;
279
+ console.log(chalk_1.default.bold.white(` ┌─────────────────────────────────────────────────┐`));
280
+ console.log(chalk_1.default.bold.white(` │`) +
281
+ chalk_1.default.bold.yellow(` 💰 ${summary.coin} WHALE ACTIVITY`.padEnd(48)) +
282
+ chalk_1.default.bold.white(`│`));
283
+ console.log(chalk_1.default.bold.white(` └─────────────────────────────────────────────────┘`));
284
+ console.log("");
285
+ // Activity Level
286
+ console.log(chalk_1.default.gray(" Activity Level: ") +
287
+ activityColor(`${summary.whaleActivity.toUpperCase()} (${summary.largeTransactions} large txs)`));
288
+ // Sentiment
289
+ const sentimentIcon = summary.sentiment === "bullish" ? "🟢" : summary.sentiment === "bearish" ? "🔴" : "🟡";
290
+ console.log(chalk_1.default.gray(" Market Sentiment: ") +
291
+ sentimentColor(`${sentimentIcon} ${summary.sentiment.toUpperCase()}`));
292
+ console.log("");
293
+ // Flow Summary
294
+ console.log(chalk_1.default.bold.white(" 📊 EXCHANGE FLOW SUMMARY"));
295
+ console.log(chalk_1.default.gray(" ┌─────────────────────────────────────────────────┐"));
296
+ console.log(chalk_1.default.gray(" │") +
297
+ chalk_1.default.red(` 📥 Inflow (Sell Pressure): ${formatUSD(summary.totalInflow).padEnd(15)}`) +
298
+ chalk_1.default.gray("│"));
299
+ console.log(chalk_1.default.gray(" │") +
300
+ chalk_1.default.green(` 📤 Outflow (Accumulation): ${formatUSD(summary.totalOutflow).padEnd(15)}`) +
301
+ chalk_1.default.gray("│"));
302
+ console.log(chalk_1.default.gray(" │") +
303
+ chalk_1.default.gray(" ─────────────────────────────────────────────") +
304
+ chalk_1.default.gray("│"));
305
+ const netFlowColor = summary.netFlow >= 0 ? chalk_1.default.green : chalk_1.default.red;
306
+ const netFlowIcon = summary.netFlow >= 0 ? "↑" : "↓";
307
+ console.log(chalk_1.default.gray(" │") +
308
+ netFlowColor(` ${netFlowIcon} Net Flow: ${formatUSD(Math.abs(summary.netFlow))} ${summary.netFlow >= 0 ? "(Bullish)" : "(Bearish)"}`.padEnd(46)) +
309
+ chalk_1.default.gray("│"));
310
+ console.log(chalk_1.default.gray(" └─────────────────────────────────────────────────┘"));
311
+ console.log("");
312
+ // Recent Transactions
313
+ if (summary.recentTransactions.length > 0) {
314
+ console.log(chalk_1.default.bold.white(" 📋 RECENT LARGE TRANSACTIONS"));
315
+ console.log("");
316
+ for (const tx of summary.recentTransactions.slice(0, 5)) {
317
+ const flowIcon = tx.flowType === "exchange_inflow"
318
+ ? chalk_1.default.red("📥 IN")
319
+ : tx.flowType === "exchange_outflow"
320
+ ? chalk_1.default.green("📤 OUT")
321
+ : chalk_1.default.gray("↔️ TFR");
322
+ const typeLabel = tx.flowType === "exchange_inflow"
323
+ ? chalk_1.default.red("→ Exchange")
324
+ : tx.flowType === "exchange_outflow"
325
+ ? chalk_1.default.green("← Exchange")
326
+ : chalk_1.default.gray("Wallet→Wallet");
327
+ console.log(chalk_1.default.gray(" ") +
328
+ flowIcon +
329
+ chalk_1.default.white(` ${formatAmount(tx.amount, tx.coin).padEnd(12)}`) +
330
+ chalk_1.default.gray(`(${formatUSD(tx.amountUSD)})`.padEnd(12)) +
331
+ typeLabel);
332
+ console.log(chalk_1.default.gray(` ${truncateAddress(tx.from)} → ${truncateAddress(tx.to)}`));
333
+ console.log(chalk_1.default.gray(` ${formatTimeAgo(tx.timestamp)}`));
334
+ console.log("");
335
+ }
336
+ }
337
+ // Interpretation
338
+ console.log(chalk_1.default.bold.white(" 💡 INTERPRETATION"));
339
+ if (summary.sentiment === "bullish") {
340
+ console.log(chalk_1.default.green(" Whales are moving coins OFF exchanges → Less sell pressure"));
341
+ console.log(chalk_1.default.green(" This typically indicates accumulation phase"));
342
+ }
343
+ else if (summary.sentiment === "bearish") {
344
+ console.log(chalk_1.default.red(" Whales are moving coins TO exchanges → Potential selling"));
345
+ console.log(chalk_1.default.red(" This may indicate distribution phase"));
346
+ }
347
+ else {
348
+ console.log(chalk_1.default.yellow(" Mixed whale activity → No clear directional bias"));
349
+ console.log(chalk_1.default.yellow(" Wait for clearer signals before trading"));
350
+ }
351
+ console.log("");
352
+ console.log(chalk_1.default.gray(" ─".repeat(25)));
353
+ console.log("");
354
+ }
355
+ }
356
+ /**
357
+ * Detect whale activity based on volume analysis from Binance
358
+ */
359
+ async function detectWhaleActivityFromVolume(symbol) {
360
+ try {
361
+ const pair = symbol.toUpperCase() + "USDT";
362
+ // Fetch recent trades
363
+ const tradesRes = await fetch(`https://api.binance.com/api/v3/trades?symbol=${pair}&limit=500`);
364
+ const trades = await tradesRes.json();
365
+ if (!Array.isArray(trades) || trades.length === 0)
366
+ return null;
367
+ // Fetch 24h ticker for context
368
+ const tickerRes = await fetch(`https://api.binance.com/api/v3/ticker/24hr?symbol=${pair}`);
369
+ const ticker = await tickerRes.json();
370
+ // Calculate average trade size
371
+ const tradeSizes = trades.map((t) => parseFloat(t.quoteQty));
372
+ const avgTradeSize = tradeSizes.reduce((a, b) => a + b, 0) / tradeSizes.length;
373
+ // Count "large" trades (>5x average)
374
+ const largeTradeThreshold = avgTradeSize * 5;
375
+ const largeTrades = trades.filter((t) => parseFloat(t.quoteQty) > largeTradeThreshold);
376
+ // Calculate buy/sell pressure from large trades
377
+ let buyVolume = 0;
378
+ let sellVolume = 0;
379
+ for (const trade of largeTrades) {
380
+ const qty = parseFloat(trade.quoteQty);
381
+ if (trade.isBuyerMaker) {
382
+ sellVolume += qty; // Taker sold
383
+ }
384
+ else {
385
+ buyVolume += qty; // Taker bought
386
+ }
387
+ }
388
+ const totalLargeVolume = buyVolume + sellVolume;
389
+ const buyPressure = totalLargeVolume > 0 ? (buyVolume / totalLargeVolume) * 100 : 50;
390
+ const sellPressure = totalLargeVolume > 0 ? (sellVolume / totalLargeVolume) * 100 : 50;
391
+ // Calculate volume ratio (current vs 24h average)
392
+ const currentVolume = tradeSizes.reduce((a, b) => a + b, 0);
393
+ const avgVolume24h = parseFloat(ticker.quoteVolume) / 24; // Hourly average
394
+ const volumeRatio = avgVolume24h > 0 ? (currentVolume / avgVolume24h) * 60 : 1; // Normalize to ~1 hour
395
+ // Determine sentiment
396
+ let sentiment = "neutral";
397
+ if (buyPressure > 60)
398
+ sentiment = "bullish";
399
+ else if (sellPressure > 60)
400
+ sentiment = "bearish";
401
+ // Determine activity level
402
+ let whaleActivity = "low";
403
+ if (largeTrades.length > 20 || volumeRatio > 2)
404
+ whaleActivity = "high";
405
+ else if (largeTrades.length > 10 || volumeRatio > 1.5)
406
+ whaleActivity = "medium";
407
+ return {
408
+ coin: symbol,
409
+ unusualVolumeRatio: volumeRatio,
410
+ largeTradeCount: largeTrades.length,
411
+ buyPressure,
412
+ sellPressure,
413
+ sentiment,
414
+ whaleActivity,
415
+ };
416
+ }
417
+ catch {
418
+ return null;
419
+ }
420
+ }
421
+ /**
422
+ * Display volume-based whale activity
423
+ */
424
+ function displayVolumeWhaleData(data) {
425
+ console.log("");
426
+ console.log(chalk_1.default.bold.magenta(" ┌─────────────────────────────────────────────────┐"));
427
+ console.log(chalk_1.default.bold.magenta(" │ 📈 VOLUME-BASED WHALE DETECTION │"));
428
+ console.log(chalk_1.default.bold.magenta(" └─────────────────────────────────────────────────┘"));
429
+ console.log("");
430
+ console.log(chalk_1.default.gray(" Based on Binance large trade analysis (500 recent trades)"));
431
+ console.log("");
432
+ for (const item of data) {
433
+ const sentimentColor = item.sentiment === "bullish"
434
+ ? chalk_1.default.green
435
+ : item.sentiment === "bearish"
436
+ ? chalk_1.default.red
437
+ : chalk_1.default.yellow;
438
+ const activityColor = item.whaleActivity === "high"
439
+ ? chalk_1.default.red
440
+ : item.whaleActivity === "medium"
441
+ ? chalk_1.default.yellow
442
+ : chalk_1.default.gray;
443
+ const sentimentIcon = item.sentiment === "bullish" ? "🟢" : item.sentiment === "bearish" ? "🔴" : "🟡";
444
+ console.log(chalk_1.default.bold.white(` ${item.coin}USDT`));
445
+ console.log(chalk_1.default.gray(" Activity Level: ") +
446
+ activityColor(`${item.whaleActivity.toUpperCase()} (${item.largeTradeCount} large trades)`));
447
+ console.log(chalk_1.default.gray(" Volume Ratio: ") +
448
+ (item.unusualVolumeRatio > 1.5 ? chalk_1.default.yellow : chalk_1.default.gray)(`${item.unusualVolumeRatio.toFixed(1)}x normal`));
449
+ console.log(chalk_1.default.gray(" Whale Pressure: ") +
450
+ chalk_1.default.green(`Buy ${item.buyPressure.toFixed(0)}%`) +
451
+ chalk_1.default.gray(" | ") +
452
+ chalk_1.default.red(`Sell ${item.sellPressure.toFixed(0)}%`));
453
+ console.log(chalk_1.default.gray(" Sentiment: ") +
454
+ sentimentColor(`${sentimentIcon} ${item.sentiment.toUpperCase()}`));
455
+ // Visual pressure bar
456
+ const buyBar = Math.round(item.buyPressure / 5);
457
+ const sellBar = Math.round(item.sellPressure / 5);
458
+ console.log(chalk_1.default.gray(" ") +
459
+ chalk_1.default.green("█".repeat(buyBar)) +
460
+ chalk_1.default.gray("░".repeat(20 - buyBar - sellBar)) +
461
+ chalk_1.default.red("█".repeat(sellBar)));
462
+ console.log("");
463
+ }
464
+ }
465
+ // ============================================
466
+ // MAIN FUNCTION
467
+ // ============================================
468
+ async function runWhaleTracker(coins = ["BTC", "ETH"]) {
469
+ const spinner = (0, ora_1.default)("Tracking whale activity...").start();
470
+ try {
471
+ spinner.text = "Fetching current prices...";
472
+ const prices = await fetchPrices();
473
+ spinner.text = "Scanning BTC blockchain for large transactions...";
474
+ const btcTxs = await fetchBTCWhaleTransactions(50); // Lower threshold
475
+ spinner.text = "Scanning ETH blockchain for large transactions...";
476
+ const ethTxs = await fetchETHWhaleTransactions(200); // Lower threshold
477
+ // Add USD values
478
+ const allTransactions = [...btcTxs, ...ethTxs].map((tx) => ({
479
+ ...tx,
480
+ amountUSD: tx.amount * (prices[tx.coin] || 0),
481
+ }));
482
+ spinner.text = "Analyzing whale activity patterns...";
483
+ const summaries = [];
484
+ for (const coin of coins) {
485
+ if (coin === "BTC" || coin === "ETH") {
486
+ summaries.push(analyzeWhaleActivity(allTransactions, coin));
487
+ }
488
+ }
489
+ // Also get volume-based whale detection
490
+ spinner.text = "Analyzing large trade patterns on Binance...";
491
+ const volumeData = [];
492
+ for (const coin of coins) {
493
+ const data = await detectWhaleActivityFromVolume(coin);
494
+ if (data)
495
+ volumeData.push(data);
496
+ }
497
+ spinner.succeed("Whale activity analysis complete");
498
+ displayWhaleActivity(summaries);
499
+ // Show volume-based analysis
500
+ if (volumeData.length > 0) {
501
+ displayVolumeWhaleData(volumeData);
502
+ }
503
+ }
504
+ catch (error) {
505
+ spinner.fail("Failed to track whale activity");
506
+ console.log(chalk_1.default.red(`\nError: ${error.message}`));
507
+ }
508
+ }
@@ -26,6 +26,24 @@ exports.SLASH_COMMANDS = [
26
26
  shortcut: "sig",
27
27
  action: "signal",
28
28
  },
29
+ {
30
+ name: "/whale",
31
+ description: "Track whale activity (large BTC/ETH transactions)",
32
+ shortcut: "w",
33
+ action: "whale",
34
+ },
35
+ {
36
+ name: "/scan",
37
+ description: "Scan market for best trading opportunities",
38
+ shortcut: "sc",
39
+ action: "scan",
40
+ },
41
+ {
42
+ name: "/news",
43
+ description: "Get latest cryptocurrency news and updates",
44
+ shortcut: "n",
45
+ action: "news",
46
+ },
29
47
  {
30
48
  name: "/model",
31
49
  description: "Switch between available AI models",
package/dist/lib/ui.js CHANGED
@@ -3,7 +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.StreamFormatter = exports.formatMarkdown = exports.showToolProgress = exports.showTodoList = exports.showDiff = exports.banner = exports.log = void 0;
6
+ exports.StreamFormatter = exports.formatMarkdown = exports.showToolProgress = exports.showTodoList = exports.showDiff = exports.banner = exports.showTokenUsageCompact = exports.showTokenUsage = exports.log = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const diff_1 = require("diff");
9
9
  // Simple ASCII art letters for custom banners
@@ -500,6 +500,50 @@ exports.log = {
500
500
  console.log(formatted);
501
501
  },
502
502
  };
503
+ /**
504
+ * Display token usage statistics with visual bar
505
+ */
506
+ const showTokenUsage = (promptTokens, completionTokens, totalTokens, modelName) => {
507
+ const maxBarWidth = 30;
508
+ const maxTokensEstimate = 8000; // Rough estimate for visualization
509
+ // Calculate bar widths
510
+ const promptWidth = Math.min(Math.round((promptTokens / maxTokensEstimate) * maxBarWidth), maxBarWidth);
511
+ const completionWidth = Math.min(Math.round((completionTokens / maxTokensEstimate) * maxBarWidth), maxBarWidth);
512
+ // Create visual bars
513
+ const promptBar = chalk_1.default.cyan("█".repeat(promptWidth) + "░".repeat(Math.max(0, 15 - promptWidth)));
514
+ const completionBar = chalk_1.default.green("█".repeat(completionWidth) + "░".repeat(Math.max(0, 15 - completionWidth)));
515
+ // Format numbers with commas
516
+ const formatNum = (n) => n.toLocaleString();
517
+ console.log(chalk_1.default.gray("─".repeat(50)));
518
+ console.log(chalk_1.default.gray("📊 ") + chalk_1.default.bold.white("Token Usage"));
519
+ if (modelName) {
520
+ console.log(chalk_1.default.gray(" Model: ") + chalk_1.default.cyan(modelName));
521
+ }
522
+ console.log(chalk_1.default.gray(" Prompt: ") +
523
+ promptBar +
524
+ chalk_1.default.gray(" ") +
525
+ chalk_1.default.cyan(formatNum(promptTokens).padStart(6)));
526
+ console.log(chalk_1.default.gray(" Completion: ") +
527
+ completionBar +
528
+ chalk_1.default.gray(" ") +
529
+ chalk_1.default.green(formatNum(completionTokens).padStart(6)));
530
+ console.log(chalk_1.default.gray(" ─────────────────────────────────"));
531
+ console.log(chalk_1.default.gray(" Total: ") + chalk_1.default.bold.yellow(formatNum(totalTokens).padStart(21)));
532
+ console.log(chalk_1.default.gray("─".repeat(50)));
533
+ };
534
+ exports.showTokenUsage = showTokenUsage;
535
+ /**
536
+ * Display compact token usage (single line)
537
+ */
538
+ const showTokenUsageCompact = (promptTokens, completionTokens, totalTokens) => {
539
+ console.log(chalk_1.default.gray(" 📊 Tokens: ") +
540
+ chalk_1.default.cyan(`↑${promptTokens.toLocaleString()}`) +
541
+ chalk_1.default.gray(" + ") +
542
+ chalk_1.default.green(`↓${completionTokens.toLocaleString()}`) +
543
+ chalk_1.default.gray(" = ") +
544
+ chalk_1.default.yellow.bold(totalTokens.toLocaleString()));
545
+ };
546
+ exports.showTokenUsageCompact = showTokenUsageCompact;
503
547
  const banner = (modelName, toolCount, customization) => {
504
548
  const cliName = customization?.cliName || "Prab CLI";
505
549
  const userName = customization?.userName;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prab-cli",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "AI-powered coding assistant for your terminal. Built with Groq's lightning-fast LLMs, featuring autonomous tool execution, syntax-highlighted output, and git integration.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {