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.
- package/dist/index.js +218 -2
- package/dist/lib/chat-handler.js +39 -5
- package/dist/lib/crypto/data-fetcher.js +86 -0
- package/dist/lib/crypto/ict-strategy.js +955 -0
- package/dist/lib/crypto/index.js +23 -1
- package/dist/lib/crypto/market-scanner.js +569 -0
- package/dist/lib/crypto/news-fetcher.js +394 -0
- package/dist/lib/crypto/orderblock-strategy.js +445 -0
- package/dist/lib/crypto/signal-generator.js +144 -17
- package/dist/lib/crypto/smc-analyzer.js +39 -7
- package/dist/lib/crypto/strategy-engine.js +803 -0
- package/dist/lib/crypto/whale-tracker.js +508 -0
- package/dist/lib/slash-commands.js +36 -0
- package/dist/lib/ui.js +45 -1
- package/dist/server/index.js +501 -0
- package/package.json +7 -1
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Smart Trading Strategy Engine
|
|
4
|
+
* Combines multiple factors for high-probability trade setups
|
|
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.generateTradeSetup = generateTradeSetup;
|
|
11
|
+
exports.displayTradeSetup = displayTradeSetup;
|
|
12
|
+
exports.runStrategy = runStrategy;
|
|
13
|
+
/* global fetch */
|
|
14
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
15
|
+
const ora_1 = __importDefault(require("ora"));
|
|
16
|
+
const data_fetcher_1 = require("./data-fetcher");
|
|
17
|
+
const indicators_1 = require("./indicators");
|
|
18
|
+
const smc_indicators_1 = require("./smc-indicators");
|
|
19
|
+
const news_fetcher_1 = require("./news-fetcher");
|
|
20
|
+
// Default config
|
|
21
|
+
const DEFAULT_CONFIG = {
|
|
22
|
+
maxLeverage: 20,
|
|
23
|
+
riskPerTrade: 2, // 2% per trade
|
|
24
|
+
style: "moderate",
|
|
25
|
+
direction: "both",
|
|
26
|
+
};
|
|
27
|
+
// ============================================
|
|
28
|
+
// HELPER FUNCTIONS
|
|
29
|
+
// ============================================
|
|
30
|
+
function calculateEMAs(closes) {
|
|
31
|
+
const ema = (data, period) => {
|
|
32
|
+
const k = 2 / (period + 1);
|
|
33
|
+
let emaValue = data.slice(0, period).reduce((a, b) => a + b, 0) / period;
|
|
34
|
+
for (let i = period; i < data.length; i++) {
|
|
35
|
+
emaValue = data[i] * k + emaValue * (1 - k);
|
|
36
|
+
}
|
|
37
|
+
return emaValue;
|
|
38
|
+
};
|
|
39
|
+
const ema9 = ema(closes, 9);
|
|
40
|
+
const ema21 = ema(closes, 21);
|
|
41
|
+
const ema50 = ema(closes, 50);
|
|
42
|
+
const ema200 = closes.length >= 200 ? ema(closes, 200) : ema50;
|
|
43
|
+
// Check alignment
|
|
44
|
+
let alignment = "neutral";
|
|
45
|
+
if (ema9 > ema21 && ema21 > ema50 && ema50 > ema200) {
|
|
46
|
+
alignment = "bullish";
|
|
47
|
+
}
|
|
48
|
+
else if (ema9 < ema21 && ema21 < ema50 && ema50 < ema200) {
|
|
49
|
+
alignment = "bearish";
|
|
50
|
+
}
|
|
51
|
+
return { ema9, ema21, ema50, ema200, alignment };
|
|
52
|
+
}
|
|
53
|
+
function detectRSIDivergence(closes, rsiValues) {
|
|
54
|
+
if (rsiValues.length < 20)
|
|
55
|
+
return { type: "none", strength: 0 };
|
|
56
|
+
const recentCloses = closes.slice(-20);
|
|
57
|
+
const recentRSI = rsiValues.slice(-20);
|
|
58
|
+
// Find local lows/highs in price
|
|
59
|
+
const priceLows = [];
|
|
60
|
+
const priceHighs = [];
|
|
61
|
+
const rsiAtLows = [];
|
|
62
|
+
const rsiAtHighs = [];
|
|
63
|
+
for (let i = 2; i < recentCloses.length - 2; i++) {
|
|
64
|
+
if (recentCloses[i] < recentCloses[i - 1] &&
|
|
65
|
+
recentCloses[i] < recentCloses[i - 2] &&
|
|
66
|
+
recentCloses[i] < recentCloses[i + 1] &&
|
|
67
|
+
recentCloses[i] < recentCloses[i + 2]) {
|
|
68
|
+
priceLows.push(recentCloses[i]);
|
|
69
|
+
rsiAtLows.push(recentRSI[i]);
|
|
70
|
+
}
|
|
71
|
+
if (recentCloses[i] > recentCloses[i - 1] &&
|
|
72
|
+
recentCloses[i] > recentCloses[i - 2] &&
|
|
73
|
+
recentCloses[i] > recentCloses[i + 1] &&
|
|
74
|
+
recentCloses[i] > recentCloses[i + 2]) {
|
|
75
|
+
priceHighs.push(recentCloses[i]);
|
|
76
|
+
rsiAtHighs.push(recentRSI[i]);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Bullish divergence: price makes lower low, RSI makes higher low
|
|
80
|
+
if (priceLows.length >= 2) {
|
|
81
|
+
const lastTwoLows = priceLows.slice(-2);
|
|
82
|
+
const lastTwoRSILows = rsiAtLows.slice(-2);
|
|
83
|
+
if (lastTwoLows[1] < lastTwoLows[0] && lastTwoRSILows[1] > lastTwoRSILows[0]) {
|
|
84
|
+
return { type: "bullish", strength: 70 };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Bearish divergence: price makes higher high, RSI makes lower high
|
|
88
|
+
if (priceHighs.length >= 2) {
|
|
89
|
+
const lastTwoHighs = priceHighs.slice(-2);
|
|
90
|
+
const lastTwoRSIHighs = rsiAtHighs.slice(-2);
|
|
91
|
+
if (lastTwoHighs[1] > lastTwoHighs[0] && lastTwoRSIHighs[1] < lastTwoRSIHighs[0]) {
|
|
92
|
+
return { type: "bearish", strength: 70 };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return { type: "none", strength: 0 };
|
|
96
|
+
}
|
|
97
|
+
function calculateLeverage(atr, price, confidence, config) {
|
|
98
|
+
// ATR as percentage of price
|
|
99
|
+
const atrPercent = (atr / price) * 100;
|
|
100
|
+
// Base leverage calculation
|
|
101
|
+
// Higher volatility = lower leverage
|
|
102
|
+
let baseLeverage;
|
|
103
|
+
if (atrPercent > 5) {
|
|
104
|
+
baseLeverage = 3; // Very high volatility
|
|
105
|
+
}
|
|
106
|
+
else if (atrPercent > 3) {
|
|
107
|
+
baseLeverage = 5;
|
|
108
|
+
}
|
|
109
|
+
else if (atrPercent > 2) {
|
|
110
|
+
baseLeverage = 10;
|
|
111
|
+
}
|
|
112
|
+
else if (atrPercent > 1) {
|
|
113
|
+
baseLeverage = 15;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
baseLeverage = 20;
|
|
117
|
+
}
|
|
118
|
+
// Adjust based on confidence
|
|
119
|
+
const confidenceMultiplier = confidence >= 80 ? 1.2 : confidence >= 60 ? 1 : 0.8;
|
|
120
|
+
// Adjust based on style
|
|
121
|
+
const styleMultiplier = config.style === "conservative" ? 0.5 : config.style === "aggressive" ? 1.5 : 1;
|
|
122
|
+
const recommended = Math.min(Math.round(baseLeverage * confidenceMultiplier * styleMultiplier), config.maxLeverage);
|
|
123
|
+
return {
|
|
124
|
+
recommended: Math.max(1, recommended),
|
|
125
|
+
max: config.maxLeverage,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function calculatePositionSize(entryPrice, stopLoss, riskPercent) {
|
|
129
|
+
const riskPerUnit = Math.abs(entryPrice - stopLoss);
|
|
130
|
+
const riskRatio = riskPerUnit / entryPrice;
|
|
131
|
+
// Position size as % of account
|
|
132
|
+
// If risk per trade is 2% and stop is 5% away, position size = 2/5 = 40%
|
|
133
|
+
const positionSize = riskPercent / (riskRatio * 100);
|
|
134
|
+
return Math.min(positionSize, 100);
|
|
135
|
+
}
|
|
136
|
+
function generateTrailingStops(entry, tp1, tp2, tp3, stopLoss, direction) {
|
|
137
|
+
const stops = [];
|
|
138
|
+
if (direction === "LONG") {
|
|
139
|
+
// Move stop to breakeven when TP1 hit
|
|
140
|
+
stops.push({
|
|
141
|
+
trigger: tp1,
|
|
142
|
+
newStop: entry * 1.001, // Slightly above entry
|
|
143
|
+
description: "Move stop to breakeven when TP1 reached",
|
|
144
|
+
});
|
|
145
|
+
// Move stop to TP1 when TP2 hit
|
|
146
|
+
stops.push({
|
|
147
|
+
trigger: tp2,
|
|
148
|
+
newStop: tp1,
|
|
149
|
+
description: "Move stop to TP1 level when TP2 reached",
|
|
150
|
+
});
|
|
151
|
+
// Move stop to TP2 when approaching TP3
|
|
152
|
+
const tp2_tp3_mid = (tp2 + tp3) / 2;
|
|
153
|
+
stops.push({
|
|
154
|
+
trigger: tp2_tp3_mid,
|
|
155
|
+
newStop: tp2,
|
|
156
|
+
description: "Move stop to TP2 level when halfway to TP3",
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// SHORT positions - reverse logic
|
|
161
|
+
stops.push({
|
|
162
|
+
trigger: tp1,
|
|
163
|
+
newStop: entry * 0.999,
|
|
164
|
+
description: "Move stop to breakeven when TP1 reached",
|
|
165
|
+
});
|
|
166
|
+
stops.push({
|
|
167
|
+
trigger: tp2,
|
|
168
|
+
newStop: tp1,
|
|
169
|
+
description: "Move stop to TP1 level when TP2 reached",
|
|
170
|
+
});
|
|
171
|
+
const tp2_tp3_mid = (tp2 + tp3) / 2;
|
|
172
|
+
stops.push({
|
|
173
|
+
trigger: tp2_tp3_mid,
|
|
174
|
+
newStop: tp2,
|
|
175
|
+
description: "Move stop to TP2 level when halfway to TP3",
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return stops;
|
|
179
|
+
}
|
|
180
|
+
// ============================================
|
|
181
|
+
// MULTI-TIMEFRAME ANALYSIS
|
|
182
|
+
// ============================================
|
|
183
|
+
async function analyzeTimeframe(symbol, interval) {
|
|
184
|
+
try {
|
|
185
|
+
const candles = await (0, data_fetcher_1.fetchOHLCV)(symbol, interval, 100);
|
|
186
|
+
if (candles.length < 50)
|
|
187
|
+
return "neutral";
|
|
188
|
+
const closes = candles.map((c) => c.close);
|
|
189
|
+
const emas = calculateEMAs(closes);
|
|
190
|
+
const rsi = (0, indicators_1.calculateRSI)(closes, 14);
|
|
191
|
+
const macd = (0, indicators_1.calculateMACD)(closes);
|
|
192
|
+
const trend = (0, indicators_1.analyzeTrend)(candles);
|
|
193
|
+
let bullishPoints = 0;
|
|
194
|
+
let bearishPoints = 0;
|
|
195
|
+
// EMA alignment
|
|
196
|
+
if (emas.alignment === "bullish")
|
|
197
|
+
bullishPoints += 2;
|
|
198
|
+
else if (emas.alignment === "bearish")
|
|
199
|
+
bearishPoints += 2;
|
|
200
|
+
// RSI
|
|
201
|
+
if (rsi.current < 40)
|
|
202
|
+
bullishPoints += 1;
|
|
203
|
+
else if (rsi.current > 60)
|
|
204
|
+
bearishPoints += 1;
|
|
205
|
+
// MACD
|
|
206
|
+
if (macd.crossover === "bullish")
|
|
207
|
+
bullishPoints += 1;
|
|
208
|
+
else if (macd.crossover === "bearish")
|
|
209
|
+
bearishPoints += 1;
|
|
210
|
+
// Trend
|
|
211
|
+
if (trend.direction === "bullish")
|
|
212
|
+
bullishPoints += 1;
|
|
213
|
+
else if (trend.direction === "bearish")
|
|
214
|
+
bearishPoints += 1;
|
|
215
|
+
if (bullishPoints >= 3)
|
|
216
|
+
return "bullish";
|
|
217
|
+
if (bearishPoints >= 3)
|
|
218
|
+
return "bearish";
|
|
219
|
+
return "neutral";
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return "neutral";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async function getMultiTimeframeAnalysis(symbol) {
|
|
226
|
+
const [m15, h1, h4] = await Promise.all([
|
|
227
|
+
analyzeTimeframe(symbol, "15m"),
|
|
228
|
+
analyzeTimeframe(symbol, "1h"),
|
|
229
|
+
analyzeTimeframe(symbol, "4h"),
|
|
230
|
+
]);
|
|
231
|
+
// Confirmed if all timeframes align
|
|
232
|
+
const confirmed = (m15 === "bullish" && h1 === "bullish" && h4 === "bullish") ||
|
|
233
|
+
(m15 === "bearish" && h1 === "bearish" && h4 === "bearish");
|
|
234
|
+
return { m15, h1, h4, confirmed };
|
|
235
|
+
}
|
|
236
|
+
// ============================================
|
|
237
|
+
// NEWS SENTIMENT ANALYSIS
|
|
238
|
+
// ============================================
|
|
239
|
+
async function getNewsSentiment(symbol) {
|
|
240
|
+
try {
|
|
241
|
+
// Extract coin from symbol (BTCUSDT -> BTC)
|
|
242
|
+
const coin = symbol.replace(/USDT$/, "").replace(/USD$/, "");
|
|
243
|
+
const newsResult = await (0, news_fetcher_1.fetchCryptoNews)(coin);
|
|
244
|
+
if (newsResult.news.length === 0) {
|
|
245
|
+
return { score: 50, status: "No recent news" };
|
|
246
|
+
}
|
|
247
|
+
const positive = newsResult.news.filter((n) => n.sentiment === "positive").length;
|
|
248
|
+
const negative = newsResult.news.filter((n) => n.sentiment === "negative").length;
|
|
249
|
+
const total = newsResult.news.length;
|
|
250
|
+
// Score: 0-100, 50 is neutral
|
|
251
|
+
const score = 50 + ((positive - negative) / total) * 50;
|
|
252
|
+
let status;
|
|
253
|
+
if (score > 65)
|
|
254
|
+
status = "Bullish news sentiment";
|
|
255
|
+
else if (score < 35)
|
|
256
|
+
status = "Bearish news sentiment";
|
|
257
|
+
else
|
|
258
|
+
status = "Neutral news sentiment";
|
|
259
|
+
return { score: Math.round(score), status };
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
return { score: 50, status: "Unable to fetch news" };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// ============================================
|
|
266
|
+
// VOLUME & WHALE ANALYSIS
|
|
267
|
+
// ============================================
|
|
268
|
+
async function getVolumeWhaleScore(candles, symbol) {
|
|
269
|
+
const volumes = candles.map((c) => c.volume);
|
|
270
|
+
const recentVolume = volumes.slice(-5).reduce((a, b) => a + b, 0) / 5;
|
|
271
|
+
const avgVolume = volumes.slice(-20).reduce((a, b) => a + b, 0) / 20;
|
|
272
|
+
const volumeRatio = recentVolume / avgVolume;
|
|
273
|
+
// Analyze recent candle volume spikes
|
|
274
|
+
const closes = candles.map((c) => c.close);
|
|
275
|
+
const lastCandle = candles[candles.length - 1];
|
|
276
|
+
const priceUp = lastCandle.close > lastCandle.open;
|
|
277
|
+
let score = 50;
|
|
278
|
+
let status = "Normal volume";
|
|
279
|
+
if (volumeRatio > 2) {
|
|
280
|
+
if (priceUp) {
|
|
281
|
+
score = 75;
|
|
282
|
+
status = "High volume buying (whale accumulation possible)";
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
score = 25;
|
|
286
|
+
status = "High volume selling (whale distribution possible)";
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else if (volumeRatio > 1.5) {
|
|
290
|
+
if (priceUp) {
|
|
291
|
+
score = 65;
|
|
292
|
+
status = "Above average buying volume";
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
score = 35;
|
|
296
|
+
status = "Above average selling volume";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (volumeRatio < 0.5) {
|
|
300
|
+
score = 50;
|
|
301
|
+
status = "Low volume - weak move";
|
|
302
|
+
}
|
|
303
|
+
return { score, status };
|
|
304
|
+
}
|
|
305
|
+
// ============================================
|
|
306
|
+
// MAIN STRATEGY FUNCTION
|
|
307
|
+
// ============================================
|
|
308
|
+
async function generateTradeSetup(symbol, config = DEFAULT_CONFIG) {
|
|
309
|
+
const spinner = (0, ora_1.default)(`Analyzing ${symbol} for trade setup...`).start();
|
|
310
|
+
try {
|
|
311
|
+
// Fetch main data (1h timeframe)
|
|
312
|
+
spinner.text = "Fetching market data...";
|
|
313
|
+
const data = await (0, data_fetcher_1.fetchCryptoData)(symbol, "1h", 200);
|
|
314
|
+
const candles = data.candles;
|
|
315
|
+
const closes = candles.map((c) => c.close);
|
|
316
|
+
const highs = candles.map((c) => c.high);
|
|
317
|
+
const lows = candles.map((c) => c.low);
|
|
318
|
+
const currentPrice = data.currentPrice;
|
|
319
|
+
// =====================
|
|
320
|
+
// TECHNICAL ANALYSIS
|
|
321
|
+
// =====================
|
|
322
|
+
spinner.text = "Calculating technical indicators...";
|
|
323
|
+
// EMAs
|
|
324
|
+
const emas = calculateEMAs(closes);
|
|
325
|
+
// RSI
|
|
326
|
+
const rsi = (0, indicators_1.calculateRSI)(closes, 14);
|
|
327
|
+
const rsiDivergence = detectRSIDivergence(closes, rsi.values);
|
|
328
|
+
// MACD
|
|
329
|
+
const macd = (0, indicators_1.calculateMACD)(closes);
|
|
330
|
+
// Bollinger Bands
|
|
331
|
+
const bb = (0, indicators_1.calculateBollingerBands)(closes);
|
|
332
|
+
// ATR for volatility
|
|
333
|
+
const atr = (0, indicators_1.calculateATR)(candles, 14);
|
|
334
|
+
// Trend
|
|
335
|
+
const trend = (0, indicators_1.analyzeTrend)(candles);
|
|
336
|
+
// Support/Resistance
|
|
337
|
+
const sr = (0, indicators_1.calculateSupportResistance)(candles);
|
|
338
|
+
// Calculate technical score
|
|
339
|
+
let technicalScore = 50;
|
|
340
|
+
let technicalBias = "neutral";
|
|
341
|
+
// EMA scoring
|
|
342
|
+
if (emas.alignment === "bullish") {
|
|
343
|
+
technicalScore += 15;
|
|
344
|
+
technicalBias = "bullish";
|
|
345
|
+
}
|
|
346
|
+
else if (emas.alignment === "bearish") {
|
|
347
|
+
technicalScore -= 15;
|
|
348
|
+
technicalBias = "bearish";
|
|
349
|
+
}
|
|
350
|
+
// Price vs EMAs
|
|
351
|
+
if (currentPrice > emas.ema9 && currentPrice > emas.ema21) {
|
|
352
|
+
technicalScore += 5;
|
|
353
|
+
}
|
|
354
|
+
else if (currentPrice < emas.ema9 && currentPrice < emas.ema21) {
|
|
355
|
+
technicalScore -= 5;
|
|
356
|
+
}
|
|
357
|
+
// RSI scoring
|
|
358
|
+
if (rsi.current < 30) {
|
|
359
|
+
technicalScore += 10; // Oversold = bullish
|
|
360
|
+
}
|
|
361
|
+
else if (rsi.current > 70) {
|
|
362
|
+
technicalScore -= 10; // Overbought = bearish
|
|
363
|
+
}
|
|
364
|
+
// RSI divergence
|
|
365
|
+
if (rsiDivergence.type === "bullish") {
|
|
366
|
+
technicalScore += 15;
|
|
367
|
+
}
|
|
368
|
+
else if (rsiDivergence.type === "bearish") {
|
|
369
|
+
technicalScore -= 15;
|
|
370
|
+
}
|
|
371
|
+
// MACD scoring
|
|
372
|
+
if (macd.crossover === "bullish") {
|
|
373
|
+
technicalScore += 10;
|
|
374
|
+
}
|
|
375
|
+
else if (macd.crossover === "bearish") {
|
|
376
|
+
technicalScore -= 10;
|
|
377
|
+
}
|
|
378
|
+
// Bollinger Band scoring
|
|
379
|
+
if (currentPrice <= bb.current.lower) {
|
|
380
|
+
technicalScore += 10; // At lower band = potential bounce
|
|
381
|
+
}
|
|
382
|
+
else if (currentPrice >= bb.current.upper) {
|
|
383
|
+
technicalScore -= 10; // At upper band = potential rejection
|
|
384
|
+
}
|
|
385
|
+
technicalScore = Math.max(0, Math.min(100, technicalScore));
|
|
386
|
+
// =====================
|
|
387
|
+
// SMC ANALYSIS
|
|
388
|
+
// =====================
|
|
389
|
+
spinner.text = "Analyzing Smart Money Concepts...";
|
|
390
|
+
const smcCandles = candles.slice(-50);
|
|
391
|
+
const orderBlocks = (0, smc_indicators_1.findOrderBlocks)(smcCandles);
|
|
392
|
+
const fvgs = (0, smc_indicators_1.findFairValueGaps)(candles.slice(-30));
|
|
393
|
+
// Note: liquidityZones requires swingPoints, but we're not using it for scoring
|
|
394
|
+
// const swingPoints = findSwingPoints(smcCandles);
|
|
395
|
+
// const liquidityZones = findLiquidityZones(smcCandles, swingPoints);
|
|
396
|
+
let smcScore = 50;
|
|
397
|
+
let smcStatus = "No clear SMC setup";
|
|
398
|
+
// Check if price is near bullish order block
|
|
399
|
+
const bullishOBs = orderBlocks.filter((ob) => ob.type === "bullish");
|
|
400
|
+
const bearishOBs = orderBlocks.filter((ob) => ob.type === "bearish");
|
|
401
|
+
for (const ob of bullishOBs) {
|
|
402
|
+
if (currentPrice >= ob.bottom && currentPrice <= ob.top * 1.01) {
|
|
403
|
+
smcScore += 20;
|
|
404
|
+
smcStatus = "Price at bullish order block";
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
for (const ob of bearishOBs) {
|
|
409
|
+
if (currentPrice <= ob.top && currentPrice >= ob.bottom * 0.99) {
|
|
410
|
+
smcScore -= 20;
|
|
411
|
+
smcStatus = "Price at bearish order block";
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Check FVG
|
|
416
|
+
const bullishFVGs = fvgs.filter((f) => f.type === "bullish" && !f.filled);
|
|
417
|
+
const bearishFVGs = fvgs.filter((f) => f.type === "bearish" && !f.filled);
|
|
418
|
+
if (bullishFVGs.length > 0) {
|
|
419
|
+
const nearestBullishFVG = bullishFVGs[bullishFVGs.length - 1];
|
|
420
|
+
if (currentPrice >= nearestBullishFVG.bottom && currentPrice <= nearestBullishFVG.top) {
|
|
421
|
+
smcScore += 15;
|
|
422
|
+
smcStatus = "Price filling bullish FVG";
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (bearishFVGs.length > 0) {
|
|
426
|
+
const nearestBearishFVG = bearishFVGs[bearishFVGs.length - 1];
|
|
427
|
+
if (currentPrice >= nearestBearishFVG.bottom && currentPrice <= nearestBearishFVG.top) {
|
|
428
|
+
smcScore -= 15;
|
|
429
|
+
smcStatus = "Price filling bearish FVG";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
smcScore = Math.max(0, Math.min(100, smcScore));
|
|
433
|
+
// =====================
|
|
434
|
+
// VOLUME & WHALE ANALYSIS
|
|
435
|
+
// =====================
|
|
436
|
+
spinner.text = "Analyzing volume and whale activity...";
|
|
437
|
+
const volumeWhale = await getVolumeWhaleScore(candles, symbol);
|
|
438
|
+
// =====================
|
|
439
|
+
// NEWS SENTIMENT
|
|
440
|
+
// =====================
|
|
441
|
+
spinner.text = "Checking news sentiment...";
|
|
442
|
+
const news = await getNewsSentiment(symbol);
|
|
443
|
+
// =====================
|
|
444
|
+
// MULTI-TIMEFRAME
|
|
445
|
+
// =====================
|
|
446
|
+
spinner.text = "Performing multi-timeframe analysis...";
|
|
447
|
+
const mtf = await getMultiTimeframeAnalysis(symbol);
|
|
448
|
+
// =====================
|
|
449
|
+
// CALCULATE OVERALL SCORE & DIRECTION
|
|
450
|
+
// =====================
|
|
451
|
+
const overallScore = Math.round(technicalScore * 0.4 + smcScore * 0.25 + volumeWhale.score * 0.2 + news.score * 0.15);
|
|
452
|
+
// Determine direction
|
|
453
|
+
let direction = "NO_TRADE";
|
|
454
|
+
const confidence = overallScore;
|
|
455
|
+
if (config.direction === "both" || config.direction === "long") {
|
|
456
|
+
if (overallScore >= 65 && (mtf.h1 === "bullish" || mtf.h4 === "bullish")) {
|
|
457
|
+
direction = "LONG";
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (config.direction === "both" || config.direction === "short") {
|
|
461
|
+
if (overallScore <= 35 && (mtf.h1 === "bearish" || mtf.h4 === "bearish")) {
|
|
462
|
+
direction = "SHORT";
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// =====================
|
|
466
|
+
// CALCULATE ENTRY, SL, TP
|
|
467
|
+
// =====================
|
|
468
|
+
let entryPrice = currentPrice;
|
|
469
|
+
let stopLoss;
|
|
470
|
+
let tp1, tp2, tp3;
|
|
471
|
+
const atrValue = atr.current;
|
|
472
|
+
if (direction === "LONG") {
|
|
473
|
+
// Entry at current price or slightly below
|
|
474
|
+
entryPrice = currentPrice;
|
|
475
|
+
// Stop loss below recent support or 1.5x ATR
|
|
476
|
+
const supportStop = sr.nearestSupport * 0.995;
|
|
477
|
+
const atrStop = currentPrice - atrValue * 1.5;
|
|
478
|
+
stopLoss = Math.max(supportStop, atrStop);
|
|
479
|
+
// Take profits
|
|
480
|
+
const riskAmount = entryPrice - stopLoss;
|
|
481
|
+
tp1 = entryPrice + riskAmount * 1.5; // 1:1.5 RR
|
|
482
|
+
tp2 = entryPrice + riskAmount * 2.5; // 1:2.5 RR
|
|
483
|
+
tp3 = entryPrice + riskAmount * 4; // 1:4 RR
|
|
484
|
+
}
|
|
485
|
+
else if (direction === "SHORT") {
|
|
486
|
+
entryPrice = currentPrice;
|
|
487
|
+
// Stop loss above recent resistance or 1.5x ATR
|
|
488
|
+
const resistanceStop = sr.nearestResistance * 1.005;
|
|
489
|
+
const atrStop = currentPrice + atrValue * 1.5;
|
|
490
|
+
stopLoss = Math.min(resistanceStop, atrStop);
|
|
491
|
+
// Take profits
|
|
492
|
+
const riskAmount = stopLoss - entryPrice;
|
|
493
|
+
tp1 = entryPrice - riskAmount * 1.5;
|
|
494
|
+
tp2 = entryPrice - riskAmount * 2.5;
|
|
495
|
+
tp3 = entryPrice - riskAmount * 4;
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// No trade
|
|
499
|
+
stopLoss = currentPrice;
|
|
500
|
+
tp1 = currentPrice;
|
|
501
|
+
tp2 = currentPrice;
|
|
502
|
+
tp3 = currentPrice;
|
|
503
|
+
}
|
|
504
|
+
// =====================
|
|
505
|
+
// RISK MANAGEMENT
|
|
506
|
+
// =====================
|
|
507
|
+
const leverage = calculateLeverage(atrValue, currentPrice, confidence, config);
|
|
508
|
+
const riskRewardRatio = direction !== "NO_TRADE" ? Math.abs(tp2 - entryPrice) / Math.abs(entryPrice - stopLoss) : 0;
|
|
509
|
+
const positionSize = calculatePositionSize(entryPrice, stopLoss, config.riskPerTrade);
|
|
510
|
+
// =====================
|
|
511
|
+
// TRAILING STOPS
|
|
512
|
+
// =====================
|
|
513
|
+
const trailingStops = direction !== "NO_TRADE"
|
|
514
|
+
? generateTrailingStops(entryPrice, tp1, tp2, tp3, stopLoss, direction)
|
|
515
|
+
: [];
|
|
516
|
+
// =====================
|
|
517
|
+
// ALERT STATUS
|
|
518
|
+
// =====================
|
|
519
|
+
let alertStatus = "FORMING";
|
|
520
|
+
const alertReasons = [];
|
|
521
|
+
if (direction !== "NO_TRADE") {
|
|
522
|
+
if (mtf.confirmed) {
|
|
523
|
+
alertStatus = "READY";
|
|
524
|
+
alertReasons.push("Multi-timeframe alignment confirmed");
|
|
525
|
+
}
|
|
526
|
+
if (confidence >= 70) {
|
|
527
|
+
alertStatus = "READY";
|
|
528
|
+
alertReasons.push(`High confidence setup (${confidence}%)`);
|
|
529
|
+
}
|
|
530
|
+
if (rsiDivergence.type !== "none") {
|
|
531
|
+
alertReasons.push(`RSI ${rsiDivergence.type} divergence detected`);
|
|
532
|
+
}
|
|
533
|
+
if (smcScore > 60 || smcScore < 40) {
|
|
534
|
+
alertReasons.push(smcStatus);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
alertReasons.push("No clear setup - waiting for better conditions");
|
|
539
|
+
}
|
|
540
|
+
// =====================
|
|
541
|
+
// WARNINGS
|
|
542
|
+
// =====================
|
|
543
|
+
const warnings = [];
|
|
544
|
+
if (atrValue / currentPrice > 0.03) {
|
|
545
|
+
warnings.push("High volatility - consider reducing position size");
|
|
546
|
+
}
|
|
547
|
+
if (!mtf.confirmed && direction !== "NO_TRADE") {
|
|
548
|
+
warnings.push("Multi-timeframe not aligned - trade with caution");
|
|
549
|
+
}
|
|
550
|
+
if (rsi.current > 80 && direction === "LONG") {
|
|
551
|
+
warnings.push("RSI extremely overbought - potential pullback");
|
|
552
|
+
}
|
|
553
|
+
if (rsi.current < 20 && direction === "SHORT") {
|
|
554
|
+
warnings.push("RSI extremely oversold - potential bounce");
|
|
555
|
+
}
|
|
556
|
+
spinner.succeed(`Analysis complete for ${symbol}`);
|
|
557
|
+
return {
|
|
558
|
+
symbol: data.symbol,
|
|
559
|
+
direction,
|
|
560
|
+
confidence,
|
|
561
|
+
entryPrice,
|
|
562
|
+
stopLoss,
|
|
563
|
+
takeProfit1: tp1,
|
|
564
|
+
takeProfit2: tp2,
|
|
565
|
+
takeProfit3: tp3,
|
|
566
|
+
recommendedLeverage: leverage.recommended,
|
|
567
|
+
maxLeverage: leverage.max,
|
|
568
|
+
riskRewardRatio,
|
|
569
|
+
positionSizePercent: positionSize,
|
|
570
|
+
technicalScore,
|
|
571
|
+
smcScore,
|
|
572
|
+
volumeWhaleScore: volumeWhale.score,
|
|
573
|
+
newsScore: news.score,
|
|
574
|
+
overallScore,
|
|
575
|
+
mtfAlignment: {
|
|
576
|
+
m15: mtf.m15,
|
|
577
|
+
h1: mtf.h1,
|
|
578
|
+
h4: mtf.h4,
|
|
579
|
+
},
|
|
580
|
+
mtfConfirmed: mtf.confirmed,
|
|
581
|
+
trailingStopLevels: trailingStops,
|
|
582
|
+
alertStatus,
|
|
583
|
+
alertReasons,
|
|
584
|
+
analysis: {
|
|
585
|
+
emaStatus: `EMA alignment: ${emas.alignment}`,
|
|
586
|
+
rsiStatus: `RSI: ${rsi.current.toFixed(1)} (${rsi.current < 30 ? "oversold" : rsi.current > 70 ? "overbought" : "neutral"})`,
|
|
587
|
+
macdStatus: `MACD: ${macd.crossover} crossover`,
|
|
588
|
+
bbStatus: `BB: Price at ${currentPrice <= bb.current.lower ? "lower" : currentPrice >= bb.current.upper ? "upper" : "middle"} band`,
|
|
589
|
+
smcStatus,
|
|
590
|
+
volumeStatus: volumeWhale.status,
|
|
591
|
+
newsStatus: news.status,
|
|
592
|
+
},
|
|
593
|
+
warnings,
|
|
594
|
+
timestamp: Date.now(),
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
spinner.fail(`Failed to analyze ${symbol}`);
|
|
599
|
+
throw error;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// ============================================
|
|
603
|
+
// DISPLAY FUNCTION
|
|
604
|
+
// ============================================
|
|
605
|
+
function displayTradeSetup(setup) {
|
|
606
|
+
console.log("");
|
|
607
|
+
// Header based on direction
|
|
608
|
+
const headerColor = setup.direction === "LONG"
|
|
609
|
+
? chalk_1.default.green
|
|
610
|
+
: setup.direction === "SHORT"
|
|
611
|
+
? chalk_1.default.red
|
|
612
|
+
: chalk_1.default.yellow;
|
|
613
|
+
const directionEmoji = setup.direction === "LONG" ? "🟢" : setup.direction === "SHORT" ? "🔴" : "⚪";
|
|
614
|
+
console.log(headerColor(" ╔═══════════════════════════════════════════════════════════════════════╗"));
|
|
615
|
+
console.log(headerColor(` ║ ${directionEmoji} SMART TRADING STRATEGY - ${setup.symbol.padEnd(20)} ║`));
|
|
616
|
+
console.log(headerColor(" ╚═══════════════════════════════════════════════════════════════════════╝"));
|
|
617
|
+
console.log("");
|
|
618
|
+
// Direction and Confidence
|
|
619
|
+
const directionBadge = setup.direction === "LONG"
|
|
620
|
+
? chalk_1.default.bgGreen.white(" LONG ")
|
|
621
|
+
: setup.direction === "SHORT"
|
|
622
|
+
? chalk_1.default.bgRed.white(" SHORT ")
|
|
623
|
+
: chalk_1.default.bgYellow.black(" NO TRADE ");
|
|
624
|
+
const confidenceBar = getConfidenceBar(setup.confidence);
|
|
625
|
+
console.log(` ${directionBadge} Confidence: ${confidenceBar} ${setup.confidence}%`);
|
|
626
|
+
console.log("");
|
|
627
|
+
// Alert Status
|
|
628
|
+
const alertColor = setup.alertStatus === "READY"
|
|
629
|
+
? chalk_1.default.green
|
|
630
|
+
: setup.alertStatus === "FORMING"
|
|
631
|
+
? chalk_1.default.yellow
|
|
632
|
+
: chalk_1.default.gray;
|
|
633
|
+
console.log(` ${chalk_1.default.cyan("📢 Alert:")} ${alertColor(setup.alertStatus)}`);
|
|
634
|
+
for (const reason of setup.alertReasons) {
|
|
635
|
+
console.log(` ${chalk_1.default.dim("•")} ${chalk_1.default.gray(reason)}`);
|
|
636
|
+
}
|
|
637
|
+
console.log("");
|
|
638
|
+
if (setup.direction !== "NO_TRADE") {
|
|
639
|
+
// Entry & Exit Box
|
|
640
|
+
console.log(chalk_1.default.cyan(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
641
|
+
console.log(chalk_1.default.cyan(" │ 📍 ENTRY & EXIT LEVELS │"));
|
|
642
|
+
console.log(chalk_1.default.cyan(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
643
|
+
console.log(` │ ${chalk_1.default.yellow("Entry Price:")} ${chalk_1.default.white.bold(formatPrice(setup.entryPrice))}`);
|
|
644
|
+
console.log(` │ ${chalk_1.default.red("Stop Loss:")} ${chalk_1.default.red.bold(formatPrice(setup.stopLoss))} ${chalk_1.default.dim(`(${((Math.abs(setup.entryPrice - setup.stopLoss) / setup.entryPrice) * 100).toFixed(2)}%)`)}`);
|
|
645
|
+
console.log(` │`);
|
|
646
|
+
console.log(` │ ${chalk_1.default.green("Take Profit 1:")} ${chalk_1.default.green(formatPrice(setup.takeProfit1))} ${chalk_1.default.dim("(1.5R)")}`);
|
|
647
|
+
console.log(` │ ${chalk_1.default.green("Take Profit 2:")} ${chalk_1.default.green(formatPrice(setup.takeProfit2))} ${chalk_1.default.dim("(2.5R)")}`);
|
|
648
|
+
console.log(` │ ${chalk_1.default.green("Take Profit 3:")} ${chalk_1.default.green(formatPrice(setup.takeProfit3))} ${chalk_1.default.dim("(4R)")}`);
|
|
649
|
+
console.log(chalk_1.default.cyan(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
650
|
+
console.log("");
|
|
651
|
+
// Risk Management Box
|
|
652
|
+
console.log(chalk_1.default.magenta(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
653
|
+
console.log(chalk_1.default.magenta(" │ ⚖️ RISK MANAGEMENT │"));
|
|
654
|
+
console.log(chalk_1.default.magenta(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
655
|
+
const leverageBadge = setup.recommendedLeverage <= 5
|
|
656
|
+
? chalk_1.default.green
|
|
657
|
+
: setup.recommendedLeverage <= 10
|
|
658
|
+
? chalk_1.default.yellow
|
|
659
|
+
: chalk_1.default.red;
|
|
660
|
+
console.log(` │ ${chalk_1.default.yellow("Recommended Leverage:")} ${leverageBadge.bold(`${setup.recommendedLeverage}x`)} ${chalk_1.default.dim(`(max: ${setup.maxLeverage}x)`)}`);
|
|
661
|
+
console.log(` │ ${chalk_1.default.yellow("Risk/Reward Ratio:")} ${chalk_1.default.cyan.bold(`1:${setup.riskRewardRatio.toFixed(1)}`)}`);
|
|
662
|
+
console.log(` │ ${chalk_1.default.yellow("Position Size:")} ${chalk_1.default.white(`${setup.positionSizePercent.toFixed(1)}% of account`)}`);
|
|
663
|
+
console.log(chalk_1.default.magenta(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
664
|
+
console.log("");
|
|
665
|
+
// Trailing Stop Box
|
|
666
|
+
console.log(chalk_1.default.blue(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
667
|
+
console.log(chalk_1.default.blue(" │ 📈 TRAILING STOP PLAN │"));
|
|
668
|
+
console.log(chalk_1.default.blue(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
669
|
+
for (let i = 0; i < setup.trailingStopLevels.length; i++) {
|
|
670
|
+
const ts = setup.trailingStopLevels[i];
|
|
671
|
+
console.log(` │ ${chalk_1.default.cyan(`Step ${i + 1}:`)} When price hits ${chalk_1.default.yellow(formatPrice(ts.trigger))}`);
|
|
672
|
+
console.log(` │ → Move stop to ${chalk_1.default.green(formatPrice(ts.newStop))}`);
|
|
673
|
+
console.log(` │ ${chalk_1.default.dim(ts.description)}`);
|
|
674
|
+
if (i < setup.trailingStopLevels.length - 1)
|
|
675
|
+
console.log(` │`);
|
|
676
|
+
}
|
|
677
|
+
console.log(chalk_1.default.blue(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
678
|
+
console.log("");
|
|
679
|
+
}
|
|
680
|
+
// Multi-Timeframe Analysis
|
|
681
|
+
console.log(chalk_1.default.yellow(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
682
|
+
console.log(chalk_1.default.yellow(" │ ⏰ MULTI-TIMEFRAME ANALYSIS │"));
|
|
683
|
+
console.log(chalk_1.default.yellow(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
684
|
+
const mtfIcon = (bias) => bias === "bullish" ? chalk_1.default.green("▲") : bias === "bearish" ? chalk_1.default.red("▼") : chalk_1.default.gray("●");
|
|
685
|
+
const mtfColor = (bias) => bias === "bullish" ? chalk_1.default.green : bias === "bearish" ? chalk_1.default.red : chalk_1.default.gray;
|
|
686
|
+
console.log(` │ ${chalk_1.default.dim("15min:")} ${mtfIcon(setup.mtfAlignment.m15)} ${mtfColor(setup.mtfAlignment.m15)(setup.mtfAlignment.m15.padEnd(10))} ${chalk_1.default.dim("1hour:")} ${mtfIcon(setup.mtfAlignment.h1)} ${mtfColor(setup.mtfAlignment.h1)(setup.mtfAlignment.h1.padEnd(10))} ${chalk_1.default.dim("4hour:")} ${mtfIcon(setup.mtfAlignment.h4)} ${mtfColor(setup.mtfAlignment.h4)(setup.mtfAlignment.h4)}`);
|
|
687
|
+
console.log(` │`);
|
|
688
|
+
console.log(` │ ${setup.mtfConfirmed ? chalk_1.default.green("✓ All timeframes aligned!") : chalk_1.default.yellow("⚠ Timeframes not fully aligned")}`);
|
|
689
|
+
console.log(chalk_1.default.yellow(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
690
|
+
console.log("");
|
|
691
|
+
// Score Breakdown
|
|
692
|
+
console.log(chalk_1.default.gray(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
693
|
+
console.log(chalk_1.default.gray(" │ 📊 SCORE BREAKDOWN │"));
|
|
694
|
+
console.log(chalk_1.default.gray(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
695
|
+
console.log(` │ ${chalk_1.default.dim("Technical (40%):")} ${getScoreBar(setup.technicalScore)} ${setup.technicalScore}%`);
|
|
696
|
+
console.log(` │ ${chalk_1.default.dim("SMC (25%):")} ${getScoreBar(setup.smcScore)} ${setup.smcScore}%`);
|
|
697
|
+
console.log(` │ ${chalk_1.default.dim("Volume (20%):")} ${getScoreBar(setup.volumeWhaleScore)} ${setup.volumeWhaleScore}%`);
|
|
698
|
+
console.log(` │ ${chalk_1.default.dim("News (15%):")} ${getScoreBar(setup.newsScore)} ${setup.newsScore}%`);
|
|
699
|
+
console.log(` │ ${"─".repeat(50)}`);
|
|
700
|
+
console.log(` │ ${chalk_1.default.white.bold("Overall:")} ${getScoreBar(setup.overallScore)} ${chalk_1.default.bold(setup.overallScore + "%")}`);
|
|
701
|
+
console.log(chalk_1.default.gray(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
702
|
+
console.log("");
|
|
703
|
+
// Detailed Analysis
|
|
704
|
+
console.log(chalk_1.default.gray(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
705
|
+
console.log(chalk_1.default.gray(" │ 🔍 DETAILED ANALYSIS │"));
|
|
706
|
+
console.log(chalk_1.default.gray(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
707
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.emaStatus}`);
|
|
708
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.rsiStatus}`);
|
|
709
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.macdStatus}`);
|
|
710
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.bbStatus}`);
|
|
711
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.smcStatus}`);
|
|
712
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.volumeStatus}`);
|
|
713
|
+
console.log(` │ ${chalk_1.default.cyan("•")} ${setup.analysis.newsStatus}`);
|
|
714
|
+
console.log(chalk_1.default.gray(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
715
|
+
console.log("");
|
|
716
|
+
// Warnings
|
|
717
|
+
if (setup.warnings.length > 0) {
|
|
718
|
+
console.log(chalk_1.default.red(" ┌─────────────────────────────────────────────────────────────────────┐"));
|
|
719
|
+
console.log(chalk_1.default.red(" │ ⚠️ WARNINGS │"));
|
|
720
|
+
console.log(chalk_1.default.red(" ├─────────────────────────────────────────────────────────────────────┤"));
|
|
721
|
+
for (const warning of setup.warnings) {
|
|
722
|
+
console.log(` │ ${chalk_1.default.yellow("!")} ${chalk_1.default.yellow(warning)}`);
|
|
723
|
+
}
|
|
724
|
+
console.log(chalk_1.default.red(" └─────────────────────────────────────────────────────────────────────┘"));
|
|
725
|
+
console.log("");
|
|
726
|
+
}
|
|
727
|
+
// Disclaimer
|
|
728
|
+
console.log(chalk_1.default.dim.italic(" ⚠️ This is not financial advice. Always manage your risk and DYOR."));
|
|
729
|
+
console.log(chalk_1.default.dim.italic(" 💡 Tip: Wait for price to reach entry zone before executing trade."));
|
|
730
|
+
console.log("");
|
|
731
|
+
}
|
|
732
|
+
// Helper functions for display
|
|
733
|
+
function formatPrice(price) {
|
|
734
|
+
if (price < 0.001)
|
|
735
|
+
return `$${price.toFixed(8)}`;
|
|
736
|
+
if (price < 1)
|
|
737
|
+
return `$${price.toFixed(6)}`;
|
|
738
|
+
if (price < 100)
|
|
739
|
+
return `$${price.toFixed(4)}`;
|
|
740
|
+
return `$${price.toLocaleString(undefined, { maximumFractionDigits: 2 })}`;
|
|
741
|
+
}
|
|
742
|
+
function getScoreBar(score, width = 15) {
|
|
743
|
+
const filled = Math.round((score / 100) * width);
|
|
744
|
+
const empty = width - filled;
|
|
745
|
+
let color = chalk_1.default.green;
|
|
746
|
+
if (score < 40)
|
|
747
|
+
color = chalk_1.default.red;
|
|
748
|
+
else if (score < 60)
|
|
749
|
+
color = chalk_1.default.yellow;
|
|
750
|
+
return color("█".repeat(filled)) + chalk_1.default.gray("░".repeat(empty));
|
|
751
|
+
}
|
|
752
|
+
function getConfidenceBar(confidence) {
|
|
753
|
+
const width = 20;
|
|
754
|
+
const filled = Math.round((confidence / 100) * width);
|
|
755
|
+
const empty = width - filled;
|
|
756
|
+
let color = chalk_1.default.green;
|
|
757
|
+
if (confidence < 40)
|
|
758
|
+
color = chalk_1.default.red;
|
|
759
|
+
else if (confidence < 60)
|
|
760
|
+
color = chalk_1.default.yellow;
|
|
761
|
+
return "[" + color("█".repeat(filled)) + chalk_1.default.gray("░".repeat(empty)) + "]";
|
|
762
|
+
}
|
|
763
|
+
// ============================================
|
|
764
|
+
// MAIN RUNNER FUNCTION
|
|
765
|
+
// ============================================
|
|
766
|
+
async function runStrategy(symbol, config) {
|
|
767
|
+
const fullConfig = {
|
|
768
|
+
...DEFAULT_CONFIG,
|
|
769
|
+
...config,
|
|
770
|
+
};
|
|
771
|
+
try {
|
|
772
|
+
const setup = await generateTradeSetup(symbol, fullConfig);
|
|
773
|
+
displayTradeSetup(setup);
|
|
774
|
+
return setup;
|
|
775
|
+
}
|
|
776
|
+
catch (error) {
|
|
777
|
+
console.log("");
|
|
778
|
+
console.log(chalk_1.default.red(" ╔═══════════════════════════════════════════════════════════════════════╗"));
|
|
779
|
+
console.log(chalk_1.default.red(" ║ ❌ STRATEGY ERROR ║"));
|
|
780
|
+
console.log(chalk_1.default.red(" ╚═══════════════════════════════════════════════════════════════════════╝"));
|
|
781
|
+
console.log("");
|
|
782
|
+
if (error.message?.includes("Invalid symbol") || error.message?.includes("Failed to fetch")) {
|
|
783
|
+
console.log(chalk_1.default.yellow(` ⚠️ Symbol not found: "${symbol.toUpperCase()}"`));
|
|
784
|
+
console.log("");
|
|
785
|
+
// Get similar symbols from Binance
|
|
786
|
+
const suggestions = await (0, data_fetcher_1.findSimilarSymbols)(symbol, 8);
|
|
787
|
+
if (suggestions.length > 0) {
|
|
788
|
+
console.log(chalk_1.default.gray(" Did you mean one of these?"));
|
|
789
|
+
console.log("");
|
|
790
|
+
console.log(chalk_1.default.cyan(` ${suggestions.join(", ")}`));
|
|
791
|
+
console.log("");
|
|
792
|
+
}
|
|
793
|
+
console.log(chalk_1.default.gray(" This symbol is not available on Binance exchange."));
|
|
794
|
+
console.log(chalk_1.default.gray(" Note: This tool only supports Binance USDT trading pairs."));
|
|
795
|
+
console.log(chalk_1.default.gray(" The token might exist on other exchanges (KuCoin, Bybit, etc.)"));
|
|
796
|
+
}
|
|
797
|
+
else {
|
|
798
|
+
console.log(chalk_1.default.yellow(` ⚠️ ${error.message || "An unexpected error occurred"}`));
|
|
799
|
+
}
|
|
800
|
+
console.log("");
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
}
|