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.
- package/dist/index.js +175 -4
- package/dist/lib/config.js +60 -1
- package/dist/lib/crypto/analyzer.js +275 -0
- package/dist/lib/crypto/chart-visual.js +548 -0
- package/dist/lib/crypto/data-fetcher.js +166 -0
- package/dist/lib/crypto/index.js +47 -0
- package/dist/lib/crypto/indicators.js +390 -0
- package/dist/lib/crypto/market-analyzer.js +497 -0
- package/dist/lib/crypto/signal-generator.js +559 -0
- package/dist/lib/crypto/smc-analyzer.js +418 -0
- package/dist/lib/crypto/smc-indicators.js +512 -0
- package/dist/lib/slash-commands.js +24 -0
- package/dist/lib/ui.js +70 -8
- package/package.json +1 -1
|
@@ -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
|
+
}
|