336 lines
12 KiB
Python
336 lines
12 KiB
Python
"""
|
|
Visualisierungs-Modul für Gold-Markt-Analysen
|
|
Erstellt Charts und Grafiken aus den gesammelten Daten
|
|
"""
|
|
import matplotlib.pyplot as plt
|
|
import matplotlib.dates as mdates
|
|
import pandas as pd
|
|
import numpy as np
|
|
from datetime import datetime, timedelta
|
|
from typing import Dict, List, Any, Optional
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Matplotlib Style
|
|
plt.style.use('seaborn-v0_8-darkgrid')
|
|
|
|
|
|
class GoldMarketVisualizer:
|
|
"""
|
|
Erstellt Visualisierungen für Gold-Markt-Daten
|
|
"""
|
|
|
|
def __init__(self):
|
|
"""
|
|
Initialisiert den Visualizer
|
|
"""
|
|
self.colors = {
|
|
'price': '#FFD700', # Gold
|
|
'volume': '#4CAF50', # Grün
|
|
'bullish': '#26A69A', # Teal
|
|
'bearish': '#EF5350', # Rot
|
|
'neutral': '#90A4AE', # Grau
|
|
'indicator': '#2196F3', # Blau
|
|
}
|
|
|
|
def plot_price_with_indicators(
|
|
self,
|
|
df: pd.DataFrame,
|
|
indicators: List[str] = None,
|
|
save_path: str = "price_chart.png"
|
|
):
|
|
"""
|
|
Erstellt einen Chart mit Preis und Indikatoren
|
|
|
|
Args:
|
|
df: DataFrame mit OHLCV und Indikatoren
|
|
indicators: Liste von Indikatoren zum Plotten
|
|
save_path: Speicherpfad
|
|
"""
|
|
if indicators is None:
|
|
indicators = ['SMA_20', 'SMA_50', 'EMA_12']
|
|
|
|
fig, axes = plt.subplots(3, 1, figsize=(15, 10), sharex=True)
|
|
|
|
# Subplot 1: Preis mit Moving Averages
|
|
ax1 = axes[0]
|
|
ax1.plot(df.index, df['Close'], label='Preis', color=self.colors['price'], linewidth=2)
|
|
|
|
for indicator in indicators:
|
|
if indicator in df.columns:
|
|
ax1.plot(df.index, df[indicator], label=indicator, alpha=0.7)
|
|
|
|
ax1.set_ylabel('Preis (USD)', fontsize=12)
|
|
ax1.set_title('Gold (GC=F Futures) - Preisentwicklung', fontsize=14, fontweight='bold')
|
|
ax1.legend(loc='upper left')
|
|
ax1.grid(True, alpha=0.3)
|
|
|
|
# Subplot 2: RSI
|
|
ax2 = axes[1]
|
|
if 'RSI_14' in df.columns:
|
|
ax2.plot(df.index, df['RSI_14'], color=self.colors['indicator'], linewidth=2)
|
|
ax2.axhline(y=70, color=self.colors['bearish'], linestyle='--', alpha=0.5, label='Überkauft')
|
|
ax2.axhline(y=30, color=self.colors['bullish'], linestyle='--', alpha=0.5, label='Überverkauft')
|
|
ax2.fill_between(df.index, 70, 100, alpha=0.1, color=self.colors['bearish'])
|
|
ax2.fill_between(df.index, 0, 30, alpha=0.1, color=self.colors['bullish'])
|
|
ax2.set_ylabel('RSI', fontsize=12)
|
|
ax2.set_ylim(0, 100)
|
|
ax2.legend(loc='upper left')
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
# Subplot 3: Volume
|
|
ax3 = axes[2]
|
|
colors = [self.colors['bullish'] if close >= open_ else self.colors['bearish']
|
|
for close, open_ in zip(df['Close'], df['Open'])]
|
|
ax3.bar(df.index, df['Volume'], color=colors, alpha=0.6)
|
|
ax3.set_ylabel('Volumen', fontsize=12)
|
|
ax3.set_xlabel('Datum', fontsize=12)
|
|
ax3.grid(True, alpha=0.3)
|
|
|
|
# X-Achse formatieren
|
|
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
|
|
plt.xticks(rotation=45)
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
logger.info(f"Chart gespeichert: {save_path}")
|
|
plt.close()
|
|
|
|
def plot_bollinger_bands(
|
|
self,
|
|
df: pd.DataFrame,
|
|
save_path: str = "bollinger_bands.png"
|
|
):
|
|
"""
|
|
Erstellt einen Bollinger Bands Chart
|
|
|
|
Args:
|
|
df: DataFrame mit Bollinger Bands
|
|
save_path: Speicherpfad
|
|
"""
|
|
fig, ax = plt.subplots(figsize=(15, 8))
|
|
|
|
# Preis
|
|
ax.plot(df.index, df['Close'], label='Preis', color=self.colors['price'], linewidth=2)
|
|
|
|
# Bollinger Bands
|
|
if all(col in df.columns for col in ['BB_upper_20', 'BB_middle_20', 'BB_lower_20']):
|
|
ax.plot(df.index, df['BB_upper_20'], 'r--', alpha=0.5, label='Oberes Band')
|
|
ax.plot(df.index, df['BB_middle_20'], 'b-', alpha=0.5, label='Mittleres Band (SMA)')
|
|
ax.plot(df.index, df['BB_lower_20'], 'g--', alpha=0.5, label='Unteres Band')
|
|
|
|
# Füllung zwischen Bändern
|
|
ax.fill_between(df.index, df['BB_lower_20'], df['BB_upper_20'],
|
|
alpha=0.1, color=self.colors['neutral'])
|
|
|
|
# Markiere Breakouts
|
|
upper_breakout = df['Close'] > df['BB_upper_20']
|
|
lower_breakout = df['Close'] < df['BB_lower_20']
|
|
|
|
ax.scatter(df.index[upper_breakout], df['Close'][upper_breakout],
|
|
color=self.colors['bearish'], marker='^', s=100,
|
|
label='Breakout oben', zorder=5)
|
|
ax.scatter(df.index[lower_breakout], df['Close'][lower_breakout],
|
|
color=self.colors['bullish'], marker='v', s=100,
|
|
label='Breakout unten', zorder=5)
|
|
|
|
ax.set_title('Gold (GC=F Futures) - Bollinger Bands', fontsize=14, fontweight='bold')
|
|
ax.set_xlabel('Datum', fontsize=12)
|
|
ax.set_ylabel('Preis (USD)', fontsize=12)
|
|
ax.legend(loc='upper left')
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
plt.xticks(rotation=45)
|
|
plt.tight_layout()
|
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
logger.info(f"Bollinger Bands Chart gespeichert: {save_path}")
|
|
plt.close()
|
|
|
|
def plot_session_comparison(
|
|
self,
|
|
session_data: Dict[str, Dict[str, float]],
|
|
save_path: str = "session_comparison.png"
|
|
):
|
|
"""
|
|
Vergleicht verschiedene Handelssessions
|
|
|
|
Args:
|
|
session_data: Dictionary mit Session-Statistiken
|
|
save_path: Speicherpfad
|
|
"""
|
|
if not session_data:
|
|
logger.warning("Keine Session-Daten zum Plotten")
|
|
return
|
|
|
|
sessions = list(session_data.keys())
|
|
metrics = ['avg_price', 'volatility', 'total_volume', 'price_change']
|
|
metric_labels = ['Ø Preis', 'Volatilität', 'Volumen', 'Preisänderung']
|
|
|
|
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
|
|
axes = axes.flatten()
|
|
|
|
for i, (metric, label) in enumerate(zip(metrics, metric_labels)):
|
|
ax = axes[i]
|
|
values = [session_data[s].get(metric, 0) for s in sessions]
|
|
|
|
bars = ax.bar(sessions, values, color=[self.colors['bullish'] if v >= 0 else self.colors['bearish']
|
|
for v in values], alpha=0.7)
|
|
ax.set_title(label, fontsize=12, fontweight='bold')
|
|
ax.set_xlabel('Session', fontsize=10)
|
|
ax.set_ylabel(label, fontsize=10)
|
|
ax.grid(True, alpha=0.3, axis='y')
|
|
|
|
# Werte auf Balken anzeigen
|
|
for bar in bars:
|
|
height = bar.get_height()
|
|
ax.text(bar.get_x() + bar.get_width()/2., height,
|
|
f'{height:.2f}',
|
|
ha='center', va='bottom' if height >= 0 else 'top',
|
|
fontsize=9)
|
|
|
|
plt.suptitle('Handelssession-Vergleich', fontsize=14, fontweight='bold', y=1.00)
|
|
plt.tight_layout()
|
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
logger.info(f"Session-Vergleich gespeichert: {save_path}")
|
|
plt.close()
|
|
|
|
def plot_sentiment_timeline(
|
|
self,
|
|
sentiment_data: List[Dict[str, Any]],
|
|
save_path: str = "sentiment_timeline.png"
|
|
):
|
|
"""
|
|
Erstellt eine Sentiment-Zeitlinie
|
|
|
|
Args:
|
|
sentiment_data: Liste von Sentiment-Daten mit Timestamps
|
|
save_path: Speicherpfad
|
|
"""
|
|
if not sentiment_data:
|
|
logger.warning("Keine Sentiment-Daten zum Plotten")
|
|
return
|
|
|
|
df = pd.DataFrame(sentiment_data)
|
|
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
|
df = df.sort_values('timestamp')
|
|
|
|
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 8), sharex=True)
|
|
|
|
# Sentiment Score über Zeit
|
|
colors = [self.colors['bullish'] if s > 0 else self.colors['bearish']
|
|
for s in df['news_sentiment']]
|
|
ax1.bar(df['timestamp'], df['news_sentiment'], color=colors, alpha=0.6)
|
|
ax1.axhline(y=0, color='black', linestyle='-', linewidth=0.5)
|
|
ax1.set_ylabel('Sentiment Score', fontsize=12)
|
|
ax1.set_title('News Sentiment über Zeit', fontsize=14, fontweight='bold')
|
|
ax1.grid(True, alpha=0.3, axis='y')
|
|
|
|
# Preis über Zeit (falls vorhanden)
|
|
if 'close' in df.columns:
|
|
ax2.plot(df['timestamp'], df['close'], color=self.colors['price'], linewidth=2)
|
|
ax2.set_ylabel('Gold-Preis (USD)', fontsize=12)
|
|
ax2.set_xlabel('Datum', fontsize=12)
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
plt.xticks(rotation=45)
|
|
plt.tight_layout()
|
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
logger.info(f"Sentiment-Timeline gespeichert: {save_path}")
|
|
plt.close()
|
|
|
|
def plot_correlation_heatmap(
|
|
self,
|
|
df: pd.DataFrame,
|
|
save_path: str = "correlation_heatmap.png"
|
|
):
|
|
"""
|
|
Erstellt eine Korrelations-Heatmap der Indikatoren
|
|
|
|
Args:
|
|
df: DataFrame mit Indikatoren
|
|
save_path: Speicherpfad
|
|
"""
|
|
# Nur numerische Spalten
|
|
numeric_cols = df.select_dtypes(include=[np.number]).columns
|
|
# Entferne OHLCV Spalten
|
|
indicator_cols = [col for col in numeric_cols
|
|
if col not in ['Open', 'High', 'Low', 'Close', 'Volume']]
|
|
|
|
if len(indicator_cols) < 2:
|
|
logger.warning("Nicht genug Indikatoren für Korrelation")
|
|
return
|
|
|
|
corr_matrix = df[indicator_cols].corr()
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 10))
|
|
im = ax.imshow(corr_matrix, cmap='coolwarm', aspect='auto', vmin=-1, vmax=1)
|
|
|
|
# Achsen beschriften
|
|
ax.set_xticks(np.arange(len(indicator_cols)))
|
|
ax.set_yticks(np.arange(len(indicator_cols)))
|
|
ax.set_xticklabels(indicator_cols, rotation=45, ha='right')
|
|
ax.set_yticklabels(indicator_cols)
|
|
|
|
# Werte in Zellen anzeigen
|
|
for i in range(len(indicator_cols)):
|
|
for j in range(len(indicator_cols)):
|
|
text = ax.text(j, i, f'{corr_matrix.iloc[i, j]:.2f}',
|
|
ha="center", va="center", color="black", fontsize=8)
|
|
|
|
ax.set_title('Indikator-Korrelation', fontsize=14, fontweight='bold')
|
|
plt.colorbar(im, ax=ax, label='Korrelation')
|
|
|
|
plt.tight_layout()
|
|
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
|
logger.info(f"Korrelations-Heatmap gespeichert: {save_path}")
|
|
plt.close()
|
|
|
|
|
|
# Beispiel-Nutzung
|
|
if __name__ == "__main__":
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
from yahoo_collector import YahooFinanceGoldCollector
|
|
from technical_indicators import TechnicalIndicatorCalculator
|
|
|
|
print("Erstelle Beispiel-Visualisierungen...")
|
|
|
|
# Daten sammeln
|
|
collector = YahooFinanceGoldCollector()
|
|
df = collector.get_historical_data("GC", period="1mo", interval="1d")
|
|
|
|
if not df.empty:
|
|
# Indikatoren berechnen
|
|
calculator = TechnicalIndicatorCalculator()
|
|
df_with_indicators = calculator.calculate_all_indicators(df)
|
|
|
|
# Visualisierungen erstellen
|
|
visualizer = GoldMarketVisualizer()
|
|
|
|
print("1. Preis-Chart mit Indikatoren...")
|
|
visualizer.plot_price_with_indicators(
|
|
df_with_indicators,
|
|
indicators=['SMA_20', 'SMA_50', 'EMA_12'],
|
|
save_path="chart_price_indicators.png"
|
|
)
|
|
|
|
print("2. Bollinger Bands...")
|
|
visualizer.plot_bollinger_bands(
|
|
df_with_indicators,
|
|
save_path="chart_bollinger_bands.png"
|
|
)
|
|
|
|
print("3. Korrelations-Heatmap...")
|
|
visualizer.plot_correlation_heatmap(
|
|
df_with_indicators,
|
|
save_path="chart_correlation.png"
|
|
)
|
|
|
|
print("\n✓ Alle Visualisierungen erstellt!")
|
|
print(" - chart_price_indicators.png")
|
|
print(" - chart_bollinger_bands.png")
|
|
print(" - chart_correlation.png")
|
|
else:
|
|
print("Keine Daten verfügbar!")
|