Крипто үчүн Марковиц портфель теориясы: нөлдөн баатырга чейин

Python менен оптималдуу крипто портфелдерди түзүү - анткени YOLO стратегия эмес. Нобель сыйлыгын алган портфель теориясын практикалык Python код мисалдары менен крипто инвестицияларга кантип колдонууну үйрөнүңүз.

Python менен оптималдуу крипто портфелдерди түзүү - анткени YOLO стратегия эмес

Markowitz Portfolio Theory Марковиц портфель теориясы: белгилүү бир тобокелдик деңгээли үчүн кирешени максималдаштыруу максатында санарип активдерге колдонулган математикалык оптималдаштыруу.


Киришүү: эмне үчүн сиздин крипто портфелиңизге сезим эмес, математика керек

Салам, крипто-дегендер! 👋

Илон твит жазганы үчүн бүт стегиңизди DOGE'го таштап жибергениңизди эстейсизби? Же болбосо акыркы кулаган учурда баарын дүрбөлөңдө сатып жибергениңизди? Ооба, баарыбыз да ушундан өттүк. Бүгүн биз портфелиңизди (жана акыл-эсиңизди) сактап кала турган нерсе жөнүндө сүйлөшөбүз: Марковиц портфель теориясы.

Гарри Марковиц бул үчүн 1990-жылы чын эле Нобель сыйлыгын алган. Негизги идея кандай? Сиз каалаган тобокелдик деңгээли үчүн эң жакшы мүмкүн болгон кирешени алуу үчүн портфелиңизди математикалык жол менен оптималдаштыра аласыз. Бул көзүңүздү таңып айдоонун ордуна инвестицияларыңыз үчүн GPS болгону менен бирдей.

Негизги түшүнүк: тобокелдик менен киреше (түбөлүктүү бий)

Кодго киришээрден мурун, эмне менен алектенип жатканыбызды түшүнөлү:

  • Күтүлгөн киреше: канча акча табам деп күтөсүз
  • Тобокелдик (өзгөрмөлүүлүк): портфелиңиздин наркы канчалык термелет
  • Корреляция: ар түрдүү активдер бири-бирине окшош кандай кыймылдайт

Сыйкыр так синхрондуу кыймылдабаган активдерди бириктиргенде пайда болот. Bitcoin кулаганда, кээ бир DeFi токендер жакшыраак кармалышы мүмкүн. Бул сиз үчүн иштеп жаткан диверсификация.

Risk vs Return Тобокелдик менен киреше: оптималдуу геометриялык орточо кирешеге жетүү үчүн жогорку кирешелүү, өзгөрмөлүү активдерди туруктуу негиз менен теңдештирүү.

Python чөйрөбүздү орнотуу

Адегенде эң башкысы - куралдарыбызды даярдайлы:

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

1-деңгээл: алгачкы кадамдар - жөнөкөй портфель математикасы

Негиздерден баштайлы. Биз жөнөкөй 2 активдүү портфель үчүн киреше менен тобокелдикти эсептейбиз.

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

Эми бир нече негизги портфель көрсөткүчтөрүн эсептейли:

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

2-деңгээл: маселе олуттуулашат - эффективдүү чек ара (efficient frontier)

Эми чыныгы иш башталат! Эффективдүү чек ара бизге бардык мүмкүн болгон оптималдуу портфелдерди көрсөтөт. Ар бир чекит берилген тобокелдик деңгээли үчүн эң жакшы мүмкүн болгон кирешени билдирет.

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 Эффективдүү чек ара: берилген тобокелдик деңгээли үчүн максималдуу мүмкүн болгон күтүлгөн кирешени билдирген ийри сызык.

3-деңгээл: оптималдаштыруу чебери - эң мыкты портфелди табуу

Кокустук иргөө кызыктуу, бирок бизге математикалык жактан оптималдуу чечим керек. Оор артиллерияны чыгаруучу учур келди - 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%}")

4-деңгээл: көп активдүү дүрбөлөң - чыныгы крипто портфель

Муну бир нече активтен турган толук кандуу крипто портфелине чейин кеңейтели:

# 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 Portfolio Optimization Көп активдүү диверсификация: туруктуу геометриялык структурада корреляциясыз крипто активдерин бириктирүү менен бекем портфель түзүү.

5-деңгээл: өнүккөн ыкмалар - Black-Litterman жана тобокелдик паритети

Портфель оптималдаштыруунун чыныгы нинзялары үчүн бир нече өнүккөн ыкманы ишке ашыралы:

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

![Portfolio Strategy Backtests](/images/blog/markowitz-backtest.webp)
*Алгоритмдик бэктестинг: теориялык оптималдаштыруу моделдерин ырастоо үчүн тарыхый көрсөткүчтөрдү симуляциялоо.*


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

Чындыкты текшерүү: Марковиц айтпаган нерселер

Математикалык оптималдаштырууга толугу менен ишенип кетээрден мурун, крипто жөнүндөгү бир нече катуу чындыктар:

1. Мурунку көрсөткүчтөр келечектеги натыйжаларга барабар эмес Крипто рыногу жаш жана баш аламан. Сиз эсептеген корреляциялар эмне болот? Жөнгө салуулар өзгөргөндө же кийинки чоң хакердик чабуул болгондо алар бир түндө тескери бурулуп кетиши мүмкүн.

2. Транзакция чыгымдары маанилүү Портфелиңизди кайра тең салмактоо акча талап кылат. DeFi'де газ акысы пайдаңызды жеп коюшу мүмкүн. Муну стратегияңызда эске алыңыз.

3. Ликвиддүүлүк маселелери Бардык криптовалюталар бирдей ликвиддүү эмес. Ал майда капиталдуу альткоин оптималдаштырууда сонун көрүнүшү мүмкүн, бирок аны кулоо учурунда сатып көрүңүз.

4. Режим өзгөрүүлөрү Крипто рыноктору ар кандай "режимдерге" ээ - өсүү рыногу, түшүү рыногу, туруктуу рынок. Биринде иштегени экинчисинде иштебеши мүмкүн.

Практикалык ишке ашыруу боюнча кеңештер

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)

Корутунду: сиздин портфелди оптималдаштыруу куралдар топтомуңуз

Эми сизде крипто портфелин оптималдаштыруу үчүн толук куралдар топтому бар:

  1. Тобокелдик менен киреше үчүн негизги эсептөөлөр
  2. Эффективдүү чек араны визуализациялоо
  3. Ар кандай максаттар үчүн математикалык оптималдаштыруу
  4. Тобокелдик паритети сыяктуу өнүккөн стратегиялар
  5. Стратегияларыңызды ырастоо үчүн бэктестинг фреймворку
  6. Реалдуу дүйнөдө ишке ашыруу үчүн практикалык ойлор

Негизги тыянактар

  • Диверсификация - бекер түшкү тамак - инвестициядагы жалгыз бекер нерсе
  • Тобокелдикке чыдамдуулугуңузга жараша оптималдаштырыңыз - максималдуу Шарп коэффициенти ар дайым сиз үчүн эң жакшысы боло бербейт
  • Системалуу түрдө кайра тең салмактаңыз, бирок ашыкча соода кылбаңыз
  • Момун болуңуз - моделдер курал, кристалл шар эмес
  • Жөнөкөйдөн баштаңыз жана үйрөнгөн сайын татаалдыкты кошуңуз

Эсиңизде болсун: криптодо эң мыкты математикалык моделдер да Илондун Dogecoin жөнүндө качан твит жазарын же кийинки биржанын качан бузуларын божомолдой албайт. Портфель теориясын негиз катары колдонуңуз, бирок ар дайым бир аз дары кургак сактаңыз жана жоготууга даяр эмес сумманы эч качан инвестицияламаңыз.

Эми барыңыз жана жоопкерчилик менен оптималдаштырыңыз! 🚀

Кошумча окуу

Код репозиторийи

Бул окуу куралынын бардык коду GitHub'до жеткиликтүү: https://github.com/suenot/markowitz

Ийгиликтүү оптималдаштыруу! 📈