Teori Portfolio Markowitz untuk Kripto: Dari Nol hingga Mahir
Membina portfolio kripto yang optimum dengan Python - kerana YOLO bukanlah strategi
Teori Portfolio Markowitz: Pengoptimuman matematik yang diterapkan pada aset digital untuk memaksimumkan pulangan bagi tahap risiko tertentu.
Pengenalan: Mengapa Portfolio Kripto Anda Memerlukan Matematik (Bukan Sekadar Naluri)
Hai para pelabur kripto! 👋
Ingat ketika anda melabur semua wang ke dalam DOGE kerana Elon menulis tweet? Atau ketika anda menjual semua aset dalam panik semasa kejatuhan terakhir? Ya, kita semua pernah melaluinya. Hari ini kita akan membincangkan sesuatu yang mungkin menyelamatkan portfolio anda (dan kewarasan anda): Teori Portfolio Markowitz.
Harry Markowitz secara harfiah memenangi Hadiah Nobel untuk perkara ini pada tahun 1990. Idea asasnya? Anda boleh mengoptimumkan portfolio anda secara matematik untuk mendapatkan pulangan terbaik yang mungkin bagi setiap tahap risiko tertentu. Ia seperti mempunyai GPS untuk pelaburan anda berbanding memandu sambil menutup mata.
Konsep Teras: Risiko vs Pulangan (Tarian Abadi)
Sebelum kita menyelami kod, mari kita fahami apa yang sedang kita hadapi:
- Pulangan Dijangka: Berapa banyak wang yang anda jangkakan akan diperolehi
- Risiko (Volatiliti): Sejauh mana nilai portfolio anda berubah-ubah
- Korelasi: Sejauh mana aset-aset berbeza bergerak seiring
Keajaibannya berlaku apabila anda menggabungkan aset-aset yang tidak bergerak secara segerak. Apabila Bitcoin jatuh, mungkin beberapa token DeFi bertahan lebih baik. Itulah kepelbagaian yang bekerja untuk anda.
Risiko vs. Pulangan: Mengimbangi aset berisiko tinggi berpulangan tinggi dengan asas yang stabil untuk mencapai pulangan min geometri yang optimum.
Menyediakan Persekitaran Python Kami
Pertama sekali - mari kita sediakan alatan kami:
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")
Tahap 1: Langkah Pertama - Matematik Portfolio Mudah
Mari kita mulakan dengan asas-asas. Kita akan mengira pulangan dan risiko untuk portfolio 2 aset yang mudah.
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())
Sekarang mari kita kira beberapa metrik portfolio asas:
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}")
Tahap 2: Menjadi Serius - Sempadan Cekap
Sekarang kita sudah sedia! Sempadan cekap menunjukkan kepada kita semua portfolio optimum yang mungkin. Setiap titik mewakili pulangan terbaik yang mungkin bagi tahap risiko tertentu.
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()
Sempadan Cekap: Lengkung yang mewakili pulangan dijangka maksimum yang mungkin bagi tahap risiko tertentu.
Tahap 3: Mahir Pengoptimuman - Mencari Portfolio Sempurna
Persampelan rawak memang menyeronokkan, tetapi kita mahukan penyelesaian yang optimum secara matematik. Tiba masanya untuk mengeluarkan senjata besar - pengoptimuman scipy!
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%}")
Tahap 4: Banyak Aset - Portfolio Kripto Sebenar
Mari kita kembangkan ini kepada portfolio kripto yang betul dengan pelbagai aset:
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}")
Kepelbagaian Pelbagai Aset: Membina portfolio yang kukuh dengan menggabungkan aset kripto yang tidak berkorelasi ke dalam struktur geometri yang stabil.
Tahap 5: Teknik Lanjutan - Black-Litterman dan Pariti Risiko
Untuk para ninja pengoptimuman portfolio sejati, mari kita laksanakan beberapa teknik lanjutan:
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()

*Backtesting Algoritmik: Mensimulasikan prestasi sejarah untuk mengesahkan model pengoptimuman teoritikal.*
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)
Pemeriksaan Realiti: Apa yang Markowitz Tidak Beritahu Anda
Sebelum anda bergantung sepenuhnya pada pengoptimuman matematik, berikut adalah beberapa kebenaran pahit tentang kripto:
1. Prestasi Lepas ≠ Hasil Masa Depan Pasaran kripto masih muda dan tidak menentu. Korelasi yang anda kirakan itu? Ia mungkin berubah dalam semalaman apabila peraturan berubah atau apabila penggodaman besar seterusnya berlaku.
2. Kos Transaksi Penting Mengimbangi semula portfolio anda memerlukan wang. Dalam DeFi, yuran gas boleh menghabiskan keuntungan anda. Ambil kira perkara ini dalam strategi anda.
3. Isu Kecairan Tidak semua kripto mempunyai kecairan yang sama. Altcoin bermodal kecil itu mungkin kelihatan bagus dalam pengoptimuman anda, tetapi cuba jualnya semasa kejatuhan pasaran.
4. Perubahan Rejim Pasaran kripto mempunyai "rejim" yang berbeza - pasaran lembu, pasaran beruang, pasaran ketam. Apa yang berkesan dalam satu rejim mungkin tidak berkesan dalam rejim lain.
Petua Pelaksanaan Praktikal
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)
Kesimpulan: Kit Alatan Pengoptimuman Portfolio Anda
Anda kini mempunyai kit alatan yang lengkap untuk pengoptimuman portfolio kripto:
- Pengiraan asas untuk risiko dan pulangan
- Visualisasi sempadan cekap
- Pengoptimuman matematik untuk objektif yang berbeza
- Strategi lanjutan seperti pariti risiko
- Rangka kerja backtesting untuk mengesahkan strategi anda
- Pertimbangan praktikal untuk pelaksanaan dunia sebenar
Perkara Utama yang Perlu Diingat
- Kepelbagaian adalah makan tengah hari percuma - satu-satunya makan tengah hari percuma dalam pelaburan
- Optimumkan berdasarkan toleransi risiko anda - nisbah Sharpe maksimum tidak selalu terbaik untuk anda
- Imbangkan semula secara sistematik tetapi jangan berdagang secara berlebihan
- Kekalkan kerendahan hati - model adalah alatan, bukan bola kristal
- Mulakan dengan mudah dan tambah kerumitan seiring dengan pembelajaran anda
Ingat: Dalam kripto, walaupun model matematik terbaik tidak dapat meramalkan bila Elon akan menulis tweet tentang Dogecoin atau bila bursa seterusnya akan digodam. Gunakan teori portfolio sebagai asas anda, tetapi sentiasa simpan sebahagian wang tunai dan jangan pernah melabur lebih daripada yang anda mampu untuk kehilangan.
Kini pergilah dan optimalkan dengan bertanggungjawab! 🚀
Bacaan Lanjut
Repositori Kod
Semua kod dari tutorial ini tersedia di GitHub: https://github.com/suenot/markowitz
Selamat mengoptimumkan! 📈
Pengarang
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.