prab-cli 1.2.0 → 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,548 @@
1
+ "use strict";
2
+ /**
3
+ * ASCII Chart Visualization for Trading Analysis
4
+ * Creates visual price charts with indicators
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.createCandlestickChart = createCandlestickChart;
11
+ exports.createPriceLevelChart = createPriceLevelChart;
12
+ exports.createSMCVisualChart = createSMCVisualChart;
13
+ exports.createTradeSetupVisual = createTradeSetupVisual;
14
+ exports.createMarketStructureVisual = createMarketStructureVisual;
15
+ exports.createOrderBlockVisual = createOrderBlockVisual;
16
+ exports.createFVGVisual = createFVGVisual;
17
+ exports.createLiquidityVisual = createLiquidityVisual;
18
+ const chalk_1 = __importDefault(require("chalk"));
19
+ // ============================================
20
+ // MINI CANDLESTICK CHART
21
+ // ============================================
22
+ /**
23
+ * Create ASCII candlestick chart
24
+ */
25
+ function createCandlestickChart(candles, width = 50, height = 15) {
26
+ const recentCandles = candles.slice(-width);
27
+ // Find price range
28
+ const allPrices = recentCandles.flatMap((c) => [c.high, c.low]);
29
+ const minPrice = Math.min(...allPrices);
30
+ const maxPrice = Math.max(...allPrices);
31
+ const priceRange = maxPrice - minPrice;
32
+ // Create chart grid
33
+ const chart = Array(height)
34
+ .fill(null)
35
+ .map(() => Array(width).fill(" "));
36
+ // Plot candles
37
+ recentCandles.forEach((candle, i) => {
38
+ const isBullish = candle.close >= candle.open;
39
+ // Calculate positions
40
+ const highY = Math.floor((1 - (candle.high - minPrice) / priceRange) * (height - 1));
41
+ const lowY = Math.floor((1 - (candle.low - minPrice) / priceRange) * (height - 1));
42
+ const openY = Math.floor((1 - (candle.open - minPrice) / priceRange) * (height - 1));
43
+ const closeY = Math.floor((1 - (candle.close - minPrice) / priceRange) * (height - 1));
44
+ const bodyTop = Math.min(openY, closeY);
45
+ const bodyBottom = Math.max(openY, closeY);
46
+ // Draw wick
47
+ for (let y = highY; y <= lowY; y++) {
48
+ if (y >= 0 && y < height) {
49
+ if (y < bodyTop || y > bodyBottom) {
50
+ chart[y][i] = "│";
51
+ }
52
+ }
53
+ }
54
+ // Draw body
55
+ for (let y = bodyTop; y <= bodyBottom; y++) {
56
+ if (y >= 0 && y < height) {
57
+ chart[y][i] = isBullish ? "█" : "░";
58
+ }
59
+ }
60
+ });
61
+ // Convert to strings with colors
62
+ const lines = [];
63
+ for (let y = 0; y < height; y++) {
64
+ let line = "";
65
+ for (let x = 0; x < width; x++) {
66
+ const char = chart[y][x];
67
+ if (char === "█") {
68
+ line += chalk_1.default.green(char);
69
+ }
70
+ else if (char === "░") {
71
+ line += chalk_1.default.red(char);
72
+ }
73
+ else if (char === "│") {
74
+ line += chalk_1.default.gray(char);
75
+ }
76
+ else {
77
+ line += chalk_1.default.gray("·");
78
+ }
79
+ }
80
+ lines.push(line);
81
+ }
82
+ return lines;
83
+ }
84
+ // ============================================
85
+ // PRICE LEVEL CHART
86
+ // ============================================
87
+ /**
88
+ * Create visual price level chart with SMC zones
89
+ */
90
+ function createPriceLevelChart(currentPrice, levels, height = 20) {
91
+ // Sort levels by price (high to low)
92
+ const sortedLevels = [...levels].sort((a, b) => b.price - a.price);
93
+ // Get price range
94
+ const prices = levels.map((l) => l.price);
95
+ const minPrice = Math.min(...prices, currentPrice) * 0.995;
96
+ const maxPrice = Math.max(...prices, currentPrice) * 1.005;
97
+ const priceRange = maxPrice - minPrice;
98
+ // Create chart lines
99
+ const lines = [];
100
+ const chartWidth = 55;
101
+ // Header
102
+ lines.push(chalk_1.default.gray("┌" + "─".repeat(chartWidth) + "┐"));
103
+ lines.push(chalk_1.default.gray("│") +
104
+ chalk_1.default.bold.white(" PRICE LEVELS & SMC ZONES".padEnd(chartWidth)) +
105
+ chalk_1.default.gray("│"));
106
+ lines.push(chalk_1.default.gray("├" + "─".repeat(chartWidth) + "┤"));
107
+ // Calculate Y positions for each level
108
+ const levelPositions = sortedLevels.map((level) => ({
109
+ level,
110
+ y: Math.floor((1 - (level.price - minPrice) / priceRange) * (height - 1)),
111
+ }));
112
+ // Current price Y position
113
+ const currentY = Math.floor((1 - (currentPrice - minPrice) / priceRange) * (height - 1));
114
+ // Draw chart
115
+ for (let y = 0; y < height; y++) {
116
+ let lineContent = "";
117
+ // Price scale on left
118
+ const priceAtY = maxPrice - (y / (height - 1)) * priceRange;
119
+ const priceLabel = formatShortPrice(priceAtY).padStart(10);
120
+ // Check if current price is at this level
121
+ const isCurrentPrice = y === currentY;
122
+ // Find levels at this Y position
123
+ const levelsAtY = levelPositions.filter((lp) => lp.y === y);
124
+ // Build the visual line
125
+ let visual = "";
126
+ let labelText = "";
127
+ if (isCurrentPrice) {
128
+ visual = chalk_1.default.yellow("━━━━━━━━━━━━━━━━") + chalk_1.default.bold.yellow(" ◄ CURRENT");
129
+ labelText = ` $${formatShortPrice(currentPrice)}`;
130
+ }
131
+ else if (levelsAtY.length > 0) {
132
+ const mainLevel = levelsAtY[0].level;
133
+ switch (mainLevel.type) {
134
+ case "ob_top":
135
+ case "ob_bottom":
136
+ visual = mainLevel.color("████████████████") + " " + mainLevel.label;
137
+ break;
138
+ case "fvg":
139
+ visual = mainLevel.color("░░░░░░░░░░░░░░░░") + " " + mainLevel.label;
140
+ break;
141
+ case "liquidity":
142
+ visual = mainLevel.color("◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆") + " " + mainLevel.label;
143
+ break;
144
+ case "entry":
145
+ visual = chalk_1.default.cyan("────────────────") + chalk_1.default.cyan(" ► ENTRY");
146
+ break;
147
+ case "stop":
148
+ visual = chalk_1.default.red("────────────────") + chalk_1.default.red(" ✖ STOP");
149
+ break;
150
+ case "target":
151
+ visual = chalk_1.default.green("────────────────") + chalk_1.default.green(" ★ TARGET");
152
+ break;
153
+ case "support":
154
+ visual = chalk_1.default.green("════════════════") + chalk_1.default.green(" SUPPORT");
155
+ break;
156
+ case "resistance":
157
+ visual = chalk_1.default.red("════════════════") + chalk_1.default.red(" RESISTANCE");
158
+ break;
159
+ default:
160
+ visual = chalk_1.default.gray("----------------") + " " + mainLevel.label;
161
+ }
162
+ }
163
+ else {
164
+ visual = chalk_1.default.gray(" ") + " ";
165
+ }
166
+ lineContent = chalk_1.default.gray(priceLabel) + " │ " + visual;
167
+ lines.push(chalk_1.default.gray("│") + lineContent.padEnd(chartWidth + 20) + chalk_1.default.gray("│"));
168
+ }
169
+ lines.push(chalk_1.default.gray("└" + "─".repeat(chartWidth) + "┘"));
170
+ return lines;
171
+ }
172
+ // ============================================
173
+ // SMC VISUAL CHART
174
+ // ============================================
175
+ /**
176
+ * Create comprehensive SMC visual chart
177
+ */
178
+ function createSMCVisualChart(currentPrice, smc, tradeSetup) {
179
+ const levels = [];
180
+ // Add order blocks
181
+ smc.orderBlocks.bullish.slice(0, 2).forEach((ob, i) => {
182
+ levels.push({
183
+ price: ob.top,
184
+ type: "ob_top",
185
+ label: `Bullish OB Top`,
186
+ color: chalk_1.default.green,
187
+ });
188
+ levels.push({
189
+ price: ob.bottom,
190
+ type: "ob_bottom",
191
+ label: `Bullish OB Bottom`,
192
+ color: chalk_1.default.green,
193
+ });
194
+ });
195
+ smc.orderBlocks.bearish.slice(0, 2).forEach((ob, i) => {
196
+ levels.push({
197
+ price: ob.top,
198
+ type: "ob_top",
199
+ label: `Bearish OB Top`,
200
+ color: chalk_1.default.red,
201
+ });
202
+ levels.push({
203
+ price: ob.bottom,
204
+ type: "ob_bottom",
205
+ label: `Bearish OB Bottom`,
206
+ color: chalk_1.default.red,
207
+ });
208
+ });
209
+ // Add FVGs
210
+ smc.fairValueGaps.unfilled.slice(0, 2).forEach((fvg, i) => {
211
+ levels.push({
212
+ price: (fvg.top + fvg.bottom) / 2,
213
+ type: "fvg",
214
+ label: `${fvg.type} FVG`,
215
+ color: fvg.type === "bullish" ? chalk_1.default.greenBright : chalk_1.default.redBright,
216
+ });
217
+ });
218
+ // Add liquidity zones
219
+ smc.liquidity.buySide.slice(0, 2).forEach((liq, i) => {
220
+ levels.push({
221
+ price: liq.level,
222
+ type: "liquidity",
223
+ label: `Buy-side Liq (${liq.strength}x)`,
224
+ color: chalk_1.default.cyan,
225
+ });
226
+ });
227
+ smc.liquidity.sellSide.slice(0, 2).forEach((liq, i) => {
228
+ levels.push({
229
+ price: liq.level,
230
+ type: "liquidity",
231
+ label: `Sell-side Liq (${liq.strength}x)`,
232
+ color: chalk_1.default.magenta,
233
+ });
234
+ });
235
+ // Add trade setup levels
236
+ if (tradeSetup) {
237
+ levels.push({
238
+ price: (tradeSetup.entry.zone.low + tradeSetup.entry.zone.high) / 2,
239
+ type: "entry",
240
+ label: "Entry Zone",
241
+ color: chalk_1.default.cyan,
242
+ });
243
+ levels.push({
244
+ price: tradeSetup.stopLoss,
245
+ type: "stop",
246
+ label: "Stop Loss",
247
+ color: chalk_1.default.red,
248
+ });
249
+ tradeSetup.targets.slice(0, 2).forEach((target, i) => {
250
+ levels.push({
251
+ price: target,
252
+ type: "target",
253
+ label: `Target ${i + 1}`,
254
+ color: chalk_1.default.green,
255
+ });
256
+ });
257
+ }
258
+ // Add premium/discount zones
259
+ levels.push({
260
+ price: smc.premiumDiscount.rangeHigh,
261
+ type: "resistance",
262
+ label: "Range High (Premium)",
263
+ color: chalk_1.default.red,
264
+ });
265
+ levels.push({
266
+ price: smc.premiumDiscount.equilibrium,
267
+ type: "ema",
268
+ label: "Equilibrium",
269
+ color: chalk_1.default.yellow,
270
+ });
271
+ levels.push({
272
+ price: smc.premiumDiscount.rangeLow,
273
+ type: "support",
274
+ label: "Range Low (Discount)",
275
+ color: chalk_1.default.green,
276
+ });
277
+ return createPriceLevelChart(currentPrice, levels, 18);
278
+ }
279
+ // ============================================
280
+ // TRADE SETUP VISUAL
281
+ // ============================================
282
+ /**
283
+ * Create visual trade setup diagram
284
+ */
285
+ function createTradeSetupVisual(currentPrice, entry, stopLoss, targets, bias) {
286
+ const lines = [];
287
+ const width = 50;
288
+ // Calculate percentages
289
+ const entryPct = (((entry - currentPrice) / currentPrice) * 100).toFixed(1);
290
+ const slPct = (((stopLoss - entry) / entry) * 100).toFixed(1);
291
+ const riskAmt = Math.abs(entry - stopLoss);
292
+ lines.push("");
293
+ lines.push(chalk_1.default.bold.white(" ┌─────────────────────────────────────────────┐"));
294
+ lines.push(chalk_1.default.bold.white(" │") +
295
+ chalk_1.default.bold.cyan(" TRADE SETUP VISUALIZATION ") +
296
+ chalk_1.default.bold.white("│"));
297
+ lines.push(chalk_1.default.bold.white(" └─────────────────────────────────────────────┘"));
298
+ lines.push("");
299
+ if (bias === "long") {
300
+ // Long trade visualization
301
+ targets
302
+ .slice(0, 3)
303
+ .reverse()
304
+ .forEach((t, i) => {
305
+ const pct = (((t - entry) / entry) * 100).toFixed(1);
306
+ const rr = (Math.abs(t - entry) / riskAmt).toFixed(1);
307
+ lines.push(chalk_1.default.green(` ★ T${targets.length - i}: ${formatShortPrice(t)} (+${pct}%) R:R ${rr}:1`));
308
+ lines.push(chalk_1.default.green(" │"));
309
+ });
310
+ lines.push(chalk_1.default.yellow(` ◄━━ CURRENT: ${formatShortPrice(currentPrice)}`));
311
+ lines.push(chalk_1.default.gray(" │"));
312
+ lines.push(chalk_1.default.cyan(` ► ENTRY: ${formatShortPrice(entry)} (${entryPct}%)`));
313
+ lines.push(chalk_1.default.gray(" │"));
314
+ lines.push(chalk_1.default.red(` ✖ STOP: ${formatShortPrice(stopLoss)} (${slPct}%)`));
315
+ }
316
+ else if (bias === "short") {
317
+ // Short trade visualization
318
+ lines.push(chalk_1.default.red(` ✖ STOP: ${formatShortPrice(stopLoss)} (+${Math.abs(parseFloat(slPct))}%)`));
319
+ lines.push(chalk_1.default.gray(" │"));
320
+ lines.push(chalk_1.default.cyan(` ► ENTRY: ${formatShortPrice(entry)}`));
321
+ lines.push(chalk_1.default.gray(" │"));
322
+ lines.push(chalk_1.default.yellow(` ◄━━ CURRENT: ${formatShortPrice(currentPrice)}`));
323
+ lines.push(chalk_1.default.gray(" │"));
324
+ targets.slice(0, 3).forEach((t, i) => {
325
+ const pct = (((entry - t) / entry) * 100).toFixed(1);
326
+ const rr = (Math.abs(entry - t) / riskAmt).toFixed(1);
327
+ lines.push(chalk_1.default.green(" │"));
328
+ lines.push(chalk_1.default.green(` ★ T${i + 1}: ${formatShortPrice(t)} (+${pct}%) R:R ${rr}:1`));
329
+ });
330
+ }
331
+ else {
332
+ lines.push(chalk_1.default.yellow(" ⚠ NO CLEAR TRADE SETUP"));
333
+ lines.push(chalk_1.default.gray(" Wait for better opportunity"));
334
+ }
335
+ lines.push("");
336
+ return lines;
337
+ }
338
+ // ============================================
339
+ // MARKET STRUCTURE VISUAL
340
+ // ============================================
341
+ /**
342
+ * Create market structure visualization
343
+ */
344
+ function createMarketStructureVisual(smc) {
345
+ const lines = [];
346
+ const { marketStructure, bias } = smc;
347
+ lines.push("");
348
+ lines.push(chalk_1.default.bold.white(" ┌─────────────────────────────────────────────┐"));
349
+ lines.push(chalk_1.default.bold.white(" │") +
350
+ chalk_1.default.bold.magenta(" MARKET STRUCTURE FLOW ") +
351
+ chalk_1.default.bold.white("│"));
352
+ lines.push(chalk_1.default.bold.white(" └─────────────────────────────────────────────┘"));
353
+ lines.push("");
354
+ // Draw structure flow
355
+ const trend = marketStructure.trend;
356
+ const trendColor = trend === "bullish" ? chalk_1.default.green : trend === "bearish" ? chalk_1.default.red : chalk_1.default.yellow;
357
+ if (trend === "bullish") {
358
+ lines.push(trendColor(" ╱╲"));
359
+ lines.push(trendColor(" HH ╱ ╲"));
360
+ lines.push(trendColor(" ╱ ╲"));
361
+ lines.push(trendColor(" ╱╲ ╱ ╲"));
362
+ lines.push(trendColor(" HH ╱ ╲ HL ╲"));
363
+ lines.push(trendColor(" ╱ ╲ ╲"));
364
+ lines.push(trendColor(" ╱╲ ╱ ╲"));
365
+ lines.push(trendColor(" ╱ ╲ HL ╲"));
366
+ lines.push(chalk_1.default.gray(" ─────────────────────────────────────────────"));
367
+ lines.push(chalk_1.default.green(" BULLISH: Higher Highs (HH) & Higher Lows (HL)"));
368
+ }
369
+ else if (trend === "bearish") {
370
+ lines.push(trendColor(" ╲"));
371
+ lines.push(trendColor(" ╲ LH ╱╲"));
372
+ lines.push(trendColor(" ╲ ╱ ╲"));
373
+ lines.push(trendColor(" ╲ ╱ ╲"));
374
+ lines.push(trendColor(" ╲ ╱╲ ╱ LH ╲"));
375
+ lines.push(trendColor(" ╱ ╲╱ ╲"));
376
+ lines.push(trendColor(" ╱ LL ╲ ╲"));
377
+ lines.push(trendColor(" ╲ LL"));
378
+ lines.push(chalk_1.default.gray(" ─────────────────────────────────────────────"));
379
+ lines.push(chalk_1.default.red(" BEARISH: Lower Highs (LH) & Lower Lows (LL)"));
380
+ }
381
+ else {
382
+ lines.push(trendColor(" ╱╲ ╱╲"));
383
+ lines.push(trendColor(" ╱ ╲ ╱ ╲"));
384
+ lines.push(trendColor(" ╱ ╲ ╱ ╲"));
385
+ lines.push(trendColor(" ──╱──────────╲─╱──────────╲────"));
386
+ lines.push(trendColor(" ╲ "));
387
+ lines.push(chalk_1.default.gray(" ─────────────────────────────────────────────"));
388
+ lines.push(chalk_1.default.yellow(" RANGING: No clear trend direction"));
389
+ }
390
+ lines.push("");
391
+ // Show BOS/CHoCH
392
+ if (marketStructure.lastBOS) {
393
+ const bosColor = marketStructure.lastBOS.direction === "bullish" ? chalk_1.default.green : chalk_1.default.red;
394
+ lines.push(bosColor(` BOS (Break of Structure): ${marketStructure.lastBOS.direction} @ $${marketStructure.lastBOS.level.toFixed(2)}`));
395
+ }
396
+ if (marketStructure.lastCHoCH) {
397
+ const chochColor = marketStructure.lastCHoCH.direction === "bullish" ? chalk_1.default.green : chalk_1.default.red;
398
+ lines.push(chochColor(` CHoCH (Change of Character): ${marketStructure.lastCHoCH.direction} @ $${marketStructure.lastCHoCH.level.toFixed(2)}`));
399
+ }
400
+ lines.push("");
401
+ return lines;
402
+ }
403
+ // ============================================
404
+ // ORDER BLOCK VISUAL
405
+ // ============================================
406
+ /**
407
+ * Create order block visualization
408
+ */
409
+ function createOrderBlockVisual(orderBlocks, currentPrice) {
410
+ const lines = [];
411
+ lines.push("");
412
+ lines.push(chalk_1.default.bold.white(" ┌─────────────────────────────────────────────┐"));
413
+ lines.push(chalk_1.default.bold.white(" │") +
414
+ chalk_1.default.bold.blue(" ORDER BLOCKS (OB) ") +
415
+ chalk_1.default.bold.white("│"));
416
+ lines.push(chalk_1.default.bold.white(" └─────────────────────────────────────────────┘"));
417
+ lines.push("");
418
+ // Bullish OBs
419
+ if (orderBlocks.bullish.length > 0) {
420
+ lines.push(chalk_1.default.green.bold(" 🟢 BULLISH ORDER BLOCKS (Demand Zones)"));
421
+ orderBlocks.bullish.slice(0, 3).forEach((ob, i) => {
422
+ const dist = (((currentPrice - ob.top) / currentPrice) * 100).toFixed(1);
423
+ const status = ob.mitigated ? chalk_1.default.gray("[MITIGATED]") : chalk_1.default.green("[ACTIVE]");
424
+ lines.push(chalk_1.default.green(` ┌────────────────────┐ $${ob.top.toFixed(2)}`));
425
+ lines.push(chalk_1.default.green(` │ ▓▓▓▓ OB ${i + 1} ▓▓▓▓ │ ${status}`));
426
+ lines.push(chalk_1.default.green(` └────────────────────┘ $${ob.bottom.toFixed(2)} (${dist}% away)`));
427
+ });
428
+ }
429
+ else {
430
+ lines.push(chalk_1.default.gray(" No active bullish order blocks"));
431
+ }
432
+ lines.push("");
433
+ // Bearish OBs
434
+ if (orderBlocks.bearish.length > 0) {
435
+ lines.push(chalk_1.default.red.bold(" 🔴 BEARISH ORDER BLOCKS (Supply Zones)"));
436
+ orderBlocks.bearish.slice(0, 3).forEach((ob, i) => {
437
+ const dist = (((ob.bottom - currentPrice) / currentPrice) * 100).toFixed(1);
438
+ const status = ob.mitigated ? chalk_1.default.gray("[MITIGATED]") : chalk_1.default.red("[ACTIVE]");
439
+ lines.push(chalk_1.default.red(` ┌────────────────────┐ $${ob.top.toFixed(2)}`));
440
+ lines.push(chalk_1.default.red(` │ ░░░░ OB ${i + 1} ░░░░ │ ${status}`));
441
+ lines.push(chalk_1.default.red(` └────────────────────┘ $${ob.bottom.toFixed(2)} (${dist}% away)`));
442
+ });
443
+ }
444
+ else {
445
+ lines.push(chalk_1.default.gray(" No active bearish order blocks"));
446
+ }
447
+ lines.push("");
448
+ return lines;
449
+ }
450
+ // ============================================
451
+ // FVG VISUAL
452
+ // ============================================
453
+ /**
454
+ * Create FVG visualization
455
+ */
456
+ function createFVGVisual(fvgs, currentPrice) {
457
+ const lines = [];
458
+ lines.push("");
459
+ lines.push(chalk_1.default.bold.white(" ┌─────────────────────────────────────────────┐"));
460
+ lines.push(chalk_1.default.bold.white(" │") +
461
+ chalk_1.default.bold.yellow(" FAIR VALUE GAPS (FVG) ") +
462
+ chalk_1.default.bold.white("│"));
463
+ lines.push(chalk_1.default.bold.white(" └─────────────────────────────────────────────┘"));
464
+ lines.push("");
465
+ const unfilled = fvgs.unfilled.slice(0, 4);
466
+ if (unfilled.length > 0) {
467
+ lines.push(chalk_1.default.yellow(" ⚡ UNFILLED GAPS (Price magnets)"));
468
+ lines.push("");
469
+ unfilled.forEach((fvg, i) => {
470
+ const gapSize = (((fvg.top - fvg.bottom) / fvg.bottom) * 100).toFixed(2);
471
+ const dist = fvg.type === "bullish"
472
+ ? (((fvg.bottom - currentPrice) / currentPrice) * 100).toFixed(1)
473
+ : (((currentPrice - fvg.top) / currentPrice) * 100).toFixed(1);
474
+ const color = fvg.type === "bullish" ? chalk_1.default.green : chalk_1.default.red;
475
+ const arrow = fvg.type === "bullish" ? "↑" : "↓";
476
+ lines.push(color(` ${arrow} ${fvg.type.toUpperCase()} FVG`));
477
+ lines.push(color(` ┌ · · · · · · · · · ┐ $${fvg.top.toFixed(2)}`));
478
+ lines.push(color(` │ GAP: ${gapSize}% │`));
479
+ lines.push(color(` └ · · · · · · · · · ┘ $${fvg.bottom.toFixed(2)}`));
480
+ lines.push(chalk_1.default.gray(` ${Math.abs(parseFloat(dist))}% ${parseFloat(dist) > 0 ? "above" : "below"} current price`));
481
+ lines.push("");
482
+ });
483
+ }
484
+ else {
485
+ lines.push(chalk_1.default.gray(" No unfilled fair value gaps"));
486
+ }
487
+ lines.push("");
488
+ return lines;
489
+ }
490
+ // ============================================
491
+ // LIQUIDITY VISUAL
492
+ // ============================================
493
+ /**
494
+ * Create liquidity zones visualization
495
+ */
496
+ function createLiquidityVisual(liquidity, currentPrice) {
497
+ const lines = [];
498
+ lines.push("");
499
+ lines.push(chalk_1.default.bold.white(" ┌─────────────────────────────────────────────┐"));
500
+ lines.push(chalk_1.default.bold.white(" │") +
501
+ chalk_1.default.bold.cyan(" LIQUIDITY POOLS ") +
502
+ chalk_1.default.bold.white("│"));
503
+ lines.push(chalk_1.default.bold.white(" └─────────────────────────────────────────────┘"));
504
+ lines.push("");
505
+ // Buy-side liquidity (above price - stops from shorts)
506
+ lines.push(chalk_1.default.cyan.bold(" 📈 BUY-SIDE LIQUIDITY (Stops above highs)"));
507
+ if (liquidity.buySide.length > 0) {
508
+ liquidity.buySide.slice(0, 3).forEach((liq, i) => {
509
+ const dist = (((liq.level - currentPrice) / currentPrice) * 100).toFixed(1);
510
+ const strength = "◆".repeat(Math.min(liq.strength, 5));
511
+ lines.push(chalk_1.default.cyan(` $${liq.level.toFixed(2)} ${strength} (${liq.strength} touches) +${dist}%`));
512
+ });
513
+ }
514
+ else {
515
+ lines.push(chalk_1.default.gray(" No buy-side liquidity detected"));
516
+ }
517
+ lines.push("");
518
+ lines.push(chalk_1.default.yellow(` ━━━━━ CURRENT PRICE: $${currentPrice.toFixed(2)} ━━━━━`));
519
+ lines.push("");
520
+ // Sell-side liquidity (below price - stops from longs)
521
+ lines.push(chalk_1.default.magenta.bold(" 📉 SELL-SIDE LIQUIDITY (Stops below lows)"));
522
+ if (liquidity.sellSide.length > 0) {
523
+ liquidity.sellSide.slice(0, 3).forEach((liq, i) => {
524
+ const dist = (((currentPrice - liq.level) / currentPrice) * 100).toFixed(1);
525
+ const strength = "◇".repeat(Math.min(liq.strength, 5));
526
+ lines.push(chalk_1.default.magenta(` $${liq.level.toFixed(2)} ${strength} (${liq.strength} touches) -${dist}%`));
527
+ });
528
+ }
529
+ else {
530
+ lines.push(chalk_1.default.gray(" No sell-side liquidity detected"));
531
+ }
532
+ lines.push("");
533
+ return lines;
534
+ }
535
+ // ============================================
536
+ // HELPERS
537
+ // ============================================
538
+ function formatShortPrice(price) {
539
+ if (price < 0.01)
540
+ return price.toFixed(6);
541
+ if (price < 1)
542
+ return price.toFixed(4);
543
+ if (price < 100)
544
+ return price.toFixed(2);
545
+ if (price < 10000)
546
+ return price.toFixed(2);
547
+ return price.toLocaleString(undefined, { maximumFractionDigits: 0 });
548
+ }