Jag fick lite feeling och fastnade en stund vid datorn. Jag uppdaterade min tidigare kod med dataserier för en enkel allvädersportfölj.
Not: Tickers i diagrammet är de som @Zino använt i första inlägget i sin tråd om allvädersportföljer:
Jag har hämtat data från Yahoo Finance och där används andra tickers, se nedan.
WEBN=WEBG.DE
Captor Iris Bond (B)=0P0001H70E.ST
EN4C=EN4C.DE
GLDA=GOLD.MI
Python-koden
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timedelta
# Datumintervall – senaste 365 dagar
end_date = datetime.today()
start_date = end_date - timedelta(days=365)
# Hämta data: WEBG, EN4C, samt GOLD i EUR, Captor Iris i SEK, och EUR/SEK-växelkurs
webg = yf.download("WEBG.DE", start=start_date, end=end_date, auto_adjust=False)
print("WEBG loaded:", not webg.empty)
en4c = yf.download("EN4C.DE", start=start_date, end=end_date, auto_adjust=False)
print("EN4C loaded:", not en4c.empty)
gold = yf.download("GOLD.MI", start=start_date, end=end_date, auto_adjust=False)
print("GOLD loaded:", not gold.empty)
eursek = yf.download("EURSEK=X", start=start_date, end=end_date, auto_adjust=False) # EUR/SEK
print("EUR/SEK loaded:", not eursek.empty)
captor_iris = yf.download("0P0001H70E.ST", start=start_date, end=end_date, auto_adjust=False) # ISIN + .ST ibland funkar
print("Captor Iris loaded:", not captor_iris.empty)
# Debug
"""
# Testa: skriv ut första 5 rader av varje
print("WEBG.DE:\n", webg.head(), "\n")
print("EN4C.DE:\n", en4c.head(), "\n")
print("GOLD.MI:\n", gold.head(), "\n")
print("EURSEK=X:\n", eursek.head(), "\n")
print("Captor Iris (SEK):\n", captor_iris.head(), "\n")
"""
# Säker extraktion av 'Close' även om vi får en DataFrame med MultiIndex (efter nya yfinance-versioner)
def extract_close(df, name):
if isinstance(df.columns, pd.MultiIndex):
return df.loc[:, ("Close", name)]
else:
return df["Close"]
# Byt namn på serierna
webg_close = extract_close(webg, "WEBG.DE")
webg_close.name = "WEBG_EUR"
en4c_close = extract_close(en4c, "EN4C.DE")
en4c_close.name = "EN4C_EUR"
gold_close = extract_close(gold, "GOLD.MI")
gold_close.name = "GOLD_EUR"
eursek_close = extract_close(eursek, "EURSEK=X")
eursek_close.name = "EURSEK"
captor_close = extract_close(captor_iris, "0P0001H70E.ST")
captor_close.name = "Captor_SEK"
# Kombinera dem i en gemensam DataFrame
df = pd.concat([webg_close, en4c_close, gold_close, eursek_close, captor_close], axis=1)
df.dropna(inplace=True)
# Ta bort alla rader där någon av dem saknas (t.ex. helgdagar eller stängd börs)
df.dropna(inplace=True)
# Kontroll om DataFrame blev tom efter sammanslagningen
if df.empty:
raise ValueError("Inga gemensamma datum – datan kan vara tom eller sakna överlapp.")
# Omvandla till SEK
df["WEBG_SEK"] = df["WEBG_EUR"] * df["EURSEK"]
df["EN4C_SEK"] = df["EN4C_EUR"] * df["EURSEK"]
df["GOLD_SEK"] = df["GOLD_EUR"] * df["EURSEK"]
# Indexera utvecklingen (börja från 100)
for col in ["WEBG_EUR", "WEBG_SEK", "EN4C_EUR", "EN4C_SEK", "GOLD_EUR", "GOLD_SEK", "Captor_SEK", "EURSEK"]:
df[f"{col}_pct"] = (df[col] / df[col].iloc[0]) * 100
# Beräkna dagliga logaritmiska avkastningar
cols_for_vol = ["WEBG_EUR", "WEBG_SEK", "EN4C_EUR", "EN4C_SEK", "GOLD_EUR", "GOLD_SEK", "Captor_SEK", "EURSEK"]
log_returns = np.log(df[cols_for_vol] / df[cols_for_vol].shift(1))
# Beräkna årlig volatilitet (standardavvikelse * sqrt(252))
volatility = log_returns.std() * np.sqrt(252)
# Konvertera till procent, t.ex. 0.25 → 25.0%
volatility_pct = (volatility * 100).round(2)
# Plot
plt.figure(figsize=(14, 7))
plot_config = [
("WEBG_EUR_pct", "Amundi ACWI (WEBN) i EUR", "blue", ":"),
("WEBG_SEK_pct", "Amundi ACWI (WEBN) i SEK", "blue", "-"),
("EN4C_EUR_pct", "Commodities (EN4C) i EUR", "red", ":"),
("EN4C_SEK_pct", "Commodities (EN4C) i SEK", "red", "-"),
("GOLD_EUR_pct", "Guld ETC (GLDA) i EUR", "orange", ":"),
("GOLD_SEK_pct", "Guld ETC (GLDA) i SEK", "orange", "-"),
("Captor_SEK_pct", "Captor Iris Bond B i SEK", "green", "-"),
("EURSEK_pct", "EUR/SEK", "black", "--")
]
for col, label, color, style in plot_config:
plt.plot(df.index, df[col], label=label, color=color, linestyle=style)
# Volatilitets-etiketter
last_date = df.index[-1]
for col, label, color, _ in plot_config:
vol = volatility_pct.get(col.replace("_pct", ""), None)
if vol is not None:
plt.text(last_date, df[col].iloc[-1], f"Vol: {vol}%", color=color, fontsize=8)
plt.title("Enkel allvädersportfölj - Indexerad utveckling & volatilitet (senaste 12 mån)")
plt.xlabel("Datum")
plt.ylabel("Index (start = 100)")
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()