import logger from '../utils/logger.js'; export enum SignalType { BUY = 'BUY', SELL = 'SELL', NONE = 'NONE' } export interface StrategyResult { signal: SignalType; ema: number; rsi: number; changed: boolean; } export class DirectionTracker { private lastSignal: SignalType = SignalType.NONE; /** * Advanced Strategy: EMA + RSI * BUY: Price > EMA-20 AND RSI < 70 (not overbought) * SELL: Price < EMA-20 AND RSI > 30 (not oversold) * * Includes a state machine to prevent duplicate signals. */ public calculateDirection(candles: any[]): StrategyResult { // Need enough data for RSI (14) and EMA (20) if (candles.length < 20) { return { signal: SignalType.NONE, ema: 0, rsi: 0, changed: false }; } const latestCandle = candles[candles.length - 1]; const latestClose = latestCandle.close; // 1. Calculate EMA-20 const ema20 = this.calculateEMA(candles.map(c => c.close), 20); // 2. Calculate RSI-14 const rsi14 = this.calculateRSI(candles.map(c => c.close), 14); // 3. Signal Logic let currentSignal = SignalType.NONE; if (latestClose > ema20 && rsi14 < 70) { currentSignal = SignalType.BUY; } else if (latestClose < ema20 && rsi14 > 30) { currentSignal = SignalType.SELL; } // 4. State Machine (Check if signal changed to avoid duplicates) const changed = currentSignal !== SignalType.NONE && currentSignal !== this.lastSignal; if (changed) { logger.info(`🚨 New Strategy Signal: ${currentSignal} (EMA: ${ema20.toFixed(2)}, RSI: ${rsi14.toFixed(2)})`); this.lastSignal = currentSignal; } return { signal: currentSignal, ema: ema20, rsi: rsi14, changed }; } private calculateEMA(data: number[], period: number): number { if (data.length === 0) return 0; const k = 2 / (period + 1); let ema = data[0] || 0; for (let i = 1; i < data.length; i++) { const current = data[i] || 0; ema = current * k + ema * (1 - k); } return ema; } private calculateRSI(data: number[], period: number): number { if (data.length <= period) return 50; // Default if not enough data let gains = 0; let losses = 0; // First average for (let i = 1; i <= period; i++) { const diff = data[i] - data[i - 1]; if (diff >= 0) gains += diff; else losses -= diff; } let avgGain = gains / period; let avgLoss = losses / period; // Smoothing for (let i = period + 1; i < data.length; i++) { const diff = data[i] - data[i - 1]; if (diff >= 0) { avgGain = (avgGain * (period - 1) + diff) / period; avgLoss = (avgLoss * (period - 1)) / period; } else { avgGain = (avgGain * (period - 1)) / period; avgLoss = (avgLoss * (period - 1) - diff) / period; } } if (avgLoss === 0) return 100; const rs = avgGain / avgLoss; return 100 - (100 / (1 + rs)); } }