""" Trading Signal Generator Kombiniert MACD-Indikatoren und News-Sentiment fΓΌr Trading-Empfehlungen """ from typing import Dict, Tuple from enum import Enum from dataclasses import dataclass from datetime import datetime from macd_indicator import MACDIndicator, MACDSignal from news_sentiment import NewsSentimentAnalyzer, SentimentScore class TradingAction(Enum): """Trading-Empfehlungs-Typen""" STRONG_BUY = "🟒 STARKER KAUF" BUY = "🟒 KAUF" HOLD = "🟑 HALTEN" SELL = "πŸ”΄ VERKAUF" STRONG_SELL = "πŸ”΄ STARKER VERKAUF" @dataclass class TradingSignal: """Datenklasse fΓΌr Trading-Signale""" action: TradingAction confidence: int # 0-100 price: float timestamp: datetime macd_signal: MACDSignal sentiment: SentimentScore reasons: list technical_details: dict sentiment_details: dict def __str__(self): return f""" ╔══════════════════════════════════════════════════════════════════╗ β•‘ BITCOIN TRADING SIGNAL - {self.timestamp.strftime('%Y-%m-%d %H:%M')} β•‘ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• πŸ“Š EMPFEHLUNG: {self.action.value} πŸ’― KONFIDENZ: {self.confidence}% πŸ’° AKTUELLER PREIS: ${self.price:,.2f} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ πŸ“ˆ TECHNISCHE ANALYSE (MACD) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Signal: {self.macd_signal.value} MACD: {self.technical_details.get('macd', 0):.2f} Signal Line: {self.technical_details.get('signal', 0):.2f} Histogram: {self.technical_details.get('histogram', 0):.2f} Preis-Γ„nderung (10 Tage): {self.technical_details.get('price_change_10d', 0):.2f}% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ πŸ“° SENTIMENT-ANALYSE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Markt-Sentiment: {self.sentiment.value} Sentiment-Score: {self.sentiment_details.get('average_sentiment', 0):.3f} Analysierte Artikel: {self.sentiment_details.get('articles_analyzed', 0)} β”œβ”€ Positiv: {self.sentiment_details.get('positive_articles', 0)} β”œβ”€ Neutral: {self.sentiment_details.get('neutral_articles', 0)} └─ Negativ: {self.sentiment_details.get('negative_articles', 0)} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ πŸ“‹ BEGRÜNDUNG ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ """ def format_reasons(self): """Formatiert die GrΓΌnde""" output = "" for i, reason in enumerate(self.reasons, 1): output += f" {i}. {reason}\n" return output class SignalGenerator: """ Generiert Trading-Signale durch Kombination von MACD und Sentiment """ def __init__(self, newsapi_key: str = None): """ Args: newsapi_key: Optional NewsAPI-SchlΓΌssel fΓΌr erweiterte News """ self.macd_indicator = MACDIndicator() self.sentiment_analyzer = NewsSentimentAnalyzer(api_key=newsapi_key) # Gewichtungen fΓΌr Signal-Berechnung self.macd_weight = 0.6 # 60% Gewichtung fΓΌr technische Analyse self.sentiment_weight = 0.4 # 40% Gewichtung fΓΌr Sentiment def calculate_combined_score(self, macd_signal: MACDSignal, sentiment: SentimentScore, macd_confidence: int, sentiment_confidence: int) -> Tuple[float, int]: """ Berechnet kombinierten Score aus MACD und Sentiment Returns: Tuple aus (Score -1 bis +1, Gesamt-Konfidenz) """ # Konvertiere Signale zu Scores (-1 bis +1) macd_score_map = { MACDSignal.STRONG_BUY: 1.0, MACDSignal.BUY: 0.5, MACDSignal.NEUTRAL: 0.0, MACDSignal.SELL: -0.5, MACDSignal.STRONG_SELL: -1.0 } sentiment_score_map = { SentimentScore.VERY_POSITIVE: 1.0, SentimentScore.POSITIVE: 0.5, SentimentScore.NEUTRAL: 0.0, SentimentScore.NEGATIVE: -0.5, SentimentScore.VERY_NEGATIVE: -1.0 } macd_score = macd_score_map.get(macd_signal, 0) sentiment_score = sentiment_score_map.get(sentiment, 0) # Gewichteter kombinierter Score combined_score = (macd_score * self.macd_weight + sentiment_score * self.sentiment_weight) # Gewichtete kombinierte Konfidenz combined_confidence = int( macd_confidence * self.macd_weight + sentiment_confidence * self.sentiment_weight ) # Bonus fΓΌr ΓΌbereinstimmende Signale if (macd_score > 0 and sentiment_score > 0) or \ (macd_score < 0 and sentiment_score < 0): combined_confidence = min(100, combined_confidence + 10) return combined_score, combined_confidence def generate_signal(self, price_df, current_price: float) -> TradingSignal: """ Generiert Trading-Signal Args: price_df: DataFrame mit historischen Preisen current_price: Aktueller Bitcoin-Preis Returns: TradingSignal mit Empfehlung """ # Hole MACD-Signal macd_signal, macd_details = self.macd_indicator.get_signal(price_df) # Hole Sentiment sentiment, sentiment_details = self.sentiment_analyzer.analyze_news_sentiment( days=1, limit=30 ) # Berechne kombinierten Score combined_score, confidence = self.calculate_combined_score( macd_signal, sentiment, macd_details['confidence'], sentiment_details['confidence'] ) # Bestimme Trading-Action if combined_score >= 0.6: action = TradingAction.STRONG_BUY elif combined_score >= 0.2: action = TradingAction.BUY elif combined_score <= -0.6: action = TradingAction.STRONG_SELL elif combined_score <= -0.2: action = TradingAction.SELL else: action = TradingAction.HOLD # Sammle GrΓΌnde reasons = [] # MACD-GrΓΌnde reasons.append(f"MACD-Signal: {macd_signal.value} (Konfidenz: {macd_details['confidence']}%)") reasons.extend(macd_details.get('reasons', [])) # Sentiment-GrΓΌnde reasons.append( f"Markt-Sentiment: {sentiment.value} " f"(Konfidenz: {sentiment_details['confidence']}%, " f"Score: {sentiment_details['average_sentiment']:.3f})" ) # ZusΓ€tzliche Hinweise if macd_signal in [MACDSignal.STRONG_BUY, MACDSignal.BUY] and \ sentiment in [SentimentScore.VERY_POSITIVE, SentimentScore.POSITIVE]: reasons.append("βœ… MACD und Sentiment stimmen ΓΌberein β†’ Starkes Signal") elif macd_signal in [MACDSignal.STRONG_SELL, MACDSignal.SELL] and \ sentiment in [SentimentScore.VERY_NEGATIVE, SentimentScore.NEGATIVE]: reasons.append("βœ… MACD und Sentiment stimmen ΓΌberein β†’ Starkes Signal") elif (macd_signal in [MACDSignal.STRONG_BUY, MACDSignal.BUY] and sentiment in [SentimentScore.NEGATIVE, SentimentScore.VERY_NEGATIVE]) or \ (macd_signal in [MACDSignal.STRONG_SELL, MACDSignal.SELL] and sentiment in [SentimentScore.POSITIVE, SentimentScore.VERY_POSITIVE]): reasons.append("⚠️ MACD und Sentiment widersprechen sich β†’ Vorsicht geboten") confidence = max(30, confidence - 20) # Reduziere Konfidenz # Erstelle Trading-Signal signal = TradingSignal( action=action, confidence=confidence, price=current_price, timestamp=datetime.now(), macd_signal=macd_signal, sentiment=sentiment, reasons=reasons, technical_details=macd_details, sentiment_details=sentiment_details ) return signal def get_recommendation_text(self, signal: TradingSignal) -> str: """ Generiert Empfehlungstext Args: signal: Trading-Signal Returns: Formatierter Empfehlungstext """ recommendation = str(signal) recommendation += signal.format_reasons() # FΓΌge Handlungsempfehlung hinzu recommendation += "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" recommendation += "πŸ’‘ HANDLUNGSEMPFEHLUNG\n" recommendation += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" if signal.action == TradingAction.STRONG_BUY: recommendation += " 🟒 STARKE KAUFGELEGENHEIT\n" recommendation += " β†’ ErwΓ€ge einen Einstieg oder Aufstockung der Position\n" recommendation += " β†’ Setze Stop-Loss ca. 5-7% unter Einstiegspreis\n" elif signal.action == TradingAction.BUY: recommendation += " 🟒 KAUFGELEGENHEIT\n" recommendation += " β†’ ErwΓ€ge einen Einstieg mit kleiner Position\n" recommendation += " β†’ Warte ggf. auf BestΓ€tigung durch weitere Signale\n" elif signal.action == TradingAction.HOLD: recommendation += " 🟑 ABWARTEN\n" recommendation += " β†’ Keine klare Richtung erkennbar\n" recommendation += " β†’ Behalte den Markt im Auge fΓΌr klarere Signale\n" elif signal.action == TradingAction.SELL: recommendation += " πŸ”΄ VERKAUFSSIGNAL\n" recommendation += " β†’ ErwΓ€ge Teilverkauf oder Gewinnmitnahme\n" recommendation += " β†’ Ziehe Stop-Loss nach, um Gewinne zu sichern\n" elif signal.action == TradingAction.STRONG_SELL: recommendation += " πŸ”΄ STARKES VERKAUFSSIGNAL\n" recommendation += " β†’ ErwΓ€ge Ausstieg aus Position\n" recommendation += " β†’ Sichere Gewinne oder begrenze Verluste\n" recommendation += f"\n ⚠️ Risiko-Hinweis: Diese Analyse hat eine Konfidenz von {signal.confidence}%\n" recommendation += " ⚠️ Keine Anlageberatung - Trading auf eigenes Risiko!\n" recommendation += "\nβ•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n" return recommendation if __name__ == "__main__": # Test from data_fetcher import BitcoinDataFetcher print("=== Bitcoin Trading Signal Generator ===\n") print("Lade Daten...\n") # Hole Preisdaten fetcher = BitcoinDataFetcher() price_df = fetcher.get_historical_data(days=30) current_price = fetcher.get_current_price() if price_df.empty or not current_price: print("Fehler beim Laden der Daten!") else: # Generiere Signal generator = SignalGenerator() signal = generator.generate_signal(price_df, current_price) # Zeige Empfehlung recommendation = generator.get_recommendation_text(signal) print(recommendation)