Markowitz-Portfoliotheorie für Krypto: Vom Anfänger zum Experten
Optimale Krypto-Portfolios mit Python aufbauen - denn YOLO ist keine Strategie
Markowitz-Portfoliotheorie: Mathematische Optimierung, angewendet auf digitale Assets, um die Rendite bei einem gegebenen Risikoniveau zu maximieren.
Einleitung: Warum dein Krypto-Portfolio Mathematik braucht (nicht nur Bauchgefühl)
Hey Crypto-Degens! 👋
Erinnerst du dich noch daran, wie du deinen gesamten Stack in DOGE geworfen hast, weil Elon getwittert hat? Oder als du beim letzten Crash panisch alles verkauft hast? Ja, das kennen wir alle. Heute sprechen wir über etwas, das dein Portfolio (und deinen Verstand) retten könnte: die Markowitz-Portfoliotheorie.
Harry Markowitz hat dafür 1990 tatsächlich den Nobelpreis gewonnen. Die Grundidee? Man kann sein Portfolio mathematisch optimieren, um die bestmögliche Rendite für ein beliebiges Risikoniveau zu erzielen. Es ist wie ein GPS für deine Investments, statt blind durch die Gegend zu fahren.
Das Kernkonzept: Risiko vs. Rendite (der ewige Tanz)
Bevor wir in den Code eintauchen, sollten wir verstehen, womit wir es zu tun haben:
- Erwartete Rendite: Wie viel Geld du zu verdienen erwartest
- Risiko (Volatilität): Wie stark der Wert deines Portfolios schwankt
- Korrelation: Wie ähnlich sich verschiedene Assets zueinander bewegen
Die Magie entsteht, wenn du Assets kombinierst, die sich nicht perfekt synchron bewegen. Wenn Bitcoin abstürzt, halten sich manche DeFi-Token vielleicht besser. Das ist Diversifikation, die für dich arbeitet.
Risiko vs. Rendite: Ausbalancieren volatiler Assets mit hoher Rendite und einer stabilen Grundlage, um die optimale geometrische mittlere Rendite zu erreichen.
Unsere Python-Umgebung einrichten
Zuerst das Wichtigste - machen wir unsere Werkzeuge bereit:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.optimize import minimize
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')
plt.style.use('dark_background')
sns.set_palette("husl")
Stufe 1: Erste Schritte - Einfache Portfolio-Mathematik
Beginnen wir mit den Grundlagen. Wir berechnen Rendite und Risiko für ein einfaches Portfolio aus zwei Assets.
def get_crypto_data(symbols, period="1y"):
"""
Fetch crypto data from Yahoo Finance
symbols: list of crypto symbols (e.g., ['BTC-USD', 'ETH-USD'])
period: time period for data
"""
data = yf.download(symbols, period=period)['Adj Close']
return data
crypto_symbols = ['BTC-USD', 'ETH-USD']
prices = get_crypto_data(crypto_symbols)
returns = prices.pct_change().dropna()
print("Daily Returns Preview:")
print(returns.head())
Jetzt berechnen wir ein paar grundlegende Portfolio-Kennzahlen:
def portfolio_performance(weights, returns):
"""
Calculate portfolio return and volatility
weights: array of portfolio weights
returns: dataframe of asset returns
"""
portfolio_return = np.sum(returns.mean() * weights) * 252
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 252, weights)))
return portfolio_return, portfolio_vol
weights_5050 = np.array([0.5, 0.5])
ret_5050, vol_5050 = portfolio_performance(weights_5050, returns)
print(f"50/50 Portfolio:")
print(f"Expected Annual Return: {ret_5050:.2%}")
print(f"Annual Volatility: {vol_5050:.2%}")
print(f"Sharpe Ratio: {ret_5050/vol_5050:.3f}")
Stufe 2: Es wird ernst - Die Effizienzgrenze
Jetzt geht's ans Eingemachte! Die Effizienzgrenze zeigt uns alle möglichen optimalen Portfolios. Jeder Punkt steht für die bestmögliche Rendite bei einem gegebenen Risikoniveau.
def generate_random_portfolios(returns, num_portfolios=10000):
"""
Generate random portfolio combinations
"""
num_assets = len(returns.columns)
results = np.zeros((4, num_portfolios))
for i in range(num_portfolios):
weights = np.random.random(num_assets)
weights /= np.sum(weights) # Normalize to sum to 1
portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
sharpe_ratio = portfolio_return / portfolio_vol
results[0,i] = portfolio_return
results[1,i] = portfolio_vol
results[2,i] = sharpe_ratio
results[3,i:] = weights
return results
results = generate_random_portfolios(returns)
portfolio_results = pd.DataFrame({
'Returns': results[0],
'Volatility': results[1],
'Sharpe_Ratio': results[2]
})
plt.figure(figsize=(12, 8))
scatter = plt.scatter(portfolio_results['Volatility'],
portfolio_results['Returns'],
c=portfolio_results['Sharpe_Ratio'],
cmap='viridis', alpha=0.6)
plt.colorbar(scatter, label='Sharpe Ratio')
plt.xlabel('Volatility (Risk)')
plt.ylabel('Expected Return')
plt.title('Efficient Frontier - Random Portfolios')
plt.show()
Die Effizienzgrenze: Die Kurve, die die maximal mögliche erwartete Rendite für ein gegebenes Risikoniveau darstellt.
Stufe 3: Optimierungs-Meister - Das perfekte Portfolio finden
Zufallsstichproben machen Spaß, aber wir wollen die mathematisch optimale Lösung. Zeit, die großen Geschütze aufzufahren - scipy-Optimierung!
def negative_sharpe_ratio(weights, returns, risk_free_rate=0.02):
"""
Calculate negative Sharpe ratio (we minimize this)
"""
portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
sharpe = (portfolio_return - risk_free_rate) / portfolio_vol
return -sharpe
def minimize_volatility(weights, returns):
"""
Calculate portfolio volatility (we minimize this)
"""
_, portfolio_vol = portfolio_performance(weights, returns)
return portfolio_vol
def portfolio_return_objective(weights, returns):
"""
Calculate portfolio return (we maximize this)
"""
portfolio_return, _ = portfolio_performance(weights, returns)
return -portfolio_return # Negative because we minimize
def optimize_portfolio(returns, objective='sharpe', target_return=None):
"""
Optimize portfolio based on different objectives
"""
num_assets = len(returns.columns)
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) # Weights sum to 1
bounds = tuple((0, 1) for _ in range(num_assets)) # No short selling
initial_guess = num_assets * [1. / num_assets]
if objective == 'sharpe':
result = minimize(negative_sharpe_ratio, initial_guess,
args=(returns,), method='SLSQP',
bounds=bounds, constraints=constraints)
elif objective == 'min_vol':
result = minimize(minimize_volatility, initial_guess,
args=(returns,), method='SLSQP',
bounds=bounds, constraints=constraints)
elif objective == 'target_return':
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
{'type': 'eq', 'fun': lambda x: portfolio_performance(x, returns)[0] - target_return})
result = minimize(minimize_volatility, initial_guess,
args=(returns,), method='SLSQP',
bounds=bounds, constraints=constraints)
return result
max_sharpe = optimize_portfolio(returns, 'sharpe')
min_vol = optimize_portfolio(returns, 'min_vol')
print("🎯 Maximum Sharpe Ratio Portfolio:")
for i, symbol in enumerate(crypto_symbols):
print(f"{symbol}: {max_sharpe.x[i]:.3f}")
ret_sharpe, vol_sharpe = portfolio_performance(max_sharpe.x, returns)
print(f"Return: {ret_sharpe:.2%}, Volatility: {vol_sharpe:.2%}")
print(f"Sharpe Ratio: {ret_sharpe/vol_sharpe:.3f}\n")
print("🛡️ Minimum Volatility Portfolio:")
for i, symbol in enumerate(crypto_symbols):
print(f"{symbol}: {min_vol.x[i]:.3f}")
ret_minvol, vol_minvol = portfolio_performance(min_vol.x, returns)
print(f"Return: {ret_minvol:.2%}, Volatility: {vol_minvol:.2%}")
Stufe 4: Multi-Asset-Wahnsinn - Ein echtes Krypto-Portfolio
Skalieren wir das jetzt auf ein richtiges Krypto-Portfolio mit mehreren Assets:
crypto_portfolio = ['BTC-USD', 'ETH-USD', 'BNB-USD', 'ADA-USD', 'SOL-USD', 'DOT-USD']
prices_multi = get_crypto_data(crypto_portfolio, period="2y")
returns_multi = prices_multi.pct_change().dropna()
correlation_matrix = returns_multi.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='RdYlBu_r', center=0)
plt.title('Crypto Asset Correlation Matrix')
plt.show()
def efficient_frontier(returns, num_portfolios=50):
"""
Calculate the efficient frontier
"""
ret_range = np.linspace(returns.mean().min()*252, returns.mean().max()*252, num_portfolios)
efficient_portfolios = []
for target_ret in ret_range:
try:
result = optimize_portfolio(returns, 'target_return', target_ret)
if result.success:
ret, vol = portfolio_performance(result.x, returns)
efficient_portfolios.append([ret, vol, result.x])
except:
continue
return np.array(efficient_portfolios)
efficient_port = efficient_frontier(returns_multi)
plt.figure(figsize=(14, 10))
random_results = generate_random_portfolios(returns_multi, 5000)
plt.scatter(random_results[1], random_results[0],
c=random_results[2], cmap='viridis', alpha=0.3, s=10)
if len(efficient_port) > 0:
plt.plot(efficient_port[:,1], efficient_port[:,0], 'r-', linewidth=3, label='Efficient Frontier')
max_sharpe_multi = optimize_portfolio(returns_multi, 'sharpe')
min_vol_multi = optimize_portfolio(returns_multi, 'min_vol')
ret_sharpe_multi, vol_sharpe_multi = portfolio_performance(max_sharpe_multi.x, returns_multi)
ret_minvol_multi, vol_minvol_multi = portfolio_performance(min_vol_multi.x, returns_multi)
plt.scatter(vol_sharpe_multi, ret_sharpe_multi, marker='*', color='gold', s=500, label='Max Sharpe')
plt.scatter(vol_minvol_multi, ret_minvol_multi, marker='*', color='red', s=500, label='Min Volatility')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility (Risk)')
plt.ylabel('Expected Return')
plt.title('Multi-Asset Crypto Portfolio Optimization')
plt.legend()
plt.show()
print("🚀 Optimal Multi-Asset Allocations:")
print("\nMaximum Sharpe Ratio Portfolio:")
sharpe_weights = pd.Series(max_sharpe_multi.x, index=crypto_portfolio).sort_values(ascending=False)
for asset, weight in sharpe_weights.items():
if weight > 0.01: # Only show significant allocations
print(f"{asset}: {weight:.1%}")
print(f"\nPortfolio Metrics:")
print(f"Expected Return: {ret_sharpe_multi:.1%}")
print(f"Volatility: {vol_sharpe_multi:.1%}")
print(f"Sharpe Ratio: {ret_sharpe_multi/vol_sharpe_multi:.2f}")
Multi-Asset-Diversifikation: Aufbau eines robusten Portfolios durch Kombination unkorrelierter Krypto-Assets zu einer stabilen geometrischen Struktur.
Stufe 5: Fortgeschrittene Techniken - Black-Litterman und Risikoparität
Für die wahren Ninjas der Portfoliooptimierung implementieren wir jetzt einige fortgeschrittene Techniken:
def risk_parity_portfolio(returns):
"""
Risk Parity Portfolio - each asset contributes equally to portfolio risk
"""
def risk_contribution(weights, cov_matrix):
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
marginal_contrib = np.dot(cov_matrix, weights) / portfolio_vol
contrib = weights * marginal_contrib
return contrib
def risk_parity_objective(weights, cov_matrix):
contrib = risk_contribution(weights, cov_matrix)
target_contrib = np.ones(len(weights)) / len(weights)
return np.sum((contrib - target_contrib)**2)
num_assets = len(returns.columns)
cov_matrix = returns.cov() * 252
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
bounds = tuple((0.001, 1) for _ in range(num_assets))
initial_guess = num_assets * [1. / num_assets]
result = minimize(risk_parity_objective, initial_guess,
args=(cov_matrix,), method='SLSQP',
bounds=bounds, constraints=constraints)
return result
risk_parity_result = risk_parity_portfolio(returns_multi)
print("⚖️ Risk Parity Portfolio:")
rp_weights = pd.Series(risk_parity_result.x, index=crypto_portfolio).sort_values(ascending=False)
for asset, weight in rp_weights.items():
print(f"{asset}: {weight:.1%}")
ret_rp, vol_rp = portfolio_performance(risk_parity_result.x, returns_multi)
print(f"\nRisk Parity Metrics:")
print(f"Expected Return: {ret_rp:.1%}")
print(f"Volatility: {vol_rp:.1%}")
print(f"Sharpe Ratio: {ret_rp/vol_rp:.2f}")
def backtest_portfolio(weights, prices):
"""
Simple backtest of portfolio performance
"""
returns = prices.pct_change().dropna()
portfolio_returns = (returns * weights).sum(axis=1)
cumulative_returns = (1 + portfolio_returns).cumprod()
total_return = cumulative_returns.iloc[-1] - 1
annualized_return = (1 + total_return) ** (252 / len(portfolio_returns)) - 1
annualized_vol = portfolio_returns.std() * np.sqrt(252)
sharpe_ratio = annualized_return / annualized_vol
max_dd = (cumulative_returns / cumulative_returns.expanding().max() - 1).min()
return {
'total_return': total_return,
'annualized_return': annualized_return,
'annualized_volatility': annualized_vol,
'sharpe_ratio': sharpe_ratio,
'max_drawdown': max_dd,
'cumulative_returns': cumulative_returns
}
strategies = {
'Max Sharpe': max_sharpe_multi.x,
'Min Volatility': min_vol_multi.x,
'Risk Parity': risk_parity_result.x,
'Equal Weight': np.ones(len(crypto_portfolio)) / len(crypto_portfolio)
}
plt.figure(figsize=(14, 8))
for name, weights in strategies.items():
backtest_results = backtest_portfolio(weights, prices_multi)
plt.plot(backtest_results['cumulative_returns'], label=f"{name} (Sharpe: {backtest_results['sharpe_ratio']:.2f})")
plt.title('Portfolio Strategy Backtests')
plt.xlabel('Date')
plt.ylabel('Cumulative Returns')
plt.legend()
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.show()

*Algorithmisches Backtesting: Simulation historischer Performance zur Validierung theoretischer Optimierungsmodelle.*
performance_summary = pd.DataFrame()
for name, weights in strategies.items():
results = backtest_portfolio(weights, prices_multi)
performance_summary[name] = [
f"{results['annualized_return']:.1%}",
f"{results['annualized_volatility']:.1%}",
f"{results['sharpe_ratio']:.2f}",
f"{results['max_drawdown']:.1%}"
]
performance_summary.index = ['Annual Return', 'Annual Volatility', 'Sharpe Ratio', 'Max Drawdown']
print("\n📊 Strategy Performance Summary:")
print(performance_summary)
Der Realitätscheck: Was Markowitz dir nicht verrät
Bevor du voll auf mathematische Optimierung setzt, hier ein paar harte Wahrheiten über Krypto:
1. Vergangene Performance ≠ Zukünftige Ergebnisse Der Kryptomarkt ist jung und chaotisch. Die Korrelationen, die du berechnet hast? Die können sich über Nacht umkehren, wenn sich Regulierungen ändern oder der nächste große Hack passiert.
2. Transaktionskosten spielen eine Rolle Das Rebalancing deines Portfolios kostet Geld. In DeFi können Gas-Gebühren dir das Mittagessen wegfressen. Berücksichtige das in deiner Strategie.
3. Liquiditätsprobleme Nicht alle Kryptowährungen sind gleich liquide. Dieser Small-Cap-Altcoin sieht in deiner Optimierung vielleicht super aus, aber versuch mal, ihn während eines Crashs zu verkaufen.
4. Regimewechsel Kryptomärkte haben verschiedene "Regime" - Bullenmärkte, Bärenmärkte, Seitwärtsmärkte. Was in dem einen funktioniert, funktioniert vielleicht nicht im anderen.
Praktische Umsetzungstipps
def practical_portfolio_rebalancing(target_weights, current_weights, threshold=0.05):
"""
Only rebalance when weights drift beyond threshold
"""
weight_diff = np.abs(target_weights - current_weights)
needs_rebalancing = np.any(weight_diff > threshold)
if needs_rebalancing:
print("🔄 Rebalancing needed!")
for i, (target, current) in enumerate(zip(target_weights, current_weights)):
if abs(target - current) > threshold:
print(f"Asset {i}: {current:.1%} → {target:.1%}")
else:
print("✅ Portfolio within tolerance, no rebalancing needed")
return needs_rebalancing
current_allocation = np.array([0.35, 0.25, 0.15, 0.10, 0.10, 0.05])
target_allocation = max_sharpe_multi.x
practical_portfolio_rebalancing(target_allocation, current_allocation)
Fazit: Dein Toolkit für Portfoliooptimierung
Du hast jetzt ein komplettes Toolkit für die Optimierung von Krypto-Portfolios:
- Grundlegende Berechnungen für Risiko und Rendite
- Effizienzgrenzen-Visualisierung
- Mathematische Optimierung für verschiedene Zielsetzungen
- Fortgeschrittene Strategien wie Risikoparität
- Backtesting-Framework zur Validierung deiner Strategien
- Praktische Überlegungen für die Umsetzung in der realen Welt
Die wichtigsten Erkenntnisse
- Diversifikation ist ein Free Lunch - das einzige Free Lunch beim Investieren
- Optimiere entsprechend deiner Risikotoleranz - maximale Sharpe Ratio ist nicht immer das Beste für dich
- Rebalanciere systematisch, aber übertreibe es nicht mit dem Handeln
- Bleib bescheiden - Modelle sind Werkzeuge, keine Kristallkugeln
- Fang einfach an und füge nach und nach Komplexität hinzu
Denk dran: Selbst die besten mathematischen Modelle können in Krypto nicht vorhersagen, wann Elon über Dogecoin twittert oder wann die nächste Börse gehackt wird. Nutze die Portfoliotheorie als Fundament, aber halte immer etwas Pulver trocken und investiere nie mehr, als du dir leisten kannst zu verlieren.
Und jetzt raus mit dir und verantwortungsvoll optimieren! 🚀
Weiterführende Lektüre
Code-Repository
Der gesamte Code aus diesem Tutorial ist auf GitHub verfügbar: https://github.com/suenot/markowitz
Viel Erfolg beim Optimieren! 📈
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.