Markowitz-Portfoliotheorie für Krypto: Vom Anfänger zum Experten
Optimale Krypto-Portfolios mit Python aufbauen - denn YOLO ist keine Strategie. Lerne, wie du die mit dem Nobelpreis ausgezeichnete Portfoliotheorie mit praktischen Python-Codebeispielen auf Krypto-Investments anwendest.

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:
# Essential libraries for our crypto portfolio optimization
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')
# Set up plotting style
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
# Get data for Bitcoin and Ethereum
crypto_symbols = ['BTC-USD', 'ETH-USD']
prices = get_crypto_data(crypto_symbols)
# Calculate daily returns
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 (annualized)
portfolio_return = np.sum(returns.mean() * weights) * 252
# Portfolio volatility (annualized)
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 252, weights)))
return portfolio_return, portfolio_vol
# Example: 50% BTC, 50% ETH
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):
# Generate random weights
weights = np.random.random(num_assets)
weights /= np.sum(weights) # Normalize to sum to 1
# Calculate portfolio metrics
portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
sharpe_ratio = portfolio_return / portfolio_vol
# Store results
results[0,i] = portfolio_return
results[1,i] = portfolio_vol
results[2,i] = sharpe_ratio
results[3,i:] = weights
return results
# Generate 10,000 random portfolios
results = generate_random_portfolios(returns)
# Create a dataframe for easier handling
portfolio_results = pd.DataFrame({
'Returns': results[0],
'Volatility': results[1],
'Sharpe_Ratio': results[2]
})
# Plot the efficient frontier
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 and bounds
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
initial_guess = num_assets * [1. / num_assets]
if objective == 'sharpe':
# Maximize Sharpe ratio
result = minimize(negative_sharpe_ratio, initial_guess,
args=(returns,), method='SLSQP',
bounds=bounds, constraints=constraints)
elif objective == 'min_vol':
# Minimize volatility
result = minimize(minimize_volatility, initial_guess,
args=(returns,), method='SLSQP',
bounds=bounds, constraints=constraints)
elif objective == 'target_return':
# Minimize volatility for 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
# Find optimal portfolios
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:
# Expand to more cryptocurrencies
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()
# Check correlations
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
"""
# Calculate range of target returns
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)
# Calculate efficient frontier
efficient_port = efficient_frontier(returns_multi)
# Plot everything together
plt.figure(figsize=(14, 10))
# Random portfolios
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)
# Efficient frontier
if len(efficient_port) > 0:
plt.plot(efficient_port[:,1], efficient_port[:,0], 'r-', linewidth=3, label='Efficient Frontier')
# Optimal portfolios
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()
# Display optimal allocations
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
# Calculate Risk Parity portfolio
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)
# Calculate cumulative returns
cumulative_returns = (1 + portfolio_returns).cumprod()
# Performance metrics
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
}
# Backtest our strategies
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 table
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
# Example usage
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! 📈