import 'package:equatable/equatable.dart'; class CandleModel extends Equatable { final DateTime timestamp; final double open; final double high; final double low; final double close; final double volume; const CandleModel({ required this.timestamp, required this.open, required this.high, required this.low, required this.close, required this.volume, }); factory CandleModel.fromJson(Map json) { return CandleModel( timestamp: DateTime.tryParse(json['timestamp']?.toString() ?? '') ?? DateTime.now(), open: (json['open'] as num?)?.toDouble() ?? 0.0, high: (json['high'] as num?)?.toDouble() ?? 0.0, low: (json['low'] as num?)?.toDouble() ?? 0.0, close: (json['close'] as num?)?.toDouble() ?? 0.0, volume: (json['volume'] as num?)?.toDouble() ?? 0.0, ); } @override List get props => [timestamp, open, high, low, close, volume]; } class IndicatorModel extends Equatable { final DateTime timestamp; final double? ema20; final double? sma50; final double? sma200; final double? rsi14; final double? macdLine; final double? macdSignal; final double? macdHistogram; final double? atr14; final double? vwap; final double? supertrendUpper; final double? supertrendLower; final String? supertrendDirection; final double? recommendedStopLoss; const IndicatorModel({ required this.timestamp, this.ema20, this.sma50, this.sma200, this.rsi14, this.macdLine, this.macdSignal, this.macdHistogram, this.atr14, this.vwap, this.supertrendUpper, this.supertrendLower, this.supertrendDirection, this.recommendedStopLoss, }); factory IndicatorModel.fromJson(Map json) { return IndicatorModel( timestamp: DateTime.tryParse(json['timestamp']?.toString() ?? '') ?? DateTime.now(), ema20: (json['ema20'] as num?)?.toDouble(), sma50: (json['sma50'] as num?)?.toDouble(), sma200: (json['sma200'] as num?)?.toDouble(), rsi14: (json['rsi14'] as num?)?.toDouble(), macdLine: (json['macdLine'] as num?)?.toDouble(), macdSignal: (json['macdSignal'] as num?)?.toDouble(), macdHistogram: (json['macdHistogram'] as num?)?.toDouble(), atr14: (json['atr14'] as num?)?.toDouble(), vwap: (json['vwap'] as num?)?.toDouble(), supertrendUpper: (json['supertrendUpper'] as num?)?.toDouble(), supertrendLower: (json['supertrendLower'] as num?)?.toDouble(), supertrendDirection: json['supertrendDirection']?.toString(), recommendedStopLoss: (json['recommendedStopLoss'] as num?)?.toDouble(), ); } @override List get props => [ timestamp, ema20, sma50, sma200, rsi14, macdLine, macdSignal, macdHistogram, atr14, vwap, supertrendUpper, supertrendLower, supertrendDirection, recommendedStopLoss ]; } class StrategySignalModel extends Equatable { final String title; final DateTime date; final double price; final String type; // BUY or SELL const StrategySignalModel({ required this.title, required this.date, required this.price, required this.type, }); factory StrategySignalModel.fromJson(Map json) { final rawDir = (json['direction'] ?? json['signalType'] ?? json['type'])?.toString().toUpperCase() ?? 'BUY'; final sigDir = (rawDir == 'BUY' || rawDir == 'SELL') ? rawDir : 'BUY'; final sigTitle = (json['title'] ?? json['type'] ?? json['description'])?.toString() ?? 'Signal'; final dateStr = (json['timestamp'] ?? json['date'] ?? json['time'])?.toString(); return StrategySignalModel( title: sigTitle, date: dateStr != null ? (DateTime.tryParse(dateStr) ?? DateTime.now()) : DateTime.now(), price: (json['price'] as num?)?.toDouble() ?? 0.0, type: sigDir, ); } @override List get props => [title, date, price, type]; } class PatternPoint extends Equatable { final DateTime time; final double price; const PatternPoint(this.time, this.price); factory PatternPoint.fromJson(Map json) => PatternPoint(DateTime.tryParse(json['time']?.toString() ?? '') ?? DateTime.now(), (json['price'] as num?)?.toDouble() ?? 0.0); @override List get props => [time, price]; } class BreakoutSignalModel extends Equatable { final String direction; // "UP", "DOWN" final double targetPrice; final double potentialPercent; const BreakoutSignalModel({ required this.direction, required this.targetPrice, required this.potentialPercent, }); factory BreakoutSignalModel.fromJson(Map json) { return BreakoutSignalModel( direction: (json['direction'] ?? json['Direction'])?.toString() ?? 'UP', targetPrice: (json['targetPrice'] ?? json['TargetPrice'] as num?)?.toDouble() ?? 0.0, potentialPercent: (json['potentialPercent'] ?? json['PotentialPercent'] as num?)?.toDouble() ?? 0.0, ); } @override List get props => [direction, targetPrice, potentialPercent]; } class ChartPatternModel extends Equatable { final String type; final String description; final double confidencePercent; final BreakoutSignalModel? breakoutSignal; final List upperLine; final List lowerLine; const ChartPatternModel({ required this.type, this.description = '', this.confidencePercent = 0.0, this.breakoutSignal, required this.upperLine, required this.lowerLine, }); factory ChartPatternModel.fromJson(Map json) { BreakoutSignalModel? breakout; final bJson = json['breakoutSignal'] ?? json['BreakoutSignal']; if (bJson != null && bJson is Map) { breakout = BreakoutSignalModel.fromJson(bJson); } return ChartPatternModel( type: json['type']?.toString() ?? json['Type']?.toString() ?? 'Pattern', description: json['description']?.toString() ?? json['Description']?.toString() ?? '', confidencePercent: (json['confidencePercent'] ?? json['ConfidencePercent'] as num?)?.toDouble() ?? 0.0, breakoutSignal: breakout, upperLine: (json['upperLine'] as List? ?? []).map((e) => PatternPoint.fromJson(e as Map)).toList(), lowerLine: (json['lowerLine'] as List? ?? []).map((e) => PatternPoint.fromJson(e as Map)).toList(), ); } @override List get props => [type, description, confidencePercent, breakoutSignal, upperLine, lowerLine]; } class TechnicalAnalysisModel extends Equatable { final String symbol; final String currency; final double? currentPrice; final String trend; final String rsi; final String macd; final String overallSignal; final String sma50; final String sma200; final double? vix; final String? sp500Trend; final double? dxy; final double? stopLossAtr; final List candles; final List indicators; final List patterns; final List signals; const TechnicalAnalysisModel({ required this.symbol, this.currency = 'EUR', this.currentPrice, required this.trend, required this.rsi, required this.macd, required this.overallSignal, required this.sma50, required this.sma200, this.vix, this.sp500Trend, this.dxy, this.stopLossAtr, this.candles = const [], this.indicators = const [], this.patterns = const [], this.signals = const [], }); factory TechnicalAnalysisModel.fromJson(Map json) { var rawCandles = json['candles'] as List? ?? []; var candlesList = rawCandles.map((c) => CandleModel.fromJson(c as Map)).toList(); var rawIndicators = json['indicators'] as List? ?? []; var indicatorsList = rawIndicators.map((i) => IndicatorModel.fromJson(i as Map)).toList(); var rawSignals = json['signals'] as List? ?? []; var signalsList = rawSignals.map((s) => StrategySignalModel.fromJson(s as Map)).toList(); var rawPatterns = json['patterns'] as List? ?? []; var patternsList = rawPatterns.map((p) => ChartPatternModel.fromJson(p as Map)).toList(); final lastInd = indicatorsList.isNotEmpty ? indicatorsList.last : null; final regime = json['marketRegime'] as Map?; String parsedTrend = lastInd?.supertrendDirection ?? 'Neutral'; if (parsedTrend.toUpperCase() == 'BUY') parsedTrend = 'Bullisch ▲'; if (parsedTrend.toUpperCase() == 'SELL') parsedTrend = 'Bearisch ▼'; String parsedSignal = 'HOLD'; if (signalsList.isNotEmpty) { parsedSignal = signalsList.last.type.toUpperCase(); } return TechnicalAnalysisModel( symbol: json['symbol']?.toString() ?? '', currency: json['currency']?.toString() ?? 'EUR', currentPrice: (json['currentPrice'] as num?)?.toDouble(), trend: parsedTrend, rsi: lastInd?.rsi14?.toStringAsFixed(1) ?? 'N/A', macd: lastInd?.macdHistogram?.toStringAsFixed(2) ?? lastInd?.macdLine?.toStringAsFixed(2) ?? 'N/A', overallSignal: parsedSignal, sma50: lastInd?.sma50?.toStringAsFixed(2) ?? 'N/A', sma200: lastInd?.sma200?.toStringAsFixed(2) ?? 'N/A', vix: (regime?['vixValue'] as num?)?.toDouble(), sp500Trend: regime?['marketTrend']?.toString(), dxy: (regime?['dxyValue'] as num?)?.toDouble(), stopLossAtr: lastInd?.recommendedStopLoss, candles: candlesList, indicators: indicatorsList, patterns: patternsList, signals: signalsList, ); } Map toJson() { return { 'symbol': symbol, 'currency': currency, 'trend': trend, 'rsi': rsi, 'macd': macd, 'overallSignal': overallSignal, 'sma50': sma50, 'sma200': sma200, 'vix': vix, 'sp500Trend': sp500Trend, 'dxy': dxy, 'stopLossAtr': stopLossAtr, }; } @override List get props => [ symbol, currency, trend, rsi, macd, overallSignal, sma50, sma200, vix, sp500Trend, dxy, stopLossAtr, candles, indicators, patterns, signals ]; }