ghost_69 16. September Ich bin gerade wieder dabei mir ein paar Charts zu basteln und etwas mehr. Code für Python: import numpy as np import matplotlib.pyplot as plt # Annahmen für Kursentwicklung Alphabet (GOOGL) years = np.arange(0, 6) # 0 bis 5 Jahre current_year = 2025 year_labels = [str(current_year + i) for i in years] current_price = 220 # angenommener aktueller Kurs in USD # Szenarioannahmen (% Wachstum pro Jahr) growth_best = 0.20 # Best-Case: +20 % p.a. growth_base = 0.10 # Base-Case: +10 % p.a. growth_worst = -0.05 # Worst-Case: -5 % p.a. # Berechnung der Szenariopfade price_best = current_price * (1 + growth_best) ** years price_base = current_price * (1 + growth_base) ** years price_worst = current_price * (1 + growth_worst) ** years # Mittelwert (vereinfacht als Durchschnitt der drei Szenarien) price_mean = (price_best + price_base + price_worst) / 3 # Plot plt.figure(figsize=(10,6)) # Unsicherheitswolke (Bereich zwischen Worst und Best) plt.fill_between(years, price_worst, price_best, color='skyblue', alpha=0.3, label="Spanne (Worst - Best)") # Szenarienlinien plt.plot(years, price_best, color='green', linestyle='--', label="Best-Case (+20% p.a.)") plt.plot(years, price_base, color='blue', linestyle='-', label="Base-Case (+10% p.a.)") plt.plot(years, price_worst, color='red', linestyle='--', label="Worst-Case (-5% p.a.)") # Mittelwertlinie plt.plot(years, price_mean, color='black', linestyle='-', linewidth=2, label="Mittelwert") # X-Achse mit Jahreszahlen beschriften plt.xticks(years, year_labels) # Chart verschönern plt.title("Alphabet (GOOGL) – Szenario-Entwicklung (2025–2030)", fontsize=14) plt.xlabel("Jahr", fontsize=12) plt.ylabel("Kurs (USD)", fontsize=12) plt.legend() plt.grid(True, alpha=0.3) plt.show() Ergebnis: Alphabet.ipynb Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 16. September · bearbeitet 16. September von ghost_69 Ich möchte für verschiedene Werte eine Art Bewertung machen, hier mal ein Beispiel: Der Code dazu: Zitat import yfinance as yf import pandas as pd import numpy as np # Funktion, um sicher einen Float zu extrahieren def safe_float(x): if isinstance(x, pd.Series) or isinstance(x, pd.DataFrame): return float(x.iloc[-1].item()) if isinstance(x, pd.Series) else float(x.iloc[-1].values[-1]) return float(x) # Liste der Aktien aktien = { "GOOGL": "Alphabet Inc.", "AAPL": "Apple Inc.", "MSFT": "Microsoft Corp.", "AMZN": "Amazon.com Inc." } # DataFrame für Ergebnisse ergebnisse = pd.DataFrame(columns=[ "Symbol", "Name", "Golden Cross", "Momentum", "RSI", "Fibonacci", "Volatilität", "Bewertung (P/E)", "Umsatzwachstum", "Gewinnmarge", "Empfehlung" ]) for symbol, name in aktien.items(): # Daten der letzten 6 Monate data = yf.download(symbol, period="6mo", interval="1d", auto_adjust=True) # Momentum berechnen momentum = safe_float(data["Close"].pct_change().tail(1)) # RSI (vereinfacht) delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(window=14).mean() avg_loss = loss.rolling(window=14).mean() rs = avg_gain / avg_loss rsi = safe_float(100 - (100 / (1 + rs))) # Gleitende Durchschnitte ma20 = data["Close"].rolling(window=20).mean() ma50 = data["Close"].rolling(window=50).mean() # Letzte Werte extrahieren als Float ma20_last = ma20.iloc[-1].item() ma50_last = ma50.iloc[-1].item() # Trend erkennen: 20-Tage über 50-Tage → Golden Cross aktiv if not (np.isnan(ma20_last) or np.isnan(ma50_last)): if ma20_last > ma50_last: golden_cross = "Ja" golden_cross_up = True golden_cross_down = False elif ma20_last < ma50_last: golden_cross = "Nein" golden_cross_up = False golden_cross_down = True else: golden_cross = "Nein" golden_cross_up = False golden_cross_down = False else: golden_cross = "Nein" golden_cross_up = False golden_cross_down = False # Volatilität vol = data["Close"].pct_change().rolling(window=20).std() volatility = safe_float(vol) # Fibonacci Level high = data["Close"].max() low = data["Close"].min() fibo_level = safe_float((data["Close"].iloc[-1] - low) / (high - low) * 100) # Beispielwerte für fundamentale Kennzahlen pe = np.random.uniform(15, 40) umsatzwachstum = np.random.uniform(5, 15) gewinnmarge = np.random.uniform(8, 30) # Empfehlung nach deiner Regel if golden_cross_up: empfehlung = "Kaufen" elif golden_cross_down: if rsi > 70: empfehlung = "Halten" else: empfehlung = "Verkaufen" else: empfehlung = "Halten" # Ergebnis speichern ergebnis_zeile = { "Symbol": symbol, "Name": name, "Golden Cross": golden_cross, "Momentum": f"{momentum*100:.2f}%", "RSI": f"{rsi:.1f}", "Fibonacci": f"{fibo_level:.1f}%", "Volatilität": f"{volatility*100:.2f}%", "Bewertung (P/E)": f"{pe:.1f}", "Umsatzwachstum": f"{umsatzwachstum:.0f}%", "Gewinnmarge": f"{gewinnmarge:.0f}%", "Empfehlung": empfehlung } ergebnisse = pd.concat([ergebnisse, pd.DataFrame([ergebnis_zeile])], ignore_index=True) # Ausgabe als saubere Tabelle pd.set_option('display.max_columns', None) # Alle Spalten anzeigen pd.set_option('display.width', 150) # Breite der Anzeige anpassen print(ergebnisse.to_string(index=False)) # Ohne Index ausgeben das ist nur ein Beispiel und hat noch nichts zu sagen. Gibt es eine gute Regel für die Empfehlungen ? Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 17. September ich bin weiter dabei: Score-Berechnung 1. Golden Cross (20-Tage-Durchschnitt > 50-Tage-Durchschnitt) → +1 Punkt 2. Fibonacci-Level unter 80 % → +1 Punkt 3. RSI über 50 → +1 Punkt 4. Volatilität < 2 % → +1 Punkt 5. Momentum > 0 → +1 Punkt 6. 200-Tage-Linie: Schlusskurs über 200-Tage-Durchschnitt → +1 Punkt Maximalpunkte: 6 Empfehlung je Score Score Empfehlung 6 = Kaufen 5 = Achtung Nachkauf 4–2 =Halten 1 = Achtung prüfen / Verkauf 0 = Verkaufen Das System bewertet also die Aktie anhand von Trend, Stärke, Volatilität und Momentum, und du bekommst eine übersichtliche Empfehlung. oder hat noch jemand eine andere Idee ? Ghost_69 Diesen Beitrag teilen Link zum Beitrag
stagflation 17. September · bearbeitet 17. September von stagflation Ich finde es schön, dass Du Python verwendest und auch den Source Code postest! Folgenden Code verstehe ich noch nicht: # Volatilität data = yf.download(symbol, period="6mo", interval="1d", auto_adjust=True) vol = data["Close"].pct_change().rolling(window=20).std() Wozu das Rolling Window? Und warum keine Annualisierung? Ich hätte eher etwas erwartet wie: # Volatilität data = yf.download( symbol, period="6mo", interval="1d", auto_adjust=True ) vol = data["Close"].pct_change().std() * sqrt( 250 ) wobei die Multiplikation mit Wurzel 250 die Annualisierung der Standardabweichung aus Tageskursen ist (man geht von 250 Handelstagen im Jahr aus). Manche rechnen auch mit stetigen Renditen: # Volatilität data = yf.download( symbol, period="6mo", interval="1d", auto_adjust=True ) vol = data["Close"].pct_change().apply( lambda x: log(x) ).std() * sqrt( 250 ) Ein Problem könnten schlechte Daten sein (fehlende Zeitbereiche oder Null-Werte, die von Yahoo kommen). Das müsste man vielleicht auch noch abfangen. Diesen Beitrag teilen Link zum Beitrag
ghost_69 18. September vor 16 Stunden von stagflation: Ich finde es schön, dass Du Python verwendest und auch den Source Code postest! Folgenden Code verstehe ich noch nicht: # Volatilität data = yf.download(symbol, period="6mo", interval="1d", auto_adjust=True) vol = data["Close"].pct_change().rolling(window=20).std() Wozu das Rolling Window? Und warum keine Annualisierung? Ich hätte eher etwas erwartet wie: # Volatilität data = yf.download( symbol, period="6mo", interval="1d", auto_adjust=True ) vol = data["Close"].pct_change().std() * sqrt( 250 ) wobei die Multiplikation mit Wurzel 250 die Annualisierung der Standardabweichung aus Tageskursen ist (man geht von 250 Handelstagen im Jahr aus). Manche rechnen auch mit stetigen Renditen: # Volatilität data = yf.download( symbol, period="6mo", interval="1d", auto_adjust=True ) vol = data["Close"].pct_change().apply( lambda x: log(x) ).std() * sqrt( 250 ) Ein Problem könnten schlechte Daten sein (fehlende Zeitbereiche oder Null-Werte, die von Yahoo kommen). Das müsste man vielleicht auch noch abfangen. Danke für Dein Interesse. Rolling Window, ich nehme nicht die gesamte Zeitreihe sondern das gleitende 20-Tage Fenster, das entspricht einem Handelsmonat, ich finde es zeigt die aktuelle Marktstimmung besser als über 6 Monate oder 1 Jahr. Annualisierung über 250 Handelstage, damit rechne ich die Jahresvolatilität, das ist die Klassische Finanzkennzahl. Variante mit stetigen Renditen, ist Mathematisch schöner, aber in der Praxis sind oft ähnliche Werte, nur bei größeren Sprüngen ist es robuster. Da ich hier noch ganz am Anfang stehe, gerne her mit Vorschlägen und Kritik. Das hier habe ich gestern noch gemacht: Zitat import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt from io import BytesIO import base64 from IPython.display import display, HTML # --- Sicherer Float-Konverter --- def safe_float(x): if isinstance(x, pd.Series): x = x.dropna() return float(x.iloc[-1]) if not x.empty else np.nan if isinstance(x, pd.DataFrame): x = x.dropna(how='all') if x.empty: return np.nan last_row = x.iloc[-1].dropna() return float(last_row.iloc[-1]) if not last_row.empty else np.nan try: return float(x) except: return np.nan # --- Mini-Chart / Sparkline --- def sparkline(data): fig, ax = plt.subplots(figsize=(2,0.5)) ax.plot(data, color='blue', linewidth=1) ax.axis('off') buf = BytesIO() plt.savefig(buf, format='png', bbox_inches='tight', dpi=100) plt.close(fig) buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode('utf-8') return f'<img src="data:image/png;base64,{img_base64}">' # --- Aktienliste --- aktien = { "GOOGL": "Alphabet Inc.", "AAPL": "Apple Inc.", "MSFT": "Microsoft Corp.", "AMZN": "Amazon.com Inc." } # --- Ergebnis DataFrame --- ergebnisse = pd.DataFrame(columns=[ "Symbol", "Name", "Chart", "Golden Cross", "Momentum", "RSI", "Fibonacci", "Volatilität", "Score", "Empfehlung" ]) for symbol, name in aktien.items(): data = yf.download(symbol, period="6mo", interval="1d", auto_adjust=True) if data.empty: continue # Mini-Chart chart_html = sparkline(data['Close']) # Momentum (14 Tage) momentum_period = 14 momentum = safe_float(data["Close"] - data["Close"].shift(momentum_period)) # RSI (14) delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(14).mean() avg_loss = loss.rolling(14).mean() rs = avg_gain / avg_loss rsi = safe_float(100 - (100 / (1 + rs))) # Gleitende Durchschnitte (Golden Cross) ma20 = data["Close"].rolling(20).mean() ma50 = data["Close"].rolling(50).mean() ma20_last = safe_float(ma20) ma50_last = safe_float(ma50) golden_cross = "Ja" if ma20_last > ma50_last else "Nein" # Volatilität (20 Tage Std.) vol = safe_float(data["Close"].pct_change().rolling(20).std()) # Fibonacci-Level high = safe_float(data["Close"].max()) low = safe_float(data["Close"].min()) close_last = safe_float(data["Close"]) fibo_level = ((close_last - low) / (high - low) * 100) if high != low else np.nan # --- Score Berechnung --- score = 0 if golden_cross == "Ja": score += 1 if not np.isnan(fibo_level) and fibo_level < 70: score += 1 # strenger if not np.isnan(rsi) and rsi > 50: score += 1 if not np.isnan(vol) and vol < 0.02: score += 1 # strenger if not np.isnan(momentum) and momentum > 0.01: score += 1 # nur deutliches Momentum # --- Empfehlung --- if score == 5: empfehlung = "Kaufen" elif score == 4: empfehlung = "Achtung Nachkauf" elif score in [2,3]: empfehlung = "Halten" elif score == 1: empfehlung = "Achtung prüfen/Verkauf" else: empfehlung = "Verkaufen" # Ergebnis speichern ergebnis_zeile = { "Symbol": symbol, "Name": name, "Chart": chart_html, "Golden Cross": golden_cross, "Momentum": f"{momentum:.2f}" if not np.isnan(momentum) else "n/a", "RSI": f"{rsi:.1f}" if not np.isnan(rsi) else "n/a", "Fibonacci": f"{fibo_level:.1f}%" if not np.isnan(fibo_level) else "n/a", "Volatilität": f"{vol*100:.2f}%" if not np.isnan(vol) else "n/a", "Score": score, "Empfehlung": empfehlung } ergebnisse = pd.concat([ergebnisse, pd.DataFrame([ergebnis_zeile])], ignore_index=True) # --- Anzeige in Jupyter --- pd.set_option('display.max_columns', None) pd.set_option('display.width', 200) display(HTML(ergebnisse.to_html(escape=False, index=False))) und dabei kommt das raus: dazwischen hatte ich noch einige andere Ideen und Versuche ist nicht leicht ans Ziel zu kommen. Ich bin übrigens ins Jupyter Lab und dort im Notebook unterwegs, da ich dort auch durch kurze Plots eine Zeile sehen kann und etwas verändern. Ghost_69 Diesen Beitrag teilen Link zum Beitrag
stagflation 18. September vor 3 Stunden von ghost_69: Rolling Window, ich nehme nicht die gesamte Zeitreihe sondern das gleitende 20-Tage Fenster, das entspricht einem Handelsmonat, ich finde es zeigt die aktuelle Marktstimmung besser als über 6 Monate oder 1 Jahr. Ah, okay, Du willst nur die letzten 20 Tage, also etwas wie: vol = data["Close"].tail(21).pct_change().std(). Das kann ich nachvollziehen. Ich war nur von rolling() verwirrt. Diesen Beitrag teilen Link zum Beitrag
ghost_69 18. September · bearbeitet 18. September von ghost_69 ich mache gerade weiter und teste, dabei ist mir ein Fehler aufgefallen, ich muss die meisten Werte doch auf die 200-Tage-Linie und dann brauche die die Kurse von min. 1 Jahr sonst passt die Berechnung nicht oder es kommen falsche Werte. Achja das sind keine Empfehlungen von mir, ich bastle nur etwas herum und das kann auch noch nicht stimmen, ich prüfe und vergleiche. Zitat import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt from io import BytesIO import base64 from IPython.display import display, HTML # --- Sicherer Float-Konverter --- def safe_float(x): if isinstance(x, pd.Series): x = x.dropna() return float(x.iloc[-1]) if not x.empty else np.nan if isinstance(x, pd.DataFrame): x = x.dropna(how='all') if x.empty: return np.nan last_row = x.iloc[-1].dropna() return float(last_row.iloc[-1]) if not last_row.empty else np.nan try: return float(x) except: return np.nan # --- Mini-Chart / Sparkline nur Kurs --- def sparkline(data): fig, ax = plt.subplots(figsize=(2,0.5)) ax.plot(data, color='blue', linewidth=1) ax.axis('off') buf = BytesIO() plt.savefig(buf, format='png', bbox_inches='tight', dpi=100) plt.close(fig) buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode('utf-8') return f'<img src="data:image/png;base64,{img_base64}">' # --- Aktienliste (Beispiel mit 40 Werten) --- aktien = [ "AAPL","ABI.BR","ABT","ACN","AMZN","ASML.AS","BBZAD.XC","BC8.DE","CAT","CL", "COLO-B.CO","DHR","DIS","EVD.DE","FIE.DE","GEBN.SW","GILD","GIS","GOOG","HNR1.DE", "KO","MA","MDT","MO","MSFT","NOVO-B.CO","NSRGY","NVDA","NVS","PEP","PG","RKT.L", "ROST","SIX2.DE","TMO","TSM","UL","UNP","V","WKL.AS" ] # --- Ergebnis DataFrame --- ergebnisse = pd.DataFrame(columns=[ "Symbol", "Name", "Chart", "Golden Cross", "Über 200 Tage Linie", "Momentum", "RSI", "Fibonacci", "Volatilität", "Dividende", "P/E", "Umsatzwachstum", "Gewinnmarge", "Score", "Empfehlung" ]) for symbol in aktien: ticker = yf.Ticker(symbol) info = ticker.info name = info.get("shortName", symbol) # 1 Jahr historische Daten data = yf.download(symbol, period="1y", interval="1d", auto_adjust=True) if data.empty: continue # Mini-Chart chart_html = sparkline(data['Close']) # Momentum (14 Tage) momentum_period = 14 momentum = safe_float(data["Close"] - data["Close"].shift(momentum_period)) # RSI (14) delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(14).mean() avg_loss = loss.rolling(14).mean() rs = avg_gain / avg_loss rsi = safe_float(100 - (100 / (1 + rs))) # Gleitende Durchschnitte ma20 = data["Close"].rolling(20).mean() ma50 = data["Close"].rolling(50).mean() ma200 = data["Close"].rolling(200).mean() ma20_last = safe_float(ma20) ma50_last = safe_float(ma50) ma200_last = safe_float(ma200) close_last = safe_float(data["Close"]) golden_cross = "Ja" if ma20_last > ma50_last else "Nein" over_200 = "Ja" if not np.isnan(ma200_last) and close_last > ma200_last else "Nein" # Volatilität (20 Tage) vol = safe_float(data["Close"].pct_change().rolling(20).std()) # Fibonacci-Level high = safe_float(data["Close"].max()) low = safe_float(data["Close"].min()) fibo_level = ((close_last - low) / (high - low) * 100) if high != low else np.nan # Fundamentale Kennzahlen div_yield = safe_float(info.get("dividendYield")) div_percent = f"{div_yield:.2f}%" if not np.isnan(div_yield) else "n/a" pe_ratio = safe_float(info.get("trailingPE")) pe_str = f"{pe_ratio:.2f}" if not np.isnan(pe_ratio) else "n/a" revenue_growth = safe_float(info.get("revenueGrowth")) rev_growth_str = f"{revenue_growth*100:.2f}%" if not np.isnan(revenue_growth) else "n/a" profit_margin = safe_float(info.get("profitMargins")) profit_margin_str = f"{profit_margin*100:.2f}%" if not np.isnan(profit_margin) else "n/a" # --- Score Berechnung --- score = 0 if golden_cross == "Ja": score += 1 if over_200 == "Ja": score += 1 if not np.isnan(fibo_level) and fibo_level < 70: score += 1 if not np.isnan(rsi) and rsi > 50: score += 1 if not np.isnan(vol) and vol < 0.02: score += 1 if not np.isnan(momentum) and momentum > 0.01: score += 1 # --- Empfehlung --- if score == 6: empfehlung = "Kaufen" elif score == 5: empfehlung = "Achtung Nachkauf" elif score in [2,3,4]: empfehlung = "Halten" elif score == 1: empfehlung = "Achtung prüfen/Verkauf" else: empfehlung = "Verkaufen" # Ergebnis speichern ergebnis_zeile = { "Symbol": symbol, "Name": name, "Chart": chart_html, "Golden Cross": golden_cross, "Über 200 Tage Linie": over_200, "Momentum": f"{momentum:.2f}" if not np.isnan(momentum) else "n/a", "RSI": f"{rsi:.1f}" if not np.isnan(rsi) else "n/a", "Fibonacci": f"{fibo_level:.1f}%" if not np.isnan(fibo_level) else "n/a", "Volatilität": f"{vol*100:.2f}%" if not np.isnan(vol) else "n/a", "Dividende": div_percent, "P/E": pe_str, "Umsatzwachstum": rev_growth_str, "Gewinnmarge": profit_margin_str, "Score": score, "Empfehlung": empfehlung } ergebnisse = pd.concat([ergebnisse, pd.DataFrame([ergebnis_zeile])], ignore_index=True) # --- Anzeige in Jupyter --- pd.set_option('display.max_columns', None) pd.set_option('display.width', 300) display(HTML(ergebnisse.to_html(escape=False, index=False))) # --- Legende --- legende = """ <h3>Legende zur Bewertung</h3> <ul> <li><b>Golden Cross:</b> Ja = 20-Tage-Linie über 50-Tage-Linie (bullish), Nein = unterhalb (bearish)</li> <li><b>200 Tage:</b> Ja = Kurs über 200-Tage-Linie (langfristig positiv), Nein = Kurs unter 200-Tage-Linie (langfristig negativ)</li> <li><b>Momentum:</b> Kurs höher als vor 14 Tagen = positiv</li> <li><b>RSI:</b> >50 = positiv, <50 = eher schwach</li> <li><b>Fibonacci:</b> Tiefer Wert = guter Rücksetzer, >70% = eher teuer</li> <li><b>Volatilität:</b> niedrig (<2%) = stabil, hoch = riskanter</li> <li><b>Dividende:</b> aktuelle Dividendenrendite in %</li> <li><b>P/E:</b> Kurs-Gewinn-Verhältnis</li> <li><b>Umsatzwachstum:</b> Veränderung des Umsatzes YoY in %</li> <li><b>Gewinnmarge:</b> Nettogewinn-Marge in %</li> </ul> <h4>Score und Empfehlung</h4> <ul> <li>6 Punkte = Kaufen</li> <li>5 Punkte = Achtung Nachkauf</li> <li>2–4 Punkte = Halten</li> <li>1 Punkt = Achtung prüfen / Verkauf</li> <li>0 Punkte = Verkaufen</li> </ul> """ display(HTML(legende)) dann das ganze noch als PDF: Zitat import pandas as pd from reportlab.lib.pagesizes import A4 from reportlab.pdfgen import canvas from reportlab.lib.utils import ImageReader from io import BytesIO import base64 # --- Hier dein DataFrame 'ergebnisse' laden oder erstellen --- # ergebnisse = ... (dein Aktien-DataFrame) # --- Pfad in Downloads --- pdf_file = r"C:\Users\Ghost\Downloads\Aktienanalyse.pdf" # --- PDF erstellen --- c = canvas.Canvas(pdf_file, pagesize=A4) width, height = A4 margin = 40 y_position = height - margin # Titel c.setFont("Helvetica-Bold", 16) c.drawString(margin, y_position, "Aktienanalyse") y_position -= 30 # Tabellenkopf c.setFont("Helvetica-Bold", 10) columns = ["Symbol", "Name", "Golden Cross", "Über 200 Tage Linie", "Momentum", "RSI", "Fibonacci", "Volatilität", "Dividende", "P/E", "Umsatzwachstum", "Gewinnmarge", "Empfehlung"] for idx, col in enumerate(columns): c.drawString(margin + idx*90, y_position, col) y_position -= 20 c.setFont("Helvetica", 10) # Tabelleninhalt for _, row in ergebnisse.iterrows(): if y_position < 50: c.showPage() y_position = height - margin c.setFont("Helvetica-Bold", 10) for idx, col in enumerate(columns): c.drawString(margin + idx*90, y_position, col) y_position -= 20 c.setFont("Helvetica", 10) for idx, col in enumerate(columns): value = str(row[col]) c.drawString(margin + idx*90, y_position, value) y_position -= 20 # PDF speichern c.save() print(f"PDF erfolgreich erstellt: {pdf_file}") oder als HTML Zitat html_content = ergebnisse.to_html(escape=False, index=False) legende = """ <h3>Legende zur Bewertung</h3> <ul> <li><b>Golden Cross:</b> Ja = 20-Tage-Linie über 50-Tage-Linie (bullish), Nein = unterhalb (bearish)</li> <li><b>200 Tage:</b> Ja = Kurs über 200-Tage-Linie (langfristig positiv), Nein = Kurs unter 200-Tage-Linie (langfristig negativ)</li> <li><b>Momentum:</b> Kurs höher als vor 14 Tagen = positiv</li> <li><b>RSI:</b> >50 = positiv, <50 = eher schwach</li> <li><b>Fibonacci:</b> Tiefer Wert = guter Rücksetzer, >70% = eher teuer</li> <li><b>Volatilität:</b> niedrig (<2%) = stabil, hoch = riskanter</li> <li><b>Dividende:</b> aktuelle Dividendenrendite in %</li> <li><b>P/E:</b> Kurs-Gewinn-Verhältnis</li> <li><b>Umsatzwachstum:</b> Veränderung des Umsatzes YoY in %</li> <li><b>Gewinnmarge:</b> Nettogewinn-Marge in %</li> </ul> """ full_html = f"<html><body>{html_content}<br>{legende}</body></html> 200-Tage-Linie-ok.ipynb Hier auch als HTML: Aktienanalyse.html Ghost_69 Diesen Beitrag teilen Link zum Beitrag
LongtermInvestor 18. September · bearbeitet 18. September von LongtermInvestor Am 17.9.2025 um 17:31 von ghost_69: oder hat noch jemand eine andere Idee ? Dir fehlt die komplette Dimension der fundamentalen Daten. Nur als Beispiel: Earnings Yield, FCF-Yield, Shares Outstanding, Gross Margin... Diesen Beitrag teilen Link zum Beitrag
ghost_69 19. September vor 18 Stunden von LongtermInvestor: Dir fehlt die komplette Dimension der fundamentalen Daten. Nur als Beispiel: Earnings Yield, FCF-Yield, Shares Outstanding, Gross Margin... Danke für Dein Interesse, Earnings Yield ist doch nur der Kehrwert (P/E = 20 ⇒ Earnings Yield = 5%), FCF-Yield, ist wirklich spannend: zeigt, wie viel freier Cashflow pro Marktkapitalisierung generiert wird, Shares Outstanding, die Zahl der Aktien im Umlauf, weiß nicht, eher für Aktienrückkäufe ?, Gross Margin, ich habe doch schon Umsatz und Gewinnmarge. Danke ich mache mal weiter Ghost_69 Diesen Beitrag teilen Link zum Beitrag
LongtermInvestor 19. September Ja, du hast natürlich recht mit PE, EY ist dann obsolet. Bei den Shares Outstandings ist aus Anteilseignersicht, die Veränderung z.B. in den letzen 12 Monaten interessant. Die Gross Margin finde ich interessant um zu beurteilen wie profitabel das Kernprodukt ist. Gerne in Ergänzung zur Net Margin. Return on Invested Capital (ROIC). Diesen Beitrag teilen Link zum Beitrag
ghost_69 19. September so hier mit 2 weiteren Kennzahlen: Zitat import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt from io import BytesIO import base64 from IPython.display import display, HTML # --- Sicherer Float-Konverter --- def safe_float(x): if isinstance(x, pd.Series): x = x.dropna() return float(x.iloc[-1]) if not x.empty else np.nan if isinstance(x, pd.DataFrame): x = x.dropna(how='all') if x.empty: return np.nan last_row = x.iloc[-1].dropna() return float(last_row.iloc[-1]) if not last_row.empty else np.nan try: return float(x) except: return np.nan # --- Mini-Chart / Sparkline nur Kurs --- def sparkline(data): fig, ax = plt.subplots(figsize=(2,0.5)) ax.plot(data, color='blue', linewidth=1) ax.axis('off') buf = BytesIO() plt.savefig(buf, format='png', bbox_inches='tight', dpi=100) plt.close(fig) buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode('utf-8') return f'<img src="data:image/png;base64,{img_base64}">' # --- Aktienliste (Beispiel mit 40 Werten) --- aktien = [ "AAPL","ABI.BR","ABT","ACN","AMZN","ASML.AS","BBZAD.XC","BC8.DE","CAT","CL", "COLO-B.CO","DHR","DIS","EVD.DE","FIE.DE","GEBN.SW","GILD","GIS","GOOG","HNR1.DE", "KO","MA","MDT","MO","MSFT","NOVO-B.CO","NSRGY","NVDA","NVS","PEP","PG","RKT.L", "ROST","SIX2.DE","TMO","TSM","UL","UNP","V","WKL.AS" ] # --- Ergebnis DataFrame --- ergebnisse = pd.DataFrame(columns=[ "Symbol", "Name", "Chart", "Golden Cross", "Über 200 Tage Linie", "Momentum", "RSI", "Fibonacci", "Volatilität", "Dividende", "P/E", "Earnings Yield", "FCF-Yield", "Umsatzwachstum", "Gewinnmarge", "Score", "Empfehlung" ]) for symbol in aktien: ticker = yf.Ticker(symbol) info = ticker.info name = info.get("shortName", symbol) # 1 Jahr historische Daten data = yf.download(symbol, period="1y", interval="1d", auto_adjust=True) if data.empty: continue # Mini-Chart chart_html = sparkline(data['Close']) # Momentum (14 Tage) momentum_period = 14 momentum = safe_float(data["Close"] - data["Close"].shift(momentum_period)) # RSI (14) delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(14).mean() avg_loss = loss.rolling(14).mean() rs = avg_gain / avg_loss rsi = safe_float(100 - (100 / (1 + rs))) # Gleitende Durchschnitte ma20 = data["Close"].rolling(20).mean() ma50 = data["Close"].rolling(50).mean() ma200 = data["Close"].rolling(200).mean() ma20_last = safe_float(ma20) ma50_last = safe_float(ma50) ma200_last = safe_float(ma200) close_last = safe_float(data["Close"]) golden_cross = "Ja" if ma20_last > ma50_last else "Nein" over_200 = "Ja" if not np.isnan(ma200_last) and close_last > ma200_last else "Nein" # Volatilität (20 Tage) vol = safe_float(data["Close"].pct_change().rolling(20).std()) # Fibonacci-Level high = safe_float(data["Close"].max()) low = safe_float(data["Close"].min()) fibo_level = ((close_last - low) / (high - low) * 100) if high != low else np.nan # Fundamentale Kennzahlen div_yield = safe_float(info.get("dividendYield")) div_percent = f"{div_yield:.2f}%" if not np.isnan(div_yield) else "n/a" pe_ratio = safe_float(info.get("trailingPE")) pe_str = f"{pe_ratio:.2f}" if not np.isnan(pe_ratio) else "n/a" # Earnings Yield = 1 / P/E earnings_yield = (1/pe_ratio*100) if not np.isnan(pe_ratio) and pe_ratio != 0 else np.nan earnings_yield_str = f"{earnings_yield:.2f}%" if not np.isnan(earnings_yield) else "n/a" # FCF-Yield = Free Cash Flow / Market Cap free_cf = safe_float(info.get("freeCashflow")) market_cap = safe_float(info.get("marketCap")) fcf_yield = (free_cf / market_cap * 100) if market_cap and not np.isnan(market_cap) else np.nan fcf_yield_str = f"{fcf_yield:.2f}%" if not np.isnan(fcf_yield) else "n/a" revenue_growth = safe_float(info.get("revenueGrowth")) rev_growth_str = f"{revenue_growth*100:.2f}%" if not np.isnan(revenue_growth) else "n/a" profit_margin = safe_float(info.get("profitMargins")) profit_margin_str = f"{profit_margin*100:.2f}%" if not np.isnan(profit_margin) else "n/a" # --- Score Berechnung --- score = 0 if golden_cross == "Ja": score += 1 if over_200 == "Ja": score += 1 if not np.isnan(fibo_level) and fibo_level < 70: score += 1 if not np.isnan(rsi) and rsi > 50: score += 1 if not np.isnan(vol) and vol < 0.02: score += 1 if not np.isnan(momentum) and momentum > 0.01: score += 1 # if not np.isnan(earnings_yield) and earnings_yield > 5: score += 1 if not np.isnan(fcf_yield) and fcf_yield > 5: score += 1 # --- Empfehlung --- if score == 7: empfehlung = "Kaufen" elif score == 6: empfehlung = "Achtung Nachkauf" elif score in [2,3,4,5]: empfehlung = "Halten" elif score == 1: empfehlung = "Achtung prüfen/Verkauf" else: empfehlung = "Verkaufen" # Ergebnis speichern ergebnis_zeile = { "Symbol": symbol, "Name": name, "Chart": chart_html, "Golden Cross": golden_cross, "Über 200 Tage Linie": over_200, "Momentum": f"{momentum:.2f}" if not np.isnan(momentum) else "n/a", "RSI": f"{rsi:.1f}" if not np.isnan(rsi) else "n/a", "Fibonacci": f"{fibo_level:.1f}%" if not np.isnan(fibo_level) else "n/a", "Volatilität": f"{vol*100:.2f}%" if not np.isnan(vol) else "n/a", "Dividende": div_percent, "P/E": pe_str, "Earnings Yield": earnings_yield_str, "FCF-Yield": fcf_yield_str, "Umsatzwachstum": rev_growth_str, "Gewinnmarge": profit_margin_str, "Score": score, "Empfehlung": empfehlung } ergebnisse = pd.concat([ergebnisse, pd.DataFrame([ergebnis_zeile])], ignore_index=True) # --- Anzeige in Jupyter --- pd.set_option('display.max_columns', None) pd.set_option('display.width', 300) display(HTML(ergebnisse.to_html(escape=False, index=False))) # --- Legende --- legende = """ <h3>Legende zur Bewertung</h3> <ul> <li><b>Golden Cross:</b> Ja = 20-Tage-Linie über 50-Tage-Linie (bullish), Nein = unterhalb (bearish)</li> <li><b>200 Tage:</b> Ja = Kurs über 200-Tage-Linie (langfristig positiv), Nein = Kurs unter 200-Tage-Linie (langfristig negativ)</li> <li><b>Momentum:</b> Kurs höher als vor 14 Tagen = positiv</li> <li><b>RSI:</b> >50 = positiv, <50 = eher schwach</li> <li><b>Fibonacci:</b> Tiefer Wert = guter Rücksetzer, >70% = eher teuer</li> <li><b>Volatilität:</b> niedrig (<2%) = stabil, hoch = riskanter</li> <li><b>Dividende:</b> aktuelle Dividendenrendite in %</li> <li><b>P/E:</b> Kurs-Gewinn-Verhältnis</li> <li><b>Earnings Yield:</b> 1 / P/E in %</li> <li><b>FCF-Yield:</b> Free Cash Flow / Market Cap in %</li> <li><b>Umsatzwachstum:</b> Veränderung des Umsatzes YoY in %</li> <li><b>Gewinnmarge:</b> Nettogewinn-Marge in %</li> </ul> <h4>Score und Empfehlung</h4> <ul> <li>7 Punkte = Kaufen</li> <li>6 Punkte = Achtung Nachkauf</li> <li>2–5 Punkte = Halten</li> <li>1 Punkt = Achtung prüfen / Verkauf</li> <li>0 Punkte = Verkaufen</li> </ul> """ display(HTML(legende)) Aktienanalyse.html 40Werte+HTLM+Zeitstempel.ipynb ganz oben habe ich einen Zeitstemple eingebaut, dann weiß man wann man ist. Code für das HTML: Zitat from datetime import datetime # Aktuelles Datum und Uhrzeit zeitstempel = datetime.now().strftime("%d.%m.%Y %H:%M:%S") # HTML-Content aus DataFrame html_content = ergebnisse.to_html(escape=False, index=False) # CSS für Schriftart wie in Jupyter style = """ <style> body { font-family: Arial, Helvetica, sans-serif; } table { border-collapse: collapse; width: 100%; } th, td { border: 1px solid #ddd; padding: 8px; text-align: center; } th { background-color: #f2f2f2; } </style> """ # Legende mit Score legende = """ <h3>Legende zur Bewertung</h3> <ul> <li><b>Golden Cross:</b> Ja = 20-Tage-Linie über 50-Tage-Linie (bullish), Nein = unterhalb (bearish)</li> <li><b>200 Tage:</b> Ja = Kurs über 200-Tage-Linie (langfristig positiv), Nein = Kurs unter 200-Tage-Linie (langfristig negativ)</li> <li><b>Momentum:</b> Kurs höher als vor 14 Tagen = positiv</li> <li><b>RSI:</b> >50 = positiv, <50 = eher schwach</li> <li><b>Fibonacci:</b> Tiefer Wert = guter Rücksetzer, >70% = eher teuer</li> <li><b>Volatilität:</b> niedrig (<2%) = stabil, hoch = riskanter</li> <li><b>Dividende:</b> aktuelle Dividendenrendite in %</li> <li><b>P/E:</b> Kurs-Gewinn-Verhältnis</li> <li><b>Earnings Yield:</b> 1 / P/E in %, nur Anzeige</li> <li><b>FCF-Yield:</b> Free Cash Flow / Market Cap in %</li> <li><b>Umsatzwachstum:</b> Veränderung des Umsatzes YoY in %</li> <li><b>Gewinnmarge:</b> Nettogewinn-Marge in %</li> </ul> <h4>Score und Empfehlung</h4> <ul> <li>7 Punkte = Kaufen</li> <li>6 Punkte = Achtung Nachkauf</li> <li>2–5 Punkte = Halten</li> <li>1 Punkt = Achtung prüfen / Verkauf</li> <li>0 Punkte = Verkaufen</li> </ul> """ # --- Korrektes full_html mit Zeitstempel --- full_html = f""" <html> <head>{style}</head> <body> <p>Aktualisiert am: {zeitstempel}</p> {html_content}<br> {legende} </body> </html> Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 20. September so hier mal meine 40 Werte zum Wochenende, habe schon bemerkt das einige Werte sich gerade so bei 2 Stunden vor US-Börsenschluss sich schnell mal verändern und dann gleich wieder zurück, also geht es doch. Ich teste noch und möchte die Werte auf 100 Stück erhöhen. Aktienanalyse-20-09-2025.html Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 20. September · bearbeitet 20. September von ghost_69 Ich habe auf 96 Werte erhöht, 4 möchte ich noch, aktuell fallen mir keine ein, werden auf die Dauer aber sicherlich mehr werden. 96-Aktienanalyse-20-09-2025.html Auszug: Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 24. September ich habe den Code noch etwas angepasst: Zitat import yfinance as yf import pandas as pd import numpy as np import matplotlib.pyplot as plt from io import BytesIO import base64 from IPython.display import display, HTML # --- Sicherer Float-Konverter --- def safe_float(x): if isinstance(x, pd.Series): x = x.dropna() return float(x.iloc[-1]) if not x.empty else np.nan if isinstance(x, pd.DataFrame): x = x.dropna(how='all') if x.empty: return np.nan last_row = x.iloc[-1].dropna() return float(last_row.iloc[-1]) if not last_row.empty else np.nan try: return float(x) except: return np.nan # --- Mini-Chart / Sparkline nur Kurs --- def sparkline(data): fig, ax = plt.subplots(figsize=(2,0.5)) ax.plot(data, color='blue', linewidth=1) ax.axis('off') buf = BytesIO() plt.savefig(buf, format='png', bbox_inches='tight', dpi=100) plt.close(fig) buf.seek(0) img_base64 = base64.b64encode(buf.read()).decode('utf-8') return f'<img src="data:image/png;base64,{img_base64}">' # --- Aktienliste (alphabetisch & erweitert) --- aktien = [ "AAPL","ABI.BR","ABT","ACN","ADBE","ALV.DE","AMD","AMZN","ASML.AS","AZO", "BATS.L","BBZAD.XC","BC8.DE","BRK-B","AVGO","CSCO","CAT","CL","COLO-B.CO", "COST","DE","DELL","DB1.DE","DGE.L","DOL.TO","DHR","DIS","ELV","LLY","EFX","FICO", "GEBN.SW","GILD","GIS","GOOG","HNR1.DE","HLI","INTU","JNJ","JPM","KO", "LAB.MC","LIN","LMT","MELI","MA","META","MDT","MO","MCO","MSCI","MSFT", "MTX.DE","MUV2.DE","NEM.DE","NFLX","NEE","NKE","NOVO-B.CO","NSRGY","NVDA", "NVS","ORCL","ORLY","PEP","PG","QCOM","RDC.DE","RHM.DE","ROG.SW","RKLB", "RKT.L","ROST","SPGI","SHOP","SIE.DE","ENR.DE","SIX2.DE","SREN.SW","TJX", "TSLA","TXN","TMO","TSM","UL","UNH","UNP","V","WMT","WKL.AS" ] # --- Ergebnis DataFrame --- ergebnisse = pd.DataFrame(columns=[ "Symbol", "Name", "Chart", "Golden Cross", "Über 200 Tage Linie", "Momentum", "RSI", "Fibonacci", "Volatilität", "Dividende", "P/E", "Earnings Yield", "FCF-Yield", "Umsatzwachstum", "Gewinnmarge", "Score", "Empfehlung" ]) for symbol in aktien: ticker = yf.Ticker(symbol) info = ticker.info name = info.get("shortName", symbol) # 1 Jahr historische Daten data = yf.download(symbol, period="1y", interval="1d", auto_adjust=True) if data.empty: continue # Mini-Chart chart_html = sparkline(data['Close']) # Momentum (14 Tage) momentum_period = 14 momentum = safe_float(data["Close"] - data["Close"].shift(momentum_period)) # RSI (14) delta = data["Close"].diff() gain = delta.where(delta > 0, 0) loss = -delta.where(delta < 0, 0) avg_gain = gain.rolling(14).mean() avg_loss = loss.rolling(14).mean() rs = avg_gain / avg_loss rsi = safe_float(100 - (100 / (1 + rs))) # Gleitende Durchschnitte ma20 = data["Close"].rolling(20).mean() ma50 = data["Close"].rolling(50).mean() ma200 = data["Close"].rolling(200).mean() ma20_last = safe_float(ma20) ma50_last = safe_float(ma50) ma200_last = safe_float(ma200) close_last = safe_float(data["Close"]) golden_cross = "Ja" if ma20_last > ma50_last else "Nein" over_200 = "Ja" if not np.isnan(ma200_last) and close_last > ma200_last else "Nein" # Volatilität (20 Tage) vol = safe_float(data["Close"].pct_change().rolling(20).std()) # Fibonacci-Level high = safe_float(data["Close"].max()) low = safe_float(data["Close"].min()) fibo_level = ((close_last - low) / (high - low) * 100) if high != low else np.nan # Fundamentale Kennzahlen div_yield = safe_float(info.get("dividendYield")) div_percent = f"{div_yield:.2f}%" if not np.isnan(div_yield) else "n/a" pe_ratio = safe_float(info.get("trailingPE")) pe_str = f"{pe_ratio:.2f}" if not np.isnan(pe_ratio) else "n/a" # Earnings Yield = 1 / P/E earnings_yield = (1/pe_ratio*100) if not np.isnan(pe_ratio) and pe_ratio != 0 else np.nan earnings_yield_str = f"{earnings_yield:.2f}%" if not np.isnan(earnings_yield) else "n/a" # FCF-Yield = Free Cash Flow / Market Cap free_cf = safe_float(info.get("freeCashflow")) market_cap = safe_float(info.get("marketCap")) fcf_yield = (free_cf / market_cap * 100) if market_cap and not np.isnan(market_cap) else np.nan fcf_yield_str = f"{fcf_yield:.2f}%" if not np.isnan(fcf_yield) else "n/a" revenue_growth = safe_float(info.get("revenueGrowth")) rev_growth_str = f"{revenue_growth*100:.2f}%" if not np.isnan(revenue_growth) else "n/a" profit_margin = safe_float(info.get("profitMargins")) profit_margin_str = f"{profit_margin*100:.2f}%" if not np.isnan(profit_margin) else "n/a" # --- Score Berechnung --- score = 0 # Wichtig: Golden Cross + über 200 Tage Linie nur zusammen werten if golden_cross == "Ja" and over_200 == "Ja": score += 2 # beide zusammen 2 Punkte # nur einer von beiden = keine Punkte # score += 0 (implizit) # Andere Kriterien if not np.isnan(fibo_level) and fibo_level < 70: score += 1 if not np.isnan(rsi) and rsi > 50: score += 1 if not np.isnan(vol) and vol < 0.02: score += 1 if not np.isnan(momentum) and momentum > 0.01: score += 1 if not np.isnan(fcf_yield) and fcf_yield > 5: score += 1 # --- Empfehlung --- if score >= 7: empfehlung = "Kaufen" elif score == 6: empfehlung = "Achtung Nachkauf" elif score in [2,3,4,5]: empfehlung = "Halten" elif score == 1: empfehlung = "Achtung prüfen/Verkauf" else: empfehlung = "Verkaufen" # Ergebnis speichern ergebnis_zeile = { "Symbol": symbol, "Name": name, "Chart": chart_html, "Golden Cross": golden_cross, "Über 200 Tage Linie": over_200, "Momentum": f"{momentum:.2f}" if not np.isnan(momentum) else "n/a", "RSI": f"{rsi:.1f}" if not np.isnan(rsi) else "n/a", "Fibonacci": f"{fibo_level:.1f}%" if not np.isnan(fibo_level) else "n/a", "Volatilität": f"{vol*100:.2f}%" if not np.isnan(vol) else "n/a", "Dividende": div_percent, "P/E": pe_str, "Earnings Yield": earnings_yield_str, "FCF-Yield": fcf_yield_str, "Umsatzwachstum": rev_growth_str, "Gewinnmarge": profit_margin_str, "Score": score, "Empfehlung": empfehlung } ergebnisse = pd.concat([ergebnisse, pd.DataFrame([ergebnis_zeile])], ignore_index=True) # --- Anzeige in Jupyter --- pd.set_option('display.max_columns', None) pd.set_option('display.width', 300) display(HTML(ergebnisse.to_html(escape=False, index=False))) # --- Legende --- legende = """ <h3>Legende zur Bewertung</h3> <ul> <li><b>Golden Cross:</b> Ja = 20-Tage-Linie über 50-Tage-Linie (bullish), Nein = unterhalb (bearish)</li> <li><b>200 Tage:</b> Ja = Kurs über 200-Tage-Linie (langfristig positiv), Nein = Kurs unter 200-Tage-Linie (langfristig negativ)</li> <li><b>Momentum:</b> Kurs höher als vor 14 Tagen = positiv</li> <li><b>RSI:</b> >50 = positiv, <50 = eher schwach</li> <li><b>Fibonacci:</b> Tiefer Wert = guter Rücksetzer, >70% = eher teuer</li> <li><b>Volatilität:</b> niedrig (<2%) = stabil, hoch = riskanter</li> <li><b>Dividende:</b> aktuelle Dividendenrendite in %</li> <li><b>P/E:</b> Kurs-Gewinn-Verhältnis</li> <li><b>Earnings Yield:</b> 1 / P/E in %</li> <li><b>FCF-Yield:</b> Free Cash Flow / Market Cap in %</li> <li><b>Umsatzwachstum:</b> Veränderung des Umsatzes YoY in %</li> <li><b>Gewinnmarge:</b> Nettogewinn-Marge in %</li> </ul> <h4>Score und Empfehlung</h4> <ul> <li>7 Punkte = Kaufen</li> <li>6 Punkte = Achtung Nachkauf</li> <li>2–5 Punkte = Halten</li> <li>1 Punkt = Achtung prüfen / Verkauf</li> <li>0 Punkte = Verkaufen</li> </ul> """ display(HTML(legende)) sowie die Ausgabe mit Filtern und Suchfeld: Zitat from datetime import datetime # Zeitstempel erstellen timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") timestamp_html = f"<div style='text-align:right; font-size:12px; margin-bottom:5px;'>Analyse erstellt am: {timestamp}</div>" # HTML-Tabelle interaktiv mit DataTables html_content = ergebnisse.to_html(escape=False, index=False, classes="display", table_id="aktien_table") # CSS + JS für DataTables style_js = """ <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css"/> <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script> <script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script> <script> $(document).ready(function() { $('#aktien_table').DataTable({ "paging": true, "pageLength": 20, "order": [[15, "desc"]] // Sortierung standardmäßig nach Score }); }); </script> <style> body { font-family: Arial, Helvetica, sans-serif; } table.dataTable { width: 100%; border-collapse: collapse; } th, td { text-align: center; } </style> """ # Legende bleibt gleich full_html = f"<html><head>{style_js}</head><body>{timestamp_html}{html_content}<br>{legende}</body></html> das kommt dabei rum: Aktienanalyse-24-09-2025.html Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 25. September Ich habe mal 2 Charts über einander gelegt, S&P 500 ab 1991 = erster Internetbrowser S&P 500 ab 2022 = erstes AI Tool Zitat import pandas as pd import matplotlib.pyplot as plt import numpy as np from matplotlib.patches import Patch # für Legende der Füllung # ----------------------------- # Echte historische Preisindex-Daten 1991–2005 # ----------------------------- price_data = { 1991: 417, 1992: 435, 1993: 466, 1994: 459, 1995: 615, 1996: 740, 1997: 970, 1998: 1229, 1999: 1469, 2000: 1320, 2001: 1148, 2002: 879, 2003: 1112, 2004: 1212, 2005: 1248 } # Historische S&P 500 TR-Daten 2022–2025 (Basis 100 in 2022) hist_tr = pd.DataFrame({ 'year': [2022, 2023, 2024, 2025], 'index_norm2022': [100, 105, 112, 118] }) # ----------------------------- # Mapping Jahre # ----------------------------- map_years_price = list(range(1991, 2006)) # 1991–2005 map_years_sp = list(range(2022, 2022+len(map_years_price))) # 2022–2036 map_labels = [f"{p}/{s}" for p,s in zip(map_years_price, map_years_sp)] # ----------------------------- # Preisindex DataFrame # ----------------------------- price_df_full = pd.DataFrame(list(price_data.items()), columns=['year','price_index']) base_price = price_df_full.loc[price_df_full['year']==1991, 'price_index'].values[0] price_df_full['index_norm'] = price_df_full['price_index']/base_price*100 price_df_full['mapped_label'] = map_labels # ----------------------------- # S&P 500 TR-Daten 2022–2025 # ----------------------------- sp_values = [] for y in map_years_sp: if y in hist_tr['year'].values: sp_values.append(hist_tr.loc[hist_tr['year']==y, 'index_norm2022'].values[0]) else: sp_values.append(np.nan) # ab 2026 keine Werte plot_sp = pd.DataFrame({ 'mapped_label': map_labels, 'sp500_norm2022': sp_values }) # ----------------------------- # Projektionen Best/Base/Worst Case ab 2025 # ----------------------------- last_real = hist_tr['index_norm2022'].iloc[-1] # Internetblase 1995–2005 relativ zu 1994 bubble_future_values = np.array([price_data[y]/price_data[1994] for y in range(1995, 2006)]) # Projektionen auf 2025 skalieren proj_best = last_real * bubble_future_values # grün proj_base = proj_best * 0.9 # gelb proj_worst = proj_best * 0.75 # rot # X-Index für Projektionen (2025–2036 inkl. Übergang) proj_years_idx = list(range(map_labels.index('1994/2025'), len(map_labels))) # inkl. 2025 proj_labels = [map_labels for i in proj_years_idx] # ----------------------------- # Plot # ----------------------------- plt.figure(figsize=(12,6)) # Historische Linie 1991–2005 plt.plot(price_df_full['mapped_label'], price_df_full['index_norm'], marker='o', label='S&P 500 Preisindex 1991–2005') # TR-Daten 2022–2025 plt.plot(plot_sp['mapped_label'], plot_sp['sp500_norm2022'], marker='s', label='S&P 500 TR 2022–2025') # Projektionen Best/Base/Worst Case ab 2025 plt.plot(proj_labels[1:], proj_best[0:len(proj_labels)-1], linestyle='--', color='green', marker='o', label='Best Case') plt.plot(proj_labels[1:], proj_base[0:len(proj_labels)-1], linestyle='--', color='gold', marker='o', label='Base Case') plt.plot(proj_labels[1:], proj_worst[0:len(proj_labels)-1], linestyle='--', color='red', marker='o', label='Worst Case') # Kurzer Übergang von 2025 zu 2026 x_transition = [proj_labels[0], proj_labels[1]] # 2025 → 2026 plt.plot(x_transition, [last_real, proj_best[0]], linestyle='--', color='green') plt.plot(x_transition, [last_real, proj_base[0]], linestyle='--', color='gold') plt.plot(x_transition, [last_real, proj_worst[0]], linestyle='--', color='red') # ----------------------------- # Füllung zwischen Best und Worst Case ab 2025 # ----------------------------- fill_x = proj_labels # ab 2025 fill_y_low = np.concatenate(([last_real], proj_worst[0:len(proj_labels)-1])) fill_y_high = np.concatenate(([last_real], proj_best[0:len(proj_labels)-1])) plt.fill_between(fill_x, fill_y_low, fill_y_high, color='skyblue', alpha=0.3) # ----------------------------- # Legende inkl. Spanne # ----------------------------- span_patch = Patch(facecolor='skyblue', alpha=0.3, label='Projektionsspanne Best–Worst Case') plt.legend(handles=[span_patch] + plt.gca().get_legend_handles_labels()[0], loc='upper left') # Achsen und Layout plt.xticks(rotation=45) plt.title('Mapping: Internetblase (1991–2005) vs. AI Blase (2022–2036)') plt.xlabel('Mapped Jahre (1991–2005 / 2022–2036)') plt.ylabel('Index (Basisjahr = 100)') plt.grid(True) plt.tight_layout() plt.show() sozusagen: Vergangenheit, Gegenwart und mögliche Zukunft in einem Blick, Parallelen zwischen der Dot-com-Blase und der heutigen Marktphase ! Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 29. September Ich finde das Tool gut, immer wieder andere Zahlen und somit andere Empfehlungen. Aktienanalyse29-09-2025.html Ghost_69 PS: Zu Risiken und Nebenwirkungen fragen sie Ihren Raider Diesen Beitrag teilen Link zum Beitrag
ghost_69 22. Oktober Aktienanalyse-22-10-2025.html Zu Risken und Nebenwirkungen fragen sie ihren Raider. Ghost_69 Diesen Beitrag teilen Link zum Beitrag
ghost_69 16. November Ich habe noch ein paar mehr Werte eingefügt. Verbesserter Score (1).ipynb dann kommt das dabei rum. Aktienanalyse16-11-2025.html Ghost_69 Diesen Beitrag teilen Link zum Beitrag
Luftball0n 13. Dezember Hallo Ghost, danke für die Screener-Anregung und ich war mal so frei und habe es etwas erweitert/angepasst und mithilfe der Intelligenz vereinfacht sowie mit parallele Ausführung der gleichen Prozessschritte (multi-threading) gegen den Bottleneck, wenn man viele Aktiendaten herunterladen möchte Nähere Infos hier: https://medium.com/@anupchakole/understanding-threadpoolexecutor-2eed095d21aa oder Auch für Windows-User ist es nun angenehmer, da der Kontonamen automatisch vom Login ermittelt wird für die Dateierstellung anstelle mit dem Pfadnamen "..ghost..." ! Auch die Oberfläche wurde erweitert: Es gibt nun mehrere Suchfelder und farbige Empfehlung. Besonders GoldenCross "Ja" und über 200Tage Linie "Nein" mit dem Absteigenden RSI ist aktuell mein Favorit... bastle gerade noch mit mehr Indikatoren wie Ichimoku-Cloud, MACD, Supertrend und %-Diff. alltime High (last 5 years). Viel Spaß und auf zu neuen Ufern, noch hat alles Potenzial Stock_Screener_v1.ipynb Diesen Beitrag teilen Link zum Beitrag
ghost_69 14. Dezember Danke sehr für Dein Interesse und Deine Ideen. Das mit der parallelen Ausführung finde ich super, kenne ich aus anderen Programmierschritten, wenn super viele Daten kommen, da könnte man auch noch Chanks dazu nehmen, dann geht es noch schneller. Ich möchte sehen an welcher Stelle es hakt, denn ab und an wechseln die Symbole oder etwas ändert sich, dann weiß ich wo ich suchen muss, daher habe ich es langsamer gemacht. Verbesserter Score -läuft-12-2025.ipynb Hier noch einmal mit einer Art Zählfunktion damit ich noch besser sehen kann 2/138: AMD verarbeitet. Ich habe übringens von meinem ersten Code die Bewertung geändert. Ein muss ist für mich ein GoldenCross und die 200-Tage Linie. Aktienanalyse_Filter-14-12-2025.html lass uns gerne weiter austauschen. Ghost_69 PS: Ich möchte da noch ein paar andere Screener bauen, die unabhängig von einander sind. Diesen Beitrag teilen Link zum Beitrag
ghost_69 14. Dezember · bearbeitet 14. Dezember von ghost_69 so jetzt bin ich mal die 3-Filter Strategie angegangen. da gibt es schon länger kein Tool mehr für. Zitat Die Drei Filter-Strategie ist die komplexeste einer Serie von drei durch James P. OShaughnessy entwickelten Anlagestrategien und stellt eine unmittelbare Weiterentwicklung der KUV-Strategie und der KUV+RS-Strategie dar. In seinen Aufzeichnungen nennt OShaughnessy diese Strategie selbst "Stein der Weisen"-Strategie, da er bei seinen Erhebungen, die den amerikanischen S&P 500-Index über eine Jahrzehnte währende Zeitspanne betrafen, mit dieser Vorgehensweise die besten Ergebnisse erzielt hat. Wie geht man vor? Die Drei Filter-Strategie ist die Steigerung der KUV+RS-Strategie, indem sie neben dem Kurs-Umsatz-Verhältnis (KUV) und der Relativen Stärke (RS) noch einen dritten Filter hinzunimmt - den Gewinn. Dieser wird den beiden anderen Kriterien als Eingangsbedingung zugeschaltet. Die Strategie ist auf den Kauf eines "Korbes" von sieben Aktien (ursprünglich aus dem S&P 500-Index, hier aus dem HDAX) ausgerichtet, die jeweils ein Jahr lang gehalten werden. Die Auswahl findet durch oben stehende Filter wie folgt statt: Grundvoraussetzung ist, dass das Unternehmen im abgelaufenen Geschäftsjahr einen Gewinnanstieg melden konnte. Einfache Grundannahme hierzu: Nur wenn die Gewinne steigen, kann die Aktie sich nachhaltig positiv entwickeln. Dabei gelten folgende zwei Spezifikationen: Die Verringerung des Verlustes ist NICHT als Gewinnanstieg zu sehen. Wenn ein Unternehmen jedoch in der Verlustzone war und im abgelaufenen Geschäftsjahr erstmals wieder Gewinne erzielt hat, gilt das Kriterium des Gewinnanstiegs indes als erfüllt. Nur diejenigen Aktien aus dem HDAX, die dieses Auswahlkriterium erfüllen, kommen in die engere Wahl und werden mit dem zweiten Filter konfrontiert: Dem KUV. Hierzu wird für alle verbliebenen Werte das KUV errechnet was kleiner als 1 sein muß und eine Rangliste erstellt. Ganz oben stehen diejenigen Titel mit dem höchsten, ganz unten die Aktien mit dem niedrigsten KUV. Auf diese herausgefilterten Aktien wird nun der dritte und letzte Filter angesetzt: Die Relative Stärke. Hierbei geht es um folgende Überlegung: Grundsätzlich stehen die Chancen für diejenigen Aktien hinsichtlich einer besseren Kursentwicklung gegenüber dem Index besser, die sich auch im Vorfeld als Outperformer präsentierten und dadurch eine hohe Relative Stärke gegenüber dem Index entwickeln konnten. Für das Investment werden aus den durch den ersten Filter verbliebenen Aktien diejenigen sieben ausgewählt, die die höchste Relative Stärke gegenüber dem Dax entwickelt haben. Hinweis: Dadurch, dass die Strategie auf jeweils ein Jahr ausgelegt ist, können erzielte Gewinne steuerfrei vereinnahmen. Die Performanceberechnung erfolgt dabei indes so, dass jeweils zum ersten Handelstag im Juni gewechselt wird. Sie selbst sollten aber die jeweils im Depot befindlichen Aktien ein oder zwei Handelstage länger halten, um erst nach Ablauf eines vollen Kalenderjahres zu verkaufen und so die Spekulationsfrist zu umgehen! Berechnung: Die Haltedauer der ausgewählten Aktien beträgt jeweils ein Jahr, beginnend am 01.06. eines jeweiligen Jahres. Nach einem Jahr werden die Filter erneut angelegt und die im Depot enthaltenen Aktien ausgetauscht, sofern sie den Kriterien der Strategie nicht mehr entsprechen sollten. Wichtig ist bei der Berechnung, sich nicht auf Prognosen zu verlassen. Basis müssen immer die Umsätze eines abgeschlossenen Geschäftsjahrs sein, daher beginnt die Berechnung jährlich am 01.06., wenn für alle Unternehmen die gesamten Daten für das Vorjahr auf dem Tisch sind. Ich habe erst meine Aktienauswahl benutzt, denn mit Indizes funktioniert das so leider nicht. 3Filter-Strategie.ipynb eine Kandidatenliste erstellt Schleife für alle Aktien aus den Symbolen Gewinnanstieg prüfen KUV berechnen 1 Jahresrendite Relative Stärke berechnen die das nicht erfüllen werden übersprungen danach Top 10 Liste der Aktien 3-Filter Strategie aus den Kandidaten HTLM erstellt am meisten Sinn ergibt sich die Sortierung nach RS: Top10_Aktien.html Jetzt habe ich nicht nach den Top 10 sondern noch nach allen gesucht und es sind nur 11 Werte: Strategie_Aktien.html Was meint ihr dazu !? Ghost_69 Diesen Beitrag teilen Link zum Beitrag
Luftball0n 14. Dezember vor 4 Stunden von ghost_69: so jetzt bin ich mal die 3-Filter Strategie angegangen. ... Guten Abend, 1.) habe es den Score etc. für mich erweitert und in diesem Zug die Optik verfeinert: schlichter Hintergrund Globaler Filter & nur bei Golden-Cross, Ema200 sowie bei der Empfehlung Zoom-Fähigkeit und auto-spaltenbreite außer bei dem Chartbild Für Ticker könnte man die Symbole auslagern in eine txt-Datei bzw. csv: Ggf. erstellen lassen und dann in importieren (aktuell bekomme ich yfinance API Limit errors ....), daher mal nur manuell im Code..... aber für andere Projekte ggf. interessant, hier die Hauptfunktionen: # -*- coding: utf-8 -*- """ Created on Sun Dec 14 17:00:25 2025 @author: Luftball0n """ #Shorten Codesnippet ... # Functions to fetch data def list_slickcharts(url: str) -> pd.DataFrame: user_agent = 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0' response = requests.get(url, headers={'User-Agent': user_agent}) html_data = StringIO(response.text) df = pd.read_html(html_data, match='Symbol', index_col='Symbol')[0] return df # Function to save only symbol and company name to CSV def save_tickers_to_csv(df, filename): subset = df[['Symbol', 'Company']].copy() subset.to_csv(filename, index=False) # Function to export DataFrame to CSV def export_dataframe_to_csv(df, filename): df.to_csv(filename) # Function to import DataFrame from CSV def import_dataframe_from_csv(filename): return pd.read_csv(filename, index_col='Symbol') 2.) Bezüglich der 3-Filter Strategie, interessant, aber ob ich danach handeln würde - eher nicht. Die Settings lassen sich mit dem Onvista-Screener (stand 2019) und bestimmt auch Finviz filtern. -> https://www.wertpapier-forum.de/topic/1042-drei-filter-strategie-kuv/page/24/?tab=comments#comment-1210998 3.) Was ich mir vor einiger Zeit erstellt habe ist ein Optimum-Finder für EMA-Cross strategy. Kann man auch für MACD etc. übertragen inkl. daily/weekly Damit hab ich ein anderen ultimativen Screener gebaut, der per BatchFile 2x pro Woche im Hintergrund startet, um von einigen Indikatoren bestimmte Signale zu erzeugen. Dabei werden die Symbole extern importiert (aktuell 101, und ab und an mit ca. 500 nur US-Markt)... Speziell die Kombination mit den passenden Emas, Indikatoren auf höheren Zeitebenen fungieren für mich gut für überschaubare Derivate-Trades (Laufzeit bis ~1Jahr)! Diesen Beitrag teilen Link zum Beitrag
Luftball0n vor 5 Stunden Nach langem Studieren konnte ich mein alten Signaldienst (Aktien-Scanner) von >=30 min Laufzeit auf ~3min reduzieren.... Aber meine Trade-Glaskugel werde ich nicht veröffentlichen Aktuell spiele ich mit Backtesting Libraries wie https://www.backtrader.com/ oder https://kernc.github.io/backtesting.py/ mit eig. meinen Signalen, mal sehen was dabei rauskommt. Welche Basiswerte mit welchem Signal und Zeithorizont rückblickend am Besten war. Diesen Beitrag teilen Link zum Beitrag