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,497 @@
1
+ "use strict";
2
+ /* global fetch */
3
+ /**
4
+ * Advanced Market Analyzer
5
+ * Multi-timeframe analysis with timing recommendations
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.analyzeMarket = analyzeMarket;
9
+ const data_fetcher_1 = require("./data-fetcher");
10
+ const indicators_1 = require("./indicators");
11
+ // ============================================
12
+ // ANALYSIS FUNCTIONS
13
+ // ============================================
14
+ /**
15
+ * Analyze a single timeframe
16
+ */
17
+ function analyzeTimeframe(data) {
18
+ const closePrices = data.candles.map((c) => c.close);
19
+ const ema9 = (0, indicators_1.calculateEMA)(closePrices, 9);
20
+ const ema21 = (0, indicators_1.calculateEMA)(closePrices, 21);
21
+ const ema50 = (0, indicators_1.calculateEMA)(closePrices, 50);
22
+ const ema200 = (0, indicators_1.calculateEMA)(closePrices, 200);
23
+ const rsi = (0, indicators_1.calculateRSI)(closePrices, 14);
24
+ const macd = (0, indicators_1.calculateMACD)(closePrices);
25
+ const bollinger = (0, indicators_1.calculateBollingerBands)(closePrices);
26
+ const trend = (0, indicators_1.analyzeTrend)(data.candles);
27
+ // Determine signal
28
+ let bullishPoints = 0;
29
+ let bearishPoints = 0;
30
+ // EMA alignment
31
+ const currentEMA9 = ema9[ema9.length - 1] || 0;
32
+ const currentEMA21 = ema21[ema21.length - 1] || 0;
33
+ const currentEMA50 = ema50[ema50.length - 1] || 0;
34
+ if (currentEMA9 > currentEMA21 && currentEMA21 > currentEMA50)
35
+ bullishPoints += 2;
36
+ else if (currentEMA9 < currentEMA21 && currentEMA21 < currentEMA50)
37
+ bearishPoints += 2;
38
+ // RSI
39
+ if (rsi.condition === "oversold")
40
+ bullishPoints += 1;
41
+ if (rsi.condition === "overbought")
42
+ bearishPoints += 1;
43
+ if (rsi.divergence === "bullish")
44
+ bullishPoints += 2;
45
+ if (rsi.divergence === "bearish")
46
+ bearishPoints += 2;
47
+ // MACD
48
+ if (macd.crossover === "bullish")
49
+ bullishPoints += 2;
50
+ if (macd.crossover === "bearish")
51
+ bearishPoints += 2;
52
+ if (macd.momentum === "increasing" && macd.current.histogram > 0)
53
+ bullishPoints += 1;
54
+ if (macd.momentum === "decreasing" && macd.current.histogram < 0)
55
+ bearishPoints += 1;
56
+ // Bollinger
57
+ if (bollinger.pricePosition === "below_lower")
58
+ bullishPoints += 1;
59
+ if (bollinger.pricePosition === "above_upper")
60
+ bearishPoints += 1;
61
+ // Trend
62
+ if (trend.direction === "bullish")
63
+ bullishPoints += 2;
64
+ if (trend.direction === "bearish")
65
+ bearishPoints += 2;
66
+ let signal = "neutral";
67
+ if (bullishPoints > bearishPoints + 2)
68
+ signal = "bullish";
69
+ else if (bearishPoints > bullishPoints + 2)
70
+ signal = "bearish";
71
+ return {
72
+ interval: data.interval,
73
+ trend,
74
+ rsi,
75
+ macd,
76
+ bollinger,
77
+ ema: {
78
+ ema9: currentEMA9,
79
+ ema21: currentEMA21,
80
+ ema50: currentEMA50,
81
+ ema200: ema200[ema200.length - 1] || 0,
82
+ },
83
+ signal,
84
+ };
85
+ }
86
+ /**
87
+ * Generate timing recommendation
88
+ */
89
+ function generateTimingRecommendation(analysis, indicators, currentPrice, recommendation) {
90
+ const { supportResistance, rsi, macd, bollinger } = indicators;
91
+ // Default entry zone
92
+ let entryLow = currentPrice * 0.99;
93
+ let entryHigh = currentPrice * 1.01;
94
+ if (recommendation.includes("BUY")) {
95
+ // For buy signals
96
+ if (rsi.current > 60) {
97
+ // RSI high, wait for pullback
98
+ entryLow = supportResistance.nearestSupport;
99
+ entryHigh = analysis.ema.ema21;
100
+ return {
101
+ action: "wait_for_pullback",
102
+ timing: "Wait for price to pull back to EMA21 or support level",
103
+ entryZone: { low: entryLow, high: entryHigh },
104
+ reason: `RSI at ${rsi.current.toFixed(0)} is elevated. Better entry near $${entryHigh.toFixed(2)}`,
105
+ };
106
+ }
107
+ else if (bollinger.squeeze) {
108
+ // Squeeze, wait for breakout
109
+ entryLow = bollinger.current.upper;
110
+ entryHigh = bollinger.current.upper * 1.01;
111
+ return {
112
+ action: "wait_for_breakout",
113
+ timing: "Wait for breakout above Bollinger upper band",
114
+ entryZone: { low: entryLow, high: entryHigh },
115
+ reason: "Bollinger squeeze detected. Wait for volatility expansion",
116
+ };
117
+ }
118
+ else if (macd.crossover === "bullish") {
119
+ // Fresh MACD crossover, enter now
120
+ return {
121
+ action: "enter_now",
122
+ timing: "Enter on current candle close",
123
+ entryZone: { low: currentPrice * 0.995, high: currentPrice * 1.005 },
124
+ reason: "Fresh MACD bullish crossover provides good entry",
125
+ };
126
+ }
127
+ else {
128
+ // Wait for confirmation
129
+ return {
130
+ action: "wait_for_confirmation",
131
+ timing: "Wait for next 1-4 hour candle to confirm direction",
132
+ entryZone: { low: analysis.ema.ema9, high: currentPrice },
133
+ reason: "Wait for price to hold above EMA9 for confirmation",
134
+ };
135
+ }
136
+ }
137
+ else if (recommendation.includes("SELL")) {
138
+ // For sell signals
139
+ if (rsi.current < 40) {
140
+ entryHigh = supportResistance.nearestResistance;
141
+ entryLow = analysis.ema.ema21;
142
+ return {
143
+ action: "wait_for_pullback",
144
+ timing: "Wait for price to bounce to resistance before shorting",
145
+ entryZone: { low: entryLow, high: entryHigh },
146
+ reason: `RSI at ${rsi.current.toFixed(0)} is low. Wait for bounce to short`,
147
+ };
148
+ }
149
+ else if (macd.crossover === "bearish") {
150
+ return {
151
+ action: "enter_now",
152
+ timing: "Enter short on current candle close",
153
+ entryZone: { low: currentPrice * 0.995, high: currentPrice * 1.005 },
154
+ reason: "Fresh MACD bearish crossover provides good short entry",
155
+ };
156
+ }
157
+ else {
158
+ return {
159
+ action: "wait_for_confirmation",
160
+ timing: "Wait for break below EMA21 to confirm",
161
+ entryZone: { low: analysis.ema.ema21 * 0.99, high: analysis.ema.ema21 },
162
+ reason: "Wait for price to break below EMA21 for confirmation",
163
+ };
164
+ }
165
+ }
166
+ else {
167
+ // HOLD
168
+ return {
169
+ action: "avoid",
170
+ timing: "No clear setup - wait for better opportunity",
171
+ entryZone: {
172
+ low: supportResistance.nearestSupport,
173
+ high: supportResistance.nearestResistance,
174
+ },
175
+ reason: "Mixed signals suggest waiting for clearer direction",
176
+ };
177
+ }
178
+ }
179
+ /**
180
+ * Generate trade setup with stop loss and targets
181
+ */
182
+ function generateTradeSetup(currentPrice, indicators, recommendation) {
183
+ const { atr, supportResistance } = indicators;
184
+ const atrValue = atr.current;
185
+ const entry = currentPrice;
186
+ let stopLoss;
187
+ let target1;
188
+ let target2;
189
+ let target3;
190
+ if (recommendation.includes("BUY") || recommendation === "HOLD") {
191
+ // Long setup
192
+ stopLoss = Math.max(supportResistance.nearestSupport * 0.99, currentPrice - atrValue * 2);
193
+ target1 = currentPrice + atrValue * 1.5;
194
+ target2 = currentPrice + atrValue * 3;
195
+ target3 = supportResistance.nearestResistance;
196
+ }
197
+ else {
198
+ // Short setup
199
+ stopLoss = Math.min(supportResistance.nearestResistance * 1.01, currentPrice + atrValue * 2);
200
+ target1 = currentPrice - atrValue * 1.5;
201
+ target2 = currentPrice - atrValue * 3;
202
+ target3 = supportResistance.nearestSupport;
203
+ }
204
+ const risk = Math.abs(entry - stopLoss);
205
+ const reward = Math.abs(target2 - entry);
206
+ const riskRewardRatio = reward / risk;
207
+ // Position size based on 2% account risk
208
+ const positionSizeRisk = (2 / ((risk / entry) * 100)) * 100;
209
+ return {
210
+ entry,
211
+ stopLoss,
212
+ target1,
213
+ target2,
214
+ target3,
215
+ riskRewardRatio: Math.round(riskRewardRatio * 10) / 10,
216
+ positionSizeRisk: Math.min(positionSizeRisk, 100),
217
+ };
218
+ }
219
+ /**
220
+ * Determine market condition
221
+ */
222
+ function determineMarketCondition(indicators, timeframe) {
223
+ const { atr, bollinger, volume, trend } = indicators;
224
+ if (bollinger.squeeze && volume.trend === "decreasing") {
225
+ return {
226
+ type: "ranging",
227
+ description: "Market is consolidating in a tight range",
228
+ tradingAdvice: "Wait for breakout or trade range boundaries",
229
+ };
230
+ }
231
+ if (atr.volatility === "high" && volume.volumeRatio > 1.5) {
232
+ return {
233
+ type: "volatile",
234
+ description: "High volatility with increased volume",
235
+ tradingAdvice: "Use wider stops, reduce position size",
236
+ };
237
+ }
238
+ if (trend.phase === "trending" && trend.strength > 70) {
239
+ return {
240
+ type: "trending",
241
+ description: `Strong ${trend.direction} trend in progress`,
242
+ tradingAdvice: trend.direction === "bullish"
243
+ ? "Buy dips, trail stops, add on pullbacks"
244
+ : "Sell rallies, trail stops, add on bounces",
245
+ };
246
+ }
247
+ if (bollinger.current.bandwidth > 10 && !bollinger.squeeze) {
248
+ return {
249
+ type: "breakout",
250
+ description: "Potential breakout in progress",
251
+ tradingAdvice: "Watch for continuation or false breakout",
252
+ };
253
+ }
254
+ return {
255
+ type: "ranging",
256
+ description: "No clear trend, choppy price action",
257
+ tradingAdvice: "Reduce position size, trade with caution",
258
+ };
259
+ }
260
+ /**
261
+ * Generate detailed reasoning
262
+ */
263
+ function generateReasoning(indicators, timeframes, currentPrice) {
264
+ const bullishFactors = [];
265
+ const bearishFactors = [];
266
+ const keyLevels = [];
267
+ const warnings = [];
268
+ const { rsi, macd, bollinger, volume, supportResistance, trend, atr } = indicators;
269
+ // RSI analysis
270
+ if (rsi.condition === "oversold") {
271
+ bullishFactors.push(`RSI oversold at ${rsi.current.toFixed(1)} - potential bounce`);
272
+ }
273
+ else if (rsi.condition === "overbought") {
274
+ bearishFactors.push(`RSI overbought at ${rsi.current.toFixed(1)} - potential pullback`);
275
+ }
276
+ if (rsi.divergence === "bullish") {
277
+ bullishFactors.push("Bullish RSI divergence detected - reversal signal");
278
+ }
279
+ else if (rsi.divergence === "bearish") {
280
+ bearishFactors.push("Bearish RSI divergence detected - reversal signal");
281
+ }
282
+ // MACD analysis
283
+ if (macd.crossover === "bullish") {
284
+ bullishFactors.push("MACD bullish crossover - momentum shifting up");
285
+ }
286
+ else if (macd.crossover === "bearish") {
287
+ bearishFactors.push("MACD bearish crossover - momentum shifting down");
288
+ }
289
+ if (macd.current.histogram > 0 && macd.momentum === "increasing") {
290
+ bullishFactors.push("MACD histogram increasing - strong bullish momentum");
291
+ }
292
+ else if (macd.current.histogram < 0 && macd.momentum === "decreasing") {
293
+ bearishFactors.push("MACD histogram decreasing - strong bearish momentum");
294
+ }
295
+ // Bollinger analysis
296
+ if (bollinger.pricePosition === "below_lower") {
297
+ bullishFactors.push("Price below lower Bollinger Band - oversold");
298
+ }
299
+ else if (bollinger.pricePosition === "above_upper") {
300
+ bearishFactors.push("Price above upper Bollinger Band - overbought");
301
+ }
302
+ if (bollinger.squeeze) {
303
+ warnings.push("Bollinger squeeze - expect volatility expansion soon");
304
+ }
305
+ // Volume analysis
306
+ if (volume.volumeRatio > 1.5 && volume.confirmation) {
307
+ bullishFactors.push("High volume confirming price movement");
308
+ }
309
+ else if (volume.volumeRatio < 0.5) {
310
+ warnings.push("Low volume - move may lack conviction");
311
+ }
312
+ // Trend analysis
313
+ if (trend.direction === "bullish" && trend.strength > 60) {
314
+ bullishFactors.push(`Strong uptrend (${trend.strength}% strength)`);
315
+ }
316
+ else if (trend.direction === "bearish" && trend.strength > 60) {
317
+ bearishFactors.push(`Strong downtrend (${trend.strength}% strength)`);
318
+ }
319
+ if (trend.phase === "reversing") {
320
+ warnings.push("Trend may be reversing - watch for confirmation");
321
+ }
322
+ // Multi-timeframe
323
+ const { short, medium, long } = timeframes;
324
+ if (short.signal === medium.signal && medium.signal === long.signal) {
325
+ if (short.signal === "bullish") {
326
+ bullishFactors.push("All timeframes aligned bullish - high probability setup");
327
+ }
328
+ else if (short.signal === "bearish") {
329
+ bearishFactors.push("All timeframes aligned bearish - high probability setup");
330
+ }
331
+ }
332
+ else {
333
+ warnings.push("Timeframes not aligned - reduced probability");
334
+ }
335
+ // Key levels
336
+ keyLevels.push(`Support: $${supportResistance.nearestSupport.toFixed(2)} (${supportResistance.distanceToSupport.toFixed(1)}% away)`);
337
+ keyLevels.push(`Resistance: $${supportResistance.nearestResistance.toFixed(2)} (${supportResistance.distanceToResistance.toFixed(1)}% away)`);
338
+ keyLevels.push(`EMA21: $${timeframes.medium.ema.ema21.toFixed(2)}`);
339
+ keyLevels.push(`EMA50: $${timeframes.medium.ema.ema50.toFixed(2)}`);
340
+ // Volatility warning
341
+ if (atr.volatility === "high") {
342
+ warnings.push(`High volatility (ATR: ${atr.percentOfPrice.toFixed(1)}%) - use wider stops`);
343
+ }
344
+ // Generate summary
345
+ let summary;
346
+ if (bullishFactors.length > bearishFactors.length + 2) {
347
+ summary = `Strong bullish setup with ${bullishFactors.length} positive factors. `;
348
+ summary += bullishFactors[0] + ". ";
349
+ if (warnings.length > 0)
350
+ summary += `Caution: ${warnings[0]}`;
351
+ }
352
+ else if (bearishFactors.length > bullishFactors.length + 2) {
353
+ summary = `Strong bearish setup with ${bearishFactors.length} negative factors. `;
354
+ summary += bearishFactors[0] + ". ";
355
+ if (warnings.length > 0)
356
+ summary += `Caution: ${warnings[0]}`;
357
+ }
358
+ else {
359
+ summary = "Mixed signals present. ";
360
+ if (bullishFactors.length > 0)
361
+ summary += bullishFactors[0] + ", but ";
362
+ if (bearishFactors.length > 0)
363
+ summary += bearishFactors[0] + ". ";
364
+ summary += "Wait for clearer setup.";
365
+ }
366
+ return { summary, bullishFactors, bearishFactors, keyLevels, warnings };
367
+ }
368
+ // ============================================
369
+ // MAIN ANALYSIS FUNCTION
370
+ // ============================================
371
+ /**
372
+ * Perform comprehensive market analysis
373
+ */
374
+ async function analyzeMarket(symbol) {
375
+ // Fetch data for multiple timeframes in parallel
376
+ const [data1h, data4h, data1d] = await Promise.all([
377
+ (0, data_fetcher_1.fetchCryptoData)(symbol, "1h", 200),
378
+ (0, data_fetcher_1.fetchCryptoData)(symbol, "4h", 200),
379
+ (0, data_fetcher_1.fetchCryptoData)(symbol, "1d", 200),
380
+ ]);
381
+ // Analyze each timeframe
382
+ const shortTF = analyzeTimeframe(data1h);
383
+ const mediumTF = analyzeTimeframe(data4h);
384
+ const longTF = analyzeTimeframe(data1d);
385
+ // Calculate all indicators on 4h (primary timeframe)
386
+ const closePrices = data4h.candles.map((c) => c.close);
387
+ const indicators = {
388
+ rsi: (0, indicators_1.calculateRSI)(closePrices),
389
+ macd: (0, indicators_1.calculateMACD)(closePrices),
390
+ bollinger: (0, indicators_1.calculateBollingerBands)(closePrices),
391
+ atr: (0, indicators_1.calculateATR)(data4h.candles),
392
+ volume: (0, indicators_1.analyzeVolume)(data4h.candles),
393
+ supportResistance: (0, indicators_1.calculateSupportResistance)(data4h.candles),
394
+ trend: (0, indicators_1.analyzeTrend)(data4h.candles),
395
+ };
396
+ // Determine timeframe alignment
397
+ let timeframeAlignment = "mixed";
398
+ if (shortTF.signal === "bullish" &&
399
+ mediumTF.signal === "bullish" &&
400
+ longTF.signal === "bullish") {
401
+ timeframeAlignment = "aligned_bullish";
402
+ }
403
+ else if (shortTF.signal === "bearish" &&
404
+ mediumTF.signal === "bearish" &&
405
+ longTF.signal === "bearish") {
406
+ timeframeAlignment = "aligned_bearish";
407
+ }
408
+ // Calculate recommendation score
409
+ let score = 0;
410
+ // RSI contribution (-2 to +2)
411
+ if (indicators.rsi.condition === "oversold")
412
+ score += 2;
413
+ else if (indicators.rsi.condition === "overbought")
414
+ score -= 2;
415
+ if (indicators.rsi.divergence === "bullish")
416
+ score += 2;
417
+ else if (indicators.rsi.divergence === "bearish")
418
+ score -= 2;
419
+ // MACD contribution (-2 to +2)
420
+ if (indicators.macd.crossover === "bullish")
421
+ score += 2;
422
+ else if (indicators.macd.crossover === "bearish")
423
+ score -= 2;
424
+ if (indicators.macd.current.histogram > 0)
425
+ score += 1;
426
+ else
427
+ score -= 1;
428
+ // Bollinger contribution (-1 to +1)
429
+ if (indicators.bollinger.pricePosition === "below_lower")
430
+ score += 1;
431
+ else if (indicators.bollinger.pricePosition === "above_upper")
432
+ score -= 1;
433
+ // Trend contribution (-3 to +3)
434
+ if (indicators.trend.direction === "bullish")
435
+ score += Math.ceil(indicators.trend.strength / 33);
436
+ else if (indicators.trend.direction === "bearish")
437
+ score -= Math.ceil(indicators.trend.strength / 33);
438
+ // Timeframe alignment contribution (-3 to +3)
439
+ if (timeframeAlignment === "aligned_bullish")
440
+ score += 3;
441
+ else if (timeframeAlignment === "aligned_bearish")
442
+ score -= 3;
443
+ // Volume confirmation
444
+ if (indicators.volume.confirmation)
445
+ score += score > 0 ? 1 : -1;
446
+ // Determine recommendation
447
+ let recommendation;
448
+ if (score >= 8)
449
+ recommendation = "STRONG_BUY";
450
+ else if (score >= 4)
451
+ recommendation = "BUY";
452
+ else if (score >= 1)
453
+ recommendation = "WAIT_TO_BUY";
454
+ else if (score >= -1)
455
+ recommendation = "HOLD";
456
+ else if (score >= -4)
457
+ recommendation = "WAIT_TO_SELL";
458
+ else if (score >= -8)
459
+ recommendation = "SELL";
460
+ else
461
+ recommendation = "STRONG_SELL";
462
+ // Calculate confidence
463
+ const maxScore = 15;
464
+ const confidence = Math.min(Math.round((Math.abs(score) / maxScore) * 100), 95);
465
+ // Determine risk level
466
+ let riskLevel = "medium";
467
+ if (indicators.atr.volatility === "high" || !indicators.volume.confirmation) {
468
+ riskLevel = "high";
469
+ }
470
+ else if (timeframeAlignment !== "mixed" && indicators.trend.strength > 70) {
471
+ riskLevel = "low";
472
+ }
473
+ const timeframes = { short: shortTF, medium: mediumTF, long: longTF };
474
+ // Generate timing recommendation
475
+ const timing = generateTimingRecommendation(mediumTF, indicators, data4h.currentPrice, recommendation);
476
+ // Generate trade setup
477
+ const tradeSetup = generateTradeSetup(data4h.currentPrice, indicators, recommendation);
478
+ // Determine market condition
479
+ const marketCondition = determineMarketCondition(indicators, mediumTF);
480
+ // Generate reasoning
481
+ const reasoning = generateReasoning(indicators, timeframes, data4h.currentPrice);
482
+ return {
483
+ symbol: data4h.symbol,
484
+ currentPrice: data4h.currentPrice,
485
+ priceChange24h: data4h.priceChangePercent24h,
486
+ recommendation,
487
+ confidence,
488
+ riskLevel,
489
+ timeframes,
490
+ timeframeAlignment,
491
+ indicators,
492
+ timing,
493
+ tradeSetup,
494
+ marketCondition,
495
+ reasoning,
496
+ };
497
+ }