prab-cli 1.2.1 → 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,625 @@
1
+ "use strict";
2
+ /**
3
+ * Trading Signal Generator with AI Reasoning
4
+ * Combines technical analysis with AI-powered insights
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.generateTradingSignal = generateTradingSignal;
11
+ exports.displaySignal = displaySignal;
12
+ exports.quickSignal = quickSignal;
13
+ exports.fullSignal = fullSignal;
14
+ exports.displayComprehensiveAnalysis = displayComprehensiveAnalysis;
15
+ exports.comprehensiveAnalysis = comprehensiveAnalysis;
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ const ora_1 = __importDefault(require("ora"));
18
+ const data_fetcher_1 = require("./data-fetcher");
19
+ const analyzer_1 = require("./analyzer");
20
+ const market_analyzer_1 = require("./market-analyzer");
21
+ const config_1 = require("../config");
22
+ const groq_provider_1 = require("../models/groq-provider");
23
+ const ui_1 = require("../ui");
24
+ /**
25
+ * Generate AI reasoning for the trading signal
26
+ */
27
+ async function generateAIReasoning(symbol, signal, price, priceChange24h) {
28
+ const apiKey = (0, config_1.getApiKey)();
29
+ if (!apiKey) {
30
+ return {
31
+ text: "AI reasoning unavailable (no API key configured)",
32
+ tokens: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
33
+ };
34
+ }
35
+ const modelConfig = (0, config_1.getModelConfig)();
36
+ const provider = new groq_provider_1.GroqProvider(modelConfig.modelId, 0.7);
37
+ provider.initialize(apiKey, modelConfig.modelId);
38
+ const prompt = `You are a crypto trading analyst. Based on the following technical analysis, provide a brief (2-3 sentences) trading insight and recommendation.
39
+
40
+ Symbol: ${symbol}
41
+ Current Price: $${price.toLocaleString()}
42
+ 24h Change: ${priceChange24h.toFixed(2)}%
43
+
44
+ Technical Analysis:
45
+ - Signal: ${signal.signal}
46
+ - Confidence: ${signal.confidence}%
47
+ - Trend: ${signal.indicators.trend}
48
+ - EMA Crossover: ${signal.indicators.emaCrossover}
49
+ - Price vs EMAs: ${signal.indicators.priceVsEMA}
50
+ - Suggested Stop-Loss: ${signal.stopLoss}%
51
+ - Suggested Take-Profit: ${signal.takeProfit}%
52
+
53
+ Key Observations:
54
+ ${signal.reasoning.map((r) => `- ${r}`).join("\n")}
55
+
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 };
58
+ try {
59
+ const stream = provider.streamChat([{ role: "user", content: prompt }], [] // No tools needed
60
+ );
61
+ let response = "";
62
+ for await (const chunk of stream) {
63
+ if (chunk.content && typeof chunk.content === "string") {
64
+ response += chunk.content;
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
+ }
81
+ }
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 };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ text: `AI reasoning unavailable: ${error.message}`,
93
+ tokens,
94
+ };
95
+ }
96
+ }
97
+ /**
98
+ * Main function to generate trading signal with AI reasoning
99
+ */
100
+ async function generateTradingSignal(symbol, interval = "1h", includeAI = true) {
101
+ const spinner = (0, ora_1.default)(`Fetching ${symbol.toUpperCase()} data...`).start();
102
+ try {
103
+ // Fetch crypto data
104
+ const data = await (0, data_fetcher_1.fetchCryptoData)(symbol, interval, 250); // Get 250 candles for EMA200
105
+ spinner.text = "Analyzing chart data...";
106
+ // Generate technical signal
107
+ const signal = (0, analyzer_1.generateSignal)(data);
108
+ spinner.text = "Generating trading signal...";
109
+ let aiReasoning;
110
+ let tokenUsage;
111
+ // Generate AI reasoning if requested
112
+ if (includeAI) {
113
+ spinner.text = "Getting AI insights...";
114
+ const aiResult = await generateAIReasoning(data.symbol, signal, data.currentPrice, data.priceChangePercent24h);
115
+ aiReasoning = aiResult.text;
116
+ tokenUsage = aiResult.tokens;
117
+ }
118
+ spinner.succeed(`Analysis complete for ${data.symbol}`);
119
+ return {
120
+ success: true,
121
+ symbol: data.symbol,
122
+ signal,
123
+ aiReasoning,
124
+ price: data.currentPrice,
125
+ priceChange24h: data.priceChangePercent24h,
126
+ tokenUsage,
127
+ };
128
+ }
129
+ catch (error) {
130
+ spinner.fail(`Failed to analyze ${symbol}`);
131
+ return {
132
+ success: false,
133
+ symbol: (0, data_fetcher_1.normalizeSymbol)(symbol),
134
+ error: error.message,
135
+ };
136
+ }
137
+ }
138
+ /**
139
+ * Display trading signal in terminal
140
+ */
141
+ function displaySignal(result) {
142
+ if (!result.success || !result.signal) {
143
+ console.log(chalk_1.default.red(`\nError: ${result.error}`));
144
+ return;
145
+ }
146
+ const { signal, symbol, price, priceChange24h, aiReasoning } = result;
147
+ // Signal colors
148
+ const signalColor = signal.signal === "BUY"
149
+ ? chalk_1.default.green.bold
150
+ : signal.signal === "SELL"
151
+ ? chalk_1.default.red.bold
152
+ : chalk_1.default.yellow.bold;
153
+ const signalIcon = signal.signal === "BUY" ? "\u{1F7E2}" : signal.signal === "SELL" ? "\u{1F534}" : "\u{1F7E1}";
154
+ const trendIcon = signal.indicators.trend === "bullish"
155
+ ? chalk_1.default.green("\u{2191}")
156
+ : signal.indicators.trend === "bearish"
157
+ ? chalk_1.default.red("\u{2193}")
158
+ : chalk_1.default.yellow("\u{2192}");
159
+ const changeColor = priceChange24h >= 0 ? chalk_1.default.green : chalk_1.default.red;
160
+ const changeSign = priceChange24h >= 0 ? "+" : "";
161
+ // Format price for display
162
+ const formattedPrice = price < 1
163
+ ? `$${price.toFixed(4)}`
164
+ : price < 100
165
+ ? `$${price.toFixed(2)}`
166
+ : `$${price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`;
167
+ // Header with current price
168
+ console.log("");
169
+ console.log(chalk_1.default.cyan("\u{250C}" + "\u{2500}".repeat(45) + "\u{2510}"));
170
+ console.log(chalk_1.default.cyan("\u{2502}") +
171
+ chalk_1.default.bold.white(` \u{1F4CA} ${symbol}`.padEnd(44)) +
172
+ chalk_1.default.cyan("\u{2502}"));
173
+ console.log(chalk_1.default.cyan("\u{2502}") +
174
+ chalk_1.default.bold.yellow(` Current Price: ${formattedPrice}`).padEnd(44) +
175
+ chalk_1.default.cyan("\u{2502}"));
176
+ console.log(chalk_1.default.cyan("\u{2502}") +
177
+ ` 24h: ${changeColor(changeSign + priceChange24h.toFixed(2) + "%")}`.padEnd(52) +
178
+ chalk_1.default.cyan("\u{2502}"));
179
+ console.log(chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(45) + "\u{2524}"));
180
+ // Signal
181
+ console.log(chalk_1.default.cyan("\u{2502}") +
182
+ ` Signal: ${signalIcon} ${signalColor(signal.signal)}`.padEnd(53) +
183
+ chalk_1.default.cyan("\u{2502}"));
184
+ console.log(chalk_1.default.cyan("\u{2502}") +
185
+ ` Confidence: ${chalk_1.default.white(signal.confidence + "%")}`.padEnd(53) +
186
+ chalk_1.default.cyan("\u{2502}"));
187
+ console.log(chalk_1.default.cyan("\u{2502}") +
188
+ ` Stop-Loss: ${chalk_1.default.red("-" + signal.stopLoss + "%")}`.padEnd(53) +
189
+ chalk_1.default.cyan("\u{2502}"));
190
+ console.log(chalk_1.default.cyan("\u{2502}") +
191
+ ` Take-Profit: ${chalk_1.default.green("+" + signal.takeProfit + "%")}`.padEnd(53) +
192
+ chalk_1.default.cyan("\u{2502}"));
193
+ // Trend info
194
+ console.log(chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(45) + "\u{2524}"));
195
+ console.log(chalk_1.default.cyan("\u{2502}") +
196
+ ` Trend: ${trendIcon} ${chalk_1.default.white(signal.indicators.trend)}`.padEnd(53) +
197
+ chalk_1.default.cyan("\u{2502}"));
198
+ // EMA Values
199
+ console.log(chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(45) + "\u{2524}"));
200
+ console.log(chalk_1.default.cyan("\u{2502}") + chalk_1.default.gray(" EMA Indicators:").padEnd(53) + chalk_1.default.cyan("\u{2502}"));
201
+ console.log(chalk_1.default.cyan("\u{2502}") +
202
+ chalk_1.default.gray(` EMA9: $${signal.indicators.currentEMA9.toFixed(2)}`).padEnd(44) +
203
+ chalk_1.default.cyan("\u{2502}"));
204
+ console.log(chalk_1.default.cyan("\u{2502}") +
205
+ chalk_1.default.gray(` EMA21: $${signal.indicators.currentEMA21.toFixed(2)}`).padEnd(44) +
206
+ chalk_1.default.cyan("\u{2502}"));
207
+ console.log(chalk_1.default.cyan("\u{2502}") +
208
+ chalk_1.default.gray(` EMA50: $${signal.indicators.currentEMA50.toFixed(2)}`).padEnd(44) +
209
+ chalk_1.default.cyan("\u{2502}"));
210
+ if (signal.indicators.currentEMA200 > 0) {
211
+ console.log(chalk_1.default.cyan("\u{2502}") +
212
+ chalk_1.default.gray(` EMA200: $${signal.indicators.currentEMA200.toFixed(2)}`).padEnd(44) +
213
+ chalk_1.default.cyan("\u{2502}"));
214
+ }
215
+ // Technical observations
216
+ console.log(chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(45) + "\u{2524}"));
217
+ console.log(chalk_1.default.cyan("\u{2502}") + chalk_1.default.gray(" Technical Analysis:").padEnd(53) + chalk_1.default.cyan("\u{2502}"));
218
+ for (const reason of signal.reasoning.slice(0, 4)) {
219
+ const truncated = reason.length > 40 ? reason.substring(0, 37) + "..." : reason;
220
+ console.log(chalk_1.default.cyan("\u{2502}") +
221
+ chalk_1.default.gray(` \u{2022} ${truncated}`).padEnd(44) +
222
+ chalk_1.default.cyan("\u{2502}"));
223
+ }
224
+ // AI Reasoning
225
+ if (aiReasoning) {
226
+ console.log(chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(45) + "\u{2524}"));
227
+ console.log(chalk_1.default.cyan("\u{2502}") +
228
+ chalk_1.default.magenta.bold(" \u{1F916} AI Insight:").padEnd(53) +
229
+ chalk_1.default.cyan("\u{2502}"));
230
+ // Word wrap AI reasoning
231
+ const words = aiReasoning.split(" ");
232
+ let line = " ";
233
+ for (const word of words) {
234
+ if (line.length + word.length > 40) {
235
+ console.log(chalk_1.default.cyan("\u{2502}") + chalk_1.default.white(line.padEnd(44)) + chalk_1.default.cyan("\u{2502}"));
236
+ line = " " + word + " ";
237
+ }
238
+ else {
239
+ line += word + " ";
240
+ }
241
+ }
242
+ if (line.trim()) {
243
+ console.log(chalk_1.default.cyan("\u{2502}") + chalk_1.default.white(line.padEnd(44)) + chalk_1.default.cyan("\u{2502}"));
244
+ }
245
+ }
246
+ // Footer
247
+ console.log(chalk_1.default.cyan("\u{2514}" + "\u{2500}".repeat(45) + "\u{2518}"));
248
+ // Token usage
249
+ if (result.tokenUsage && result.tokenUsage.totalTokens > 0) {
250
+ (0, ui_1.showTokenUsageCompact)(result.tokenUsage.promptTokens, result.tokenUsage.completionTokens, result.tokenUsage.totalTokens);
251
+ }
252
+ // Disclaimer
253
+ console.log("");
254
+ console.log(chalk_1.default.gray.italic(" \u{26A0}\u{FE0F} This is not financial advice. Always do your own research."));
255
+ console.log("");
256
+ }
257
+ /**
258
+ * Quick signal check (no AI)
259
+ */
260
+ async function quickSignal(symbol) {
261
+ const result = await generateTradingSignal(symbol, "1h", false);
262
+ displaySignal(result);
263
+ }
264
+ /**
265
+ * Full signal with AI reasoning
266
+ */
267
+ async function fullSignal(symbol, interval = "1h") {
268
+ const result = await generateTradingSignal(symbol, interval, true);
269
+ displaySignal(result);
270
+ }
271
+ // ============================================
272
+ // COMPREHENSIVE ANALYSIS
273
+ // ============================================
274
+ /**
275
+ * Generate AI analysis for comprehensive market data
276
+ */
277
+ async function generateComprehensiveAIAnalysis(analysis) {
278
+ const apiKey = (0, config_1.getApiKey)();
279
+ if (!apiKey) {
280
+ return {
281
+ text: "AI analysis unavailable (no API key configured)",
282
+ tokens: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
283
+ };
284
+ }
285
+ const modelConfig = (0, config_1.getModelConfig)();
286
+ const provider = new groq_provider_1.GroqProvider(modelConfig.modelId, 0.7);
287
+ provider.initialize(apiKey, modelConfig.modelId);
288
+ const prompt = `You are an expert crypto trading analyst. Provide a detailed but concise analysis (4-6 sentences) based on the following comprehensive market data.
289
+
290
+ Symbol: ${analysis.symbol}
291
+ Current Price: $${analysis.currentPrice.toLocaleString()}
292
+ 24h Change: ${analysis.priceChange24h.toFixed(2)}%
293
+
294
+ RECOMMENDATION: ${analysis.recommendation}
295
+ Confidence: ${analysis.confidence}%
296
+ Risk Level: ${analysis.riskLevel}
297
+
298
+ MULTI-TIMEFRAME ANALYSIS:
299
+ - 1H Trend: ${analysis.timeframes.short.signal} (${analysis.timeframes.short.trend.direction})
300
+ - 4H Trend: ${analysis.timeframes.medium.signal} (${analysis.timeframes.medium.trend.direction})
301
+ - 1D Trend: ${analysis.timeframes.long.signal} (${analysis.timeframes.long.trend.direction})
302
+ - Timeframe Alignment: ${analysis.timeframeAlignment}
303
+
304
+ INDICATORS:
305
+ - RSI(14): ${analysis.indicators.rsi.current.toFixed(1)} (${analysis.indicators.rsi.condition})
306
+ - MACD: ${analysis.indicators.macd.crossover} crossover, momentum ${analysis.indicators.macd.momentum}
307
+ - Bollinger: Price ${analysis.indicators.bollinger.pricePosition}, ${analysis.indicators.bollinger.squeeze ? "SQUEEZE detected" : "normal bandwidth"}
308
+ - Volume: ${analysis.indicators.volume.volumeRatio.toFixed(1)}x average, ${analysis.indicators.volume.trend} trend
309
+ - ATR Volatility: ${analysis.indicators.atr.volatility} (${analysis.indicators.atr.percentOfPrice.toFixed(1)}% of price)
310
+
311
+ KEY LEVELS:
312
+ - Support: $${analysis.indicators.supportResistance.nearestSupport.toFixed(2)} (${analysis.indicators.supportResistance.distanceToSupport.toFixed(1)}% away)
313
+ - Resistance: $${analysis.indicators.supportResistance.nearestResistance.toFixed(2)} (${analysis.indicators.supportResistance.distanceToResistance.toFixed(1)}% away)
314
+
315
+ TIMING:
316
+ - Action: ${analysis.timing.action}
317
+ - Entry Zone: $${analysis.timing.entryZone.low.toFixed(2)} - $${analysis.timing.entryZone.high.toFixed(2)}
318
+
319
+ TRADE SETUP:
320
+ - Entry: $${analysis.tradeSetup.entry.toFixed(2)}
321
+ - Stop Loss: $${analysis.tradeSetup.stopLoss.toFixed(2)}
322
+ - Target 1: $${analysis.tradeSetup.target1.toFixed(2)}
323
+ - Target 2: $${analysis.tradeSetup.target2.toFixed(2)}
324
+ - Risk/Reward: ${analysis.tradeSetup.riskRewardRatio}:1
325
+
326
+ MARKET CONDITION: ${analysis.marketCondition.type}
327
+ ${analysis.marketCondition.description}
328
+
329
+ Bullish Factors:
330
+ ${analysis.reasoning.bullishFactors
331
+ .slice(0, 3)
332
+ .map((f) => `- ${f}`)
333
+ .join("\n")}
334
+
335
+ Bearish Factors:
336
+ ${analysis.reasoning.bearishFactors
337
+ .slice(0, 3)
338
+ .map((f) => `- ${f}`)
339
+ .join("\n")}
340
+
341
+ Warnings:
342
+ ${analysis.reasoning.warnings.map((w) => `- ${w}`).join("\n")}
343
+
344
+ Provide a detailed trading analysis (4-6 sentences) that:
345
+ 1. Explains the current market structure and why the recommendation makes sense
346
+ 2. Specifies WHEN to enter (now, wait for pullback, wait for breakout, etc.)
347
+ 3. Mentions specific price levels to watch
348
+ 4. Highlights any risks or cautions
349
+ Be specific with prices and actionable advice.`;
350
+ const tokens = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
351
+ try {
352
+ const stream = provider.streamChat([{ role: "user", content: prompt }], []);
353
+ let response = "";
354
+ for await (const chunk of stream) {
355
+ if (chunk.content && typeof chunk.content === "string") {
356
+ response += chunk.content;
357
+ }
358
+ // Capture token usage (check multiple formats)
359
+ if (chunk.usage_metadata) {
360
+ tokens.promptTokens =
361
+ chunk.usage_metadata.input_tokens || chunk.usage_metadata.prompt_tokens || 0;
362
+ tokens.completionTokens =
363
+ chunk.usage_metadata.output_tokens || chunk.usage_metadata.completion_tokens || 0;
364
+ tokens.totalTokens =
365
+ chunk.usage_metadata.total_tokens || tokens.promptTokens + tokens.completionTokens;
366
+ }
367
+ if (chunk.response_metadata?.usage) {
368
+ const usage = chunk.response_metadata.usage;
369
+ tokens.promptTokens = usage.prompt_tokens || usage.input_tokens || 0;
370
+ tokens.completionTokens = usage.completion_tokens || usage.output_tokens || 0;
371
+ tokens.totalTokens = usage.total_tokens || tokens.promptTokens + tokens.completionTokens;
372
+ }
373
+ }
374
+ // Estimate tokens if not provided by API (rough estimate: ~4 chars per token)
375
+ if (tokens.totalTokens === 0 && response.length > 0) {
376
+ tokens.promptTokens = Math.ceil(prompt.length / 4);
377
+ tokens.completionTokens = Math.ceil(response.length / 4);
378
+ tokens.totalTokens = tokens.promptTokens + tokens.completionTokens;
379
+ }
380
+ return { text: response.trim(), tokens };
381
+ }
382
+ catch (error) {
383
+ return { text: `AI analysis unavailable: ${error.message}`, tokens };
384
+ }
385
+ }
386
+ /**
387
+ * Format price for display
388
+ */
389
+ function formatPrice(price) {
390
+ if (price < 0.01)
391
+ return `$${price.toFixed(6)}`;
392
+ if (price < 1)
393
+ return `$${price.toFixed(4)}`;
394
+ if (price < 100)
395
+ return `$${price.toFixed(2)}`;
396
+ return `$${price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`;
397
+ }
398
+ /**
399
+ * Word wrap text for terminal display
400
+ */
401
+ function wordWrap(text, maxWidth) {
402
+ const words = text.split(" ");
403
+ const lines = [];
404
+ let currentLine = "";
405
+ for (const word of words) {
406
+ if (currentLine.length + word.length + 1 > maxWidth) {
407
+ lines.push(currentLine.trim());
408
+ currentLine = word + " ";
409
+ }
410
+ else {
411
+ currentLine += word + " ";
412
+ }
413
+ }
414
+ if (currentLine.trim()) {
415
+ lines.push(currentLine.trim());
416
+ }
417
+ return lines;
418
+ }
419
+ /**
420
+ * Display comprehensive analysis in terminal
421
+ */
422
+ function displayComprehensiveAnalysis(analysis, aiAnalysis, tokenUsage) {
423
+ const boxWidth = 55;
424
+ const contentWidth = boxWidth - 4;
425
+ const border = {
426
+ top: chalk_1.default.cyan("\u{250C}" + "\u{2500}".repeat(boxWidth) + "\u{2510}"),
427
+ mid: chalk_1.default.cyan("\u{251C}" + "\u{2500}".repeat(boxWidth) + "\u{2524}"),
428
+ bot: chalk_1.default.cyan("\u{2514}" + "\u{2500}".repeat(boxWidth) + "\u{2518}"),
429
+ left: chalk_1.default.cyan("\u{2502}"),
430
+ right: chalk_1.default.cyan("\u{2502}"),
431
+ };
432
+ const line = (content, padEnd = contentWidth) => {
433
+ console.log(border.left + " " + content.padEnd(padEnd) + " " + border.right);
434
+ };
435
+ // Recommendation colors and icons
436
+ const recColors = {
437
+ STRONG_BUY: chalk_1.default.green.bold,
438
+ BUY: chalk_1.default.green,
439
+ WAIT_TO_BUY: chalk_1.default.greenBright,
440
+ HOLD: chalk_1.default.yellow,
441
+ WAIT_TO_SELL: chalk_1.default.redBright,
442
+ SELL: chalk_1.default.red,
443
+ STRONG_SELL: chalk_1.default.red.bold,
444
+ };
445
+ const recIcons = {
446
+ STRONG_BUY: "\u{1F7E2}\u{1F7E2}",
447
+ BUY: "\u{1F7E2}",
448
+ WAIT_TO_BUY: "\u{1F7E1}\u{2197}",
449
+ HOLD: "\u{1F7E1}",
450
+ WAIT_TO_SELL: "\u{1F7E1}\u{2198}",
451
+ SELL: "\u{1F534}",
452
+ STRONG_SELL: "\u{1F534}\u{1F534}",
453
+ };
454
+ const changeColor = analysis.priceChange24h >= 0 ? chalk_1.default.green : chalk_1.default.red;
455
+ const changeSign = analysis.priceChange24h >= 0 ? "+" : "";
456
+ console.log("");
457
+ // Header
458
+ console.log(border.top);
459
+ line(chalk_1.default.bold.white(`\u{1F4CA} ${analysis.symbol} - Comprehensive Analysis`));
460
+ line(chalk_1.default.bold.yellow(`Current Price: ${formatPrice(analysis.currentPrice)}`) +
461
+ ` ${changeColor(changeSign + analysis.priceChange24h.toFixed(2) + "%")}`);
462
+ console.log(border.mid);
463
+ // Main Recommendation
464
+ const recColor = recColors[analysis.recommendation] || chalk_1.default.white;
465
+ const recIcon = recIcons[analysis.recommendation] || "";
466
+ line(chalk_1.default.bold(`\u{1F3AF} RECOMMENDATION: ${recIcon} ${recColor(analysis.recommendation)}`));
467
+ line(` Confidence: ${chalk_1.default.white(analysis.confidence + "%")} | Risk: ${analysis.riskLevel === "low"
468
+ ? chalk_1.default.green(analysis.riskLevel)
469
+ : analysis.riskLevel === "medium"
470
+ ? chalk_1.default.yellow(analysis.riskLevel)
471
+ : chalk_1.default.red(analysis.riskLevel)}`);
472
+ // Timing
473
+ console.log(border.mid);
474
+ line(chalk_1.default.bold("\u{23F0} TIMING"));
475
+ const timingAction = analysis.timing.action.replace(/_/g, " ").toUpperCase();
476
+ line(` ${chalk_1.default.cyan(timingAction)}`);
477
+ line(` Entry Zone: ${chalk_1.default.white(formatPrice(analysis.timing.entryZone.low))} - ${chalk_1.default.white(formatPrice(analysis.timing.entryZone.high))}`);
478
+ const timingLines = wordWrap(analysis.timing.reason, contentWidth - 3);
479
+ timingLines.forEach((l) => line(chalk_1.default.gray(` ${l}`)));
480
+ // Trade Setup
481
+ console.log(border.mid);
482
+ line(chalk_1.default.bold("\u{1F4B0} TRADE SETUP"));
483
+ line(` Entry: ${chalk_1.default.white(formatPrice(analysis.tradeSetup.entry))}`);
484
+ line(` Stop Loss: ${chalk_1.default.red(formatPrice(analysis.tradeSetup.stopLoss))} (${chalk_1.default.red("-" +
485
+ (((analysis.tradeSetup.entry - analysis.tradeSetup.stopLoss) / analysis.tradeSetup.entry) *
486
+ 100).toFixed(1) +
487
+ "%")})`);
488
+ line(` Target 1: ${chalk_1.default.green(formatPrice(analysis.tradeSetup.target1))} (${chalk_1.default.green("+" +
489
+ (((analysis.tradeSetup.target1 - analysis.tradeSetup.entry) / analysis.tradeSetup.entry) *
490
+ 100).toFixed(1) +
491
+ "%")})`);
492
+ line(` Target 2: ${chalk_1.default.green(formatPrice(analysis.tradeSetup.target2))} (${chalk_1.default.green("+" +
493
+ (((analysis.tradeSetup.target2 - analysis.tradeSetup.entry) / analysis.tradeSetup.entry) *
494
+ 100).toFixed(1) +
495
+ "%")})`);
496
+ line(` R/R Ratio: ${chalk_1.default.cyan(analysis.tradeSetup.riskRewardRatio + ":1")}`);
497
+ // Multi-Timeframe
498
+ console.log(border.mid);
499
+ line(chalk_1.default.bold("\u{1F4C8} MULTI-TIMEFRAME ANALYSIS"));
500
+ const tfIcon = (signal) => signal === "bullish"
501
+ ? chalk_1.default.green("\u{2191}")
502
+ : signal === "bearish"
503
+ ? chalk_1.default.red("\u{2193}")
504
+ : chalk_1.default.yellow("\u{2192}");
505
+ line(` 1H: ${tfIcon(analysis.timeframes.short.signal)} ${analysis.timeframes.short.signal.padEnd(8)} | RSI: ${analysis.timeframes.short.rsi.current.toFixed(0)}`);
506
+ line(` 4H: ${tfIcon(analysis.timeframes.medium.signal)} ${analysis.timeframes.medium.signal.padEnd(8)} | RSI: ${analysis.timeframes.medium.rsi.current.toFixed(0)}`);
507
+ line(` 1D: ${tfIcon(analysis.timeframes.long.signal)} ${analysis.timeframes.long.signal.padEnd(8)} | RSI: ${analysis.timeframes.long.rsi.current.toFixed(0)}`);
508
+ const alignmentColor = analysis.timeframeAlignment === "aligned_bullish"
509
+ ? chalk_1.default.green
510
+ : analysis.timeframeAlignment === "aligned_bearish"
511
+ ? chalk_1.default.red
512
+ : chalk_1.default.yellow;
513
+ line(` Alignment: ${alignmentColor(analysis.timeframeAlignment.replace(/_/g, " "))}`);
514
+ // Indicators
515
+ console.log(border.mid);
516
+ line(chalk_1.default.bold("\u{1F4CA} INDICATORS"));
517
+ // RSI
518
+ const rsiColor = analysis.indicators.rsi.condition === "overbought"
519
+ ? chalk_1.default.red
520
+ : analysis.indicators.rsi.condition === "oversold"
521
+ ? chalk_1.default.green
522
+ : chalk_1.default.white;
523
+ line(` RSI(14): ${rsiColor(analysis.indicators.rsi.current.toFixed(1))} (${analysis.indicators.rsi.condition})${analysis.indicators.rsi.divergence !== "none"
524
+ ? chalk_1.default.magenta(` - ${analysis.indicators.rsi.divergence} div`)
525
+ : ""}`);
526
+ // MACD
527
+ const macdColor = analysis.indicators.macd.crossover === "bullish"
528
+ ? chalk_1.default.green
529
+ : analysis.indicators.macd.crossover === "bearish"
530
+ ? chalk_1.default.red
531
+ : chalk_1.default.gray;
532
+ line(` MACD: ${macdColor(analysis.indicators.macd.crossover)} | momentum ${analysis.indicators.macd.momentum}`);
533
+ // Bollinger
534
+ const bbPosition = analysis.indicators.bollinger.pricePosition.replace(/_/g, " ");
535
+ line(` Bollinger: ${bbPosition}${analysis.indicators.bollinger.squeeze ? chalk_1.default.yellow(" [SQUEEZE]") : ""}`);
536
+ // Volume
537
+ const volColor = analysis.indicators.volume.volumeRatio > 1.2
538
+ ? chalk_1.default.green
539
+ : analysis.indicators.volume.volumeRatio < 0.8
540
+ ? chalk_1.default.red
541
+ : chalk_1.default.white;
542
+ line(` Volume: ${volColor(analysis.indicators.volume.volumeRatio.toFixed(1) + "x")} avg | ${analysis.indicators.volume.trend}`);
543
+ // ATR
544
+ const atrColor = analysis.indicators.atr.volatility === "high"
545
+ ? chalk_1.default.red
546
+ : analysis.indicators.atr.volatility === "low"
547
+ ? chalk_1.default.green
548
+ : chalk_1.default.yellow;
549
+ line(` Volatility: ${atrColor(analysis.indicators.atr.volatility)} (ATR: ${analysis.indicators.atr.percentOfPrice.toFixed(1)}%)`);
550
+ // Key Levels
551
+ console.log(border.mid);
552
+ line(chalk_1.default.bold("\u{1F511} KEY LEVELS"));
553
+ line(` Support: ${chalk_1.default.green(formatPrice(analysis.indicators.supportResistance.nearestSupport))} (${analysis.indicators.supportResistance.distanceToSupport.toFixed(1)}% away)`);
554
+ line(` Resistance: ${chalk_1.default.red(formatPrice(analysis.indicators.supportResistance.nearestResistance))} (${analysis.indicators.supportResistance.distanceToResistance.toFixed(1)}% away)`);
555
+ line(` EMA21: ${chalk_1.default.gray(formatPrice(analysis.timeframes.medium.ema.ema21))}`);
556
+ line(` EMA50: ${chalk_1.default.gray(formatPrice(analysis.timeframes.medium.ema.ema50))}`);
557
+ line(` EMA200: ${chalk_1.default.gray(formatPrice(analysis.timeframes.medium.ema.ema200))}`);
558
+ // Market Condition
559
+ console.log(border.mid);
560
+ line(chalk_1.default.bold(`\u{1F30A} MARKET: ${analysis.marketCondition.type.toUpperCase()}`));
561
+ const conditionLines = wordWrap(analysis.marketCondition.tradingAdvice, contentWidth - 3);
562
+ conditionLines.forEach((l) => line(chalk_1.default.gray(` ${l}`)));
563
+ // Bullish/Bearish Factors
564
+ if (analysis.reasoning.bullishFactors.length > 0 ||
565
+ analysis.reasoning.bearishFactors.length > 0) {
566
+ console.log(border.mid);
567
+ if (analysis.reasoning.bullishFactors.length > 0) {
568
+ line(chalk_1.default.green.bold("\u{2705} BULLISH FACTORS"));
569
+ analysis.reasoning.bullishFactors.slice(0, 3).forEach((f) => {
570
+ const lines = wordWrap(f, contentWidth - 5);
571
+ lines.forEach((l, i) => line(chalk_1.default.green(` ${i === 0 ? "\u{2022}" : " "} ${l}`)));
572
+ });
573
+ }
574
+ if (analysis.reasoning.bearishFactors.length > 0) {
575
+ line(chalk_1.default.red.bold("\u{274C} BEARISH FACTORS"));
576
+ analysis.reasoning.bearishFactors.slice(0, 3).forEach((f) => {
577
+ const lines = wordWrap(f, contentWidth - 5);
578
+ lines.forEach((l, i) => line(chalk_1.default.red(` ${i === 0 ? "\u{2022}" : " "} ${l}`)));
579
+ });
580
+ }
581
+ }
582
+ // Warnings
583
+ if (analysis.reasoning.warnings.length > 0) {
584
+ console.log(border.mid);
585
+ line(chalk_1.default.yellow.bold("\u{26A0}\u{FE0F} WARNINGS"));
586
+ analysis.reasoning.warnings.forEach((w) => {
587
+ const lines = wordWrap(w, contentWidth - 5);
588
+ lines.forEach((l, i) => line(chalk_1.default.yellow(` ${i === 0 ? "\u{2022}" : " "} ${l}`)));
589
+ });
590
+ }
591
+ // AI Analysis
592
+ if (aiAnalysis) {
593
+ console.log(border.mid);
594
+ line(chalk_1.default.magenta.bold("\u{1F916} AI ANALYSIS"));
595
+ const aiLines = wordWrap(aiAnalysis, contentWidth - 3);
596
+ aiLines.forEach((l) => line(chalk_1.default.white(` ${l}`)));
597
+ }
598
+ // Footer
599
+ console.log(border.bot);
600
+ // Token usage
601
+ if (tokenUsage && tokenUsage.totalTokens > 0) {
602
+ (0, ui_1.showTokenUsageCompact)(tokenUsage.promptTokens, tokenUsage.completionTokens, tokenUsage.totalTokens);
603
+ }
604
+ console.log("");
605
+ console.log(chalk_1.default.gray.italic(" \u{26A0}\u{FE0F} This is not financial advice. Always do your own research."));
606
+ console.log("");
607
+ }
608
+ /**
609
+ * Perform and display comprehensive market analysis
610
+ */
611
+ async function comprehensiveAnalysis(symbol) {
612
+ const spinner = (0, ora_1.default)(`Analyzing ${symbol.toUpperCase()} across multiple timeframes...`).start();
613
+ try {
614
+ spinner.text = "Fetching 1H, 4H, and 1D data...";
615
+ const analysis = await (0, market_analyzer_1.analyzeMarket)(symbol);
616
+ spinner.text = "Generating AI insights...";
617
+ const aiResult = await generateComprehensiveAIAnalysis(analysis);
618
+ spinner.succeed(`Comprehensive analysis complete for ${analysis.symbol}`);
619
+ displayComprehensiveAnalysis(analysis, aiResult.text, aiResult.tokens);
620
+ }
621
+ catch (error) {
622
+ spinner.fail(`Failed to analyze ${symbol}`);
623
+ console.log(chalk_1.default.red(`\nError: ${error.message}`));
624
+ }
625
+ }