Files
Aktienmanager/stock_data_manager.py
2025-08-10 15:18:12 +02:00

201 lines
7.6 KiB
Python

import yfinance as yf
import pandas as pd
import sqlite3
from datetime import datetime
class StockDataManager:
def __init__(self, db_name="stock_data.db"):
self.db_name = db_name
self.conn = None
self.cursor = None
self._connect_db()
self._create_tables()
def _connect_db(self):
"""Stellt eine Verbindung zur SQLite-Datenbank her."""
try:
self.conn = sqlite3.connect(self.db_name)
self.cursor = self.conn.cursor()
print(f"Erfolgreich mit Datenbank '{self.db_name}' verbunden.")
except sqlite3.Error as e:
print(f"Fehler beim Verbinden mit der Datenbank: {e}")
def _create_tables(self):
"""Erstellt die Tabellen für Aktiendaten, falls sie noch nicht existieren."""
if not self.conn:
print("Keine Datenbankverbindung vorhanden.")
return
try:
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS stocks (
symbol TEXT PRIMARY KEY,
company_name TEXT
)
''')
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS daily_prices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
symbol TEXT NOT NULL,
date TEXT NOT NULL,
open REAL,
high REAL,
low REAL,
close REAL,
adj_close REAL,
volume INTEGER,
FOREIGN KEY (symbol) REFERENCES stocks (symbol) ON DELETE CASCADE,
UNIQUE (symbol, date)
)
''')
self.conn.commit()
print("Datenbanktabellen überprüft/erstellt.")
except sqlite3.Error as e:
print(f"Fehler beim Erstellen der Tabellen: {e}")
def add_stock(self, symbol, company_name=""):
"""Fügt ein Aktiensymbol zur 'stocks'-Tabelle hinzu."""
if not self.conn: return
try:
self.cursor.execute("INSERT OR IGNORE INTO stocks (symbol, company_name) VALUES (?, ?)", (symbol.upper(), company_name))
self.conn.commit()
print(f"Aktie '{symbol.upper()}' zur Datenbank hinzugefügt (falls neu).")
return True
except sqlite3.Error as e:
print(f"Fehler beim Hinzufügen der Aktie {symbol}: {e}")
return False
def get_all_symbols(self):
"""Gibt eine Liste aller in der Datenbank gespeicherten Symbole zurück."""
if not self.conn: return []
self.cursor.execute("SELECT symbol FROM stocks")
return [row[0] for row in self.cursor.fetchall()]
def fetch_and_store_data(self, symbol, period="1y"):
"""
Holt historische Kursdaten für ein Symbol und speichert sie in der Datenbank.
period: '1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max'
"""
symbol = symbol.upper()
print(f"Hole Daten für {symbol}...")
try:
ticker = yf.Ticker(symbol)
hist = ticker.history(period="1y", auto_adjust=False)
if hist.empty:
print(f"Keine Daten für {symbol} gefunden oder ungültiges Symbol.")
return False
# Füge das Symbol hinzu, falls es noch nicht existiert (z.B. wenn es direkt per API geholt wird)
self.add_stock(symbol, ticker.info.get('longName', ''))
data_to_insert = []
for date, row in hist.iterrows():
# Formatiere Datum als YYYY-MM-DD
date_str = date.strftime('%Y-%m-%d')
data_to_insert.append((
symbol,
date_str,
row['Open'],
row['High'],
row['Low'],
row['Close'],
row['Adj Close'],
row['Volume']
))
# Verwende INSERT OR IGNORE, um Duplikate zu vermeiden
self.cursor.executemany("""
INSERT OR IGNORE INTO daily_prices (symbol, date, open, high, low, close, adj_close, volume)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
""", data_to_insert)
self.conn.commit()
print(f"Daten für {symbol} erfolgreich gespeichert/aktualisiert.")
return True
except Exception as e:
print(f"Fehler beim Holen/Speichern der Daten für {symbol}: {e}")
return False
def get_stock_data(self, symbol, start_date=None, end_date=None):
"""
Holt historische Kursdaten für ein Symbol aus der Datenbank als Pandas DataFrame.
Optional: Filter nach Start- und Enddatum.
"""
symbol = symbol.upper()
query = "SELECT date, open, high, low, close, adj_close, volume FROM daily_prices WHERE symbol = ?"
params = [symbol]
if start_date and end_date:
query += " AND date BETWEEN ? AND ?"
params.append(start_date)
params.append(end_date)
elif start_date:
query += " AND date >= ?"
params.append(start_date)
elif end_date:
query += " AND date <= ?"
params.append(end_date)
query += " ORDER BY date ASC"
try:
df = pd.read_sql(query, self.conn, params=params, parse_dates=['date'], index_col='date')
if df.empty:
print(f"Keine Daten für {symbol} im angegebenen Zeitraum in der Datenbank gefunden.")
return df
except sqlite3.Error as e:
print(f"Fehler beim Abrufen der Daten für {symbol} aus der Datenbank: {e}")
return pd.DataFrame()
def close(self):
"""Schließt die Datenbankverbindung."""
if self.conn:
self.conn.close()
print("Datenbankverbindung geschlossen.")
# Beispielnutzung (kann später entfernt werden, wenn GUI fertig ist)
if __name__ == "__main__":
manager = StockDataManager("my_stocks.db")
# Symbole aus CSV hinzufügen (Beispiel)
# Angenommen, du hast eine stocks.csv mit 'Symbol,CompanyName'
# Beispiel-CSV-Inhalt:
# Symbol,CompanyName
# AAPL,Apple Inc.
# MSFT,Microsoft Corp.
# GOOGL,Alphabet Inc. (GOOGL)
# AMZN,Amazon.com Inc.
csv_file = "stocks.csv" # Erstelle diese Datei manuell für den Test
try:
df_symbols = pd.read_csv(csv_file)
for index, row in df_symbols.iterrows():
manager.add_stock(row['Symbol'], row.get('CompanyName', ''))
except FileNotFoundError:
print(f"'{csv_file}' nicht gefunden. Bitte erstellen Sie eine CSV-Datei mit 'Symbol,CompanyName'.")
except KeyError:
print(f"Fehler: '{csv_file}' muss Spalten 'Symbol' und optional 'CompanyName' enthalten.")
symbols_to_fetch = manager.get_all_symbols()
if not symbols_to_fetch:
# Fallback: Wenn CSV leer ist oder nicht existiert, einige bekannte Symbole holen
symbols_to_fetch = ["AAPL", "MSFT", "GOOGL"]
for s in symbols_to_fetch:
manager.add_stock(s)
for symbol in symbols_to_fetch:
manager.fetch_and_store_data(symbol, period="1y") # Holt Daten für 1 Jahr
# Testen des Datenabrufs
aapl_data = manager.get_stock_data("AAPL")
if not aapl_data.empty:
print("\nAAPL Daten (erste 5 Reihen):")
print(aapl_data.head())
msft_data = manager.get_stock_data("MSFT", start_date="2024-01-01")
if not msft_data.empty:
print("\nMSFT Daten seit 2024-01-01 (letzte 5 Reihen):")
print(msft_data.tail())
manager.close()