prab-cli 1.2.4 → 1.2.6

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.
@@ -20,13 +20,17 @@ const analyzer_1 = require("./analyzer");
20
20
  const market_analyzer_1 = require("./market-analyzer");
21
21
  const config_1 = require("../config");
22
22
  const groq_provider_1 = require("../models/groq-provider");
23
+ const ui_1 = require("../ui");
23
24
  /**
24
25
  * Generate AI reasoning for the trading signal
25
26
  */
26
27
  async function generateAIReasoning(symbol, signal, price, priceChange24h) {
27
28
  const apiKey = (0, config_1.getApiKey)();
28
29
  if (!apiKey) {
29
- return "AI reasoning unavailable (no API key configured)";
30
+ return {
31
+ text: "AI reasoning unavailable (no API key configured)",
32
+ tokens: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
33
+ };
30
34
  }
31
35
  const modelConfig = (0, config_1.getModelConfig)();
32
36
  const provider = new groq_provider_1.GroqProvider(modelConfig.modelId, 0.7);
@@ -50,6 +54,7 @@ Key Observations:
50
54
  ${signal.reasoning.map((r) => `- ${r}`).join("\n")}
51
55
 
52
56
  Provide a concise trading insight (2-3 sentences) explaining the signal and any cautions. Be direct and actionable.`;
57
+ const tokens = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
53
58
  try {
54
59
  const stream = provider.streamChat([{ role: "user", content: prompt }], [] // No tools needed
55
60
  );
@@ -58,11 +63,35 @@ Provide a concise trading insight (2-3 sentences) explaining the signal and any
58
63
  if (chunk.content && typeof chunk.content === "string") {
59
64
  response += chunk.content;
60
65
  }
66
+ // Capture token usage (check multiple formats)
67
+ if (chunk.usage_metadata) {
68
+ tokens.promptTokens =
69
+ chunk.usage_metadata.input_tokens || chunk.usage_metadata.prompt_tokens || 0;
70
+ tokens.completionTokens =
71
+ chunk.usage_metadata.output_tokens || chunk.usage_metadata.completion_tokens || 0;
72
+ tokens.totalTokens =
73
+ chunk.usage_metadata.total_tokens || tokens.promptTokens + tokens.completionTokens;
74
+ }
75
+ if (chunk.response_metadata?.usage) {
76
+ const usage = chunk.response_metadata.usage;
77
+ tokens.promptTokens = usage.prompt_tokens || usage.input_tokens || 0;
78
+ tokens.completionTokens = usage.completion_tokens || usage.output_tokens || 0;
79
+ tokens.totalTokens = usage.total_tokens || tokens.promptTokens + tokens.completionTokens;
80
+ }
61
81
  }
62
- return response.trim();
82
+ // Estimate tokens if not provided by API (rough estimate: ~4 chars per token)
83
+ if (tokens.totalTokens === 0 && response.length > 0) {
84
+ tokens.promptTokens = Math.ceil(prompt.length / 4);
85
+ tokens.completionTokens = Math.ceil(response.length / 4);
86
+ tokens.totalTokens = tokens.promptTokens + tokens.completionTokens;
87
+ }
88
+ return { text: response.trim(), tokens };
63
89
  }
64
90
  catch (error) {
65
- return `AI reasoning unavailable: ${error.message}`;
91
+ return {
92
+ text: `AI reasoning unavailable: ${error.message}`,
93
+ tokens,
94
+ };
66
95
  }
67
96
  }
68
97
  /**
@@ -78,10 +107,13 @@ async function generateTradingSignal(symbol, interval = "1h", includeAI = true)
78
107
  const signal = (0, analyzer_1.generateSignal)(data);
79
108
  spinner.text = "Generating trading signal...";
80
109
  let aiReasoning;
110
+ let tokenUsage;
81
111
  // Generate AI reasoning if requested
82
112
  if (includeAI) {
83
113
  spinner.text = "Getting AI insights...";
84
- aiReasoning = await generateAIReasoning(data.symbol, signal, data.currentPrice, data.priceChangePercent24h);
114
+ const aiResult = await generateAIReasoning(data.symbol, signal, data.currentPrice, data.priceChangePercent24h);
115
+ aiReasoning = aiResult.text;
116
+ tokenUsage = aiResult.tokens;
85
117
  }
86
118
  spinner.succeed(`Analysis complete for ${data.symbol}`);
87
119
  return {
@@ -91,6 +123,7 @@ async function generateTradingSignal(symbol, interval = "1h", includeAI = true)
91
123
  aiReasoning,
92
124
  price: data.currentPrice,
93
125
  priceChange24h: data.priceChangePercent24h,
126
+ tokenUsage,
94
127
  };
95
128
  }
96
129
  catch (error) {
@@ -105,9 +138,35 @@ async function generateTradingSignal(symbol, interval = "1h", includeAI = true)
105
138
  /**
106
139
  * Display trading signal in terminal
107
140
  */
108
- function displaySignal(result) {
141
+ async function displaySignal(result) {
109
142
  if (!result.success || !result.signal) {
110
- console.log(chalk_1.default.red(`\nError: ${result.error}`));
143
+ console.log("");
144
+ console.log(chalk_1.default.red(" ╔═══════════════════════════════════════════════════════════════════════╗"));
145
+ console.log(chalk_1.default.red(" ║ ❌ SIGNAL ERROR ║"));
146
+ console.log(chalk_1.default.red(" ╚═══════════════════════════════════════════════════════════════════════╝"));
147
+ console.log("");
148
+ if (result.error?.includes("Invalid symbol") || result.error?.includes("Failed to fetch")) {
149
+ // Extract the base symbol from the normalized symbol
150
+ const baseSymbol = result.symbol.replace("USDT", "");
151
+ console.log(chalk_1.default.yellow(` ⚠️ Symbol not found: "${baseSymbol}"`));
152
+ console.log("");
153
+ // Get similar symbols from Binance
154
+ const suggestions = await (0, data_fetcher_1.findSimilarSymbols)(baseSymbol, 8);
155
+ if (suggestions.length > 0) {
156
+ console.log(chalk_1.default.gray(" Did you mean one of these?"));
157
+ console.log("");
158
+ console.log(chalk_1.default.cyan(` ${suggestions.join(", ")}`));
159
+ console.log("");
160
+ }
161
+ console.log(chalk_1.default.gray(" This symbol is not available on Binance exchange."));
162
+ console.log(chalk_1.default.gray(" Note: This tool only supports Binance USDT trading pairs."));
163
+ console.log(chalk_1.default.gray(" The token might exist on other exchanges (KuCoin, Bybit, etc.)"));
164
+ console.log("");
165
+ }
166
+ else {
167
+ console.log(chalk_1.default.yellow(` ⚠️ ${result.error || "An unexpected error occurred"}`));
168
+ }
169
+ console.log("");
111
170
  return;
112
171
  }
113
172
  const { signal, symbol, price, priceChange24h, aiReasoning } = result;
@@ -212,6 +271,10 @@ function displaySignal(result) {
212
271
  }
213
272
  // Footer
214
273
  console.log(chalk_1.default.cyan("\u{2514}" + "\u{2500}".repeat(45) + "\u{2518}"));
274
+ // Token usage
275
+ if (result.tokenUsage && result.tokenUsage.totalTokens > 0) {
276
+ (0, ui_1.showTokenUsageCompact)(result.tokenUsage.promptTokens, result.tokenUsage.completionTokens, result.tokenUsage.totalTokens);
277
+ }
215
278
  // Disclaimer
216
279
  console.log("");
217
280
  console.log(chalk_1.default.gray.italic(" \u{26A0}\u{FE0F} This is not financial advice. Always do your own research."));
@@ -221,15 +284,27 @@ function displaySignal(result) {
221
284
  * Quick signal check (no AI)
222
285
  */
223
286
  async function quickSignal(symbol) {
224
- const result = await generateTradingSignal(symbol, "1h", false);
225
- displaySignal(result);
287
+ try {
288
+ const result = await generateTradingSignal(symbol, "1h", false);
289
+ await displaySignal(result);
290
+ }
291
+ catch (error) {
292
+ // Fallback error handling
293
+ console.log(chalk_1.default.red(`\n Error: ${error.message || "Failed to generate signal"}\n`));
294
+ }
226
295
  }
227
296
  /**
228
297
  * Full signal with AI reasoning
229
298
  */
230
299
  async function fullSignal(symbol, interval = "1h") {
231
- const result = await generateTradingSignal(symbol, interval, true);
232
- displaySignal(result);
300
+ try {
301
+ const result = await generateTradingSignal(symbol, interval, true);
302
+ await displaySignal(result);
303
+ }
304
+ catch (error) {
305
+ // Fallback error handling
306
+ console.log(chalk_1.default.red(`\n Error: ${error.message || "Failed to generate signal"}\n`));
307
+ }
233
308
  }
234
309
  // ============================================
235
310
  // COMPREHENSIVE ANALYSIS
@@ -240,7 +315,10 @@ async function fullSignal(symbol, interval = "1h") {
240
315
  async function generateComprehensiveAIAnalysis(analysis) {
241
316
  const apiKey = (0, config_1.getApiKey)();
242
317
  if (!apiKey) {
243
- return "AI analysis unavailable (no API key configured)";
318
+ return {
319
+ text: "AI analysis unavailable (no API key configured)",
320
+ tokens: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
321
+ };
244
322
  }
245
323
  const modelConfig = (0, config_1.getModelConfig)();
246
324
  const provider = new groq_provider_1.GroqProvider(modelConfig.modelId, 0.7);
@@ -307,6 +385,7 @@ Provide a detailed trading analysis (4-6 sentences) that:
307
385
  3. Mentions specific price levels to watch
308
386
  4. Highlights any risks or cautions
309
387
  Be specific with prices and actionable advice.`;
388
+ const tokens = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
310
389
  try {
311
390
  const stream = provider.streamChat([{ role: "user", content: prompt }], []);
312
391
  let response = "";
@@ -314,11 +393,32 @@ Be specific with prices and actionable advice.`;
314
393
  if (chunk.content && typeof chunk.content === "string") {
315
394
  response += chunk.content;
316
395
  }
396
+ // Capture token usage (check multiple formats)
397
+ if (chunk.usage_metadata) {
398
+ tokens.promptTokens =
399
+ chunk.usage_metadata.input_tokens || chunk.usage_metadata.prompt_tokens || 0;
400
+ tokens.completionTokens =
401
+ chunk.usage_metadata.output_tokens || chunk.usage_metadata.completion_tokens || 0;
402
+ tokens.totalTokens =
403
+ chunk.usage_metadata.total_tokens || tokens.promptTokens + tokens.completionTokens;
404
+ }
405
+ if (chunk.response_metadata?.usage) {
406
+ const usage = chunk.response_metadata.usage;
407
+ tokens.promptTokens = usage.prompt_tokens || usage.input_tokens || 0;
408
+ tokens.completionTokens = usage.completion_tokens || usage.output_tokens || 0;
409
+ tokens.totalTokens = usage.total_tokens || tokens.promptTokens + tokens.completionTokens;
410
+ }
411
+ }
412
+ // Estimate tokens if not provided by API (rough estimate: ~4 chars per token)
413
+ if (tokens.totalTokens === 0 && response.length > 0) {
414
+ tokens.promptTokens = Math.ceil(prompt.length / 4);
415
+ tokens.completionTokens = Math.ceil(response.length / 4);
416
+ tokens.totalTokens = tokens.promptTokens + tokens.completionTokens;
317
417
  }
318
- return response.trim();
418
+ return { text: response.trim(), tokens };
319
419
  }
320
420
  catch (error) {
321
- return `AI analysis unavailable: ${error.message}`;
421
+ return { text: `AI analysis unavailable: ${error.message}`, tokens };
322
422
  }
323
423
  }
324
424
  /**
@@ -357,7 +457,7 @@ function wordWrap(text, maxWidth) {
357
457
  /**
358
458
  * Display comprehensive analysis in terminal
359
459
  */
360
- function displayComprehensiveAnalysis(analysis, aiAnalysis) {
460
+ function displayComprehensiveAnalysis(analysis, aiAnalysis, tokenUsage) {
361
461
  const boxWidth = 55;
362
462
  const contentWidth = boxWidth - 4;
363
463
  const border = {
@@ -535,6 +635,10 @@ function displayComprehensiveAnalysis(analysis, aiAnalysis) {
535
635
  }
536
636
  // Footer
537
637
  console.log(border.bot);
638
+ // Token usage
639
+ if (tokenUsage && tokenUsage.totalTokens > 0) {
640
+ (0, ui_1.showTokenUsageCompact)(tokenUsage.promptTokens, tokenUsage.completionTokens, tokenUsage.totalTokens);
641
+ }
538
642
  console.log("");
539
643
  console.log(chalk_1.default.gray.italic(" \u{26A0}\u{FE0F} This is not financial advice. Always do your own research."));
540
644
  console.log("");
@@ -548,12 +652,35 @@ async function comprehensiveAnalysis(symbol) {
548
652
  spinner.text = "Fetching 1H, 4H, and 1D data...";
549
653
  const analysis = await (0, market_analyzer_1.analyzeMarket)(symbol);
550
654
  spinner.text = "Generating AI insights...";
551
- const aiAnalysis = await generateComprehensiveAIAnalysis(analysis);
655
+ const aiResult = await generateComprehensiveAIAnalysis(analysis);
552
656
  spinner.succeed(`Comprehensive analysis complete for ${analysis.symbol}`);
553
- displayComprehensiveAnalysis(analysis, aiAnalysis);
657
+ displayComprehensiveAnalysis(analysis, aiResult.text, aiResult.tokens);
554
658
  }
555
659
  catch (error) {
556
660
  spinner.fail(`Failed to analyze ${symbol}`);
557
- console.log(chalk_1.default.red(`\nError: ${error.message}`));
661
+ console.log("");
662
+ console.log(chalk_1.default.red(" ╔═══════════════════════════════════════════════════════════════════════╗"));
663
+ console.log(chalk_1.default.red(" ║ ❌ ANALYSIS ERROR ║"));
664
+ console.log(chalk_1.default.red(" ╚═══════════════════════════════════════════════════════════════════════╝"));
665
+ console.log("");
666
+ if (error.message?.includes("Invalid symbol") || error.message?.includes("Failed to fetch")) {
667
+ console.log(chalk_1.default.yellow(` ⚠️ Symbol not found: "${symbol.toUpperCase()}"`));
668
+ console.log("");
669
+ // Get similar symbols from Binance
670
+ const suggestions = await (0, data_fetcher_1.findSimilarSymbols)(symbol, 8);
671
+ if (suggestions.length > 0) {
672
+ console.log(chalk_1.default.gray(" Did you mean one of these?"));
673
+ console.log("");
674
+ console.log(chalk_1.default.cyan(` ${suggestions.join(", ")}`));
675
+ console.log("");
676
+ }
677
+ console.log(chalk_1.default.gray(" This symbol is not available on Binance exchange."));
678
+ console.log(chalk_1.default.gray(" Note: This tool only supports Binance USDT trading pairs."));
679
+ console.log(chalk_1.default.gray(" The token might exist on other exchanges (KuCoin, Bybit, etc.)"));
680
+ }
681
+ else {
682
+ console.log(chalk_1.default.yellow(` ⚠️ ${error.message || "An unexpected error occurred"}`));
683
+ }
684
+ console.log("");
558
685
  }
559
686
  }
@@ -15,6 +15,7 @@ const data_fetcher_1 = require("./data-fetcher");
15
15
  const smc_indicators_1 = require("./smc-indicators");
16
16
  const config_1 = require("../config");
17
17
  const groq_provider_1 = require("../models/groq-provider");
18
+ const ui_1 = require("../ui");
18
19
  const chart_visual_1 = require("./chart-visual");
19
20
  // ============================================
20
21
  // TRADE SETUP GENERATION
@@ -121,8 +122,12 @@ function generateSMCTradeSetup(smc, currentPrice) {
121
122
  // ============================================
122
123
  async function generateSMCAIAnalysis(analysis) {
123
124
  const apiKey = (0, config_1.getApiKey)();
124
- if (!apiKey)
125
- return "AI analysis unavailable (no API key configured)";
125
+ if (!apiKey) {
126
+ return {
127
+ text: "AI analysis unavailable (no API key configured)",
128
+ tokens: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
129
+ };
130
+ }
126
131
  const modelConfig = (0, config_1.getModelConfig)();
127
132
  const provider = new groq_provider_1.GroqProvider(modelConfig.modelId, 0.7);
128
133
  provider.initialize(apiKey, modelConfig.modelId);
@@ -173,6 +178,7 @@ Provide SMC-focused analysis explaining:
173
178
  3. Best entry strategy (order block, FVG, or liquidity sweep)
174
179
  4. Key levels to watch and when to enter
175
180
  Be specific with prices and SMC terminology.`;
181
+ const tokens = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
176
182
  try {
177
183
  const stream = provider.streamChat([{ role: "user", content: prompt }], []);
178
184
  let response = "";
@@ -180,11 +186,32 @@ Be specific with prices and SMC terminology.`;
180
186
  if (chunk.content && typeof chunk.content === "string") {
181
187
  response += chunk.content;
182
188
  }
189
+ // Capture token usage (check multiple formats)
190
+ if (chunk.usage_metadata) {
191
+ tokens.promptTokens =
192
+ chunk.usage_metadata.input_tokens || chunk.usage_metadata.prompt_tokens || 0;
193
+ tokens.completionTokens =
194
+ chunk.usage_metadata.output_tokens || chunk.usage_metadata.completion_tokens || 0;
195
+ tokens.totalTokens =
196
+ chunk.usage_metadata.total_tokens || tokens.promptTokens + tokens.completionTokens;
197
+ }
198
+ if (chunk.response_metadata?.usage) {
199
+ const usage = chunk.response_metadata.usage;
200
+ tokens.promptTokens = usage.prompt_tokens || usage.input_tokens || 0;
201
+ tokens.completionTokens = usage.completion_tokens || usage.output_tokens || 0;
202
+ tokens.totalTokens = usage.total_tokens || tokens.promptTokens + tokens.completionTokens;
203
+ }
183
204
  }
184
- return response.trim();
205
+ // Estimate tokens if not provided by API (rough estimate: ~4 chars per token)
206
+ if (tokens.totalTokens === 0 && response.length > 0) {
207
+ tokens.promptTokens = Math.ceil(prompt.length / 4);
208
+ tokens.completionTokens = Math.ceil(response.length / 4);
209
+ tokens.totalTokens = tokens.promptTokens + tokens.completionTokens;
210
+ }
211
+ return { text: response.trim(), tokens };
185
212
  }
186
213
  catch (error) {
187
- return `AI analysis unavailable: ${error.message}`;
214
+ return { text: `AI analysis unavailable: ${error.message}`, tokens };
188
215
  }
189
216
  }
190
217
  // ============================================
@@ -216,7 +243,7 @@ function wordWrap(text, maxWidth) {
216
243
  lines.push(currentLine.trim());
217
244
  return lines;
218
245
  }
219
- function displaySMCAnalysis(analysis, aiAnalysis) {
246
+ function displaySMCAnalysis(analysis, aiAnalysis, tokenUsage) {
220
247
  const boxWidth = 58;
221
248
  const contentWidth = boxWidth - 4;
222
249
  const border = {
@@ -380,6 +407,11 @@ function displaySMCAnalysis(analysis, aiAnalysis) {
380
407
  console.log("");
381
408
  const smcChart = (0, chart_visual_1.createSMCVisualChart)(currentPrice, smc, tradeSetup);
382
409
  smcChart.forEach((l) => console.log(l));
410
+ // Token usage
411
+ if (tokenUsage && tokenUsage.totalTokens > 0) {
412
+ console.log("");
413
+ (0, ui_1.showTokenUsageCompact)(tokenUsage.promptTokens, tokenUsage.completionTokens, tokenUsage.totalTokens);
414
+ }
383
415
  console.log("");
384
416
  console.log(chalk_1.default.gray.italic(" \u{26A0}\u{FE0F} This is not financial advice. Always do your own research."));
385
417
  console.log("");
@@ -407,9 +439,9 @@ async function runSMCAnalysis(symbol) {
407
439
  candles: data.candles,
408
440
  };
409
441
  spinner.text = "Getting AI insights...";
410
- const aiAnalysis = await generateSMCAIAnalysis(analysis);
442
+ const aiResult = await generateSMCAIAnalysis(analysis);
411
443
  spinner.succeed(`SMC analysis complete for ${data.symbol}`);
412
- displaySMCAnalysis(analysis, aiAnalysis);
444
+ displaySMCAnalysis(analysis, aiResult.text, aiResult.tokens);
413
445
  }
414
446
  catch (error) {
415
447
  spinner.fail(`Failed to analyze ${symbol}`);