using TradingBot.Models; using System.Text.Json; namespace TradingBot.Services; /// /// Service for managing trading indicators configuration and signals /// public class IndicatorsService { private readonly Dictionary _indicators = new(); private readonly Dictionary> _indicatorStatus = new(); private readonly List _recentSignals = new(); private readonly string _configPath; private const int MaxSignals = 100; public event Action? OnIndicatorsChanged; public event Action? OnSignalGenerated; public IndicatorsService() { _configPath = Path.Combine(Directory.GetCurrentDirectory(), "data", "indicators-config.json"); InitializeDefaultIndicators(); LoadConfiguration(); } private void InitializeDefaultIndicators() { _indicators["rsi"] = new IndicatorConfig { Id = "rsi", Name = "RSI", Description = "Relative Strength Index - Misura la forza del trend", Type = IndicatorType.RSI, IsEnabled = true, Period = 14, OverboughtThreshold = 70, OversoldThreshold = 30 }; _indicators["macd"] = new IndicatorConfig { Id = "macd", Name = "MACD", Description = "Moving Average Convergence Divergence - Identifica cambi di trend", Type = IndicatorType.MACD, IsEnabled = true, FastPeriod = 12, SlowPeriod = 26, SignalPeriod = 9 }; _indicators["sma_20"] = new IndicatorConfig { Id = "sma_20", Name = "SMA 20", Description = "Simple Moving Average 20 periodi - Trend a breve termine", Type = IndicatorType.SMA, IsEnabled = true, Period = 20 }; _indicators["sma_50"] = new IndicatorConfig { Id = "sma_50", Name = "SMA 50", Description = "Simple Moving Average 50 periodi - Trend a medio termine", Type = IndicatorType.SMA, IsEnabled = true, Period = 50 }; _indicators["ema_12"] = new IndicatorConfig { Id = "ema_12", Name = "EMA 12", Description = "Exponential Moving Average 12 periodi - Reattivo ai cambiamenti", Type = IndicatorType.EMA, IsEnabled = true, Period = 12 }; _indicators["bollinger"] = new IndicatorConfig { Id = "bollinger", Name = "Bollinger Bands", Description = "Bande di Bollinger - Misura volatilità e livelli estremi", Type = IndicatorType.BollingerBands, IsEnabled = true, Period = 20 }; _indicators["stochastic"] = new IndicatorConfig { Id = "stochastic", Name = "Stochastic", Description = "Oscillatore Stocastico - Identifica momenti di inversione", Type = IndicatorType.Stochastic, IsEnabled = false, Period = 14, OverboughtThreshold = 80, OversoldThreshold = 20 }; } /// /// Get all indicator configurations /// public IReadOnlyDictionary GetIndicators() { return _indicators; } /// /// Get enabled indicators only /// public IEnumerable GetEnabledIndicators() { return _indicators.Values.Where(i => i.IsEnabled); } /// /// Update indicator configuration /// public void UpdateIndicator(string id, IndicatorConfig config) { _indicators[id] = config; SaveConfiguration(); OnIndicatorsChanged?.Invoke(); } /// /// Toggle indicator on/off /// public void ToggleIndicator(string id, bool enabled) { if (_indicators.TryGetValue(id, out var indicator)) { indicator.IsEnabled = enabled; SaveConfiguration(); OnIndicatorsChanged?.Invoke(); } } /// /// Update indicator status for a symbol /// public void UpdateIndicatorStatus(string indicatorId, string symbol, IndicatorStatus status) { if (!_indicatorStatus.ContainsKey(symbol)) { _indicatorStatus[symbol] = new Dictionary(); } _indicatorStatus[symbol][indicatorId] = status; } /// /// Get indicator status for a symbol /// public IndicatorStatus? GetIndicatorStatus(string indicatorId, string symbol) { if (_indicatorStatus.TryGetValue(symbol, out var symbolIndicators)) { symbolIndicators.TryGetValue(indicatorId, out var status); return status; } return null; } /// /// Get all indicator statuses for a symbol /// public IEnumerable GetSymbolIndicators(string symbol) { if (_indicatorStatus.TryGetValue(symbol, out var symbolIndicators)) { return symbolIndicators.Values; } return Enumerable.Empty(); } /// /// Generate and record a signal /// public void GenerateSignal(IndicatorSignal signal) { _recentSignals.Insert(0, signal); // Maintain max size while (_recentSignals.Count > MaxSignals) { _recentSignals.RemoveAt(_recentSignals.Count - 1); } OnSignalGenerated?.Invoke(signal); } /// /// Get recent signals /// public IReadOnlyList GetRecentSignals(int count = 20) { return _recentSignals.Take(count).ToList().AsReadOnly(); } /// /// Get signals for a specific symbol /// public IReadOnlyList GetSymbolSignals(string symbol, int count = 20) { return _recentSignals .Where(s => s.Symbol.Equals(symbol, StringComparison.OrdinalIgnoreCase)) .Take(count) .ToList() .AsReadOnly(); } /// /// Analyze indicators and generate trading recommendation /// public TradingRecommendation AnalyzeIndicators(string symbol) { var recommendation = new TradingRecommendation { Symbol = symbol, Timestamp = DateTime.UtcNow }; var symbolIndicators = GetSymbolIndicators(symbol).ToList(); if (!symbolIndicators.Any()) { recommendation.Action = "HOLD"; recommendation.Confidence = 0; recommendation.Reason = "Indicatori non disponibili"; return recommendation; } int buySignals = 0; int sellSignals = 0; int totalEnabled = GetEnabledIndicators().Count(); foreach (var status in symbolIndicators) { if (!_indicators.TryGetValue(status.IndicatorId, out var config) || !config.IsEnabled) continue; switch (status.Condition) { case MarketCondition.Oversold: case MarketCondition.Bullish: buySignals++; recommendation.SupportingIndicators.Add($"{config.Name}: {status.Recommendation}"); break; case MarketCondition.Overbought: case MarketCondition.Bearish: sellSignals++; recommendation.SupportingIndicators.Add($"{config.Name}: {status.Recommendation}"); break; } } // Determine action based on signals if (buySignals > sellSignals && buySignals >= totalEnabled * 0.6m) { recommendation.Action = "BUY"; recommendation.Confidence = (decimal)buySignals / totalEnabled * 100; recommendation.Reason = $"{buySignals}/{totalEnabled} indicatori suggeriscono acquisto"; } else if (sellSignals > buySignals && sellSignals >= totalEnabled * 0.6m) { recommendation.Action = "SELL"; recommendation.Confidence = (decimal)sellSignals / totalEnabled * 100; recommendation.Reason = $"{sellSignals}/{totalEnabled} indicatori suggeriscono vendita"; } else { recommendation.Action = "HOLD"; recommendation.Confidence = 50; recommendation.Reason = "Segnali contrastanti - attendere conferma"; } return recommendation; } private void SaveConfiguration() { try { var directory = Path.GetDirectoryName(_configPath); if (directory != null && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } var json = JsonSerializer.Serialize(_indicators, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllText(_configPath, json); } catch (Exception ex) { Console.WriteLine($"Error saving indicators configuration: {ex.Message}"); } } private void LoadConfiguration() { try { if (File.Exists(_configPath)) { var json = File.ReadAllText(_configPath); var loaded = JsonSerializer.Deserialize>(json); if (loaded != null) { foreach (var kvp in loaded) { _indicators[kvp.Key] = kvp.Value; } } } } catch (Exception ex) { Console.WriteLine($"Error loading indicators configuration: {ex.Message}"); } } } /// /// Trading recommendation based on multiple indicators /// public class TradingRecommendation { public string Symbol { get; set; } = string.Empty; public DateTime Timestamp { get; set; } public string Action { get; set; } = "HOLD"; // BUY, SELL, HOLD public decimal Confidence { get; set; } public string Reason { get; set; } = string.Empty; public List SupportingIndicators { get; set; } = new(); }