Update app.py
Browse files
app.py
CHANGED
@@ -15,9 +15,9 @@ def get_base64_image(image_path):
|
|
15 |
return base64.b64encode(img_file.read()).decode()
|
16 |
|
17 |
# Logo als Base64 einlesen
|
18 |
-
image_base64 = get_base64_image("JQ.png")
|
19 |
|
20 |
-
# CSS für fixiertes Logo OHNE Hintergrund
|
21 |
st.markdown(
|
22 |
f"""
|
23 |
<style>
|
@@ -29,12 +29,6 @@ st.markdown(
|
|
29 |
z-index: 1000;
|
30 |
text-align: center;
|
31 |
}}
|
32 |
-
.small-text {{
|
33 |
-
font-size: 14px; /* Kleinere Schriftgröße */
|
34 |
-
margin-top: 30px; /* Abstand nach unten */
|
35 |
-
text-align: center;
|
36 |
-
color: gray; /* Dezente graue Farbe */
|
37 |
-
}}
|
38 |
</style>
|
39 |
<div class="fixed-logo">
|
40 |
<img src="data:image/png;base64,{image_base64}" width="250">
|
@@ -53,12 +47,49 @@ st.markdown("<br><br><br>", unsafe_allow_html=True)
|
|
53 |
# Titel der App
|
54 |
st.title("📈 Interaktive Analyse von Aktienindizes")
|
55 |
|
56 |
-
# Liste der wichtigsten Aktien-Indizes mit Yahoo Finance Ticker
|
57 |
indices = {
|
|
|
|
|
|
|
|
|
|
|
58 |
"S&P 500 (USA)": "^GSPC",
|
59 |
"Nasdaq 100 (USA)": "^NDX",
|
60 |
-
"Dow Jones (USA)": "^DJI",
|
61 |
"Russell 2000 (USA)": "^RUT",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
# Dropdown für Ticker 1 & Ticker 2
|
@@ -76,37 +107,72 @@ end_date = st.date_input("Enddatum", default_end_date)
|
|
76 |
# Farben für die Linien (fixe Farben für Konsistenz)
|
77 |
color_ticker1 = "#1f77b4" # Blau
|
78 |
color_ticker2 = "#ff7f0e" # Orange
|
|
|
79 |
|
80 |
# Button zum Laden der Daten
|
81 |
if st.button("GO"):
|
82 |
|
83 |
-
# Daten abrufen
|
84 |
-
|
|
|
85 |
for name, symbol in [(ticker1_name, ticker1), (ticker2_name, ticker2)]:
|
86 |
ticker = yf.Ticker(symbol)
|
87 |
df = ticker.history(start=start_date, end=end_date)
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
-
# In DataFrame umwandeln
|
91 |
-
df_indices = pd.DataFrame(data)
|
92 |
|
93 |
-
# Falls keine Daten geladen wurden, abbrechen
|
94 |
-
if df_indices.empty:
|
95 |
-
st.error("❌ Keine Daten für die gewählten Indizes gefunden!")
|
96 |
-
else:
|
97 |
# --------- 1. INTERAKTIVER CHART: Vergleich mit zwei Achsen ---------
|
98 |
fig1 = go.Figure()
|
99 |
-
|
100 |
# Linke Achse (Ticker 1)
|
101 |
fig1.add_trace(go.Scatter(
|
102 |
x=df_indices.index, y=df_indices[ticker1_name], mode='lines',
|
103 |
name=ticker1_name, yaxis='y1', line=dict(color=color_ticker1)))
|
104 |
-
|
105 |
# Rechte Achse (Ticker 2)
|
106 |
fig1.add_trace(go.Scatter(
|
107 |
x=df_indices.index, y=df_indices[ticker2_name], mode='lines',
|
108 |
name=ticker2_name, yaxis='y2', line=dict(color=color_ticker2)))
|
109 |
-
|
110 |
# Layout mit zwei Achsen definieren
|
111 |
fig1.update_layout(
|
112 |
title=f"Vergleich der Close-Preise: {ticker1_name} vs. {ticker2_name}",
|
@@ -116,83 +182,72 @@ if st.button("GO"):
|
|
116 |
legend=dict(x=0, y=1),
|
117 |
hovermode="x"
|
118 |
)
|
119 |
-
|
120 |
# Streamlit: Interaktives Chart anzeigen
|
121 |
st.plotly_chart(fig1, use_container_width=True)
|
122 |
-
|
123 |
# --------- 2. INTERAKTIVER CHART: Normierter Vergleich ---------
|
124 |
df_normalized = df_indices / df_indices.iloc[0] * 100
|
125 |
-
|
126 |
# Rendite berechnen (% Veränderung von Start bis Ende)
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
start_price_ticker2 = df_indices[ticker2_name].iloc[0]
|
132 |
-
end_price_ticker2 = df_indices[ticker2_name].iloc[-1]
|
133 |
-
return_ticker2 = ((end_price_ticker2 - start_price_ticker2) / start_price_ticker2) * 100
|
134 |
-
|
135 |
-
# Rundung auf 2 Nachkommastellen
|
136 |
-
return_ticker1 = round(return_ticker1, 2)
|
137 |
-
return_ticker2 = round(return_ticker2, 2)
|
138 |
-
|
139 |
# Namen mit Rendite für die Legende anpassen
|
140 |
legend_ticker1 = f"{ticker1_name} = {return_ticker1}%"
|
141 |
legend_ticker2 = f"{ticker2_name} = {return_ticker2}%"
|
142 |
-
|
143 |
# Diagramm 2 mit Renditen in der Legende anzeigen
|
144 |
fig2 = px.line(df_normalized, x=df_normalized.index, y=df_normalized.columns,
|
145 |
title="Normierter Vergleich der Close-Preise",
|
146 |
labels={"value": "Index (Startwert = 100)", "variable": "Index"},
|
147 |
template="plotly_white",
|
148 |
color_discrete_map={ticker1_name: color_ticker1, ticker2_name: color_ticker2})
|
149 |
-
|
150 |
# Legenden-Namen ändern
|
151 |
fig2.for_each_trace(lambda t: t.update(name=legend_ticker1 if t.name == ticker1_name else legend_ticker2))
|
152 |
-
|
153 |
# Legendenposition anpassen (oben rechts, wie in Chart 1)
|
154 |
fig2.update_layout(legend=dict(x=0, y=1))
|
155 |
-
|
156 |
-
|
157 |
# Streamlit: Interaktives Chart anzeigen
|
158 |
st.plotly_chart(fig2, use_container_width=True)
|
159 |
-
|
160 |
-
|
161 |
df_relative_performance = df_normalized[ticker1_name] - df_normalized[ticker2_name]
|
162 |
-
|
163 |
-
#
|
164 |
-
|
165 |
-
|
166 |
-
relative_performance_change = end_relative - start_relative # Direkte Differenz
|
167 |
-
|
168 |
-
# Rundung auf 2 Nachkommastellen
|
169 |
-
relative_performance_change = round(relative_performance_change, 2)
|
170 |
-
|
171 |
# Legenden-Text mit relativer Performance
|
172 |
-
legend_relative = f"{ticker1_name} - {ticker2_name} = {relative_performance_change}
|
173 |
-
|
174 |
# Chart für relative Performance erstellen
|
175 |
fig3 = px.line(x=df_relative_performance.index, y=df_relative_performance,
|
176 |
title=f"Relative Performance: {ticker1_name} vs. {ticker2_name}",
|
177 |
-
labels={"x": "Datum", "y": "Relative Performance"},
|
178 |
template="plotly_white")
|
179 |
-
|
180 |
# Farbe der Linie auf die gleiche wie Index 1 setzen
|
181 |
-
fig3.update_traces(line=dict(color=
|
182 |
-
|
183 |
# Legendenposition anpassen (oben rechts, wie in Chart 1 und 2)
|
184 |
fig3.update_layout(legend=dict(x=0, y=1))
|
185 |
-
|
186 |
# Letzten Wert als Text direkt ins Chart einfügen
|
187 |
fig3.add_annotation(
|
188 |
-
x=df_relative_performance.index[-1],
|
189 |
-
y=df_relative_performance.iloc[-1],
|
190 |
-
text=f"{df_relative_performance.iloc[-1]:.2f}%",
|
191 |
showarrow=True,
|
192 |
arrowhead=2,
|
193 |
-
font=dict(size=14, color=
|
194 |
-
ax=20, ay=-40
|
195 |
)
|
196 |
-
|
197 |
# Streamlit: Chart anzeigen
|
198 |
st.plotly_chart(fig3, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
15 |
return base64.b64encode(img_file.read()).decode()
|
16 |
|
17 |
# Logo als Base64 einlesen
|
18 |
+
image_base64 = get_base64_image("JQ.png") # Bild lokal im Space hochgeladen
|
19 |
|
20 |
+
# CSS für fixiertes Logo OHNE Hintergrund
|
21 |
st.markdown(
|
22 |
f"""
|
23 |
<style>
|
|
|
29 |
z-index: 1000;
|
30 |
text-align: center;
|
31 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
</style>
|
33 |
<div class="fixed-logo">
|
34 |
<img src="data:image/png;base64,{image_base64}" width="250">
|
|
|
47 |
# Titel der App
|
48 |
st.title("📈 Interaktive Analyse von Aktienindizes")
|
49 |
|
|
|
50 |
indices = {
|
51 |
+
# 🌎 Globale Indizes
|
52 |
+
#"MSCI World": "^MSCI",
|
53 |
+
#"MSCI Emerging Markets": "EEM",
|
54 |
+
|
55 |
+
# 🇺🇸 USA
|
56 |
"S&P 500 (USA)": "^GSPC",
|
57 |
"Nasdaq 100 (USA)": "^NDX",
|
58 |
+
"Dow Jones Industrial Average (USA)": "^DJI",
|
59 |
"Russell 2000 (USA)": "^RUT",
|
60 |
+
|
61 |
+
# 🇪🇺 Europa
|
62 |
+
"DAX (Deutschland)": "^GDAXI",
|
63 |
+
"MDAX (Deutschland)": "^MDAXI",
|
64 |
+
"CAC 40 (Frankreich)": "^FCHI",
|
65 |
+
"FTSE 100 (UK)": "^FTSE",
|
66 |
+
"EURO STOXX 50": "^STOXX50E",
|
67 |
+
"AEX (Niederlande)": "^AEX",
|
68 |
+
"IBEX 35 (Spanien)": "^IBEX",
|
69 |
+
|
70 |
+
# 🇨🇭 Schweiz
|
71 |
+
"SMI (Schweiz)": "^SSMI",
|
72 |
+
|
73 |
+
# 🇨🇳 China
|
74 |
+
"Shanghai Composite": "000001.SS",
|
75 |
+
"Hang Seng (Hongkong)": "^HSI",
|
76 |
+
|
77 |
+
# 🇯🇵 Japan
|
78 |
+
"Nikkei 225": "^N225",
|
79 |
+
"TOPIX": "^TOPX",
|
80 |
+
|
81 |
+
# 🇮🇳 Indien
|
82 |
+
"BSE Sensex": "^BSESN",
|
83 |
+
"Nifty 50": "^NSEI",
|
84 |
+
|
85 |
+
# 🇦🇺 Australien
|
86 |
+
"ASX 200": "^AXJO",
|
87 |
+
|
88 |
+
# 🇨🇦 Kanada
|
89 |
+
"TSX Composite": "^GSPTSE",
|
90 |
+
|
91 |
+
# 🇧🇷 Brasilien
|
92 |
+
"Bovespa": "^BVSP"
|
93 |
}
|
94 |
|
95 |
# Dropdown für Ticker 1 & Ticker 2
|
|
|
107 |
# Farben für die Linien (fixe Farben für Konsistenz)
|
108 |
color_ticker1 = "#1f77b4" # Blau
|
109 |
color_ticker2 = "#ff7f0e" # Orange
|
110 |
+
color_ticker3 = "#e377c2" # Pink
|
111 |
|
112 |
# Button zum Laden der Daten
|
113 |
if st.button("GO"):
|
114 |
|
115 |
+
# Daten abrufen (Erst `Adj Close`, falls nicht verfügbar `Close`)
|
116 |
+
data_dict = {}
|
117 |
+
|
118 |
for name, symbol in [(ticker1_name, ticker1), (ticker2_name, ticker2)]:
|
119 |
ticker = yf.Ticker(symbol)
|
120 |
df = ticker.history(start=start_date, end=end_date)
|
121 |
+
|
122 |
+
# 🕒 Datum als Index setzen & Uhrzeit entfernen
|
123 |
+
df.index = pd.to_datetime(df.index).date # Konvertiert Timestamp zu Date ohne Uhrzeit
|
124 |
+
|
125 |
+
# Prüfe, ob `Adj Close` existiert, falls nicht, nutze `Close`
|
126 |
+
if "Adj Close" in df.columns and not df["Adj Close"].isna().all():
|
127 |
+
selected_data = df["Adj Close"]
|
128 |
+
elif "Close" in df.columns and not df["Close"].isna().all():
|
129 |
+
selected_data = df["Close"]
|
130 |
+
else:
|
131 |
+
st.warning(f"⚠️ Keine gültigen Daten für {name} ({symbol}) gefunden!")
|
132 |
+
continue # Falls keine Daten vorhanden sind, überspringen
|
133 |
+
|
134 |
+
# 🔹 Doppelte Einträge pro Tag entfernen → den letzten Wert behalten
|
135 |
+
selected_data = selected_data[~selected_data.index.duplicated(keep="last")]
|
136 |
+
|
137 |
+
# Spaltennamen für spätere Zusammenführung setzen
|
138 |
+
selected_data.name = name # Name des Index setzen
|
139 |
+
|
140 |
+
# Speichere bereinigte Daten für den Index
|
141 |
+
data_dict[name] = selected_data
|
142 |
+
|
143 |
+
# Sicherstellen, dass beide DataFrames existieren
|
144 |
+
if ticker1_name in data_dict and ticker2_name in data_dict:
|
145 |
+
df1 = data_dict[ticker1_name].to_frame()
|
146 |
+
df2 = data_dict[ticker2_name].to_frame()
|
147 |
+
|
148 |
+
# 🔹 Beide DataFrames über gemeinsamen `Index (Datum)` zusammenführen
|
149 |
+
df_indices = pd.merge(df1, df2, left_index=True, right_index=True, how="outer")
|
150 |
+
|
151 |
+
# Fehlende Werte mit vorherigem Wert füllen
|
152 |
+
df_indices.ffill(inplace=True)
|
153 |
+
|
154 |
+
# Falls noch NaN-Werte übrig sind, entfernen
|
155 |
+
df_indices.dropna(inplace=True)
|
156 |
+
|
157 |
+
# Falls keine Daten geladen wurden, abbrechen
|
158 |
+
if df_indices.empty:
|
159 |
+
st.error("❌ Keine gemeinsamen Daten für die gewählten Indizes gefunden!")
|
160 |
+
st.stop()
|
161 |
|
|
|
|
|
162 |
|
|
|
|
|
|
|
|
|
163 |
# --------- 1. INTERAKTIVER CHART: Vergleich mit zwei Achsen ---------
|
164 |
fig1 = go.Figure()
|
165 |
+
|
166 |
# Linke Achse (Ticker 1)
|
167 |
fig1.add_trace(go.Scatter(
|
168 |
x=df_indices.index, y=df_indices[ticker1_name], mode='lines',
|
169 |
name=ticker1_name, yaxis='y1', line=dict(color=color_ticker1)))
|
170 |
+
|
171 |
# Rechte Achse (Ticker 2)
|
172 |
fig1.add_trace(go.Scatter(
|
173 |
x=df_indices.index, y=df_indices[ticker2_name], mode='lines',
|
174 |
name=ticker2_name, yaxis='y2', line=dict(color=color_ticker2)))
|
175 |
+
|
176 |
# Layout mit zwei Achsen definieren
|
177 |
fig1.update_layout(
|
178 |
title=f"Vergleich der Close-Preise: {ticker1_name} vs. {ticker2_name}",
|
|
|
182 |
legend=dict(x=0, y=1),
|
183 |
hovermode="x"
|
184 |
)
|
185 |
+
|
186 |
# Streamlit: Interaktives Chart anzeigen
|
187 |
st.plotly_chart(fig1, use_container_width=True)
|
188 |
+
|
189 |
# --------- 2. INTERAKTIVER CHART: Normierter Vergleich ---------
|
190 |
df_normalized = df_indices / df_indices.iloc[0] * 100
|
191 |
+
|
192 |
# Rendite berechnen (% Veränderung von Start bis Ende)
|
193 |
+
return_ticker1 = round(((df_indices[ticker1_name].iloc[-1] - df_indices[ticker1_name].iloc[0]) / df_indices[ticker1_name].iloc[0]) * 100, 2)
|
194 |
+
return_ticker2 = round(((df_indices[ticker2_name].iloc[-1] - df_indices[ticker2_name].iloc[0]) / df_indices[ticker2_name].iloc[0]) * 100, 2)
|
195 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
# Namen mit Rendite für die Legende anpassen
|
197 |
legend_ticker1 = f"{ticker1_name} = {return_ticker1}%"
|
198 |
legend_ticker2 = f"{ticker2_name} = {return_ticker2}%"
|
199 |
+
|
200 |
# Diagramm 2 mit Renditen in der Legende anzeigen
|
201 |
fig2 = px.line(df_normalized, x=df_normalized.index, y=df_normalized.columns,
|
202 |
title="Normierter Vergleich der Close-Preise",
|
203 |
labels={"value": "Index (Startwert = 100)", "variable": "Index"},
|
204 |
template="plotly_white",
|
205 |
color_discrete_map={ticker1_name: color_ticker1, ticker2_name: color_ticker2})
|
206 |
+
|
207 |
# Legenden-Namen ändern
|
208 |
fig2.for_each_trace(lambda t: t.update(name=legend_ticker1 if t.name == ticker1_name else legend_ticker2))
|
209 |
+
|
210 |
# Legendenposition anpassen (oben rechts, wie in Chart 1)
|
211 |
fig2.update_layout(legend=dict(x=0, y=1))
|
212 |
+
|
|
|
213 |
# Streamlit: Interaktives Chart anzeigen
|
214 |
st.plotly_chart(fig2, use_container_width=True)
|
215 |
+
|
216 |
+
# --------- 3. INTERAKTIVER CHART: Relative Performance (Differenz der normierten Werte) ---------
|
217 |
df_relative_performance = df_normalized[ticker1_name] - df_normalized[ticker2_name]
|
218 |
+
|
219 |
+
# Letzter Wert der relativen Performance berechnen
|
220 |
+
relative_performance_change = round(df_relative_performance.iloc[-1], 2)
|
221 |
+
|
|
|
|
|
|
|
|
|
|
|
222 |
# Legenden-Text mit relativer Performance
|
223 |
+
legend_relative = f"{ticker1_name} - {ticker2_name} = {relative_performance_change}%"
|
224 |
+
|
225 |
# Chart für relative Performance erstellen
|
226 |
fig3 = px.line(x=df_relative_performance.index, y=df_relative_performance,
|
227 |
title=f"Relative Performance: {ticker1_name} vs. {ticker2_name}",
|
228 |
+
labels={"x": "Datum", "y": "Relative Performance (%)"},
|
229 |
template="plotly_white")
|
230 |
+
|
231 |
# Farbe der Linie auf die gleiche wie Index 1 setzen
|
232 |
+
fig3.update_traces(line=dict(color=color_ticker3), name=legend_relative)
|
233 |
+
|
234 |
# Legendenposition anpassen (oben rechts, wie in Chart 1 und 2)
|
235 |
fig3.update_layout(legend=dict(x=0, y=1))
|
236 |
+
|
237 |
# Letzten Wert als Text direkt ins Chart einfügen
|
238 |
fig3.add_annotation(
|
239 |
+
x=df_relative_performance.index[-1],
|
240 |
+
y=df_relative_performance.iloc[-1],
|
241 |
+
text=f"{df_relative_performance.iloc[-1]:.2f}%",
|
242 |
showarrow=True,
|
243 |
arrowhead=2,
|
244 |
+
font=dict(size=14, color=color_ticker3),
|
245 |
+
ax=20, ay=-40
|
246 |
)
|
247 |
+
|
248 |
# Streamlit: Chart anzeigen
|
249 |
st.plotly_chart(fig3, use_container_width=True)
|
250 |
+
|
251 |
+
# Daten-Check: DataFrame anzeigen
|
252 |
+
#st.subheader("📊 Rohdaten der ausgewählten Indizes")
|
253 |
+
#st.dataframe(df_indices)
|