← Мақалаларға оралу
September 25, 2025
5 мин оқу

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

Криптовалютаға арналған Марковиц портфель теориясы: нөлден кейіпкерге дейін
#portfolio optimization
#Markowitz
#crypto
#Python
#quantitative finance
#risk management
#diversification
#efficient frontier
#Sharpe ratio
#algorithmic trading
📊
Part 1 of 5 · Collection
Portfolio Construction & Risk

Python көмегімен оңтайлы крипто портфельдер құру - себебі YOLO стратегия емес

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


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

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

Илон твит жазғаны үшін бүкіл стегіңізді DOGE-ге тастаған кезіңізді есіңізде ме? Немесе соңғы құлдырау кезінде бәрін дүрбелеңмен сатып жіберген кезіңізді? Иә, бәріміз де осыдан өттік. Бүгін біз портфеліңізді (және ақыл-есіңізді) құтқара алатын нәрсе туралы сөйлесеміз: Марковиц портфель теориясы.

Гарри Марковиц бұл үшін 1990 жылы шын мәнінде Нобель сыйлығын алды. Негізгі идея қандай? Кез келген берілген тәуекел деңгейінде ең жақсы мүмкін кірісті алу үшін портфеліңізді математикалық түрде оңтайландыруға болады. Бұл көзіңізді таңып жүргізудің орнына инвестицияларыңыз үшін GPS болғанмен бірдей.

Негізгі түсінік: тәуекел мен кіріс (мәңгілік би)

Кодқа кірмес бұрын, немен айналысатынымызды түсінейік:

  • Күтілетін кіріс: қанша ақша табамын деп күтесіз
  • Тәуекел (құбылмалылық): портфеліңіздің құны қаншалықты ауытқиды
  • Корреляция: әртүрлі активтер бір-біріне ұқсас қалай қозғалады

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

Risk vs Return Тәуекел мен кіріс: оңтайлы геометриялық орта кірісті алу үшін жоғары кірісті, құбылмалы активтерді тұрақты негізбен теңгеру.

Python ортамызды дайындау

Ең алдымен - құралдарымызды дайындайық:

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

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

crypto_symbols = ['BTC-USD', 'ETH-USD']
prices = get_crypto_data(crypto_symbols)

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

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

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 = ({'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%}")

4-деңгей: көп активті ерекшелік - нағыз крипто портфель

Мұны бірнеше активтен тұратын толыққанды крипто портфельге дейін кеңейтейік:

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

5-деңгей: жетілдірілген техникалар - Блэк-Литтерман және тәуекел паритеті

Портфельді оңтайландырудың нағыз ниндзялары үшін бірнеше жетілдірілген техниканы жүзеге асырайық:

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

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


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

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

Сәтті оңтайландыру! 📈

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Нарықтан бір қадам алда болыңыз

AI сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.