Teori Portofolio Markowitz untuk Kripto: Dari Nol Hingga Mahir

Membangun portofolio kripto yang optimal dengan Python - karena YOLO bukan sebuah strategi. Pelajari cara menerapkan teori portofolio pemenang Hadiah Nobel pada investasi kripto dengan contoh kode Python yang praktis.

Membangun portofolio kripto yang optimal dengan Python - karena YOLO bukan sebuah strategi

Teori Portofolio Markowitz Teori Portofolio Markowitz: Optimasi matematis yang diterapkan pada aset digital untuk memaksimalkan imbal hasil pada tingkat risiko tertentu.


Pendahuluan: Mengapa Portofolio Kripto Anda Membutuhkan Matematika (Bukan Hanya Insting)

Halo para degens kripto! 👋

Ingat ketika kamu melempar seluruh modalmu ke DOGE hanya karena Elon nge-tweet? Atau saat kamu panik-jual semuanya selama crash terakhir? Ya, kita semua pernah ada di sana. Hari ini kita akan membahas sesuatu yang mungkin bisa menyelamatkan portofoliomu (dan kewarasanmu): Teori Portofolio Markowitz.

Harry Markowitz secara harfiah memenangkan Hadiah Nobel untuk ini pada tahun 1990. Ide dasarnya? Kamu bisa mengoptimalkan portofoliomu secara matematis untuk mendapatkan imbal hasil terbaik yang mungkin untuk setiap tingkat risiko yang diberikan. Ini seperti memiliki GPS untuk investasimu, bukan berkendara dengan mata tertutup.

Konsep Inti: Risiko vs Imbal Hasil (Tarian Abadi)

Sebelum kita menyelami kode, mari pahami apa yang kita hadapi:

  • Imbal Hasil yang Diharapkan: Seberapa banyak uang yang kamu harapkan untuk didapatkan
  • Risiko (Volatilitas): Seberapa banyak nilai portofoliomu naik turun
  • Korelasi: Seberapa mirip pergerakan aset-aset yang berbeda satu sama lain

Keajaiban terjadi ketika kamu menggabungkan aset-aset yang tidak bergerak secara sempurna serempak. Saat Bitcoin jatuh, mungkin beberapa token DeFi bertahan lebih baik. Itulah diversifikasi yang bekerja untukmu.

Risiko vs Imbal Hasil Risiko vs. Imbal Hasil: Menyeimbangkan aset bervolatilitas tinggi dengan imbal hasil tinggi bersama fondasi yang stabil untuk mencapai imbal hasil rata-rata geometris yang optimal.

Menyiapkan Lingkungan Python Kita

Pertama-tama - mari siapkan alat-alat kita:

# 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")

Level 1: Langkah Awal - Matematika Portofolio Sederhana

Mari mulai dengan dasar-dasarnya. Kita akan menghitung imbal hasil dan risiko untuk portofolio 2 aset sederhana.

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())

Sekarang mari hitung beberapa metrik portofolio dasar:

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}")

Level 2: Serius - The Efficient Frontier

Sekarang kita memasak! The efficient frontier menunjukkan kepada kita semua portofolio optimal yang mungkin. Setiap titik mewakili imbal hasil terbaik yang mungkin untuk tingkat 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):
        # 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()

Efficient Frontier The Efficient Frontier: Kurva yang mewakili imbal hasil yang diharapkan maksimum yang mungkin untuk tingkat risiko tertentu.

Level 3: Penguasaan Optimasi - Menemukan Portofolio Sempurna

Pengambilan sampel acak itu menyenangkan, tetapi kita menginginkan solusi yang optimal secara matematis. Saatnya mengeluarkan senjata besar - optimasi 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 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%}")

Level 4: Multi-Aset - Portofolio Kripto Nyata

Mari skalakan ini ke portofolio kripto yang tepat dengan beberapa aset:

# 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}")

Optimasi Portofolio Multi-Aset Diversifikasi Multi-Aset: Membangun portofolio yang kuat dengan menggabungkan aset kripto yang tidak berkorelasi menjadi struktur geometris yang stabil.

Level 5: Teknik Lanjutan - Black-Litterman dan Risk Parity

Untuk para ninja optimasi portofolio sejati, mari implementasikan 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

# 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()

![Backtesting Strategi Portofolio](/images/blog/markowitz-backtest.webp)
*Backtesting Algoritmik: Mensimulasikan kinerja historis untuk memvalidasi model optimasi teoretis.*


# 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)

Pemeriksaan Realitas: Apa yang Tidak Diceritakan Markowitz

Sebelum kamu terjun penuh ke optimasi matematis, berikut beberapa kebenaran pahit tentang kripto:

1. Kinerja Masa Lalu ≠ Hasil Masa Depan Pasar kripto masih muda dan kacau. Korelasi yang kamu hitung? Bisa berbalik dalam semalam ketika regulasi berubah atau ketika hack besar berikutnya terjadi.

2. Biaya Transaksi Penting Menyeimbangkan kembali portofoliomu membutuhkan biaya. Di DeFi, biaya gas bisa menggerogoti keuntunganmu. Masukkan ini ke dalam strategimu.

3. Masalah Likuiditas Tidak semua kripto sama-sama likuid. Altcoin berkapitalisasi kecil itu mungkin terlihat bagus dalam optimasimu, tetapi coba jual saat crash.

4. Perubahan Rezim Pasar kripto memiliki "rezim" yang berbeda - bull market, bear market, crab market. Apa yang berhasil di satu kondisi mungkin tidak berhasil di kondisi lain.

Tips Implementasi Praktis

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)

Kesimpulan: Perangkat Optimasi Portofoliomu

Kamu sekarang memiliki perangkat lengkap untuk optimasi portofolio kripto:

  1. Kalkulasi dasar untuk risiko dan imbal hasil
  2. Visualisasi efficient frontier
  3. Optimasi matematis untuk berbagai tujuan
  4. Strategi lanjutan seperti risk parity
  5. Kerangka backtesting untuk memvalidasi strategimu
  6. Pertimbangan praktis untuk implementasi di dunia nyata

Poin-Poin Utama

  • Diversifikasi adalah makan siang gratis - satu-satunya makan siang gratis dalam berinvestasi
  • Optimalkan berdasarkan toleransi risikomu - max Sharpe tidak selalu terbaik untukmu
  • Seimbangkan kembali secara sistematis tetapi jangan terlalu banyak trading
  • Tetap rendah hati - model adalah alat, bukan bola kristal
  • Mulai dari yang sederhana dan tambahkan kompleksitas seiring belajar

Ingat: Dalam kripto, bahkan model matematis terbaik pun tidak bisa memprediksi kapan Elon akan men-tweet tentang Dogecoin atau kapan bursa berikutnya akan diretas. Gunakan teori portofolio sebagai fondasimu, tetapi selalu simpan sebagian cadangan dan jangan pernah berinvestasi lebih dari yang bisa kamu tanggung untuk kehilangkan.

Sekarang majulah dan optimalkan dengan bertanggung jawab! 🚀

Bacaan Lebih Lanjut

Repositori Kode

Semua kode dari tutorial ini tersedia di GitHub: https://github.com/suenot/markowitz

Selamat mengoptimalkan! 📈