Портфель минимальной волатильности (Minimum Variance Portfolio)

Портфель минимальной волатильности (Minimum Variance Portfolio, MVP) — метод построения инвестиционного портфеля, где целевая функция оптимизации направлена исключительно на минимизацию риска, без учета ожидаемой доходности активов. Подход решает задачу поиска такой комбинации весов активов, при которой волатильность портфеля достигает минимального значения при заданных ограничениях.

Классическая теория портфеля Марковица требует оценки двух параметров: ожидаемой доходности и ковариационной матрицы. MVP устраняет необходимость прогнозирования доходностей, используя только ковариационную матрицу. Это ключевое упрощение, поскольку ошибки в оценке ожидаемой доходности значительно превышают ошибки в оценке ковариаций. Результат — более стабильные веса активов и устойчивость к переобучению.

Эмпирические исследования демонстрируют аномалию низкой волатильности: портфели с минимальным риском показывают доходность, сопоставимую или превышающую рыночную, при существенно меньшей волатильности. Эффект объясняется систематическими искажениями в ценообразовании активов: инвесторы переплачивают за высокорисковые инструменты из-за ограничений на использование кредитного плеча и поведенческих факторов.

Теоретические основы минимизации волатильности

Построение портфеля минимальной волатильности базируется на квадратичной оптимизации с линейными ограничениями. Математическая формализация задачи определяет структуру решения и накладывает требования на исходные данные.

Математический базис MVP

Целевая функция оптимизации представляет квадратичную форму весов активов и ковариационной матрицы. Ее формула:

σ²(w) = w^T Σ w → min

где:

  • w — вектор весов активов в портфеле (размерность n×1);
  • Σ — ковариационная матрица доходностей (размерность n×n);
  • σ²(w) — дисперсия доходности портфеля.

Ковариационная матрица содержит дисперсии активов на главной диагонали и ковариации между парами активов в недиагональных элементах. Матрица должна быть положительно определенной для существования единственного решения.

На практике при малом числе наблюдений или высокой корреляции активов матрица становится вырожденной или близкой к вырожденной, что требует регуляризации.

Стандартные ограничения задачи:

∑ wᵢ = 1 (полное инвестирование капитала)

wᵢ ≥ 0 (запрет коротких позиций)

0 ≤ wᵢ ≤ wₘₐₓ (ограничение концентрации)

где:

  • wᵢ — вес i-го актива;
  • wₘₐₓ — максимально допустимый вес одного актива.

Дополнительные ограничения могут включать секторальные лимиты, требования к ликвидности или минимальный порог веса для сокращения транзакционных издержек.

Отличия от портфеля Марковица

Классический подход Марковица максимизирует отношение избыточной доходности к риску, что требует оценки вектора ожидаемых доходностей μ. Целевая функция в этом случае:

👉🏻  Алгоритмы оценки хеджирующих стратегий

(μ^T w — rբ) / √(w^T Σ w) → max

где:

  • μ — вектор ожидаемых доходностей активов;
  • rբ — безрисковая ставка.

Портфель минимальной волатильности исключает ожидаемую доходность (μ) из процесса оптимизации. Это убирает главный источник ошибок: стандартное отклонение оценки доходности актива пропорционально 1/√T, где T — число наблюдений. Для обычного горизонта в 252 торговых дня ошибка составляет около 6% в год. Ковариационная матрица оценивается точнее, с погрешностью порядка 1/T.

В результате веса портфеля минимальной волатильности (MVP) меняются медленнее при обновлении данных. Результаты исторических тестов показывают, что оборот такого портфеля в 2–3 раза ниже, чем у портфеля с максимальным коэффициентом Шарпа (Sharpe ratio). Это важно для стратегий с частой ребалансировкой, где комиссии могут съедать значительную часть доходности.

Структура ковариационной матрицы и индивидуальные волатильности активов. Корреляции определяют диверсификационный потенциал портфеля

Рис. 1: Структура ковариационной матрицы и индивидуальные волатильности активов. Корреляции определяют диверсификационный потенциал портфеля

Методы построения портфеля минимальной волатильности

Существует 2 основных подхода к решению задачи оптимизации: аналитический и численный. Выбор метода зависит от наличия ограничений на веса активов и вычислительных требований.

Аналитическое решение

Если в задаче нет ограничений на знак весов (допускаются короткие позиции по торговле активом), решение можно записать в явном виде.

Используя метод Лагранжа для минимизации дисперсии портфеля (σ²(w)) при условии, что сумма весов равна 1 (w^T 1 = 1), получаем формулу для оптимальных весов:

w* = (Σ⁻¹ 1) / (1^T Σ⁻¹ 1)

где:

  • Σ⁻¹ — обратная ковариационная матрица;
  • 1 — вектор из единиц (размерность n×1);
  • 1^T Σ⁻¹ 1 — скаляр нормировки.

Вектор весов пропорционален сумме строк обратной ковариационной матрицы и нормируется так, чтобы их сумма была равна 1. Активы с низкой дисперсией и слабой корреляцией с другими активами получают больший вес. И решение в данном случае одно и уникально, если ковариационная матрица Σ не вырождена.

Аналитическое решение работает быстро для портфелей с 100–200 активами. Основная вычислительная сложность связана с обращением матрицы и составляет O(n³). Для больших портфелей эффективнее использовать численные методы с разреженными матрицами.

Ограничение этого подхода — допущение отрицательных весов. На реальных рынках короткие позиции требуют маржинального обеспечения, могут быть ограничены по доступности бумаг для заимствования и создают дополнительные издержки. Если разрешены только длинные позиции (long-only), приходится использовать численные методы оптимизации.

👉🏻  Винрейт (Winrate) и Соотношение риск/прибыль (Risk/Reward Ratio, RRR)

Численная оптимизация с ограничениями

Метод последовательных наименьших квадратов с ограничениями (Sequential Least Squares Programming, SLSQP) решает задачу квадратичного программирования с линейными и нелинейными ограничениями. Алгоритм работает итеративно: в окрестности текущей точки целевая функция аппроксимируется квадратичной моделью, а ограничения — линейными функциями.

Для портфеля с длинными позициями (long-only) типичная постановка выглядит так:

Minimize: w^T Σ w

При условиях:

  • ∑ wᵢ = 1;
  • wᵢ ≥ 0;
  • wᵢ ≤ 0.3.

Ограничение сверху (0.3) предотвращает чрезмерную концентрацию в одном активе. Порог 30% — стандартная практика в институциональном управлении, которая помогает сохранять диверсификацию и одновременно формировать значимые позиции.

Для работы с вырожденными ковариационными матрицами нужна регуляризация. Основные методы:

1. Сжатие по Ледуа и Вольфу (Ledoit-Wolf):

Σ̃ = δF + (1-δ)Σ

где:

  • F — целевая матрица (обычно диагональная или с равной корреляцией);
  • δ — коэффициент сжатия, который оценивается по данным. Метод минимизирует среднеквадратичную ошибку оценки ковариации.

2. Добавление регуляризационного члена к целевой функции:

σ²(w) + λ||w||₂²

где λ — параметр регуляризации. Такой подход аналогичен гребневой регрессии (ridge regression) и штрафует слишком большие веса.

Практическая реализация на Python

Построение портфеля минимальной волатильности включает загрузку данных, расчет ковариационной матрицы и численную оптимизацию. Код лучше организовать модульно для повторного использования компонентов.

Подготовка данных

Для анализа используем исторические цены активов. Доходности считаем как логарифмические изменения цен. Такой подход делает доходности аддитивными во времени и более симметричными.

import yfinance as yf
import numpy as np
import pandas as pd

# Тикеры портфеля из 5 секторальных ETF
tickers = ['XLF', 'XLE', 'XLK', 'XLV', 'XLI']  # Финансы, Энергетика, Технологии, Здравоохранение, Промышленность

# Загрузка скорректированных цен закрытия
data = yf.download(tickers, start='2022-11-01', end='2025-11-01', progress=False, auto_adjust=True)

# Если вернулся DataFrame с мультииндексом колонок, выбираем только цены закрытия
if isinstance(data.columns, pd.MultiIndex):
    data = data['Close']

# Расчет логарифмических доходностей
returns = np.log(data / data.shift(1)).dropna()

# Ковариационная матрица (годовая, 252 торговых дня)
cov_matrix_annual = returns.cov() * 252

print(f"Период данных: {returns.index[0].date()} - {returns.index[-1].date()}")
print(f"Число наблюдений: {len(returns)}")
print(f"\nГодовые волатильности активов (%):")
for ticker in tickers:
    vol = np.sqrt(cov_matrix_annual.loc[ticker, ticker]) * 100
    print(f"{ticker}: {vol:.2f}%")
Период данных: 2022-11-02 - 2025-10-31
Число наблюдений: 752

Годовые волатильности активов (%):
XLF: 17.03%
XLE: 22.74%
XLK: 23.84%
XLV: 13.61%
XLI: 16.19%

Ковариационная матрица оценивается по выборочным ковариациям и аннуализируется для года. Для дневных данных используем множитель 252 торговых дней.

👉🏻  Модели переключения режимов (Regime Switching) для моделирования изменений финансовых рынков

Такая оценка предполагает, что доходности стабильны на исторической выборке. Это допущение может нарушаться в периоды крупных изменений на рынке.

Оптимизация портфеля

Функция оптимизации принимает ковариационную матрицу и ограничения, возвращает оптимальные веса и характеристики портфеля.

from scipy.optimize import minimize

def optimize_minimum_variance(cov_matrix, max_weight=0.5):
    """
    Оптимизация портфеля минимальной волатильности
    
    Parameters:
    -----------
    cov_matrix : pd.DataFrame
        Ковариационная матрица доходностей
    max_weight : float
        Максимальный вес одного актива
    
    Returns:
    --------
    dict : оптимальные веса и метрики портфеля
    """
    n_assets = len(cov_matrix)
    
    # Целевая функция: волатильность портфеля
    def portfolio_volatility(weights, cov_matrix):
        return np.sqrt(weights @ cov_matrix @ weights)
    
    # Ограничения
    constraints = [
        {'type': 'eq', 'fun': lambda w: np.sum(w) - 1}  # сумма весов = 1
    ]
    
    # Границы весов
    bounds = tuple((0, max_weight) for _ in range(n_assets))
    
    # Начальное приближение: равные веса
    initial_weights = np.array([1/n_assets] * n_assets)
    
    # Оптимизация
    result = minimize(
        portfolio_volatility,
        initial_weights,
        args=(cov_matrix,),
        method='SLSQP',
        bounds=bounds,
        constraints=constraints,
        options={'ftol': 1e-9, 'maxiter': 1000}
    )
    
    if not result.success:
        raise ValueError(f"Оптимизация не сошлась: {result.message}")
    
    optimal_weights = result.x
    portfolio_vol = result.fun
    
    return {
        'weights': pd.Series(optimal_weights, index=cov_matrix.index),
        'volatility': portfolio_vol,
        'success': result.success
    }

# Оптимизация MVP
mvp_result = optimize_minimum_variance(cov_matrix_annual, max_weight=0.5)

print("\nОптимальные веса портфеля минимальной волатильности:")
for ticker, weight in mvp_result['weights'].items():
    print(f"{ticker}: {weight*100:.2f}%")

print(f"\nВолатильность портфеля: {mvp_result['volatility']*100:.2f}%")
Оптимальные веса портфеля минимальной волатильности:
XLE: 8.83%
XLF: 11.57%
XLI: 29.61%
XLK: 0.00%
XLV: 50.00%

Волатильность портфеля: 13.10%

Функция использует метод SLSQP с высокой точностью сходимости (ftol=1e-9). Начальное приближение — равновзвешенный портфель, что ускоряет сходимость при умеренной корреляции активов. Для портфелей с сильными кластерами корреляций эффективнее инициализировать веса результатом иерархической кластеризации.

Параметр max_weight=0.5 ограничивает концентрацию. Без него оптимизатор часто назначает 60–80% портфеля активу с минимальной волатильностью, что увеличивает специфический риск и снижает диверсификацию.

# Визуализация структуры портфеля
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

# Веса активов
weights_pct = mvp_result['weights'] * 100
colors = plt.cm.Set3(range(len(weights_pct)))
bars = ax1.barh(range(len(weights_pct)), weights_pct, color=colors, alpha=0.8)
ax1.set_yticks(range(len(weights_pct)))
ax1.set_yticklabels(weights_pct.index)
ax1.set_xlabel('Weight (%)', fontsize=10)
ax1.set_title('Minimum Variance Portfolio Weights', fontsize=11, pad=10)
ax1.grid(axis='x', alpha=0.3)

# Добавление значений на график
for i, (idx, val) in enumerate(weights_pct.items()):
    ax1.text(val + 1, i, f'{val:.1f}%', va='center', fontsize=9)

# Сравнение волатильностей
individual_vols = np.sqrt(np.diag(cov_matrix_annual)) * 100
portfolio_vol = mvp_result['volatility'] * 100

x_pos = np.arange(len(tickers) + 1)
vols_to_plot = list(individual_vols) + [portfolio_vol]
labels_plot = tickers + ['MVP']
colors_bars = ['steelblue'] * len(tickers) + ['darkgreen']

bars = ax2.bar(x_pos, vols_to_plot, color=colors_bars, alpha=0.7)
ax2.set_xticks(x_pos)
ax2.set_xticklabels(labels_plot, rotation=45, ha='right')
ax2.set_ylabel('Volatility (%)', fontsize=10)
ax2.set_title('Individual vs Portfolio Volatility', fontsize=11, pad=10)
ax2.grid(axis='y', alpha=0.3)

# Линия для обозначения эффекта диверсификации
ax2.axhline(y=portfolio_vol, color='red', linestyle='--', linewidth=1.5, 
            alpha=0.7, label='Portfolio Vol')
ax2.legend(fontsize=9)

plt.tight_layout()
plt.show()

Структура портфеля минимальной волатильности и сравнение с индивидуальными волатильностями активов. Диверсификация снижает риск ниже минимального риска отдельных компонентов

Рис. 2: Структура портфеля минимальной волатильности и сравнение с индивидуальными волатильностями активов. Диверсификация снижает риск ниже минимального риска отдельных компонентов

Сравнение с равновесным портфелем

Равновзвешенный портфель (1/N) — наивная стратегия, присваивающая каждому активу одинаковый вес. Исследования показывают, что 1/N часто превосходит оптимизированные портфели на коротких горизонтах из-за ошибок оценки параметров. MVP должен демонстрировать преимущество на достаточно длинных периодах.

# Равновзвешенный портфель
equal_weights = np.array([1/len(tickers)] * len(tickers))
equal_portfolio_vol = np.sqrt(equal_weights @ cov_matrix_annual @ equal_weights)

# Расчет исторических доходностей портфелей
mvp_weights_array = mvp_result['weights'].values
mvp_returns = (returns @ mvp_weights_array)
equal_returns = (returns @ equal_weights)

# Кумулятивная доходность
mvp_cumulative = (1 + mvp_returns).cumprod()
equal_cumulative = (1 + equal_returns).cumprod()

# Метрики
def calculate_metrics(returns_series):
    total_return = (1 + returns_series).prod() - 1
    annual_return = (1 + total_return) ** (252 / len(returns_series)) - 1
    annual_vol = returns_series.std() * np.sqrt(252)
    sharpe = annual_return / annual_vol
    
    cumulative = (1 + returns_series).cumprod()
    running_max = cumulative.expanding().max()
    drawdown = (cumulative - running_max) / running_max
    max_drawdown = drawdown.min()
    
    return {
        'Annual Return': annual_return,
        'Annual Volatility': annual_vol,
        'Sharpe Ratio': sharpe,
        'Max Drawdown': max_drawdown
    }

mvp_metrics = calculate_metrics(mvp_returns)
equal_metrics = calculate_metrics(equal_returns)

print("\nСравнение портфелей:")
print("-" * 60)
print(f"{'Метрика':<25} {'MVP':>15} {'1/N':>15}")
print("-" * 60)
for key in mvp_metrics.keys():
    mvp_val = mvp_metrics[key]
    equal_val = equal_metrics[key]
    if 'Return' in key or 'Volatility' in key or 'Drawdown' in key:
        print(f"{key:<25} {mvp_val:>14.2%} {equal_val:>14.2%}")
    else:
        print(f"{key:<25} {mvp_val:>14.2f} {equal_val:>14.2f}")
print("-" * 60)

# Визуализация кумулятивной доходности
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))

# Кумулятивная доходность
ax1.plot(mvp_cumulative.index, (mvp_cumulative - 1) * 100, 
         label='Minimum Variance', linewidth=2, color='darkgreen')
ax1.plot(equal_cumulative.index, (equal_cumulative - 1) * 100, 
         label='Equal Weight', linewidth=2, color='steelblue', alpha=0.7)
ax1.set_ylabel('Cumulative Return (%)', fontsize=10)
ax1.set_title('Portfolio Performance Comparison', fontsize=11, pad=10)
ax1.legend(fontsize=10)
ax1.grid(True, alpha=0.3)

# Просадки
mvp_cum = (1 + mvp_returns).cumprod()
equal_cum = (1 + equal_returns).cumprod()

mvp_running_max = mvp_cum.expanding().max()
equal_running_max = equal_cum.expanding().max()

mvp_dd = (mvp_cum - mvp_running_max) / mvp_running_max * 100
equal_dd = (equal_cum - equal_running_max) / equal_running_max * 100

ax2.fill_between(mvp_dd.index, mvp_dd, 0, alpha=0.5, color='darkgreen', label='MVP Drawdown')
ax2.fill_between(equal_dd.index, equal_dd, 0, alpha=0.5, color='steelblue', label='1/N Drawdown')
ax2.set_xlabel('Date', fontsize=10)
ax2.set_ylabel('Drawdown (%)', fontsize=10)
ax2.set_title('Portfolio Drawdowns', fontsize=11, pad=10)
ax2.legend(fontsize=10)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
Сравнение портфелей:
------------------------------------------------------------
Метрика                               MVP             1/N
------------------------------------------------------------
Annual Return                      9.24%         13.97%
Annual Volatility                 13.10%         14.88%
Sharpe Ratio                        0.71           0.94
Max Drawdown                     -13.96%        -17.30%
------------------------------------------------------------

Сравнение производительности портфелей минимальной волатильности (Minimum Variance, MVP) и равновзвешенного (Equal Weight, 1/N). Верхняя часть — кумулятивная доходность, нижняя — просадки портфелей

Рис. 3: Сравнение производительности портфелей минимальной волатильности (Minimum Variance, MVP) и равновзвешенного (Equal Weight, 1/N). Верхняя часть — кумулятивная доходность, нижняя — просадки портфелей

В представленном выше примере метрики рассчитываются на полном историческом периоде. Годовая доходность (Annual Return) аннуализируется геометрически. Коэффициент Шарпа (Sharpe Ratio) не учитывает безрисковую ставку для упрощения сравнения. Максимальная просадка (Max Drawdown) показывает наибольшее падение от пика и важна для оценки рисков редких, сильных потерь.

👉🏻  Ускорение численных вычислений в Python: Numba, JIT на примерах из Data Science

Портфель минимальной волатильности (Minimum Volatility Portfolio, MVP) изначально устроен так, чтобы колебаться меньше рынка. Его доходность зависит от того, проявилась ли премия за риск низковолатильных активов. Когда рынок растет, активы с высокой волатильностью обычно приносят больший доход и обгоняют такую стратегию. Зато на падающем или стабильном рынке портфель минимальной волатильности часто показывает лучшие результаты.

Проблемы и решения

Практическое применение MVP сталкивается с тремя основными проблемами:

  1. Ошибки оценки ковариационной матрицы;
  2. Концентрация весов в небольшом числе активов;
  3. Высокая чувствительность к изменениям данных.

Давайте рассмотрим их подробнее.

Ошибки оценки ковариационной матрицы

Выборочная ковариационная матрица — несмещенная, но высоковариативная оценка истинной матрицы. При n активах и T наблюдениях, когда n/T > 0.1, собственные значения матрицы систематически искажены: максимальное собственное значение завышено, минимальное — занижено.

Сжатие методом Ledoit-Wolf оценивает оптимальный коэффициент δ минимизацией ожидаемой квадратичной ошибки:

δ* = argmin E[||δF + (1-δ)Σ — Σtrue||²]

Метод аналитически вычисляет δ* из данных без кросс-валидации. Для месячных данных типичные значения δ = 0.2-0.4, для дневных данных δ = 0.05-0.15.

Альтернатива — экспоненциальное взвешивание наблюдений. Недавние данные получают больший вес:

Σ = ∑ λⁱ⁻¹ (rᵢ — μ)(rᵢ — μ)ᵀ / ∑ λⁱ⁻¹

где λ — параметр распада (обычно 0.94-0.97 для дневных данных).

Подход адаптируется к изменениям режима волатильности быстрее, чем равновзвешенная оценка.

Концентрация весов и нестабильность

Без ограничений оптимизатор часто присваивает большие веса активам с низкой оценочной волатильностью. Ошибки в оценке усиливают концентрацию: если истинная волатильность актива 20%, а оценочная 18%, его вес может увеличиться в 2–3 раза.

👉🏻  LSTM для прогнозирования волатильности: многослойные архитектуры и sequence-to-sequence подходы

Практические ограничения концентрации:

  • Максимальный вес 20–40% для небольших портфелей (5–10 активов);
  • Максимальный вес 10–20% для крупных портфелей (20+ активов);
  • Минимальный вес 2–5% для снижения оборота, веса ниже порога обнуляются.

Нестабильность весов проявляется при ребалансировке: малые изменения ковариаций могут перераспределять 10–15% капитала между активами.

Решения:

  • Пороговая ребалансировка: обновляем портфель только если отклонение текущих весов от целевых превышает 5–10%.
  • Включение транзакционных издержек в целевую функцию через штраф за изменение весов.

Эмпирические результаты

Исследования на данных развитых рынков показывают, что портфель минимальной волатильности (MVP) стабильно превосходит капитализационно-взвешенные индексы по соотношению риск–доходность.

Например, исследования Clarke, de Silva и Thorley (2006) показали, что на акциях S&P 500 за 1968–2005 годы MVP имел коэффициент Шарпа (Sharpe ratio) 0.86 против 0.39 у рыночного индекса, при волатильности 12% против 15%.

Аномалия низкой волатильности объясняется факторной экспозицией MVP: портфель имеет отрицательную бету к рынку (0.6–0.8) и положительную экспозицию к факторам size и value. Премия за низкую волатильность оценивается в 3–5% годовых после учета факторов Fama-French.

Эффективность MVP зависит от рыночного режима:

  • В периоды высокой волатильности (VIX > 25) преимущество усиливается — доходность с учетом риска превышает рыночную в 1.5–2 раза;
  • На растущих рынках с низкой волатильностью (VIX < 15) абсолютная доходность может отставать на 5–7% годовых, но Sharpe ratio остается выше за счет меньшего риска.

Просадки MVP на 30–40% меньше рыночных в кризисные периоды (2008, 2020). Максимальная просадка за 2000–2024 годы составила около 25% против 55% для S&P 500. Время восстановления после просадок также значительно короче — 12–18 месяцев против 3–5 лет для широкого рынка.

👉🏻  Портфель максимальной диверсификации (Maximum Diversification Portfolio)

Заключение

Портфель минимальной волатильности решает ключевую проблему классической оптимизации Марковица — высокую чувствительность к ошибкам в оценке ожидаемой доходности. Исключение прогнозных параметров из целевой функции делает веса активов более стабильными и снижает риск переобучения. Эмпирические данные подтверждают устойчивую премию за низкую волатильность на горизонтах от 3 лет, что делает метод практичным для консервативных инвесторов.

Практическая реализация требует внимания к деталям:

  • Регуляризация ковариационной матрицы методами Ledoit-Wolf или экспоненциального взвешивания помогает избежать вырожденности;
  • Ограничения концентрации и пороговая ребалансировка контролируют транзакционные издержки, которые без этих мер могут полностью нивелировать теоретическое преимущество стратегии.

В результате MVP предлагает надежный подход к построению портфеля, обеспечивая привлекательную доходность с учетом риска при минимальных предположениях о будущем поведении рынка.