prab-cli 1.2.4 → 1.2.6

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,445 @@
1
+ "use strict";
2
+ /**
3
+ * Order Block Trading Strategy
4
+ * Focused analysis for order block-based trading signals
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.generateOrderBlockSignal = generateOrderBlockSignal;
11
+ exports.displayOrderBlockSignal = displayOrderBlockSignal;
12
+ exports.runOrderBlockStrategy = runOrderBlockStrategy;
13
+ const chalk_1 = __importDefault(require("chalk"));
14
+ const ora_1 = __importDefault(require("ora"));
15
+ const data_fetcher_1 = require("./data-fetcher");
16
+ const smc_indicators_1 = require("./smc-indicators");
17
+ const indicators_1 = require("./indicators");
18
+ // ============================================
19
+ // ORDER BLOCK ANALYSIS
20
+ // ============================================
21
+ /**
22
+ * Find the nearest unmitigated order block to current price
23
+ */
24
+ function findNearestOrderBlock(orderBlocks, currentPrice, direction) {
25
+ const filtered = direction
26
+ ? orderBlocks.filter((ob) => ob.type === direction && !ob.mitigated)
27
+ : orderBlocks.filter((ob) => !ob.mitigated);
28
+ if (filtered.length === 0)
29
+ return null;
30
+ return filtered.sort((a, b) => {
31
+ const distA = Math.min(Math.abs(currentPrice - a.top), Math.abs(currentPrice - a.bottom));
32
+ const distB = Math.min(Math.abs(currentPrice - b.top), Math.abs(currentPrice - b.bottom));
33
+ return distA - distB;
34
+ })[0];
35
+ }
36
+ /**
37
+ * Check if price is in or near an order block zone
38
+ */
39
+ function checkPriceInOBZone(price, ob, tolerance = 0.005 // 0.5%
40
+ ) {
41
+ const toleranceAmount = (ob.top - ob.bottom) * tolerance;
42
+ const expandedTop = ob.top + toleranceAmount;
43
+ const expandedBottom = ob.bottom - toleranceAmount;
44
+ const inZone = price >= ob.bottom && price <= ob.top;
45
+ const nearZone = price >= expandedBottom && price <= expandedTop;
46
+ let distancePercent;
47
+ if (inZone) {
48
+ distancePercent = 0;
49
+ }
50
+ else if (price > ob.top) {
51
+ distancePercent = ((price - ob.top) / price) * 100;
52
+ }
53
+ else {
54
+ distancePercent = ((ob.bottom - price) / price) * 100;
55
+ }
56
+ return { inZone, nearZone, distancePercent };
57
+ }
58
+ /**
59
+ * Calculate confidence score for order block trade
60
+ */
61
+ function calculateOBConfidence(ob, currentPrice, trend, premiumDiscount, rsi) {
62
+ let score = 50;
63
+ // Order block strength
64
+ if (ob.strength === "strong")
65
+ score += 15;
66
+ else if (ob.strength === "moderate")
67
+ score += 10;
68
+ else
69
+ score += 5;
70
+ // Trend alignment
71
+ if (ob.type === "bullish" && trend === "bullish")
72
+ score += 15;
73
+ else if (ob.type === "bearish" && trend === "bearish")
74
+ score += 15;
75
+ else if (trend === "sideways")
76
+ score += 5;
77
+ else
78
+ score -= 10; // Counter-trend
79
+ // Premium/Discount alignment
80
+ if (ob.type === "bullish" && premiumDiscount === "discount")
81
+ score += 10;
82
+ else if (ob.type === "bearish" && premiumDiscount === "premium")
83
+ score += 10;
84
+ // RSI confirmation
85
+ if (ob.type === "bullish" && rsi < 40)
86
+ score += 10;
87
+ else if (ob.type === "bearish" && rsi > 60)
88
+ score += 10;
89
+ // Price proximity
90
+ const { inZone, nearZone, distancePercent } = checkPriceInOBZone(currentPrice, ob);
91
+ if (inZone)
92
+ score += 15;
93
+ else if (nearZone)
94
+ score += 10;
95
+ else if (distancePercent < 2)
96
+ score += 5;
97
+ return Math.min(100, Math.max(0, score));
98
+ }
99
+ // ============================================
100
+ // MAIN STRATEGY FUNCTION
101
+ // ============================================
102
+ async function generateOrderBlockSignal(symbol, interval = "4h") {
103
+ const spinner = (0, ora_1.default)(`Analyzing ${symbol} for Order Block setups...`).start();
104
+ try {
105
+ // Fetch data
106
+ spinner.text = "Fetching market data...";
107
+ const data = await (0, data_fetcher_1.fetchCryptoData)(symbol, interval, 200);
108
+ const candles = data.candles;
109
+ const currentPrice = data.currentPrice;
110
+ // Find Order Blocks
111
+ spinner.text = "Identifying Order Blocks...";
112
+ const allOBs = (0, smc_indicators_1.findOrderBlocks)(candles, 100);
113
+ const bullishOBs = allOBs.filter((ob) => ob.type === "bullish" && !ob.mitigated);
114
+ const bearishOBs = allOBs.filter((ob) => ob.type === "bearish" && !ob.mitigated);
115
+ // Find swing points for context
116
+ const swingPoints = (0, smc_indicators_1.findSwingPoints)(candles, 3);
117
+ // Calculate indicators
118
+ const closes = candles.map((c) => c.close);
119
+ const rsi = (0, indicators_1.calculateRSI)(closes, 14);
120
+ const trend = (0, indicators_1.analyzeTrend)(candles);
121
+ const premiumDiscount = (0, smc_indicators_1.calculatePremiumDiscount)(candles, 50);
122
+ const atr = (0, indicators_1.calculateATR)(candles, 14);
123
+ // Find nearest order blocks
124
+ const nearestBullishOB = findNearestOrderBlock(allOBs, currentPrice, "bullish");
125
+ const nearestBearishOB = findNearestOrderBlock(allOBs, currentPrice, "bearish");
126
+ const nearestOB = findNearestOrderBlock(allOBs, currentPrice);
127
+ // Determine signal
128
+ let signal = "WAIT";
129
+ let activeOB = null;
130
+ let entry = {
131
+ price: currentPrice,
132
+ zone: { low: currentPrice, high: currentPrice },
133
+ type: "Wait for OB",
134
+ };
135
+ let stopLoss = currentPrice;
136
+ let tp1 = currentPrice, tp2 = currentPrice, tp3 = currentPrice;
137
+ const reasoning = [];
138
+ const warnings = [];
139
+ let confidence = 0;
140
+ // Check if price is at a bullish OB (BUY signal)
141
+ if (nearestBullishOB) {
142
+ const check = checkPriceInOBZone(currentPrice, nearestBullishOB);
143
+ if (check.inZone || (check.nearZone && check.distancePercent < 1)) {
144
+ signal = "BUY";
145
+ confidence = calculateOBConfidence(nearestBullishOB, currentPrice, trend.direction, premiumDiscount.zone, rsi.current);
146
+ activeOB = {
147
+ type: "bullish",
148
+ top: nearestBullishOB.top,
149
+ bottom: nearestBullishOB.bottom,
150
+ midpoint: (nearestBullishOB.top + nearestBullishOB.bottom) / 2,
151
+ strength: nearestBullishOB.strength,
152
+ distancePercent: check.distancePercent,
153
+ priceInZone: check.inZone,
154
+ };
155
+ entry = {
156
+ price: (nearestBullishOB.top + nearestBullishOB.bottom) / 2,
157
+ zone: { low: nearestBullishOB.bottom, high: nearestBullishOB.top },
158
+ type: "Bullish Order Block",
159
+ };
160
+ // Stop loss below OB
161
+ stopLoss = nearestBullishOB.bottom * 0.995;
162
+ // Take profits based on risk
163
+ const risk = entry.price - stopLoss;
164
+ tp1 = entry.price + risk * 1.5;
165
+ tp2 = entry.price + risk * 2.5;
166
+ tp3 = entry.price + risk * 4;
167
+ reasoning.push(`Price at ${nearestBullishOB.strength} bullish Order Block`);
168
+ reasoning.push(`OB Zone: $${nearestBullishOB.bottom.toFixed(2)} - $${nearestBullishOB.top.toFixed(2)}`);
169
+ if (trend.direction === "bullish") {
170
+ reasoning.push("Trend aligned with OB (bullish)");
171
+ }
172
+ else if (trend.direction === "bearish") {
173
+ warnings.push("Counter-trend trade - higher risk");
174
+ }
175
+ if (premiumDiscount.zone === "discount") {
176
+ reasoning.push("Price in discount zone - favorable for longs");
177
+ }
178
+ }
179
+ }
180
+ // Check if price is at a bearish OB (SELL signal)
181
+ if (nearestBearishOB && signal === "WAIT") {
182
+ const check = checkPriceInOBZone(currentPrice, nearestBearishOB);
183
+ if (check.inZone || (check.nearZone && check.distancePercent < 1)) {
184
+ signal = "SELL";
185
+ confidence = calculateOBConfidence(nearestBearishOB, currentPrice, trend.direction, premiumDiscount.zone, rsi.current);
186
+ activeOB = {
187
+ type: "bearish",
188
+ top: nearestBearishOB.top,
189
+ bottom: nearestBearishOB.bottom,
190
+ midpoint: (nearestBearishOB.top + nearestBearishOB.bottom) / 2,
191
+ strength: nearestBearishOB.strength,
192
+ distancePercent: check.distancePercent,
193
+ priceInZone: check.inZone,
194
+ };
195
+ entry = {
196
+ price: (nearestBearishOB.top + nearestBearishOB.bottom) / 2,
197
+ zone: { low: nearestBearishOB.bottom, high: nearestBearishOB.top },
198
+ type: "Bearish Order Block",
199
+ };
200
+ // Stop loss above OB
201
+ stopLoss = nearestBearishOB.top * 1.005;
202
+ // Take profits based on risk
203
+ const risk = stopLoss - entry.price;
204
+ tp1 = entry.price - risk * 1.5;
205
+ tp2 = entry.price - risk * 2.5;
206
+ tp3 = entry.price - risk * 4;
207
+ reasoning.push(`Price at ${nearestBearishOB.strength} bearish Order Block`);
208
+ reasoning.push(`OB Zone: $${nearestBearishOB.bottom.toFixed(2)} - $${nearestBearishOB.top.toFixed(2)}`);
209
+ if (trend.direction === "bearish") {
210
+ reasoning.push("Trend aligned with OB (bearish)");
211
+ }
212
+ else if (trend.direction === "bullish") {
213
+ warnings.push("Counter-trend trade - higher risk");
214
+ }
215
+ if (premiumDiscount.zone === "premium") {
216
+ reasoning.push("Price in premium zone - favorable for shorts");
217
+ }
218
+ }
219
+ }
220
+ // If no active signal, provide waiting guidance
221
+ if (signal === "WAIT") {
222
+ confidence = 30;
223
+ if (nearestBullishOB) {
224
+ const distBullish = checkPriceInOBZone(currentPrice, nearestBullishOB).distancePercent;
225
+ reasoning.push(`Nearest bullish OB at $${nearestBullishOB.bottom.toFixed(2)} (${distBullish.toFixed(1)}% away)`);
226
+ }
227
+ if (nearestBearishOB) {
228
+ const distBearish = checkPriceInOBZone(currentPrice, nearestBearishOB).distancePercent;
229
+ reasoning.push(`Nearest bearish OB at $${nearestBearishOB.top.toFixed(2)} (${distBearish.toFixed(1)}% away)`);
230
+ }
231
+ if (bullishOBs.length === 0 && bearishOBs.length === 0) {
232
+ reasoning.push("No valid Order Blocks found - wait for new OB formation");
233
+ }
234
+ else {
235
+ reasoning.push("Wait for price to reach an Order Block zone");
236
+ }
237
+ }
238
+ // Add general context
239
+ reasoning.push(`RSI: ${rsi.current.toFixed(1)} (${rsi.current < 30 ? "oversold" : rsi.current > 70 ? "overbought" : "neutral"})`);
240
+ reasoning.push(`Trend: ${trend.direction} | Premium/Discount: ${premiumDiscount.zone}`);
241
+ // Calculate R:R
242
+ const riskAmount = Math.abs(entry.price - stopLoss);
243
+ const rewardAmount = Math.abs(tp2 - entry.price);
244
+ const riskRewardRatio = riskAmount > 0 ? rewardAmount / riskAmount : 0;
245
+ // Add warnings
246
+ if (atr.current / currentPrice > 0.03) {
247
+ warnings.push("High volatility - consider smaller position size");
248
+ }
249
+ if (rsi.current > 75 && signal === "BUY") {
250
+ warnings.push("RSI overbought - potential reversal risk");
251
+ }
252
+ else if (rsi.current < 25 && signal === "SELL") {
253
+ warnings.push("RSI oversold - potential bounce risk");
254
+ }
255
+ spinner.succeed(`Order Block analysis complete for ${data.symbol}`);
256
+ return {
257
+ symbol: data.symbol,
258
+ currentPrice,
259
+ signal,
260
+ confidence,
261
+ activeOB,
262
+ bullishOBs,
263
+ bearishOBs,
264
+ entry,
265
+ stopLoss,
266
+ takeProfit1: tp1,
267
+ takeProfit2: tp2,
268
+ takeProfit3: tp3,
269
+ riskRewardRatio,
270
+ trend: trend.direction,
271
+ premiumDiscount: premiumDiscount.zone,
272
+ reasoning,
273
+ warnings,
274
+ timestamp: Date.now(),
275
+ };
276
+ }
277
+ catch (error) {
278
+ spinner.fail(`Failed to analyze ${symbol}`);
279
+ throw error;
280
+ }
281
+ }
282
+ // ============================================
283
+ // DISPLAY FUNCTION
284
+ // ============================================
285
+ function formatPrice(price) {
286
+ if (price < 0.001)
287
+ return `$${price.toFixed(8)}`;
288
+ if (price < 1)
289
+ return `$${price.toFixed(6)}`;
290
+ if (price < 100)
291
+ return `$${price.toFixed(4)}`;
292
+ return `$${price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`;
293
+ }
294
+ function getConfidenceBar(confidence, width = 20) {
295
+ const filled = Math.round((confidence / 100) * width);
296
+ const empty = width - filled;
297
+ let color = chalk_1.default.green;
298
+ if (confidence < 40)
299
+ color = chalk_1.default.red;
300
+ else if (confidence < 60)
301
+ color = chalk_1.default.yellow;
302
+ return "[" + color("█".repeat(filled)) + chalk_1.default.gray("░".repeat(empty)) + "]";
303
+ }
304
+ function displayOrderBlockSignal(result) {
305
+ console.log("");
306
+ // Header
307
+ const headerColor = result.signal === "BUY" ? chalk_1.default.green : result.signal === "SELL" ? chalk_1.default.red : chalk_1.default.yellow;
308
+ const signalIcon = result.signal === "BUY" ? "🟢" : result.signal === "SELL" ? "🔴" : "⏳";
309
+ console.log(headerColor(" ╔═══════════════════════════════════════════════════════════════════════╗"));
310
+ console.log(headerColor(` ║ ${signalIcon} ORDER BLOCK STRATEGY - ${result.symbol.padEnd(20)} ║`));
311
+ console.log(headerColor(" ╚═══════════════════════════════════════════════════════════════════════╝"));
312
+ console.log("");
313
+ // Current Price
314
+ console.log(` ${chalk_1.default.cyan("Current Price:")} ${chalk_1.default.white.bold(formatPrice(result.currentPrice))}`);
315
+ console.log("");
316
+ // Signal Badge
317
+ const signalBadge = result.signal === "BUY"
318
+ ? chalk_1.default.bgGreen.white.bold(" BUY ")
319
+ : result.signal === "SELL"
320
+ ? chalk_1.default.bgRed.white.bold(" SELL ")
321
+ : chalk_1.default.bgYellow.black.bold(" WAIT ");
322
+ const confidenceBar = getConfidenceBar(result.confidence);
323
+ console.log(` ${signalBadge} Confidence: ${confidenceBar} ${result.confidence}%`);
324
+ console.log("");
325
+ // Order Block Details
326
+ console.log(chalk_1.default.cyan(" ┌─────────────────────────────────────────────────────────────────────┐"));
327
+ console.log(chalk_1.default.cyan(" │ 📦 ORDER BLOCK ANALYSIS │"));
328
+ console.log(chalk_1.default.cyan(" ├─────────────────────────────────────────────────────────────────────┤"));
329
+ console.log(` │ ${chalk_1.default.green("Bullish OBs:")} ${result.bullishOBs.length} unmitigated`);
330
+ console.log(` │ ${chalk_1.default.red("Bearish OBs:")} ${result.bearishOBs.length} unmitigated`);
331
+ if (result.activeOB) {
332
+ const obColor = result.activeOB.type === "bullish" ? chalk_1.default.green : chalk_1.default.red;
333
+ console.log(` │`);
334
+ console.log(` │ ${chalk_1.default.yellow("Active OB:")} ${obColor(result.activeOB.type.toUpperCase())} (${result.activeOB.strength})`);
335
+ console.log(` │ ${chalk_1.default.dim("Zone:")} ${formatPrice(result.activeOB.bottom)} - ${formatPrice(result.activeOB.top)}`);
336
+ console.log(` │ ${chalk_1.default.dim("Midpoint:")} ${formatPrice(result.activeOB.midpoint)}`);
337
+ console.log(` │ ${chalk_1.default.dim("Price in Zone:")} ${result.activeOB.priceInZone ? chalk_1.default.green("YES") : chalk_1.default.yellow("NEAR")}`);
338
+ }
339
+ else {
340
+ console.log(` │`);
341
+ console.log(` │ ${chalk_1.default.yellow("No active Order Block at current price")}`);
342
+ }
343
+ console.log(chalk_1.default.cyan(" └─────────────────────────────────────────────────────────────────────┘"));
344
+ console.log("");
345
+ // Trade Setup (if signal is BUY or SELL)
346
+ if (result.signal !== "WAIT") {
347
+ console.log(chalk_1.default.magenta(" ┌─────────────────────────────────────────────────────────────────────┐"));
348
+ console.log(chalk_1.default.magenta(" │ 📍 TRADE SETUP │"));
349
+ console.log(chalk_1.default.magenta(" ├─────────────────────────────────────────────────────────────────────┤"));
350
+ console.log(` │ ${chalk_1.default.cyan("Entry Type:")} ${result.entry.type}`);
351
+ console.log(` │ ${chalk_1.default.yellow("Entry Zone:")} ${formatPrice(result.entry.zone.low)} - ${formatPrice(result.entry.zone.high)}`);
352
+ console.log(` │ ${chalk_1.default.yellow("Entry Price:")} ${chalk_1.default.white.bold(formatPrice(result.entry.price))}`);
353
+ console.log(` │`);
354
+ console.log(` │ ${chalk_1.default.red("Stop Loss:")} ${chalk_1.default.red.bold(formatPrice(result.stopLoss))} ${chalk_1.default.dim(`(${((Math.abs(result.entry.price - result.stopLoss) / result.entry.price) * 100).toFixed(2)}%)`)}`);
355
+ console.log(` │`);
356
+ console.log(` │ ${chalk_1.default.green("Take Profit 1:")} ${chalk_1.default.green(formatPrice(result.takeProfit1))} ${chalk_1.default.dim("(1.5R)")}`);
357
+ console.log(` │ ${chalk_1.default.green("Take Profit 2:")} ${chalk_1.default.green(formatPrice(result.takeProfit2))} ${chalk_1.default.dim("(2.5R)")}`);
358
+ console.log(` │ ${chalk_1.default.green("Take Profit 3:")} ${chalk_1.default.green(formatPrice(result.takeProfit3))} ${chalk_1.default.dim("(4R)")}`);
359
+ console.log(` │`);
360
+ console.log(` │ ${chalk_1.default.cyan("Risk/Reward:")} ${chalk_1.default.cyan.bold(result.riskRewardRatio.toFixed(1) + ":1")}`);
361
+ console.log(chalk_1.default.magenta(" └─────────────────────────────────────────────────────────────────────┘"));
362
+ console.log("");
363
+ }
364
+ // Visual Order Block Map
365
+ console.log(chalk_1.default.blue(" ┌─────────────────────────────────────────────────────────────────────┐"));
366
+ console.log(chalk_1.default.blue(" │ 📊 ORDER BLOCK MAP │"));
367
+ console.log(chalk_1.default.blue(" ├─────────────────────────────────────────────────────────────────────┤"));
368
+ // Show nearest OBs visually
369
+ const allOBs = [...result.bullishOBs, ...result.bearishOBs].sort((a, b) => b.top - a.top);
370
+ const visibleOBs = allOBs.slice(0, 6);
371
+ if (visibleOBs.length > 0) {
372
+ visibleOBs.forEach((ob) => {
373
+ const obColor = ob.type === "bullish" ? chalk_1.default.green : chalk_1.default.red;
374
+ const icon = ob.type === "bullish" ? "▲" : "▼";
375
+ const isNear = result.currentPrice >= ob.bottom * 0.99 && result.currentPrice <= ob.top * 1.01;
376
+ const marker = isNear ? chalk_1.default.yellow(" ← PRICE HERE") : "";
377
+ console.log(` │ ${obColor(icon)} ${ob.type.padEnd(8)} ${formatPrice(ob.bottom).padStart(12)} - ${formatPrice(ob.top).padEnd(12)} [${ob.strength}]${marker}`);
378
+ });
379
+ }
380
+ else {
381
+ console.log(` │ ${chalk_1.default.gray("No unmitigated Order Blocks found")}`);
382
+ }
383
+ // Show current price line
384
+ console.log(` │`);
385
+ console.log(` │ ${chalk_1.default.white("→ Current:")} ${chalk_1.default.white.bold(formatPrice(result.currentPrice))}`);
386
+ console.log(chalk_1.default.blue(" └─────────────────────────────────────────────────────────────────────┘"));
387
+ console.log("");
388
+ // Context
389
+ console.log(chalk_1.default.gray(" ┌─────────────────────────────────────────────────────────────────────┐"));
390
+ console.log(chalk_1.default.gray(" │ 🔍 CONTEXT & REASONING │"));
391
+ console.log(chalk_1.default.gray(" ├─────────────────────────────────────────────────────────────────────┤"));
392
+ console.log(` │ ${chalk_1.default.dim("Trend:")} ${result.trend === "bullish" ? chalk_1.default.green("▲ Bullish") : result.trend === "bearish" ? chalk_1.default.red("▼ Bearish") : chalk_1.default.yellow("● Sideways")}`);
393
+ console.log(` │ ${chalk_1.default.dim("Zone:")} ${result.premiumDiscount === "discount" ? chalk_1.default.green("Discount") : result.premiumDiscount === "premium" ? chalk_1.default.red("Premium") : chalk_1.default.yellow("Equilibrium")}`);
394
+ console.log(` │`);
395
+ result.reasoning.forEach((r) => {
396
+ console.log(` │ ${chalk_1.default.cyan("•")} ${chalk_1.default.gray(r)}`);
397
+ });
398
+ console.log(chalk_1.default.gray(" └─────────────────────────────────────────────────────────────────────┘"));
399
+ console.log("");
400
+ // Warnings
401
+ if (result.warnings.length > 0) {
402
+ console.log(chalk_1.default.red(" ┌─────────────────────────────────────────────────────────────────────┐"));
403
+ console.log(chalk_1.default.red(" │ ⚠️ WARNINGS │"));
404
+ console.log(chalk_1.default.red(" ├─────────────────────────────────────────────────────────────────────┤"));
405
+ result.warnings.forEach((w) => {
406
+ console.log(` │ ${chalk_1.default.yellow("!")} ${chalk_1.default.yellow(w)}`);
407
+ });
408
+ console.log(chalk_1.default.red(" └─────────────────────────────────────────────────────────────────────┘"));
409
+ console.log("");
410
+ }
411
+ // How Order Blocks are Calculated (Educational)
412
+ console.log(chalk_1.default.dim(" ┌─────────────────────────────────────────────────────────────────────┐"));
413
+ console.log(chalk_1.default.dim(" │ 📚 HOW ORDER BLOCKS WORK │"));
414
+ console.log(chalk_1.default.dim(" ├─────────────────────────────────────────────────────────────────────┤"));
415
+ console.log(chalk_1.default.dim(" │ Bullish OB: Last bearish candle before strong bullish move │"));
416
+ console.log(chalk_1.default.dim(" │ → BUY when price returns to this zone │"));
417
+ console.log(chalk_1.default.dim(" │ Bearish OB: Last bullish candle before strong bearish move │"));
418
+ console.log(chalk_1.default.dim(" │ → SELL when price returns to this zone │"));
419
+ console.log(chalk_1.default.dim(' │ Mitigated: OB is "used" when price fully trades through it │'));
420
+ console.log(chalk_1.default.dim(" └─────────────────────────────────────────────────────────────────────┘"));
421
+ console.log("");
422
+ // Disclaimer
423
+ console.log(chalk_1.default.dim.italic(" ⚠️ This is not financial advice. Always manage your risk and DYOR."));
424
+ console.log("");
425
+ }
426
+ // ============================================
427
+ // RUNNER
428
+ // ============================================
429
+ async function runOrderBlockStrategy(symbol, interval = "4h") {
430
+ try {
431
+ const result = await generateOrderBlockSignal(symbol, interval);
432
+ displayOrderBlockSignal(result);
433
+ return result;
434
+ }
435
+ catch (error) {
436
+ console.log("");
437
+ console.log(chalk_1.default.red(" ╔═══════════════════════════════════════════════════════════════════════╗"));
438
+ console.log(chalk_1.default.red(" ║ ❌ ORDER BLOCK ERROR ║"));
439
+ console.log(chalk_1.default.red(" ╚═══════════════════════════════════════════════════════════════════════╝"));
440
+ console.log("");
441
+ console.log(chalk_1.default.yellow(` ⚠️ ${error.message || "An unexpected error occurred"}`));
442
+ console.log("");
443
+ return null;
444
+ }
445
+ }