prab-cli 1.2.1 → 1.2.5

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,166 @@
1
+ "use strict";
2
+ /* global fetch */
3
+ /**
4
+ * Cryptocurrency Data Fetcher
5
+ * Fetches OHLCV data from Binance public API (no API key required)
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.normalizeSymbol = normalizeSymbol;
9
+ exports.fetchOHLCV = fetchOHLCV;
10
+ exports.fetch24hTicker = fetch24hTicker;
11
+ exports.fetchCryptoData = fetchCryptoData;
12
+ exports.getSupportedSymbols = getSupportedSymbols;
13
+ exports.isValidSymbol = isValidSymbol;
14
+ // Common crypto symbols mapping (user-friendly -> Binance format)
15
+ const SYMBOL_MAP = {
16
+ btc: "BTCUSDT",
17
+ bitcoin: "BTCUSDT",
18
+ eth: "ETHUSDT",
19
+ ethereum: "ETHUSDT",
20
+ sol: "SOLUSDT",
21
+ solana: "SOLUSDT",
22
+ xrp: "XRPUSDT",
23
+ ripple: "XRPUSDT",
24
+ doge: "DOGEUSDT",
25
+ dogecoin: "DOGEUSDT",
26
+ ada: "ADAUSDT",
27
+ cardano: "ADAUSDT",
28
+ bnb: "BNBUSDT",
29
+ dot: "DOTUSDT",
30
+ polkadot: "DOTUSDT",
31
+ matic: "MATICUSDT",
32
+ polygon: "MATICUSDT",
33
+ link: "LINKUSDT",
34
+ chainlink: "LINKUSDT",
35
+ avax: "AVAXUSDT",
36
+ avalanche: "AVAXUSDT",
37
+ ltc: "LTCUSDT",
38
+ litecoin: "LTCUSDT",
39
+ atom: "ATOMUSDT",
40
+ cosmos: "ATOMUSDT",
41
+ uni: "UNIUSDT",
42
+ uniswap: "UNIUSDT",
43
+ xlm: "XLMUSDT",
44
+ stellar: "XLMUSDT",
45
+ algo: "ALGOUSDT",
46
+ algorand: "ALGOUSDT",
47
+ near: "NEARUSDT",
48
+ apt: "APTUSDT",
49
+ aptos: "APTUSDT",
50
+ arb: "ARBUSDT",
51
+ arbitrum: "ARBUSDT",
52
+ op: "OPUSDT",
53
+ optimism: "OPUSDT",
54
+ sui: "SUIUSDT",
55
+ pepe: "PEPEUSDT",
56
+ shib: "SHIBUSDT",
57
+ wif: "WIFUSDT",
58
+ };
59
+ const BINANCE_API_BASE = "https://api.binance.com/api/v3";
60
+ /**
61
+ * Normalize symbol to Binance format
62
+ */
63
+ function normalizeSymbol(input) {
64
+ const lower = input.toLowerCase().trim();
65
+ // Check if it's in our mapping
66
+ if (SYMBOL_MAP[lower]) {
67
+ return SYMBOL_MAP[lower];
68
+ }
69
+ // If already in USDT format, return uppercase
70
+ if (lower.endsWith("usdt")) {
71
+ return lower.toUpperCase();
72
+ }
73
+ // If ends with USD (not USDT), replace with USDT
74
+ if (lower.endsWith("usd")) {
75
+ return lower.slice(0, -3).toUpperCase() + "USDT";
76
+ }
77
+ // If ends with other quote currencies, keep as is
78
+ if (lower.endsWith("btc") || lower.endsWith("eth") || lower.endsWith("bnb")) {
79
+ return lower.toUpperCase();
80
+ }
81
+ // Default: append USDT
82
+ return lower.toUpperCase() + "USDT";
83
+ }
84
+ /**
85
+ * Fetch OHLCV candle data from Binance
86
+ */
87
+ async function fetchOHLCV(symbol, interval = "1h", limit = 100) {
88
+ const normalizedSymbol = normalizeSymbol(symbol);
89
+ const url = `${BINANCE_API_BASE}/klines?symbol=${normalizedSymbol}&interval=${interval}&limit=${limit}`;
90
+ const response = await fetch(url);
91
+ if (!response.ok) {
92
+ const error = await response.text();
93
+ throw new Error(`Failed to fetch data for ${normalizedSymbol}: ${error}`);
94
+ }
95
+ const data = await response.json();
96
+ // Binance klines format:
97
+ // [0] Open time, [1] Open, [2] High, [3] Low, [4] Close, [5] Volume, ...
98
+ return data.map((candle) => ({
99
+ timestamp: candle[0],
100
+ open: parseFloat(candle[1]),
101
+ high: parseFloat(candle[2]),
102
+ low: parseFloat(candle[3]),
103
+ close: parseFloat(candle[4]),
104
+ volume: parseFloat(candle[5]),
105
+ }));
106
+ }
107
+ /**
108
+ * Fetch 24h ticker data for price change info
109
+ */
110
+ async function fetch24hTicker(symbol) {
111
+ const normalizedSymbol = normalizeSymbol(symbol);
112
+ const url = `${BINANCE_API_BASE}/ticker/24hr?symbol=${normalizedSymbol}`;
113
+ const response = await fetch(url);
114
+ if (!response.ok) {
115
+ const error = await response.text();
116
+ throw new Error(`Failed to fetch ticker for ${normalizedSymbol}: ${error}`);
117
+ }
118
+ const data = await response.json();
119
+ return {
120
+ price: parseFloat(data.lastPrice),
121
+ priceChange: parseFloat(data.priceChange),
122
+ priceChangePercent: parseFloat(data.priceChangePercent),
123
+ high24h: parseFloat(data.highPrice),
124
+ low24h: parseFloat(data.lowPrice),
125
+ volume24h: parseFloat(data.volume),
126
+ };
127
+ }
128
+ /**
129
+ * Fetch complete crypto data for analysis
130
+ */
131
+ async function fetchCryptoData(symbol, interval = "1h", limit = 100) {
132
+ const normalizedSymbol = normalizeSymbol(symbol);
133
+ // Fetch both OHLCV and 24h ticker in parallel
134
+ const [candles, ticker] = await Promise.all([
135
+ fetchOHLCV(symbol, interval, limit),
136
+ fetch24hTicker(symbol),
137
+ ]);
138
+ return {
139
+ symbol: normalizedSymbol,
140
+ interval,
141
+ candles,
142
+ currentPrice: ticker.price,
143
+ priceChange24h: ticker.priceChange,
144
+ priceChangePercent24h: ticker.priceChangePercent,
145
+ };
146
+ }
147
+ /**
148
+ * Get list of supported symbols
149
+ */
150
+ function getSupportedSymbols() {
151
+ return [...new Set(Object.keys(SYMBOL_MAP))];
152
+ }
153
+ /**
154
+ * Check if a symbol is valid/supported
155
+ */
156
+ async function isValidSymbol(symbol) {
157
+ try {
158
+ const normalizedSymbol = normalizeSymbol(symbol);
159
+ const url = `${BINANCE_API_BASE}/ticker/price?symbol=${normalizedSymbol}`;
160
+ const response = await fetch(url);
161
+ return response.ok;
162
+ }
163
+ catch {
164
+ return false;
165
+ }
166
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /**
3
+ * Crypto Trading Signal Module
4
+ * Exports all crypto-related functionality
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.displayNews = exports.fetchCryptoNews = exports.runCryptoNews = exports.displayScanResults = exports.runMarketScanner = exports.displayWhaleActivity = exports.runWhaleTracker = exports.displaySMCAnalysis = exports.runSMCAnalysis = exports.calculatePremiumDiscount = exports.findLiquidityZones = exports.findFairValueGaps = exports.findOrderBlocks = exports.findSwingPoints = exports.analyzeSMC = exports.analyzeTrend = exports.calculateSupportResistance = exports.analyzeVolume = exports.calculateATR = exports.calculateBollingerBands = exports.calculateMACD = exports.calculateRSI = exports.analyzeMarket = exports.displayComprehensiveAnalysis = exports.comprehensiveAnalysis = exports.fullSignal = exports.quickSignal = exports.displaySignal = exports.generateTradingSignal = exports.formatSignalSummary = exports.generateSignal = exports.calculateIndicators = exports.calculateAllEMAs = exports.calculateEMA = exports.isValidSymbol = exports.getSupportedSymbols = exports.normalizeSymbol = exports.fetch24hTicker = exports.fetchOHLCV = exports.fetchCryptoData = void 0;
8
+ var data_fetcher_1 = require("./data-fetcher");
9
+ Object.defineProperty(exports, "fetchCryptoData", { enumerable: true, get: function () { return data_fetcher_1.fetchCryptoData; } });
10
+ Object.defineProperty(exports, "fetchOHLCV", { enumerable: true, get: function () { return data_fetcher_1.fetchOHLCV; } });
11
+ Object.defineProperty(exports, "fetch24hTicker", { enumerable: true, get: function () { return data_fetcher_1.fetch24hTicker; } });
12
+ Object.defineProperty(exports, "normalizeSymbol", { enumerable: true, get: function () { return data_fetcher_1.normalizeSymbol; } });
13
+ Object.defineProperty(exports, "getSupportedSymbols", { enumerable: true, get: function () { return data_fetcher_1.getSupportedSymbols; } });
14
+ Object.defineProperty(exports, "isValidSymbol", { enumerable: true, get: function () { return data_fetcher_1.isValidSymbol; } });
15
+ var analyzer_1 = require("./analyzer");
16
+ Object.defineProperty(exports, "calculateEMA", { enumerable: true, get: function () { return analyzer_1.calculateEMA; } });
17
+ Object.defineProperty(exports, "calculateAllEMAs", { enumerable: true, get: function () { return analyzer_1.calculateAllEMAs; } });
18
+ Object.defineProperty(exports, "calculateIndicators", { enumerable: true, get: function () { return analyzer_1.calculateIndicators; } });
19
+ Object.defineProperty(exports, "generateSignal", { enumerable: true, get: function () { return analyzer_1.generateSignal; } });
20
+ Object.defineProperty(exports, "formatSignalSummary", { enumerable: true, get: function () { return analyzer_1.formatSignalSummary; } });
21
+ var signal_generator_1 = require("./signal-generator");
22
+ Object.defineProperty(exports, "generateTradingSignal", { enumerable: true, get: function () { return signal_generator_1.generateTradingSignal; } });
23
+ Object.defineProperty(exports, "displaySignal", { enumerable: true, get: function () { return signal_generator_1.displaySignal; } });
24
+ Object.defineProperty(exports, "quickSignal", { enumerable: true, get: function () { return signal_generator_1.quickSignal; } });
25
+ Object.defineProperty(exports, "fullSignal", { enumerable: true, get: function () { return signal_generator_1.fullSignal; } });
26
+ Object.defineProperty(exports, "comprehensiveAnalysis", { enumerable: true, get: function () { return signal_generator_1.comprehensiveAnalysis; } });
27
+ Object.defineProperty(exports, "displayComprehensiveAnalysis", { enumerable: true, get: function () { return signal_generator_1.displayComprehensiveAnalysis; } });
28
+ var market_analyzer_1 = require("./market-analyzer");
29
+ Object.defineProperty(exports, "analyzeMarket", { enumerable: true, get: function () { return market_analyzer_1.analyzeMarket; } });
30
+ var indicators_1 = require("./indicators");
31
+ Object.defineProperty(exports, "calculateRSI", { enumerable: true, get: function () { return indicators_1.calculateRSI; } });
32
+ Object.defineProperty(exports, "calculateMACD", { enumerable: true, get: function () { return indicators_1.calculateMACD; } });
33
+ Object.defineProperty(exports, "calculateBollingerBands", { enumerable: true, get: function () { return indicators_1.calculateBollingerBands; } });
34
+ Object.defineProperty(exports, "calculateATR", { enumerable: true, get: function () { return indicators_1.calculateATR; } });
35
+ Object.defineProperty(exports, "analyzeVolume", { enumerable: true, get: function () { return indicators_1.analyzeVolume; } });
36
+ Object.defineProperty(exports, "calculateSupportResistance", { enumerable: true, get: function () { return indicators_1.calculateSupportResistance; } });
37
+ Object.defineProperty(exports, "analyzeTrend", { enumerable: true, get: function () { return indicators_1.analyzeTrend; } });
38
+ var smc_indicators_1 = require("./smc-indicators");
39
+ Object.defineProperty(exports, "analyzeSMC", { enumerable: true, get: function () { return smc_indicators_1.analyzeSMC; } });
40
+ Object.defineProperty(exports, "findSwingPoints", { enumerable: true, get: function () { return smc_indicators_1.findSwingPoints; } });
41
+ Object.defineProperty(exports, "findOrderBlocks", { enumerable: true, get: function () { return smc_indicators_1.findOrderBlocks; } });
42
+ Object.defineProperty(exports, "findFairValueGaps", { enumerable: true, get: function () { return smc_indicators_1.findFairValueGaps; } });
43
+ Object.defineProperty(exports, "findLiquidityZones", { enumerable: true, get: function () { return smc_indicators_1.findLiquidityZones; } });
44
+ Object.defineProperty(exports, "calculatePremiumDiscount", { enumerable: true, get: function () { return smc_indicators_1.calculatePremiumDiscount; } });
45
+ var smc_analyzer_1 = require("./smc-analyzer");
46
+ Object.defineProperty(exports, "runSMCAnalysis", { enumerable: true, get: function () { return smc_analyzer_1.runSMCAnalysis; } });
47
+ Object.defineProperty(exports, "displaySMCAnalysis", { enumerable: true, get: function () { return smc_analyzer_1.displaySMCAnalysis; } });
48
+ var whale_tracker_1 = require("./whale-tracker");
49
+ Object.defineProperty(exports, "runWhaleTracker", { enumerable: true, get: function () { return whale_tracker_1.runWhaleTracker; } });
50
+ Object.defineProperty(exports, "displayWhaleActivity", { enumerable: true, get: function () { return whale_tracker_1.displayWhaleActivity; } });
51
+ var market_scanner_1 = require("./market-scanner");
52
+ Object.defineProperty(exports, "runMarketScanner", { enumerable: true, get: function () { return market_scanner_1.runMarketScanner; } });
53
+ Object.defineProperty(exports, "displayScanResults", { enumerable: true, get: function () { return market_scanner_1.displayScanResults; } });
54
+ var news_fetcher_1 = require("./news-fetcher");
55
+ Object.defineProperty(exports, "runCryptoNews", { enumerable: true, get: function () { return news_fetcher_1.runCryptoNews; } });
56
+ Object.defineProperty(exports, "fetchCryptoNews", { enumerable: true, get: function () { return news_fetcher_1.fetchCryptoNews; } });
57
+ Object.defineProperty(exports, "displayNews", { enumerable: true, get: function () { return news_fetcher_1.displayNews; } });
@@ -0,0 +1,390 @@
1
+ "use strict";
2
+ /* global fetch */
3
+ /**
4
+ * Technical Indicators Module
5
+ * Comprehensive technical analysis calculations
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.calculateSMA = calculateSMA;
9
+ exports.calculateEMA = calculateEMA;
10
+ exports.calculateRSI = calculateRSI;
11
+ exports.calculateMACD = calculateMACD;
12
+ exports.calculateBollingerBands = calculateBollingerBands;
13
+ exports.calculateATR = calculateATR;
14
+ exports.analyzeVolume = analyzeVolume;
15
+ exports.calculateSupportResistance = calculateSupportResistance;
16
+ exports.analyzeTrend = analyzeTrend;
17
+ // ============================================
18
+ // MOVING AVERAGES
19
+ // ============================================
20
+ /**
21
+ * Calculate Simple Moving Average
22
+ */
23
+ function calculateSMA(prices, period) {
24
+ if (prices.length < period)
25
+ return [];
26
+ const sma = [];
27
+ for (let i = period - 1; i < prices.length; i++) {
28
+ let sum = 0;
29
+ for (let j = 0; j < period; j++) {
30
+ sum += prices[i - j];
31
+ }
32
+ sma.push(sum / period);
33
+ }
34
+ return sma;
35
+ }
36
+ /**
37
+ * Calculate Exponential Moving Average
38
+ */
39
+ function calculateEMA(prices, period) {
40
+ if (prices.length < period)
41
+ return [];
42
+ const ema = [];
43
+ const multiplier = 2 / (period + 1);
44
+ // First EMA is SMA
45
+ let sum = 0;
46
+ for (let i = 0; i < period; i++) {
47
+ sum += prices[i];
48
+ }
49
+ ema.push(sum / period);
50
+ for (let i = period; i < prices.length; i++) {
51
+ const currentEMA = (prices[i] - ema[ema.length - 1]) * multiplier + ema[ema.length - 1];
52
+ ema.push(currentEMA);
53
+ }
54
+ return ema;
55
+ }
56
+ /**
57
+ * Calculate RSI
58
+ */
59
+ function calculateRSI(prices, period = 14) {
60
+ if (prices.length < period + 1) {
61
+ return { values: [], current: 50, condition: "neutral", divergence: "none" };
62
+ }
63
+ const gains = [];
64
+ const losses = [];
65
+ // Calculate price changes
66
+ for (let i = 1; i < prices.length; i++) {
67
+ const change = prices[i] - prices[i - 1];
68
+ gains.push(change > 0 ? change : 0);
69
+ losses.push(change < 0 ? Math.abs(change) : 0);
70
+ }
71
+ const rsiValues = [];
72
+ // First average
73
+ let avgGain = gains.slice(0, period).reduce((a, b) => a + b, 0) / period;
74
+ let avgLoss = losses.slice(0, period).reduce((a, b) => a + b, 0) / period;
75
+ for (let i = period; i < gains.length; i++) {
76
+ avgGain = (avgGain * (period - 1) + gains[i]) / period;
77
+ avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
78
+ const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;
79
+ const rsi = 100 - 100 / (1 + rs);
80
+ rsiValues.push(rsi);
81
+ }
82
+ const current = rsiValues[rsiValues.length - 1] || 50;
83
+ // Determine condition
84
+ let condition = "neutral";
85
+ if (current >= 70)
86
+ condition = "overbought";
87
+ else if (current <= 30)
88
+ condition = "oversold";
89
+ // Check for divergence (simplified)
90
+ let divergence = "none";
91
+ if (rsiValues.length >= 10) {
92
+ const recentRSI = rsiValues.slice(-10);
93
+ const recentPrices = prices.slice(-10);
94
+ const rsiTrend = recentRSI[recentRSI.length - 1] - recentRSI[0];
95
+ const priceTrend = recentPrices[recentPrices.length - 1] - recentPrices[0];
96
+ // Bullish divergence: price making lower lows, RSI making higher lows
97
+ if (priceTrend < 0 && rsiTrend > 0 && current < 40) {
98
+ divergence = "bullish";
99
+ }
100
+ // Bearish divergence: price making higher highs, RSI making lower highs
101
+ if (priceTrend > 0 && rsiTrend < 0 && current > 60) {
102
+ divergence = "bearish";
103
+ }
104
+ }
105
+ return { values: rsiValues, current, condition, divergence };
106
+ }
107
+ /**
108
+ * Calculate MACD
109
+ */
110
+ function calculateMACD(prices, fastPeriod = 12, slowPeriod = 26, signalPeriod = 9) {
111
+ const ema12 = calculateEMA(prices, fastPeriod);
112
+ const ema26 = calculateEMA(prices, slowPeriod);
113
+ // Align arrays
114
+ const offset = slowPeriod - fastPeriod;
115
+ const macdLine = [];
116
+ for (let i = 0; i < ema26.length; i++) {
117
+ macdLine.push(ema12[i + offset] - ema26[i]);
118
+ }
119
+ const signalLine = calculateEMA(macdLine, signalPeriod);
120
+ // Align MACD and Signal
121
+ const signalOffset = signalPeriod - 1;
122
+ const histogram = [];
123
+ for (let i = 0; i < signalLine.length; i++) {
124
+ histogram.push(macdLine[i + signalOffset] - signalLine[i]);
125
+ }
126
+ const current = {
127
+ macd: macdLine[macdLine.length - 1] || 0,
128
+ signal: signalLine[signalLine.length - 1] || 0,
129
+ histogram: histogram[histogram.length - 1] || 0,
130
+ };
131
+ // Detect crossover
132
+ let crossover = "none";
133
+ if (histogram.length >= 2) {
134
+ const prevHist = histogram[histogram.length - 2];
135
+ const currHist = histogram[histogram.length - 1];
136
+ if (prevHist <= 0 && currHist > 0)
137
+ crossover = "bullish";
138
+ if (prevHist >= 0 && currHist < 0)
139
+ crossover = "bearish";
140
+ }
141
+ // Momentum direction
142
+ let momentum = "neutral";
143
+ if (histogram.length >= 3) {
144
+ const recent = histogram.slice(-3);
145
+ if (recent[2] > recent[1] && recent[1] > recent[0])
146
+ momentum = "increasing";
147
+ if (recent[2] < recent[1] && recent[1] < recent[0])
148
+ momentum = "decreasing";
149
+ }
150
+ return { macdLine, signalLine, histogram, current, crossover, momentum };
151
+ }
152
+ /**
153
+ * Calculate Bollinger Bands
154
+ */
155
+ function calculateBollingerBands(prices, period = 20, stdDev = 2) {
156
+ const middle = calculateSMA(prices, period);
157
+ const upper = [];
158
+ const lower = [];
159
+ for (let i = period - 1; i < prices.length; i++) {
160
+ const slice = prices.slice(i - period + 1, i + 1);
161
+ const mean = middle[i - period + 1];
162
+ // Calculate standard deviation
163
+ const squaredDiffs = slice.map((p) => Math.pow(p - mean, 2));
164
+ const variance = squaredDiffs.reduce((a, b) => a + b, 0) / period;
165
+ const std = Math.sqrt(variance);
166
+ upper.push(mean + stdDev * std);
167
+ lower.push(mean - stdDev * std);
168
+ }
169
+ const currentUpper = upper[upper.length - 1] || 0;
170
+ const currentMiddle = middle[middle.length - 1] || 0;
171
+ const currentLower = lower[lower.length - 1] || 0;
172
+ const currentPrice = prices[prices.length - 1];
173
+ // Bandwidth (volatility measure)
174
+ const bandwidth = ((currentUpper - currentLower) / currentMiddle) * 100;
175
+ // %B (where price is within the bands)
176
+ const percentB = ((currentPrice - currentLower) / (currentUpper - currentLower)) * 100;
177
+ // Squeeze detection (low volatility)
178
+ const squeeze = bandwidth < 5; // Adjust threshold as needed
179
+ // Price position
180
+ let pricePosition;
181
+ if (currentPrice > currentUpper)
182
+ pricePosition = "above_upper";
183
+ else if (currentPrice > currentMiddle)
184
+ pricePosition = "above_middle";
185
+ else if (currentPrice > currentLower)
186
+ pricePosition = "below_middle";
187
+ else
188
+ pricePosition = "below_lower";
189
+ return {
190
+ upper,
191
+ middle,
192
+ lower,
193
+ current: {
194
+ upper: currentUpper,
195
+ middle: currentMiddle,
196
+ lower: currentLower,
197
+ bandwidth,
198
+ percentB,
199
+ },
200
+ squeeze,
201
+ pricePosition,
202
+ };
203
+ }
204
+ /**
205
+ * Calculate ATR
206
+ */
207
+ function calculateATR(candles, period = 14) {
208
+ if (candles.length < period + 1) {
209
+ return { values: [], current: 0, volatility: "medium", percentOfPrice: 0 };
210
+ }
211
+ const trueRanges = [];
212
+ for (let i = 1; i < candles.length; i++) {
213
+ const high = candles[i].high;
214
+ const low = candles[i].low;
215
+ const prevClose = candles[i - 1].close;
216
+ const tr = Math.max(high - low, Math.abs(high - prevClose), Math.abs(low - prevClose));
217
+ trueRanges.push(tr);
218
+ }
219
+ // Calculate ATR using EMA
220
+ const atrValues = [];
221
+ let atr = trueRanges.slice(0, period).reduce((a, b) => a + b, 0) / period;
222
+ atrValues.push(atr);
223
+ for (let i = period; i < trueRanges.length; i++) {
224
+ atr = (atr * (period - 1) + trueRanges[i]) / period;
225
+ atrValues.push(atr);
226
+ }
227
+ const current = atrValues[atrValues.length - 1] || 0;
228
+ const currentPrice = candles[candles.length - 1].close;
229
+ const percentOfPrice = (current / currentPrice) * 100;
230
+ // Volatility classification
231
+ let volatility = "medium";
232
+ if (percentOfPrice < 1.5)
233
+ volatility = "low";
234
+ else if (percentOfPrice > 3)
235
+ volatility = "high";
236
+ return { values: atrValues, current, volatility, percentOfPrice };
237
+ }
238
+ /**
239
+ * Analyze Volume
240
+ */
241
+ function analyzeVolume(candles, period = 20) {
242
+ if (candles.length < period) {
243
+ return {
244
+ averageVolume: 0,
245
+ currentVolume: 0,
246
+ volumeRatio: 1,
247
+ trend: "stable",
248
+ confirmation: true,
249
+ };
250
+ }
251
+ const volumes = candles.map((c) => c.volume);
252
+ const recentVolumes = volumes.slice(-period);
253
+ const averageVolume = recentVolumes.reduce((a, b) => a + b, 0) / period;
254
+ const currentVolume = volumes[volumes.length - 1];
255
+ const volumeRatio = currentVolume / averageVolume;
256
+ // Volume trend
257
+ const recentVols = volumes.slice(-5);
258
+ const oldVols = volumes.slice(-10, -5);
259
+ const recentAvg = recentVols.reduce((a, b) => a + b, 0) / 5;
260
+ const oldAvg = oldVols.reduce((a, b) => a + b, 0) / 5;
261
+ let trend = "stable";
262
+ if (recentAvg > oldAvg * 1.2)
263
+ trend = "increasing";
264
+ else if (recentAvg < oldAvg * 0.8)
265
+ trend = "decreasing";
266
+ // Check if volume confirms price movement
267
+ const priceChange = candles[candles.length - 1].close - candles[candles.length - 2].close;
268
+ const confirmation = (priceChange > 0 && volumeRatio > 1) || (priceChange < 0 && volumeRatio > 1);
269
+ return { averageVolume, currentVolume, volumeRatio, trend, confirmation };
270
+ }
271
+ /**
272
+ * Calculate Support and Resistance levels
273
+ */
274
+ function calculateSupportResistance(candles, lookback = 50) {
275
+ const prices = candles.slice(-lookback);
276
+ const currentPrice = candles[candles.length - 1].close;
277
+ // Find pivot points (local highs and lows)
278
+ const pivotHighs = [];
279
+ const pivotLows = [];
280
+ for (let i = 2; i < prices.length - 2; i++) {
281
+ const high = prices[i].high;
282
+ const low = prices[i].low;
283
+ // Pivot high
284
+ if (high > prices[i - 1].high &&
285
+ high > prices[i - 2].high &&
286
+ high > prices[i + 1].high &&
287
+ high > prices[i + 2].high) {
288
+ pivotHighs.push(high);
289
+ }
290
+ // Pivot low
291
+ if (low < prices[i - 1].low &&
292
+ low < prices[i - 2].low &&
293
+ low < prices[i + 1].low &&
294
+ low < prices[i + 2].low) {
295
+ pivotLows.push(low);
296
+ }
297
+ }
298
+ // Cluster nearby levels (within 1%)
299
+ const clusterLevels = (levels) => {
300
+ if (levels.length === 0)
301
+ return [];
302
+ levels.sort((a, b) => a - b);
303
+ const clustered = [];
304
+ let cluster = [levels[0]];
305
+ for (let i = 1; i < levels.length; i++) {
306
+ if ((levels[i] - levels[i - 1]) / levels[i - 1] < 0.01) {
307
+ cluster.push(levels[i]);
308
+ }
309
+ else {
310
+ clustered.push(cluster.reduce((a, b) => a + b, 0) / cluster.length);
311
+ cluster = [levels[i]];
312
+ }
313
+ }
314
+ clustered.push(cluster.reduce((a, b) => a + b, 0) / cluster.length);
315
+ return clustered;
316
+ };
317
+ const resistances = clusterLevels(pivotHighs).filter((r) => r > currentPrice);
318
+ const supports = clusterLevels(pivotLows).filter((s) => s < currentPrice);
319
+ const nearestResistance = resistances.length > 0 ? Math.min(...resistances) : currentPrice * 1.05;
320
+ const nearestSupport = supports.length > 0 ? Math.max(...supports) : currentPrice * 0.95;
321
+ const distanceToResistance = ((nearestResistance - currentPrice) / currentPrice) * 100;
322
+ const distanceToSupport = ((currentPrice - nearestSupport) / currentPrice) * 100;
323
+ return {
324
+ supports: supports.slice(-3),
325
+ resistances: resistances.slice(0, 3),
326
+ nearestSupport,
327
+ nearestResistance,
328
+ distanceToSupport,
329
+ distanceToResistance,
330
+ };
331
+ }
332
+ /**
333
+ * Analyze trend structure
334
+ */
335
+ function analyzeTrend(candles) {
336
+ if (candles.length < 20) {
337
+ return {
338
+ direction: "sideways",
339
+ strength: 50,
340
+ phase: "consolidating",
341
+ higherHighs: false,
342
+ higherLows: false,
343
+ };
344
+ }
345
+ const recent = candles.slice(-20);
346
+ const highs = recent.map((c) => c.high);
347
+ const lows = recent.map((c) => c.low);
348
+ // Check for higher highs and higher lows
349
+ let hhCount = 0;
350
+ let hlCount = 0;
351
+ let lhCount = 0;
352
+ let llCount = 0;
353
+ for (let i = 5; i < highs.length; i += 5) {
354
+ const prevHigh = Math.max(...highs.slice(i - 5, i));
355
+ const currHigh = Math.max(...highs.slice(i, Math.min(i + 5, highs.length)));
356
+ const prevLow = Math.min(...lows.slice(i - 5, i));
357
+ const currLow = Math.min(...lows.slice(i, Math.min(i + 5, lows.length)));
358
+ if (currHigh > prevHigh)
359
+ hhCount++;
360
+ else
361
+ lhCount++;
362
+ if (currLow > prevLow)
363
+ hlCount++;
364
+ else
365
+ llCount++;
366
+ }
367
+ const higherHighs = hhCount > lhCount;
368
+ const higherLows = hlCount > llCount;
369
+ // Determine direction
370
+ let direction = "sideways";
371
+ if (higherHighs && higherLows)
372
+ direction = "bullish";
373
+ else if (!higherHighs && !higherLows)
374
+ direction = "bearish";
375
+ // Calculate strength
376
+ const totalSwings = hhCount + lhCount + hlCount + llCount;
377
+ const trendingSwings = direction === "bullish" ? hhCount + hlCount : lhCount + llCount;
378
+ const strength = totalSwings > 0 ? Math.round((trendingSwings / totalSwings) * 100) : 50;
379
+ // Determine phase
380
+ const prices = candles.slice(-10).map((c) => c.close);
381
+ const priceRange = (Math.max(...prices) - Math.min(...prices)) / prices[0];
382
+ let phase = "trending";
383
+ if (priceRange < 0.02)
384
+ phase = "consolidating";
385
+ else if ((direction === "bullish" && prices[prices.length - 1] < prices[0]) ||
386
+ (direction === "bearish" && prices[prices.length - 1] > prices[0])) {
387
+ phase = "reversing";
388
+ }
389
+ return { direction, strength, phase, higherHighs, higherLows };
390
+ }