Показатели Альфа, Бета, коэффициент Шарпа: Расчеты в Python

В мире финансовых инвестиций необходимо уметь объективно оценивать эффективность портфеля и отдельных активов. Ключевые метрики: Альфа, Бета и коэффициент Шарпа — являются фундаментальными инструментами для принятия взвешенных инвестиционных решений. Эти показатели помогают численно оценить риск, доходность и эффективность вложений относительно рынка.

В этой статье я детально рассмотрю природу этих важнейших коэффициентов, проанализирую их математическую основу и покажу, как рассчитывать их с помощью Python. Мы рассмотрим не только теоретические аспекты, но и практическое применение в современном количественном анализе. Я покажу, как профессионалы используют эти метрики для оценки эффективности портфелей, стратегий и отдельных активов, а также предложу несколько нетривиальных подходов к их интерпретации.

Понятие риска и доходности в инвестициях

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

Доходность: виды и методы расчета

Доходность — это относительный показатель, отражающий изменение стоимости актива или портфеля за определенный период времени. При работе с финансовыми данными обычно рассматривают два основных вида доходности:

Абсолютная доходность показывает процентное изменение стоимости актива за выбранный период. Она рассчитывается по формуле:

Формула абсолютной доходности

где:

  • Pt — цена актива в момент времени t;
  • Pt-1 — цена актива в предыдущий момент времени t-1;
  • Rt — доходность за период.

Логарифмическая доходность имеет ряд математических преимуществ и часто используется в количественном анализе. Она рассчитывается как:

Формула логарифмической доходности

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

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

Риск: волатильность и ее измерение

Риск в финансовой математике чаще всего отождествляется с волатильностью, то есть с изменчивостью цены актива. Основной статистической мерой риска является стандартное отклонение доходности:

Формула стандартного отклонения доходности

где:

  • σ — стандартное отклонение, мера волатильности (риск);
  • r(t) — доходность актива в момент времени t;
  • T — количество наблюдений (например, дней торгов).

Стандартное отклонение показывает, насколько в среднем отклоняется доходность от своего среднего значения. Чем выше стандартное отклонение, тем более волатильным (и, следовательно, более рисковым) считается актив.

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

Коэффициент Бета: измерение систематического риска

Бета — один из ключевых показателей в современной теории портфельных инвестиций, который позволяет оценить систематический (рыночный) риск актива. Это мера чувствительности доходности актива к изменениям доходности рыночного индекса или бенчмарка.

Что такое Бета и как ее интерпретировать

Бета-коэффициент показывает, насколько сильно изменяется доходность актива при изменении доходности рынка. Интерпретация значений бета следующая:

  • β = 1: доходность актива изменяется пропорционально рынку;
  • β > 1: актив более волатилен, чем рынок (агрессивная инвестиция);
  • β < 1: актив менее волатилен, чем рынок (консервативная инвестиция);
  • β < 0: доходность актива имеет тенденцию двигаться в направлении, противоположном рынку.

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

Математическая формула расчета Беты

Бета-коэффициент рассчитывается как отношение ковариации доходности актива и рынка к дисперсии доходности рынка:

Формула расчета коэффициента Бета

где:

  • β(i) — бета-коэффициент актива i;
  • r(i) — вектор доходностей актива i;
  • r(m) — вектор доходностей рынка (например, индекс SP500);
  • Cov(ri, rm) — ковариация между доходностью актива и рынка;
  • Var(rm) — дисперсия доходности рыночного портфеля.

Другое выражение для расчета беты через коэффициент корреляции:

Формула расчета Бета через коэффициент корреляции

где:

  • p(i,m) — коэффициент корреляции между доходностью актива i и рыночной доходностью m;
  • σ(i) — стандартное отклонение доходности актива i (его волатильность);
  • σ(m) — стандартное отклонение доходнности рынка.

Расчет коэффициента Бета в Python

Давайте рассмотрим, как рассчитать бета-коэффициент с использованием Python. Для этого мы будем использовать библиотеки pandas, numpy и Alpha Vantage API для получения исторических данных. Для этого в коде в строке alpha_vantage_api_key нужно ввести свой API токен. Получить его можно по этой ссылке https://www.alphavantage.co/support/#api-key . А для определения тикеров нужных активов можно воспользоваться следующей ссылкой: https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=tesco&apikey=demo , нужно заменить tesco на интересующую вас акцию, бонд или фьючерс, а в apikey подставить свой ключ.

Alpha Vantage — мощная альтернатива Yahoo Finance. Однако нужно учитывать, что на бесплатном тарифе доступно до 25 выгрузок данных в сутки на API.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import pandas_datareader.data as web
import time

# Задаем даты, тикер актива, тикер рынка, API ключ
start_date = '2022-09-01'
end_date = '2025-05-01'
stock_ticker = 'GOOG'  
market_ticker = 'VCNIX'  #NASDAQ-100
# Замените строку ниже на ваш собственный API-ключ Alpha Vantage
alpha_vantage_api_key = '_____________'

# Загрузка данных
def get_stock_data(ticker, start, end):
    for attempt in range(5):
        try:
            print(f"Загружаем данные для {ticker}...")
            data = web.DataReader(
                ticker, 
                'av-daily', 
                start=start, 
                end=end, 
                api_key=alpha_vantage_api_key
            )
            return data['close']
        except Exception as e:
            print(f"Ошибка при загрузке {ticker}: {e}. Повтор через 5 сек...")
            time.sleep(5)
    raise ConnectionError(f"Не удалось загрузить данные для {ticker}")

# Получаем данные по акции и по рынку
try:
    stock_prices = get_stock_data(stock_ticker, start_date, end_date)
    market_prices = get_stock_data(market_ticker, start_date, end_date)
except ConnectionError as e:
    print("Ошибка:", e)
    exit()

# Объединяем в один DataFrame и удаляем NaN
data = pd.DataFrame({
    stock_ticker: stock_prices,
    market_ticker: market_prices
}).dropna()

if len(data) < 2: print("Недостаточно данных для анализа.") exit() # Расчет доходностей (Close -> log returns)
returns = np.log(data / data.shift(1)).dropna()

# Расчет бета
def calculate_beta(stock_returns, market_returns):
    beta_cov = np.cov(stock_returns, market_returns)[0, 1] / np.var(market_returns)
    slope, intercept, r_value, _, _ = stats.linregress(market_returns, stock_returns)
    return beta_cov, slope, r_value**2

try:
    stock_returns = returns[stock_ticker]
    market_returns = returns[market_ticker]
    beta_cov, beta_reg, r_squared = calculate_beta(stock_returns, market_returns)
except Exception as e:
    print("Ошибка в расчётах:", e)
    exit()

# Вывод результатов
print(f"\nБета коэффициент для {stock_ticker} (метод ковариации): {beta_cov:.4f}")
print(f"Бета коэффициент для {stock_ticker} (метод регрессии): {beta_reg:.4f}")
print(f"Коэффициент детерминации R²: {r_squared:.4f}")

# Визуализация
plt.figure(figsize=(10, 6))
plt.scatter(market_returns, stock_returns, alpha=0.6, label='Дневные доходности')
plt.plot(market_returns, beta_reg * market_returns + np.mean(stock_returns - beta_reg * market_returns),
         color='red', label=f'Линия регрессии (β = {beta_reg:.2f})')
plt.title(f'Бета для {stock_ticker} относительно {market_ticker}')
plt.xlabel(f'Доходность рынка ({market_ticker})')
plt.ylabel(f'Доходность акции ({stock_ticker})')
plt.ylim(-0.5, 0.5)
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

Бета коэффициент для GOOG (метод ковариации): 0.5944
Бета коэффициент для GOOG (метод регрессии): 0.5935
Коэффициент детерминации R²: 0.3053

Читайте также:  Гомоскедастичность и Гетероскедастичность временных рядов

Визуализация коэффициента Бета для Google относительно Nasdaq-100

Рис. 1: Визуализация коэффициента Бета для Google относительно Nasdaq-100

В этом коде мы реализуем два метода расчета бета-коэффициента:

  • Метод ковариации/дисперсии, соответствующий математической формуле;
  • Метод линейной регрессии, где бета является угловым коэффициентом (slope) регрессионной прямой.

Оба метода должны давать очень близкие результаты при достаточном объеме данных. Регрессионный метод дополнительно предоставляет значение R-квадрат, которое показывает, какая доля вариации доходности актива объясняется вариацией доходности рынка.

Динамический расчет беты с использованием скользящего окна

В реальной аналитике часто важно отслеживать изменение беты во времени. Рассмотрим реализацию расчета в Python динамической беты с использованием скользящего окна:

# Расчет динамической беты с использованием скользящего окна
def calculate_rolling_beta(stock_returns, market_returns, window=252):
    # Создаем DataFrame с доходностями
    df = pd.DataFrame({'stock': stock_returns, 'market': market_returns})
    
    # Рассчитываем ковариацию и дисперсию с использованием скользящего окна
    rolling_cov = df['stock'].rolling(window=window).cov(df['market'])
    rolling_var = df['market'].rolling(window=window).var()
    
    # Рассчитываем бету
    rolling_beta = rolling_cov / rolling_var
    
    return rolling_beta

# Расчет динамической беты
window_size = 252  # примерно 1 год торговых дней
rolling_beta = calculate_rolling_beta(returns[stock_ticker], returns[market_ticker], window=window_size)

# Визуализация динамической беты
plt.figure(figsize=(12, 6))
rolling_beta.plot()
plt.axhline(y=1, color='r', linestyle='--')
plt.title(f'Динамический бета-коэффициент для {stock_ticker} (окно {window_size} дней)')
plt.ylabel('Бета')
plt.grid(True)
plt.tight_layout()
plt.show()

График изменения коэффициента Бета в динамике для акций Google с окном 252 дней

Рис. 2: График изменения коэффициента Бета в динамике для акций Google с окном 252 дней

Этот код позволяет наблюдать эволюцию бета-коэффициента во времени. Горизонтальная красная линия при β = 1 помогает визуально определить периоды, когда актив был более или менее волатильным относительно рынка.

Коэффициент Альфа: оценка избыточной доходности

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

Что такое Альфа и как ее интерпретировать

Альфа-коэффициент представляет собой разницу между фактической доходностью актива и его теоретической ожидаемой доходностью, рассчитанной на основе модели ценообразования капитальных активов (CAPM — Capital Asset Pricing Model).

Интерпретация значений Альфа:

  • α > 0: актив/портфель демонстрирует доходность выше ожидаемой с учетом риска (переоценен);
  • α = 0: актив/портфель показывает доходность, точно соответствующую его уровню риска (справедливо оценен);
  • α < 0: актив/портфель демонстрирует доходность ниже ожидаемой с учетом риска (недооценен).

В своей практике я заметил, что устойчиво положительная альфа на длительных промежутках времени — довольно редкое явление. Большинство активно управляемых фондов на горизонте 5-10 лет показывают нейтральную или даже отрицательную альфу после учета комиссий и издержек.

Математическая формула расчета Альфы

Альфа-коэффициент рассчитывается на основе модели CAPM следующим образом:

Формула расчета коэффициента Альфа

где:

  • a(i) — альфа актива i, показывает избыточную доходность относительно ожидаемой;
  • r(i) — средняя фактическая доходность актива за период;
  • r(f) — безрисковая ставка (например, доходность по краткосрочным государственным облигациям);
  • b(i) — бета-коэффициент актива, измеряющий его чувствительность к рыночным движениям;
  • r(m) — средняя доходность рыночного портфеля (например, индекс SP500)

Альфа также может быть получена как свободный член (intercept) в регрессионной модели, где зависимая переменная — избыточная доходность актива (над безрисковой ставкой), а независимая переменная — избыточная доходность рынка:

Формула расчета коэффициента Альфа через регрессию

где:

  • r(i) — доходность актива;
  • r(f) — безрисковая ставка;
  • r(m) — доходность рыночного портфеля;
  • b — коэффициент Бета (чувствительность к рынку);
  • epsilon — случайная ошибка.

Расчет коэффициента Альфа в Python

Рассмотрим реализацию расчета показателя Альфа с использованием Python.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import pandas_datareader.data as web
import time

# Параметры
start_date = '2022-09-01'
end_date = '2025-05-01'
stock_ticker = 'GOOG'        # Акция
market_ticker = 'VCNIX'      #NASDAQ-100
alpha_vantage_api_key = '___________'  # Замените на свой ключ
# Безрисковая ставка
risk_free_rate = 0.05 / 252  # Пример: 5% годовых → дневная ставка
# Или можно использовать реальную ставку из FRED (через DataReader)
# treasury_data = web.DataReader('DGS3MO', 'fred', start=start_date, end=end_date)
# risk_free_rate = float(treasury_data.iloc[-1] / 100 / 252)

# Загрузка данных
def get_stock_data(ticker, start, end):
    for attempt in range(5):
        try:
            print(f"Загружаем данные для {ticker}...")
            data = web.DataReader(
                ticker,
                'av-daily',
                start=start,
                end=end,
                api_key=alpha_vantage_api_key
            )
            return data['close']
        except Exception as e:
            print(f"Ошибка при загрузке {ticker}: {e}. Повтор через 5 сек...")
            time.sleep(5)
    raise ConnectionError(f"Не удалось загрузить данные для {ticker}")

# Получаем данные по акции и рынку
try:
    stock_prices = get_stock_data(stock_ticker, start_date, end_date)
    market_prices = get_stock_data(market_ticker, start_date, end_date)
except ConnectionError as e:
    print("Ошибка:", e)
    exit()

# Объединяем в один DataFrame и удаляем NaN
data = pd.DataFrame({
    stock_ticker: stock_prices,
    market_ticker: market_prices
}).dropna()

if len(data) < 2:
    print("Недостаточно данных для анализа.")
    exit()

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

# Расчет дневной и годовой альфы через регрессию избыточной доходности
def calculate_alpha(stock_returns, market_returns, risk_free_rate):
    excess_stock = stock_returns - risk_free_rate
    excess_market = market_returns - risk_free_rate

    slope, intercept, r_value, p_value, std_err = stats.linregress(excess_market, excess_stock)

    alpha_daily = intercept
    alpha_annual = alpha_daily * 252  # Аннуализация (252 торговых дня)

    return alpha_daily, alpha_annual, slope, r_value**2

# Вычисление альфы
try:
    stock_returns = returns[stock_ticker]
    market_returns = returns[market_ticker]
    alpha_daily, alpha_annual, beta, r_squared = calculate_alpha(
        stock_returns, market_returns, risk_free_rate
    )
except Exception as e:
    print("Ошибка в расчётах:", e)
    exit()

# Вывод результатов
print(f"Дневная альфа для {stock_ticker}: {alpha_daily:.6f}")
print(f"Годовая альфа для {stock_ticker}: {alpha_annual:.4f} ({alpha_annual*100:.2f}%)")
print(f"Бета: {beta:.4f}")
print(f"R-квадрат: {r_squared:.4f}")

# Визуализация
excess_stock = stock_returns - risk_free_rate
excess_market = market_returns - risk_free_rate

plt.figure(figsize=(10, 6))
plt.scatter(excess_market, excess_stock, alpha=0.6, label='Избыточная доходность')
plt.plot(np.array([-0.1, 0.1]), beta * np.array([-0.1, 0.1]) + alpha_daily, 'r', label='Линия регрессии')
plt.title(f'Альфа для {stock_ticker} относительно {market_ticker}')
plt.xlabel(f'Избыточная доходность рынка ({market_ticker})')
plt.ylabel(f'Избыточная доходность акции ({stock_ticker})')
plt.ylim(-0.5, 0.5)
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

Дневная альфа для GOOG: 0.000418
Годовая альфа для GOOG: 0.1052 (10.52%)
Бета: 0.5935
R-квадрат: 0.3053

Визуализация коэффициента Альфа (избыточной доходности) и линии регрессии для акций Google относительно индекса NASDAQ

Рис. 3: Визуализация коэффициента Альфа (избыточной доходности) и линии регрессии для акций Google относительно индекса NASDAQ

Дневная альфа для GOOG: 0.000418
Годовая альфа для GOOG: 0.1052 (10.52%)
Бета: 0.5935
R-квадрат: 0.3053

В этом коде мы:

  1. Рассчитываем избыточную доходность (excess return) как разницу между фактической доходностью и безрисковой ставкой;
  2. Используем линейную регрессию для получения альфы (свободный член регрессии);
  3. Аннуализируем полученное значение альфы для лучшей интерпретации.

Динамический расчет Альфы с использованием скользящего окна

Аналогично Бете, Альфа не является константой и может меняться со временем. Давайте также рассмотрим реализацию динамического расчета данного показателя:

# Расчет динамической альфы с использованием скользящего окна
def calculate_rolling_alpha(stock_returns, market_returns, risk_free_rate, window=252):
    # Расчёт избыточных доходностей
    excess_stock = stock_returns - risk_free_rate
    excess_market = market_returns - risk_free_rate

    # Объединяем в DataFrame
    df = pd.DataFrame({
        'excess_stock': excess_stock,
        'excess_market': excess_market
    }).dropna()

    # Проверка достаточности данных
    if len(df) < window:
        raise ValueError(f"Недостаточно данных для анализа (минимум {window} точек)")

    rolling_alpha = []
    rolling_beta = []

    # Простой цикл по окнам
    for i in range(window, len(df) + 1):
        window_data = df.iloc[i - window:i]
        x = window_data['excess_market']
        y = window_data['excess_stock']

        # Линейная регрессия
        try:
            slope, intercept, _, _, _ = stats.linregress(x, y)
        except:
            slope, intercept = np.nan, np.nan

        rolling_alpha.append(intercept)
        rolling_beta.append(slope)

    # Преобразуем в pandas Series
    rolling_alpha_series = pd.Series(rolling_alpha, index=df.index[window-1:])
    rolling_beta_series = pd.Series(rolling_beta, index=df.index[window-1:])

    # Аннуализируем альфу
    rolling_alpha_annual = rolling_alpha_series * 252

    return rolling_alpha_series, rolling_alpha_annual, rolling_beta_series

# Расчет динамической альфы
window_size = 252  # примерно 1 год торговых дней
rolling_alpha, rolling_alpha_annual, rolling_beta_from_alpha = calculate_rolling_alpha(
    returns[stock_ticker], returns[market_ticker], risk_free_rate, window=window_size
)

# Визуализация динамической альфы
plt.figure(figsize=(12, 6))
rolling_alpha_annual.plot()
plt.axhline(y=0, color='r', linestyle='--')
plt.title(f'Динамическая годовая альфа для {stock_ticker} (окно {window_size} дней)')
plt.ylabel('Альфа (%)')
plt.grid(True)
plt.tight_layout()
plt.show()

График изменения коэффициента Альфа в динамике для акций Google с окном 252 дней

Рис. 4: График изменения коэффициента Альфа в динамике для акций Google с окном 252 дней

Этот код позволяет наблюдать изменение альфа-коэффициента во времени. Горизонтальная красная линия при α = 0 помогает визуально определить периоды избыточной доходности (альфа > 0) и недостаточной доходности (альфа < 0).

Читайте также:  Модели ценообразования активов: CAPM и APT

Для лучшей интерпретируемости мы также можем построить график кумулятивных доходностей сравниваемого актива и рынка. В нашей случае это наложенные друг на друга графики акций Google и Nasdaq-100.

cumulative_returns = (1 + returns).cumprod()
cumulative_returns[[stock_ticker, market_ticker]].plot(figsize=(12, 6))
plt.title('Сравнение кумулятивной доходности за период: Акция vs Индекс')
plt.grid(True)
plt.show()

Сравнение кумулятивной доходности за период: акции Google vs индекс Nasdaq-100

Рис. 5: Сравнение кумулятивной доходности за период: акции Google vs индекс Nasdaq-100

По графику выше видно что акции Google в последние годы часто опережали индекс Nasdaq-100 в доходности, что подтверждает график Альфы и ее положительные значения дневной (0.000418) и годовой (0.1052).

Интересно связана ли лучшая кумулятивная доходность акций Google относительно рынка с большей волатильностью их котировок? Давайте проверим эту гипотезу:

# Расчёт исторической волатильности
def calculate_volatility(returns_series, annualize=True):
    volatility = np.std(returns_series, ddof=1)
    if annualize:
        volatility *= np.sqrt(252)
    return volatility

stock_volatility = calculate_volatility(returns[stock_ticker])
market_volatility = calculate_volatility(returns[market_ticker])

print(f"Годовая волатильность {stock_ticker}: {stock_volatility:.4f}")
print(f"Годовая волатильность {market_ticker}: {market_volatility:.4f}")
print(f"Волатильность {stock_ticker} / {market_ticker}: {stock_volatility / market_volatility:.2f}")

Годовая волатильность GOOG: 0.3186
Годовая волатильность VCNIX: 0.2966
Волатильность GOOG / VCNIX: 1.07

Видим что волатильность акций Google и индекса Nasdaq не сильно отличаются друг от друга. Акции техногиганта лишь на 7% более волатильны.

Мы также можем визуализировать периоды когда акции были более волатильны чем индекс.

vol_ratio = rolling_stock_vol / rolling_market_vol

plt.figure(figsize=(12, 4))
vol_ratio.plot()
plt.axhline(y=1.0, color='red', linestyle='--', alpha=0.7)
plt.title('Отношение волатильностей: Акция / Рынок')
plt.ylabel('Volatility Ratio')
plt.grid(True)
plt.tight_layout()
plt.show()

Отношение волатильностей: Акции Google vs Индекс Nasdaq

Рис. 6: Отношение волатильностей: Акции Google vs Индекс Nasdaq

Видим что каких-то резких всплесков на горизонте последних лет не было, акции Google стабильно более волатильны, чем индекс Nasdaq.

Коэффициент Шарпа: оценка эффективности с учетом риска

Коэффициент Шарпа — один из наиболее широко используемых показателей эффективности инвестиций, который учитывает не только доходность, но и риск. Этот показатель разработан нобелевским лауреатом Уильямом Шарпом и назван в его честь.

Что такое коэффициент Шарпа и как его интерпретировать

Коэффициент Шарпа измеряет избыточную доходность (превышение над безрисковой ставкой) на единицу общего риска, выраженного стандартным отклонением доходности. Он отвечает на вопрос: «Сколько дополнительной доходности получает инвестор за каждую дополнительную единицу принятого риска?»

Интерпретация значений коэффициента Шарпа:

  • SR < 0: инвестиция имеет доходность ниже безрисковой ставки (плохо);
  • 0 < SR < 0.5: плохое соотношение риск/доходность;
  • 0.5 < SR < 1: приемлемое соотношение риск/доходность;
  • 1 < SR < 2: хорошее соотношение риск/доходность;
  • 2 < SR < 3: очень хорошее соотношение риск/доходность;
  • SR > 3: отличное соотношение риск/доходность (встречается редко).

В теории все предельно просто — инвестору нужно фокусироваться на активах с максимальными коэффициентами Шарпа. Однако на практике, увы, все не так радужно — значения коэффициента Шарпа выше 2 на длительных промежутках времени (год и более) на современном рынке встречаются крайне редко и часто свидетельствуют о возможных проблемах с данными или методологией расчета, а не о действительно исключительной эффективности стратегии.

Математическая формула расчета коэффициента Шарпа

Коэффициент Шарпа рассчитывается по формуле:

Формула коэффициента Шарпа

где:

  • r(p) — средняя доходность портфеля/актива;
  • r(f) — безрисковая ставка доходности;
  • E( r(p) — r(f) ) — ожидаемая избыточная доходность портфеля над безрисковой ставкой;
  • σ(p) — стандартное отклонение доходности портфеля / актива (волатильность / мера риска).

Для аннуализации коэффициента Шарпа, рассчитанного на основе данных с другой периодичностью (например, дневных или месячных), используется формула:

Формула аннуализации коэффициента Шарпа

где N — количество периодов в году (например, 252 для торговых дней, 52 недельных данных, 12 для месяцев).

Расчет коэффициента Шарпа в Python

Рассмотрим реализацию расчета коэффициента Шарпа с использованием Python:

# Расчет коэффициента Шарпа для заданного ряда доходностей
def calculate_sharpe_ratio(returns, risk_free_rate, periods_per_year=252):
    # Расчет избыточной доходности
    excess_returns = returns - risk_free_rate
    
    # Расчет средней избыточной доходности
    mean_excess_return = excess_returns.mean()
    
    # Расчет стандартного отклонения доходности
    std_dev = returns.std()
    
    # Расчет коэффициента Шарпа
    sharpe_ratio = mean_excess_return / std_dev
    
    # Аннуализация коэффициента Шарпа
    sharpe_ratio_annual = sharpe_ratio * np.sqrt(periods_per_year)
    
    return sharpe_ratio, sharpe_ratio_annual

# Расчет коэффициента Шарпа для акции и индекса
sharpe_stock, sharpe_stock_annual = calculate_sharpe_ratio(returns[stock_ticker], risk_free_rate)
sharpe_market, sharpe_market_annual = calculate_sharpe_ratio(returns[market_ticker], risk_free_rate)

print(f"Коэффициент Шарпа для {stock_ticker} (дневной): {sharpe_stock:.4f}")
print(f"Коэффициент Шарпа для {stock_ticker} (годовой): {sharpe_stock_annual:.4f}")
print(f"Коэффициент Шарпа для {market_ticker} (дневной): {sharpe_market:.4f}")
print(f"Коэффициент Шарпа для {market_ticker} (годовой): {sharpe_market_annual:.4f}")

Коэффициент Шарпа для GOOG (дневной): 0.0190
Коэффициент Шарпа для GOOG (годовой): 0.3020
Коэффициент Шарпа для VCNIX (дневной): -0.0032
Коэффициент Шарпа для VCNIX (годовой): -0.0513

В этом коде мы:

  1. Рассчитываем избыточную доходность как разницу между фактической доходностью и безрисковой ставкой;
  2. Вычисляем среднюю избыточную доходность и стандартное отклонение;
  3. Рассчитываем коэффициент Шарпа как отношение средней избыточной доходности к стандартному отклонению;
  4. Аннуализируем полученное значение, умножая на квадратный корень из количества периодов в году.

Динамический расчет коэффициента Шарпа с использованием скользящего окна

Коэффициент Шарпа также может значительно меняться со временем. Рассмотрим реализацию динамического расчета этого коэффициента:

# Расчет динамического коэффициента Шарпа с использованием скользящего окна
def calculate_rolling_sharpe_ratio(returns, risk_free_rate, window=252, periods_per_year=252):
    # Расчет избыточной доходности
    excess_returns = returns - risk_free_rate
    
    # Расчет скользящего среднего избыточной доходности
    rolling_mean = excess_returns.rolling(window=window).mean()
    
    # Расчет скользящего стандартного отклонения
    rolling_std = returns.rolling(window=window).std()
    
    # Расчет коэффициента Шарпа
    rolling_sharpe = rolling_mean / rolling_std
    
    # Аннуализация коэффициента Шарпа
    rolling_sharpe_annual = rolling_sharpe * np.sqrt(periods_per_year)
    
    return rolling_sharpe, rolling_sharpe_annual

# Расчет динамического коэффициента Шарпа
window_size = 252  # примерно 1 год торговых дней
rolling_sharpe, rolling_sharpe_annual = calculate_rolling_sharpe_ratio(
    returns[stock_ticker], risk_free_rate, window=window_size
)

# Визуализация динамического коэффициента Шарпа
plt.figure(figsize=(12, 6))
rolling_sharpe_annual.plot()
plt.axhline(y=0, color='r', linestyle='--')
plt.axhline(y=1, color='g', linestyle='--')
plt.title(f'Динамический коэффициент Шарпа для {stock_ticker} (окно {window_size} дней)')
plt.ylabel('Коэффициент Шарпа (годовой)')
plt.grid(True)
plt.tight_layout()
plt.show()

График динамического коэффициента Шарпа с использованием скользящего окна (252 дней)

Рис. 7: График динамического коэффициента Шарпа с использованием скользящего окна (252 дней)

Этот код позволяет наблюдать эволюцию коэффициента Шарпа во времени. Горизонтальные линии при значениях 0 и 1 помогают визуально оценить периоды с различным соотношением риска и доходности.

Улучшенные методы расчета и интерпретации

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

Модифицированный коэффициент Шарпа с учетом скошенности и эксцесса

Классический коэффициент Шарпа имеет ограничение: он предполагает нормальное распределение доходностей. Однако на практике распределения часто имеют «тяжелые хвосты» (высокий эксцесс) и асимметрию (скошенность). Модифицированный коэффициент Шарпа учитывает эти особенности:

import pandas as pd
import numpy as np
from scipy.stats import skew, kurtosis

# Расчет классического и модифицированного коэффициентов Шарпа
def calculate_modified_sharpe_ratio(returns, risk_free_rate, periods_per_year=252):
    excess_returns = returns - risk_free_rate
    mean = excess_returns.mean()
    std = returns.std()
    s = skew(returns)
    k = kurtosis(returns)  # Это уже эксцесс над нормальным распределением (Fisher), то есть kurtosis - 3
    
    if std == 0:
        return float('nan'), float('nan'), s, k

    # Формула Cornish-Fisher (CF-adjusted Sharpe Ratio)
    z_cf = mean / std * (1 + 
                        s * (mean / std) / 6 + 
                        k * (mean / std)**2 / 24 - 
                        s**2 * (mean / std)**3 / 36)

    # Аннуализация
    modified_sharpe_annual = z_cf * np.sqrt(periods_per_year)
    
    return mean / std * np.sqrt(periods_per_year), modified_sharpe_annual, s, k


# Расчет классического и модифицированного коэффициентов Шарпа
results = {}

for ticker in [stock_ticker, market_ticker]:
    classic_sharpe, modified_sharpe, skewness, kurtosis_val = calculate_modified_sharpe_ratio(
        returns[ticker], risk_free_rate
    )
    
    results[ticker] = {
        'Классический Шарп': round(classic_sharpe, 4),
        'Модифицированный Шарп': round(modified_sharpe, 4),
        'Скошенность': round(skewness, 4),
        'Эксцесс': round(kurtosis_val, 4)
    }

# Вывод результатов
print("\nСравнение классического и модифицированного коэффициентов Шарпа:")
sharpe_df = pd.DataFrame(results).T
print(sharpe_df)

# Визуализация
plt.figure(figsize=(10, 6))
x = np.arange(len(sharpe_df.index))
width = 0.35

plt.bar(x - width/2, sharpe_df['Классический Шарп'], width, label='Классический Шарп', color='skyblue')
plt.bar(x + width/2, sharpe_df['Модифицированный Шарп'], width, label='Модифицированный Шарп', color='salmon')

plt.xticks(x, sharpe_df.index)
plt.title('Сравнение коэффициентов Шарпа (годовых)')
plt.ylabel('Значение коэффициента Шарпа')
plt.legend()
plt.grid(True, axis='y')
plt.tight_layout()
plt.show()
Сравнение классического и модифицированного коэффициентов Шарпа:
       Классический Шарп  Модифицированный Шарп  Скошенность  Эксцесс
GOOG              0.3020                 0.3018      -0.2227   3.9976
VCNIX            -0.0513                -0.0514      -5.0806  72.5633

Модифицированный коэффициент Шарпа использует расширение Корниша-Фишера для учета скошенности и эксцесса распределения доходностей. Это позволяет получить более точную оценку эффективности инвестиций, особенно для стратегий с асимметричным профилем риска.

Читайте также:  Теория вероятностей и биржевая торговля

В примере выше разница между классическим Шарпом и модифицированным крайне мала и составляет всего 0.0001-0.0002. Это связано с тем что в котировках Google, Nasdaq за рассматриваемый период не было сильной положительной или отрицательной скошенности (skewness) и высокого эксцесса (kurtosis). Однако так бывает не всегда. К примеру, в оционных стратегиях или в котировках криптовалют разница между значениями этого показателя бывает весьма значительной.

Условная бета и альфа с учетом рыночной волатильности

Классический расчет беты предполагает, что взаимосвязь между доходностью актива и рынка остается постоянной. Однако исследования показывают, что бета может зависеть от состояния рынка, в частности от его волатильности. Рассмотрим как можно выполнить расчет условной беты с помощью Python, Pandas, Scipy:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats 
import pandas_datareader.data as web
import time

# Функция расчёта альфы и беты
def calculate_alpha(stock_returns, market_returns, risk_free_rate):
    excess_stock = stock_returns - risk_free_rate
    excess_market = market_returns - risk_free_rate

    if len(excess_stock) < 2 or len(excess_market) < 2: return np.nan, np.nan, np.nan, np.nan slope, intercept, r_value, _, _ = stats.linregress(excess_market, excess_stock) alpha_daily = intercept alpha_annual = alpha_daily * 252 # Аннуализация альфы return alpha_daily, alpha_annual, slope, r_value**2 # Расчет условной беты по квантилям волатильности def calculate_conditional_beta(stock_returns, market_returns, risk_free_rate, quantiles=5): # Скользящая месячная волатильность рынка (21 день) rolling_market_vol = market_returns.rolling(window=21).std() # Удаляем NaN перед разбиением на квантили valid_indices = rolling_market_vol.dropna().index stock_returns = stock_returns[valid_indices] market_returns = market_returns[valid_indices] rolling_market_vol = rolling_market_vol.loc[valid_indices] # Разделение на квантили по волатильности try: vol_quantiles = pd.qcut(rolling_market_vol, q=quantiles, labels=False, duplicates='drop') except ValueError as e: print("Ошибка при разделении на квантили:", e) return pd.DataFrame() # Возвращаем пустой DataFrame # Подготовка таблицы с результатами conditional_betas = pd.DataFrame(index=range(quantiles), columns=['Квантиль волатильности', 'Условная бета', 'Условная альфа (%)', 'R-квадрат']) # Цикл по квантилям for i in range(quantiles): mask = (vol_quantiles == i) if sum(mask) > 30:  # Проверяем, достаточно ли данных
            try:
                alpha, alpha_annual, beta, r_squared = calculate_alpha(
                    stock_returns[mask], market_returns[mask], risk_free_rate
                )
                annualized_volatility = rolling_market_vol[mask].mean() * np.sqrt(252) * 100
                conditional_betas.loc[i] = [
                    f'Q{i+1} ({annualized_volatility:.2f}%)',
                    round(beta, 2),
                    round(alpha_annual * 100, 2),
                    round(r_squared, 2)
                ]
            except Exception as e:
                print(f"Ошибка при расчёте для квантиля {i}: {e}")
                conditional_betas.loc[i] = [f'Q{i+1}', np.nan, np.nan, np.nan]
        else:
            conditional_betas.loc[i] = [f'Q{i+1}', np.nan, np.nan, np.nan]

    return conditional_betas.dropna(how='all').reset_index(drop=True)


# Объединение и проверка
if len(stock_prices) < 2 or len(market_prices) < 2:
    print("Недостаточно данных для анализа.")
    exit()

data = pd.DataFrame({
    stock_ticker: stock_prices,
    market_ticker: market_prices
}).dropna()

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

# Рассчитываем условную бету
ticker_to_analyze = 'GOOG'
market_ticker = 'VCNIX'

conditional_betas = calculate_conditional_beta(
    returns[ticker_to_analyze],
    returns[market_ticker],
    risk_free_rate,
    quantiles=5
)

# Вывод результатов
print(f"\nУсловная бета для {ticker_to_analyze} в зависимости от волатильности рынка:")
print(conditional_betas)

# Визуализация
plt.figure(figsize=(10, 6))
plt.bar(conditional_betas['Квантиль волатильности'], conditional_betas['Условная бета'])
plt.title(f'Условная бета для {ticker_to_analyze} в зависимости от волатильности рынка')
plt.xlabel('Квантиль волатильности рынка (годовая)')
plt.ylabel('Бета-коэффициент')
plt.grid(True, axis='y')
plt.tight_layout()
plt.show()
Условная бета для GOOG в зависимости от волатильности рынка:
  Квантиль волатильности Условная бета Условная альфа (%) R-квадрат
0            Q1 (13.17%)          0.82              51.34       0.2
1            Q2 (16.37%)          1.06               2.09      0.42
2            Q3 (18.41%)          1.05             -35.27      0.42
3            Q4 (22.99%)          1.06                7.0      0.48
4            Q5 (50.03%)          0.39                5.4      0.28

Распределение условной Беты акций Google по квантилям волатильности рынка

Рис. 8: Распределение условной Беты акций Google по квантилям волатильности рынка

Этот код анализирует, как бета-коэффициент меняется в зависимости от уровня рыночной волатильности. Разделяя данные на квантили по волатильности рынка, мы можем оценить, как актив реагирует на различные рыночные условия. Это особенно полезно для управления риском в периоды высокой волатильности.

Заключение

В данной статье мы рассмотрели три ключевых показателя оценки эффективности инвестиций: Альфа, Бета и коэффициент Шарпа. Эти метрики позволяют инвесторам и трейдерам количественно оценивать доходность, риск и рыночную чувствительность активов, что критически важно для принятия обоснованных решений.

Основные выводы:

  1. Бета отражает систематический риск актива относительно рынка. Мы убедились, что этот коэффициент непостоянен и может значительно варьироваться в зависимости от рыночных условий, особенно в периоды высокой волатильности.
  2. Альфа показывает избыточную доходность актива после учета рыночного риска. Положительная Альфа свидетельствует о превосходстве стратегии или управления, но на длительных горизонтах устойчиво добиваться такой доходности крайне сложно.
  3. Коэффициент Шарпа оценивает эффективность инвестиций с поправкой на риск. Его модифицированные версии, учитывающие асимметрию и «тяжелые хвосты» распределения, дают более точную оценку, особенно для активов с нестандартной динамикой.

С помощью Python и библиотек, таких как pandas, numpy и scipy, мы автоматизировали расчет этих показателей, что позволяет:

  1. Сравнивать активы и портфели по уровню риска и доходности;
  2. Анализировать динамику Альфы и Беты во времени, выявляя изменения рыночной чувствительности;
  3. Оптимизировать инвестиционные стратегии, отбирая инструменты с лучшим соотношением риск/доходность.

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

    Для более точной оценки инвестиций стоит комбинировать эти метрики с другими подходами:

    • Анализом drawdown (максимальных просадок);
    • Многопериодными стресс-тестами;
    • Моделями, учитывающими нелинейные зависимости (например, machine learning).

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