Zum Inhalt springen
ghost_69

Programmieren, Charts und Analysen

Empfohlene Beiträge

ghost_69

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:

image.png.c762a61af8e00cb17f051fc7c86170f5.png

 

Alphabet.ipynb

 

Ghost_69 :-*

Diesen Beitrag teilen


Link zum Beitrag
ghost_69
· bearbeitet von ghost_69

Ich möchte für verschiedene Werte eine Art Bewertung machen,

hier mal ein Beispiel:

 

image.png.b9f9a9090f199ea7e058c50d53901f60.png

 

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

ich bin weiter dabei:

image.png.8487e3a892628c76d4a6003dd5ef0a0c.png

 

 

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
· bearbeitet 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
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:

image.png.da5032045d786ae7a90aa09650aca49d.png

 

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
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
· bearbeitet 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.

image.thumb.png.dfb7638b3ce950e07f195b8f06acc739.png

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
· bearbeitet 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
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

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

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))

image.thumb.png.82be26aa54ce5cd82403369f06d941de.png

 

image.png.a6308bf851f2c02986dbd685d5befa90.png

 

Aktienanalyse.html

 

40Werte+HTLM+Zeitstempel.ipynb

 

ganz oben habe ich einen Zeitstemple eingebaut, dann weiß man wann man ist.

image.png.2ae7f510d1c07b3cd799a0440eaa1eaf.png

 

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

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
· bearbeitet 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:

Ausschnitt-20-09-2025.thumb.JPG.642f33f41555f76b7c1bb95fcaec7da8.JPG

 

Ghost_69 :-*

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

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:

image.thumb.png.ce846db61e9a5424002285241e8c65a1.png

 

image.thumb.png.c67dcadecc205ad5034a0c50500c8dba.png

 

Aktienanalyse-24-09-2025.html

 

Ghost_69 :-*

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

Ich habe mal 2 Charts über einander gelegt,

S&P 500 ab 1991 = erster Internetbrowser

S&P 500 ab 2022 = erstes AI Tool

download.thumb.png.b32ddb154aed84565b17c86d873ca69f.png

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

Ich finde das Tool gut, immer wieder andere Zahlen und somit andere Empfehlungen.

image.thumb.png.5dba6a8bc34d2cffe262b135bdab6bb2.png

 

image.thumb.png.54c3d3da5233a5e46abda576b3e788e8.png

 

Aktienanalyse29-09-2025.html

 

Ghost_69 :-*

 

PS: Zu Risiken und Nebenwirkungen fragen sie Ihren Raider 

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

Aktienanalyse-22-10-2025.html

 

image.thumb.png.a03a8fe996eea46785041a3f9bd29a4a.png

 

image.thumb.png.88d0f8f8564ed532d926a8d1947ea8ea.png

 

Zu Risken und Nebenwirkungen fragen sie ihren Raider.

 

Ghost_69 :-*

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

Ich habe noch ein paar mehr Werte eingefügt.

 

Verbesserter Score (1).ipynb

 

dann kommt das dabei rum.

 

Aktienanalyse16-11-2025.html

 

image.thumb.png.80d678227ee2fa41c260f9aa45c4671a.png

 

Ghost_69 :-*

 

 

 

 

 

 

Diesen Beitrag teilen


Link zum Beitrag
Luftball0n

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:)

Python_Screener.thumb.png.700f25802bd06506843d58f219ed72e1.png

Stock_Screener_v1.ipynb

Diesen Beitrag teilen


Link zum Beitrag
ghost_69

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

 

image.thumb.png.63d8a9ead383a851d4620bb0c1b07d74.png

 

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
· bearbeitet 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

image.thumb.png.8f38118d26f1c0d39a76167b7a628685.png

 

Jetzt habe ich nicht nach den Top 10 sondern noch nach allen gesucht und es sind nur 11 Werte:

 

Strategie_Aktien.html

image.thumb.png.9da9b8af49d17426cd4d8073856a33aa.png

 

Was meint ihr dazu !?

 

Ghost_69 :-*

 

Diesen Beitrag teilen


Link zum Beitrag
Luftball0n
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:

 

Screener_v2.thumb.png.28868ef2e26ddc1f9aa64de1bc7882b0.png

  • 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 :)

HeatMap

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

Nach langem Studieren konnte ich mein alten Signaldienst (Aktien-Scanner) von >=30 min Laufzeit auf ~3min reduzieren.... 

image.thumb.png.83f00a7236442d823bfff51a144d3ae8.png

 

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

Erstelle ein Benutzerkonto oder melde dich an, um zu kommentieren

Du musst ein Benutzerkonto haben, um einen Kommentar verfassen zu können

Benutzerkonto erstellen

Neues Benutzerkonto für unsere Community erstellen. Es ist einfach!

Neues Benutzerkonto erstellen

Anmelden

Du hast bereits ein Benutzerkonto? Melde dich hier an.

Jetzt anmelden

×
×
  • Neu erstellen...