prab-cli 1.2.1 → 1.2.4

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,512 @@
1
+ "use strict";
2
+ /**
3
+ * Smart Money Concepts (SMC) Indicators
4
+ * Institutional trading strategy analysis
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.findSwingPoints = findSwingPoints;
8
+ exports.detectStructureBreaks = detectStructureBreaks;
9
+ exports.findOrderBlocks = findOrderBlocks;
10
+ exports.findFairValueGaps = findFairValueGaps;
11
+ exports.findLiquidityZones = findLiquidityZones;
12
+ exports.calculatePremiumDiscount = calculatePremiumDiscount;
13
+ exports.analyzeSMC = analyzeSMC;
14
+ // ============================================
15
+ // SWING POINT DETECTION
16
+ // ============================================
17
+ /**
18
+ * Find swing highs and lows
19
+ */
20
+ function findSwingPoints(candles, lookback = 3) {
21
+ const swingPoints = [];
22
+ for (let i = lookback; i < candles.length - lookback; i++) {
23
+ const current = candles[i];
24
+ // Check for swing high
25
+ let isSwingHigh = true;
26
+ let isSwingLow = true;
27
+ for (let j = 1; j <= lookback; j++) {
28
+ if (candles[i - j].high >= current.high || candles[i + j].high >= current.high) {
29
+ isSwingHigh = false;
30
+ }
31
+ if (candles[i - j].low <= current.low || candles[i + j].low <= current.low) {
32
+ isSwingLow = false;
33
+ }
34
+ }
35
+ if (isSwingHigh) {
36
+ swingPoints.push({
37
+ index: i,
38
+ price: current.high,
39
+ type: "high",
40
+ timestamp: current.timestamp,
41
+ });
42
+ }
43
+ if (isSwingLow) {
44
+ swingPoints.push({
45
+ index: i,
46
+ price: current.low,
47
+ type: "low",
48
+ timestamp: current.timestamp,
49
+ });
50
+ }
51
+ }
52
+ return swingPoints.sort((a, b) => a.index - b.index);
53
+ }
54
+ // ============================================
55
+ // MARKET STRUCTURE ANALYSIS
56
+ // ============================================
57
+ /**
58
+ * Detect Break of Structure (BOS) and Change of Character (CHoCH)
59
+ */
60
+ function detectStructureBreaks(candles, swingPoints) {
61
+ const breaks = [];
62
+ if (swingPoints.length < 4)
63
+ return breaks;
64
+ // Get recent swing highs and lows
65
+ const highs = swingPoints.filter((p) => p.type === "high");
66
+ const lows = swingPoints.filter((p) => p.type === "low");
67
+ // Track current trend
68
+ let currentTrend = "unknown";
69
+ // Analyze structure
70
+ for (let i = 2; i < highs.length; i++) {
71
+ const prevHigh = highs[i - 1];
72
+ const currHigh = highs[i];
73
+ // Find lows between these highs
74
+ const lowsBetween = lows.filter((l) => l.index > prevHigh.index && l.index < currHigh.index);
75
+ if (lowsBetween.length > 0) {
76
+ const lowestLow = lowsBetween.reduce((a, b) => (a.price < b.price ? a : b));
77
+ // Check for BOS or CHoCH
78
+ for (let j = currHigh.index; j < candles.length; j++) {
79
+ const candle = candles[j];
80
+ // Bullish BOS: Price breaks above previous high in uptrend
81
+ if (candle.close > prevHigh.price) {
82
+ if (currentTrend === "bullish") {
83
+ breaks.push({
84
+ type: "BOS",
85
+ direction: "bullish",
86
+ level: prevHigh.price,
87
+ index: j,
88
+ timestamp: candle.timestamp,
89
+ });
90
+ }
91
+ else if (currentTrend === "bearish") {
92
+ breaks.push({
93
+ type: "CHoCH",
94
+ direction: "bullish",
95
+ level: prevHigh.price,
96
+ index: j,
97
+ timestamp: candle.timestamp,
98
+ });
99
+ currentTrend = "bullish";
100
+ }
101
+ else {
102
+ currentTrend = "bullish";
103
+ }
104
+ break;
105
+ }
106
+ // Bearish BOS: Price breaks below previous low in downtrend
107
+ if (candle.close < lowestLow.price) {
108
+ if (currentTrend === "bearish") {
109
+ breaks.push({
110
+ type: "BOS",
111
+ direction: "bearish",
112
+ level: lowestLow.price,
113
+ index: j,
114
+ timestamp: candle.timestamp,
115
+ });
116
+ }
117
+ else if (currentTrend === "bullish") {
118
+ breaks.push({
119
+ type: "CHoCH",
120
+ direction: "bearish",
121
+ level: lowestLow.price,
122
+ index: j,
123
+ timestamp: candle.timestamp,
124
+ });
125
+ currentTrend = "bearish";
126
+ }
127
+ else {
128
+ currentTrend = "bearish";
129
+ }
130
+ break;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ return breaks;
136
+ }
137
+ // ============================================
138
+ // ORDER BLOCKS
139
+ // ============================================
140
+ /**
141
+ * Detect Order Blocks (institutional buying/selling zones)
142
+ */
143
+ function findOrderBlocks(candles, lookback = 50) {
144
+ const orderBlocks = [];
145
+ const currentPrice = candles[candles.length - 1].close;
146
+ for (let i = 2; i < Math.min(candles.length - 1, lookback); i++) {
147
+ const idx = candles.length - 1 - i;
148
+ const candle = candles[idx];
149
+ const nextCandle = candles[idx + 1];
150
+ const prevCandle = candles[idx - 1];
151
+ // Bullish Order Block: Last bearish candle before strong bullish move
152
+ if (candle.close < candle.open && // Bearish candle
153
+ nextCandle.close > nextCandle.open && // Followed by bullish
154
+ nextCandle.close > candle.high && // Strong bullish move
155
+ nextCandle.close - nextCandle.open > (candle.open - candle.close) * 1.5 // Momentum
156
+ ) {
157
+ const ob = {
158
+ type: "bullish",
159
+ top: candle.open,
160
+ bottom: candle.low,
161
+ mitigated: currentPrice < candle.low,
162
+ strength: "moderate",
163
+ index: idx,
164
+ timestamp: candle.timestamp,
165
+ };
166
+ // Check strength based on displacement
167
+ const displacement = (nextCandle.close - candle.high) / candle.high;
168
+ if (displacement > 0.02)
169
+ ob.strength = "strong";
170
+ if (displacement < 0.01)
171
+ ob.strength = "weak";
172
+ // Check if mitigated
173
+ for (let j = idx + 2; j < candles.length; j++) {
174
+ if (candles[j].low <= ob.bottom) {
175
+ ob.mitigated = true;
176
+ break;
177
+ }
178
+ }
179
+ if (!ob.mitigated) {
180
+ orderBlocks.push(ob);
181
+ }
182
+ }
183
+ // Bearish Order Block: Last bullish candle before strong bearish move
184
+ if (candle.close > candle.open && // Bullish candle
185
+ nextCandle.close < nextCandle.open && // Followed by bearish
186
+ nextCandle.close < candle.low && // Strong bearish move
187
+ nextCandle.open - nextCandle.close > (candle.close - candle.open) * 1.5 // Momentum
188
+ ) {
189
+ const ob = {
190
+ type: "bearish",
191
+ top: candle.high,
192
+ bottom: candle.open,
193
+ mitigated: currentPrice > candle.high,
194
+ strength: "moderate",
195
+ index: idx,
196
+ timestamp: candle.timestamp,
197
+ };
198
+ // Check strength
199
+ const displacement = (candle.low - nextCandle.close) / candle.low;
200
+ if (displacement > 0.02)
201
+ ob.strength = "strong";
202
+ if (displacement < 0.01)
203
+ ob.strength = "weak";
204
+ // Check if mitigated
205
+ for (let j = idx + 2; j < candles.length; j++) {
206
+ if (candles[j].high >= ob.top) {
207
+ ob.mitigated = true;
208
+ break;
209
+ }
210
+ }
211
+ if (!ob.mitigated) {
212
+ orderBlocks.push(ob);
213
+ }
214
+ }
215
+ }
216
+ return orderBlocks;
217
+ }
218
+ // ============================================
219
+ // FAIR VALUE GAPS (FVG)
220
+ // ============================================
221
+ /**
222
+ * Detect Fair Value Gaps (imbalances)
223
+ */
224
+ function findFairValueGaps(candles, lookback = 50) {
225
+ const fvgs = [];
226
+ const currentPrice = candles[candles.length - 1].close;
227
+ for (let i = 2; i < Math.min(candles.length, lookback); i++) {
228
+ const idx = candles.length - 1 - i;
229
+ if (idx < 1)
230
+ continue;
231
+ const candle1 = candles[idx - 1];
232
+ const candle2 = candles[idx];
233
+ const candle3 = candles[idx + 1];
234
+ // Bullish FVG: Gap between candle 1 high and candle 3 low
235
+ if (candle3.low > candle1.high) {
236
+ const fvg = {
237
+ type: "bullish",
238
+ top: candle3.low,
239
+ bottom: candle1.high,
240
+ filled: false,
241
+ fillPercentage: 0,
242
+ index: idx,
243
+ timestamp: candle2.timestamp,
244
+ };
245
+ // Check if filled
246
+ let lowestReach = candle3.low;
247
+ for (let j = idx + 2; j < candles.length; j++) {
248
+ if (candles[j].low < lowestReach) {
249
+ lowestReach = candles[j].low;
250
+ }
251
+ }
252
+ if (lowestReach <= candle1.high) {
253
+ fvg.filled = true;
254
+ fvg.fillPercentage = 100;
255
+ }
256
+ else {
257
+ const gapSize = candle3.low - candle1.high;
258
+ const filledSize = candle3.low - lowestReach;
259
+ fvg.fillPercentage = (filledSize / gapSize) * 100;
260
+ }
261
+ fvgs.push(fvg);
262
+ }
263
+ // Bearish FVG: Gap between candle 1 low and candle 3 high
264
+ if (candle3.high < candle1.low) {
265
+ const fvg = {
266
+ type: "bearish",
267
+ top: candle1.low,
268
+ bottom: candle3.high,
269
+ filled: false,
270
+ fillPercentage: 0,
271
+ index: idx,
272
+ timestamp: candle2.timestamp,
273
+ };
274
+ // Check if filled
275
+ let highestReach = candle3.high;
276
+ for (let j = idx + 2; j < candles.length; j++) {
277
+ if (candles[j].high > highestReach) {
278
+ highestReach = candles[j].high;
279
+ }
280
+ }
281
+ if (highestReach >= candle1.low) {
282
+ fvg.filled = true;
283
+ fvg.fillPercentage = 100;
284
+ }
285
+ else {
286
+ const gapSize = candle1.low - candle3.high;
287
+ const filledSize = highestReach - candle3.high;
288
+ fvg.fillPercentage = (filledSize / gapSize) * 100;
289
+ }
290
+ fvgs.push(fvg);
291
+ }
292
+ }
293
+ return fvgs;
294
+ }
295
+ // ============================================
296
+ // LIQUIDITY ZONES
297
+ // ============================================
298
+ /**
299
+ * Find liquidity zones (stop loss clusters)
300
+ */
301
+ function findLiquidityZones(candles, swingPoints) {
302
+ const buySide = [];
303
+ const sellSide = [];
304
+ const currentPrice = candles[candles.length - 1].close;
305
+ // Group swing highs (buy-side liquidity - stops above)
306
+ const highs = swingPoints.filter((p) => p.type === "high" && p.price > currentPrice);
307
+ const lows = swingPoints.filter((p) => p.type === "low" && p.price < currentPrice);
308
+ // Cluster nearby highs
309
+ const clusterThreshold = 0.005; // 0.5%
310
+ highs.forEach((high) => {
311
+ const existing = buySide.find((z) => Math.abs(z.level - high.price) / high.price < clusterThreshold);
312
+ if (existing) {
313
+ existing.strength++;
314
+ existing.level = (existing.level + high.price) / 2;
315
+ }
316
+ else {
317
+ buySide.push({
318
+ type: "buy_side",
319
+ level: high.price,
320
+ strength: 1,
321
+ swept: false,
322
+ index: high.index,
323
+ });
324
+ }
325
+ });
326
+ // Cluster nearby lows
327
+ lows.forEach((low) => {
328
+ const existing = sellSide.find((z) => Math.abs(z.level - low.price) / low.price < clusterThreshold);
329
+ if (existing) {
330
+ existing.strength++;
331
+ existing.level = (existing.level + low.price) / 2;
332
+ }
333
+ else {
334
+ sellSide.push({
335
+ type: "sell_side",
336
+ level: low.price,
337
+ strength: 1,
338
+ swept: false,
339
+ index: low.index,
340
+ });
341
+ }
342
+ });
343
+ // Sort by proximity to current price
344
+ buySide.sort((a, b) => a.level - b.level);
345
+ sellSide.sort((a, b) => b.level - a.level);
346
+ return { buySide: buySide.slice(0, 5), sellSide: sellSide.slice(0, 5) };
347
+ }
348
+ // ============================================
349
+ // PREMIUM/DISCOUNT ZONES
350
+ // ============================================
351
+ /**
352
+ * Calculate Premium/Discount zones using recent range
353
+ */
354
+ function calculatePremiumDiscount(candles, lookback = 50) {
355
+ const recentCandles = candles.slice(-lookback);
356
+ const rangeHigh = Math.max(...recentCandles.map((c) => c.high));
357
+ const rangeLow = Math.min(...recentCandles.map((c) => c.low));
358
+ const equilibrium = (rangeHigh + rangeLow) / 2;
359
+ const currentPrice = candles[candles.length - 1].close;
360
+ // Calculate Fibonacci level (0 = low, 0.5 = equilibrium, 1 = high)
361
+ const fibLevel = (currentPrice - rangeLow) / (rangeHigh - rangeLow);
362
+ let zone;
363
+ if (fibLevel > 0.618) {
364
+ zone = "premium";
365
+ }
366
+ else if (fibLevel < 0.382) {
367
+ zone = "discount";
368
+ }
369
+ else {
370
+ zone = "equilibrium";
371
+ }
372
+ return {
373
+ zone,
374
+ fibLevel: fibLevel * 100,
375
+ rangeHigh,
376
+ rangeLow,
377
+ equilibrium,
378
+ };
379
+ }
380
+ // ============================================
381
+ // MAIN SMC ANALYSIS
382
+ // ============================================
383
+ /**
384
+ * Perform complete SMC analysis
385
+ */
386
+ function analyzeSMC(candles) {
387
+ // Find swing points
388
+ const swingPoints = findSwingPoints(candles, 3);
389
+ const swingHighs = swingPoints.filter((p) => p.type === "high");
390
+ const swingLows = swingPoints.filter((p) => p.type === "low");
391
+ // Detect structure breaks
392
+ const structureBreaks = detectStructureBreaks(candles, swingPoints);
393
+ const lastBOS = structureBreaks.filter((b) => b.type === "BOS").pop() || null;
394
+ const lastCHoCH = structureBreaks.filter((b) => b.type === "CHoCH").pop() || null;
395
+ // Determine market structure trend
396
+ let marketTrend = "ranging";
397
+ if (lastBOS) {
398
+ marketTrend = lastBOS.direction;
399
+ }
400
+ if (lastCHoCH && (!lastBOS || lastCHoCH.index > lastBOS.index)) {
401
+ marketTrend = lastCHoCH.direction;
402
+ }
403
+ // Find order blocks
404
+ const orderBlocks = findOrderBlocks(candles);
405
+ const bullishOBs = orderBlocks.filter((ob) => ob.type === "bullish");
406
+ const bearishOBs = orderBlocks.filter((ob) => ob.type === "bearish");
407
+ // Find nearest unmitigated order block
408
+ const currentPrice = candles[candles.length - 1].close;
409
+ const nearestOB = orderBlocks
410
+ .filter((ob) => !ob.mitigated)
411
+ .sort((a, b) => {
412
+ const distA = Math.min(Math.abs(currentPrice - a.top), Math.abs(currentPrice - a.bottom));
413
+ const distB = Math.min(Math.abs(currentPrice - b.top), Math.abs(currentPrice - b.bottom));
414
+ return distA - distB;
415
+ })[0] || null;
416
+ // Find FVGs
417
+ const fvgs = findFairValueGaps(candles);
418
+ const bullishFVGs = fvgs.filter((f) => f.type === "bullish");
419
+ const bearishFVGs = fvgs.filter((f) => f.type === "bearish");
420
+ const unfilledFVGs = fvgs.filter((f) => !f.filled);
421
+ // Find liquidity
422
+ const liquidity = findLiquidityZones(candles, swingPoints);
423
+ const nextLiqTarget = marketTrend === "bullish"
424
+ ? liquidity.buySide[0] || null
425
+ : marketTrend === "bearish"
426
+ ? liquidity.sellSide[0] || null
427
+ : null;
428
+ // Premium/Discount
429
+ const premiumDiscount = calculatePremiumDiscount(candles);
430
+ // Generate bias
431
+ const reasoning = [];
432
+ let biasScore = 0;
433
+ // Structure-based bias
434
+ if (marketTrend === "bullish") {
435
+ biasScore += 30;
436
+ reasoning.push(`Bullish market structure (${lastBOS ? "BOS" : "CHoCH"} to upside)`);
437
+ }
438
+ else if (marketTrend === "bearish") {
439
+ biasScore -= 30;
440
+ reasoning.push(`Bearish market structure (${lastBOS ? "BOS" : "CHoCH"} to downside)`);
441
+ }
442
+ // Order block proximity
443
+ if (nearestOB) {
444
+ if (nearestOB.type === "bullish" && currentPrice <= nearestOB.top * 1.01) {
445
+ biasScore += 20;
446
+ reasoning.push(`Price at bullish order block ($${nearestOB.bottom.toFixed(2)} - $${nearestOB.top.toFixed(2)})`);
447
+ }
448
+ else if (nearestOB.type === "bearish" && currentPrice >= nearestOB.bottom * 0.99) {
449
+ biasScore -= 20;
450
+ reasoning.push(`Price at bearish order block ($${nearestOB.bottom.toFixed(2)} - $${nearestOB.top.toFixed(2)})`);
451
+ }
452
+ }
453
+ // Premium/Discount zone
454
+ if (premiumDiscount.zone === "discount") {
455
+ biasScore += 15;
456
+ reasoning.push(`Price in discount zone (${premiumDiscount.fibLevel.toFixed(0)}% of range)`);
457
+ }
458
+ else if (premiumDiscount.zone === "premium") {
459
+ biasScore -= 15;
460
+ reasoning.push(`Price in premium zone (${premiumDiscount.fibLevel.toFixed(0)}% of range)`);
461
+ }
462
+ // Unfilled FVGs
463
+ const bullishUnfilled = unfilledFVGs.filter((f) => f.type === "bullish" && f.bottom > currentPrice);
464
+ const bearishUnfilled = unfilledFVGs.filter((f) => f.type === "bearish" && f.top < currentPrice);
465
+ if (bullishUnfilled.length > 0) {
466
+ reasoning.push(`${bullishUnfilled.length} unfilled bullish FVG(s) above`);
467
+ }
468
+ if (bearishUnfilled.length > 0) {
469
+ reasoning.push(`${bearishUnfilled.length} unfilled bearish FVG(s) below`);
470
+ }
471
+ // Liquidity targets
472
+ if (liquidity.buySide.length > 0) {
473
+ const nearestBuySide = liquidity.buySide[0];
474
+ reasoning.push(`Buy-side liquidity at $${nearestBuySide.level.toFixed(2)} (${nearestBuySide.strength} touches)`);
475
+ }
476
+ if (liquidity.sellSide.length > 0) {
477
+ const nearestSellSide = liquidity.sellSide[0];
478
+ reasoning.push(`Sell-side liquidity at $${nearestSellSide.level.toFixed(2)} (${nearestSellSide.strength} touches)`);
479
+ }
480
+ const biasDirection = biasScore > 20 ? "bullish" : biasScore < -20 ? "bearish" : "neutral";
481
+ const confidence = Math.min(Math.abs(biasScore), 100);
482
+ return {
483
+ marketStructure: {
484
+ trend: marketTrend,
485
+ lastBOS,
486
+ lastCHoCH,
487
+ swingHighs: swingHighs.slice(-10),
488
+ swingLows: swingLows.slice(-10),
489
+ },
490
+ orderBlocks: {
491
+ bullish: bullishOBs.slice(0, 5),
492
+ bearish: bearishOBs.slice(0, 5),
493
+ nearest: nearestOB,
494
+ },
495
+ fairValueGaps: {
496
+ bullish: bullishFVGs.slice(0, 5),
497
+ bearish: bearishFVGs.slice(0, 5),
498
+ unfilled: unfilledFVGs.slice(0, 5),
499
+ },
500
+ liquidity: {
501
+ buySide: liquidity.buySide,
502
+ sellSide: liquidity.sellSide,
503
+ nextTarget: nextLiqTarget,
504
+ },
505
+ premiumDiscount,
506
+ bias: {
507
+ direction: biasDirection,
508
+ confidence,
509
+ reasoning,
510
+ },
511
+ };
512
+ }
@@ -8,6 +8,24 @@ exports.showSlashCommandMenu = showSlashCommandMenu;
8
8
  const search_1 = __importDefault(require("@inquirer/search"));
9
9
  const chalk_1 = __importDefault(require("chalk"));
10
10
  exports.SLASH_COMMANDS = [
11
+ {
12
+ name: "/smc",
13
+ description: "Smart Money Concepts analysis (Order Blocks, FVG, Liquidity)",
14
+ shortcut: "smc",
15
+ action: "smc",
16
+ },
17
+ {
18
+ name: "/analyze",
19
+ description: "Deep crypto analysis with multi-timeframe & all indicators",
20
+ shortcut: "a",
21
+ action: "analyze",
22
+ },
23
+ {
24
+ name: "/signal",
25
+ description: "Quick crypto trading signal (e.g., /signal btc)",
26
+ shortcut: "sig",
27
+ action: "signal",
28
+ },
11
29
  {
12
30
  name: "/model",
13
31
  description: "Switch between available AI models",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prab-cli",
3
- "version": "1.2.1",
3
+ "version": "1.2.4",
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": {