File size: 9,807 Bytes
b6fe7cb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8158647
b6fe7cb
8158647
b6fe7cb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
798da31
b6fe7cb
 
 
 
 
8158647
 
 
 
 
b6fe7cb
 
8158647
b6fe7cb
8158647
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b6fe7cb
 
 
 
 
 
 
 
 
 
 
de57fca
dbc669a
 
b6fe7cb
798da31
 
 
8158647
798da31
b6fe7cb
798da31
b6fe7cb
8158647
 
 
b6fe7cb
 
 
8158647
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b6fe7cb
 
 
 
8158647
b6fe7cb
798da31
 
 
8158647
b6fe7cb
798da31
 
 
8158647
b6fe7cb
 
 
 
feedbd9
 
b6fe7cb
 
 
8158647
b6fe7cb
 
8158647
b6fe7cb
 
8158647
798da31
8158647
 
 
798da31
 
 
8158647
798da31
b6fe7cb
798da31
b6fe7cb
798da31
 
8158647
798da31
 
8158647
798da31
 
8158647
b6fe7cb
 
8158647
 
798da31
8158647
 
 
 
798da31
8158647
 
798da31
 
 
8158647
798da31
8158647
798da31
8158647
 
798da31
 
8158647
798da31
 
8158647
 
 
798da31
 
8158647
 
798da31
8158647
798da31
 
8158647
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import streamlit as st
import yfinance as yf
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime, timedelta
import base64

# === Grundlegendes Design & Titel ===
st.set_page_config(page_title="JQuant - Marktanalyse", page_icon="📊", layout="wide")

# Funktion zum Konvertieren des Bildes in Base64
def get_base64_image(image_path):
    with open(image_path, "rb") as img_file:
        return base64.b64encode(img_file.read()).decode()

# Logo als Base64 einlesen
image_base64 = get_base64_image("JQ.png")  # Bild lokal im Space hochgeladen

# CSS für fixiertes Logo OHNE Hintergrund
st.markdown(
    f"""
    <style>
        .fixed-logo {{
            position: fixed;
            top: 10px;
            left: 50%;
            transform: translateX(-50%);
            z-index: 1000;
            text-align: center;
        }}
    </style>
    <div class="fixed-logo">
        <img src="data:image/png;base64,{image_base64}" width="250">
    </div>
    """,
    unsafe_allow_html=True
)

# Standardwerte für das Datum: heute und ein Jahr davor
default_end_date = datetime.today().date()
default_start_date = default_end_date - timedelta(days=365)

# Abstand nach unten hinzufügen, damit der Titel nicht direkt unter dem fixierten Logo ist
st.markdown("<br><br><br>", unsafe_allow_html=True)

# Titel der App
st.title("📈 Interaktive Analyse von Aktienindizes")

indices = {
    # 🌎 Globale Indizes
    #"MSCI World": "^MSCI",
    #"MSCI Emerging Markets": "EEM",

    # 🇺🇸 USA
    "S&P 500 (USA)": "^GSPC",
    "Nasdaq 100 (USA)": "^NDX",
    "Dow Jones Industrial Average (USA)": "^DJI",
    "Russell 2000 (USA)": "^RUT",

    # 🇪🇺 Europa
    "DAX (Deutschland)": "^GDAXI",
    "MDAX (Deutschland)": "^MDAXI",
    "CAC 40 (Frankreich)": "^FCHI",
    "FTSE 100 (UK)": "^FTSE",
    "EURO STOXX 50": "^STOXX50E",
    "AEX (Niederlande)": "^AEX",
    "IBEX 35 (Spanien)": "^IBEX",

    # 🇨🇭 Schweiz
    "SMI (Schweiz)": "^SSMI",

    # 🇨🇳 China
    "Shanghai Composite": "000001.SS",
    "Hang Seng (Hongkong)": "^HSI",

    # 🇯🇵 Japan
    "Nikkei 225": "^N225",
    "TOPIX": "^TOPX",

    # 🇮🇳 Indien
    "BSE Sensex": "^BSESN",
    "Nifty 50": "^NSEI",

    # 🇦🇺 Australien
    "ASX 200": "^AXJO",

    # 🇨🇦 Kanada
    "TSX Composite": "^GSPTSE",

    # 🇧🇷 Brasilien
    "Bovespa": "^BVSP"
}

# Dropdown für Ticker 1 & Ticker 2
ticker1_name = st.selectbox("📈 Wähle Index 1:", list(indices.keys()), index=0)
ticker2_name = st.selectbox("📈 Wähle Index 2:", list(indices.keys()), index=1)

# Die zugehörigen Ticker aus dem Dictionary holen
ticker1 = indices[ticker1_name]
ticker2 = indices[ticker2_name]

# Benutzer kann das Datum weiterhin anpassen
start_date = st.date_input("Startdatum", default_start_date, min_value=datetime(1900, 1, 1).date(), max_value=default_end_date)
# Enddatum kann ebenfalls bis 1900 zurückgehen
end_date = st.date_input("Enddatum", default_end_date, min_value=datetime(1900, 1, 1).date(), max_value=datetime.today().date())

# Farben für die Linien (fixe Farben für Konsistenz)
color_ticker1 = "#1f77b4"  # Blau
color_ticker2 = "#ff7f0e"  # Orange
color_ticker3 = "#e377c2"  # Pink

# Button zum Laden der Daten
if st.button("GO"):

    # Daten abrufen (Erst `Adj Close`, falls nicht verfügbar `Close`)
    data_dict = {}
    
    for name, symbol in [(ticker1_name, ticker1), (ticker2_name, ticker2)]:
        ticker = yf.Ticker(symbol)
        df = ticker.history(start=start_date, end=end_date)
    
        # 🕒 Datum als Index setzen & Uhrzeit entfernen
        df.index = pd.to_datetime(df.index).date  # Konvertiert Timestamp zu Date ohne Uhrzeit
    
        # Prüfe, ob `Adj Close` existiert, falls nicht, nutze `Close`
        if "Adj Close" in df.columns and not df["Adj Close"].isna().all():
            selected_data = df["Adj Close"]
        elif "Close" in df.columns and not df["Close"].isna().all():
            selected_data = df["Close"]
        else:
            st.warning(f"⚠️ Keine gültigen Daten für {name} ({symbol}) gefunden!")
            continue  # Falls keine Daten vorhanden sind, überspringen
    
        # 🔹 Doppelte Einträge pro Tag entfernen → den letzten Wert behalten
        selected_data = selected_data[~selected_data.index.duplicated(keep="last")]
    
        # Spaltennamen für spätere Zusammenführung setzen
        selected_data.name = name  # Name des Index setzen
    
        # Speichere bereinigte Daten für den Index
        data_dict[name] = selected_data
    
    # Sicherstellen, dass beide DataFrames existieren
    if ticker1_name in data_dict and ticker2_name in data_dict:
        df1 = data_dict[ticker1_name].to_frame()
        df2 = data_dict[ticker2_name].to_frame()
    
        # 🔹 Beide DataFrames über gemeinsamen `Index (Datum)` zusammenführen
        df_indices = pd.merge(df1, df2, left_index=True, right_index=True, how="outer")
    
        # Fehlende Werte mit vorherigem Wert füllen
        df_indices.ffill(inplace=True)
    
        # Falls noch NaN-Werte übrig sind, entfernen
        df_indices.dropna(inplace=True)
    
        # Falls keine Daten geladen wurden, abbrechen
        if df_indices.empty:
            st.error("❌ Keine gemeinsamen Daten für die gewählten Indizes gefunden!")
            st.stop()


        # --------- 1. INTERAKTIVER CHART: Vergleich mit zwei Achsen ---------
        fig1 = go.Figure()
    
        # Linke Achse (Ticker 1)
        fig1.add_trace(go.Scatter(
            x=df_indices.index, y=df_indices[ticker1_name], mode='lines',
            name=ticker1_name, yaxis='y1', line=dict(color=color_ticker1)))
    
        # Rechte Achse (Ticker 2)
        fig1.add_trace(go.Scatter(
            x=df_indices.index, y=df_indices[ticker2_name], mode='lines',
            name=ticker2_name, yaxis='y2', line=dict(color=color_ticker2)))
    
        # Layout mit zwei Achsen definieren
        fig1.update_layout(
            title=f"Vergleich der Close-Preise: {ticker1_name} vs. {ticker2_name}",
            xaxis=dict(title="Datum"),
            yaxis=dict(title=f"{ticker1_name} Index", side='left', showgrid=False),
            yaxis2=dict(title=f"{ticker2_name} Index", side='right', overlaying='y', showgrid=False),
            legend=dict(x=0, y=1),
            hovermode="x"
        )
    
        # Streamlit: Interaktives Chart anzeigen
        st.plotly_chart(fig1, use_container_width=True)
    
        # --------- 2. INTERAKTIVER CHART: Normierter Vergleich ---------
        df_normalized = df_indices / df_indices.iloc[0] * 100
    
        # Rendite berechnen (% Veränderung von Start bis Ende)
        return_ticker1 = round(((df_indices[ticker1_name].iloc[-1] - df_indices[ticker1_name].iloc[0]) / df_indices[ticker1_name].iloc[0]) * 100, 2)
        return_ticker2 = round(((df_indices[ticker2_name].iloc[-1] - df_indices[ticker2_name].iloc[0]) / df_indices[ticker2_name].iloc[0]) * 100, 2)
    
        # Namen mit Rendite für die Legende anpassen
        legend_ticker1 = f"{ticker1_name} = {return_ticker1}%"
        legend_ticker2 = f"{ticker2_name} = {return_ticker2}%"
    
        # Diagramm 2 mit Renditen in der Legende anzeigen
        fig2 = px.line(df_normalized, x=df_normalized.index, y=df_normalized.columns,
                       title="Normierter Vergleich der Close-Preise",
                       labels={"value": "Index (Startwert = 100)", "variable": "Index"},
                       template="plotly_white",
                       color_discrete_map={ticker1_name: color_ticker1, ticker2_name: color_ticker2})
    
        # Legenden-Namen ändern
        fig2.for_each_trace(lambda t: t.update(name=legend_ticker1 if t.name == ticker1_name else legend_ticker2))
    
        # Legendenposition anpassen (oben rechts, wie in Chart 1)
        fig2.update_layout(legend=dict(x=0, y=1))
    
        # Streamlit: Interaktives Chart anzeigen
        st.plotly_chart(fig2, use_container_width=True)
    
        # --------- 3. INTERAKTIVER CHART: Relative Performance (Differenz der normierten Werte) ---------
        df_relative_performance = df_normalized[ticker1_name] - df_normalized[ticker2_name]
    
        # Letzter Wert der relativen Performance berechnen
        relative_performance_change = round(df_relative_performance.iloc[-1], 2)
    
        # Legenden-Text mit relativer Performance
        legend_relative = f"{ticker1_name} - {ticker2_name} = {relative_performance_change}%"
    
        # Chart für relative Performance erstellen
        fig3 = px.line(x=df_relative_performance.index, y=df_relative_performance,
                       title=f"Relative Performance: {ticker1_name} vs. {ticker2_name}",
                       labels={"x": "Datum", "y": "Relative Performance (%)"},
                       template="plotly_white")
    
        # Farbe der Linie auf die gleiche wie Index 1 setzen
        fig3.update_traces(line=dict(color=color_ticker3), name=legend_relative)
    
        # Legendenposition anpassen (oben rechts, wie in Chart 1 und 2)
        fig3.update_layout(legend=dict(x=0, y=1))
    
        # Letzten Wert als Text direkt ins Chart einfügen
        fig3.add_annotation(
            x=df_relative_performance.index[-1],
            y=df_relative_performance.iloc[-1],
            text=f"{df_relative_performance.iloc[-1]:.2f}%",
            showarrow=True,
            arrowhead=2,
            font=dict(size=14, color=color_ticker3),
            ax=20, ay=-40
        )
    
        # Streamlit: Chart anzeigen
        st.plotly_chart(fig3, use_container_width=True)
        
        # Daten-Check: DataFrame anzeigen
        #st.subheader("📊 Rohdaten der ausgewählten Indizes")
        #st.dataframe(df_indices)