Критерий Келли: научный подход к выбору размера позиции в трейдинге

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

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

Происхождение и математическое обоснование Критерия Келли

Критерий Келли был разработан Джоном Ларри Келли младшим в 1956 году, когда он работал в Bell Labs. Изначально формула была создана для решения проблемы передачи информации по зашумленному телефонному каналу, но быстро нашла применение в совершенно другой сфере — азартных играх и финансах.

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

Для самого простого случая — игры с двумя исходами (выигрыш или проигрыш) — формула Келли выглядит следующим образом:

Формула критерия Келли для игры с 2 исходами: выигрыш или проигрыш

где:

  • f* — оптимальная доля капитала для ставки;
  • b — чистый выигрыш в случае успеха (например, если за 1 доллар вы получаете 3, то b = 2);
  • p — вероятность выигрыша;
  • q — вероятность проигрыша (q = 1 — p).

Пример: если у вас есть игра с 60% шансом на выигрыш, где в случае успеха вы получаете сумму, равную вашей ставке (b = 1), то критерий Келли рекомендует ставить 20% вашего капитала на каждую игру:

Расчет размера ставки по критерию Келли

Эта формула может быть выведена из максимизации ожидаемого логарифмического прироста капитала. Если мы обозначим начальный капитал как V0​, а капитал после n ставок как Vn​, то логарифмический прирост будет:

Формула ожидаемого логарифмического прироста капитала

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

Расширение формулы для торговли на финансовых рынках

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

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

Формула критерия Келли для нормально распределенных доходностей

где:

  • μ — ожидаемая доходность (математическое ожидание);
  • σ^2 — дисперсия доходности

Эта формула предполагает, что мы можем занимать как длинные, так и короткие позиции. Если мы ограничены только длинными позициями, формула несколько модифицируется.

Важно отметить, что эта формула предполагает отсутствие безрисковой ставки. Если же мы включаем в модель безрисковую ставку rf​, то формула принимает вид:

Формула критерия Келли с учетом безрисковой ставки

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

Практическое применение Критерия Келли в алгоритмической торговле

Теория — это прекрасно, но как же применить Критерий Келли на практике? Давайте рассмотрим несколько конкретных сценариев, где данный подход может быть полезен при разработке торговых алгоритмов.

Оценка параметров для Критерия Келли

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

👉🏻  Расчет доверительных интервалов и уровня значимости с Python

Вот несколько методов оценки этих параметров:

  1. Историческое моделирование — использование исторических данных для оценки вероятности успеха и соотношения выигрыша/проигрыша;
  2. Метод Монте-Карло — генерация случайных сценариев для оценки параметров;
  3. Байесовский подход — включение предварительных убеждений и обновление их на основе новых данных.

Давайте рассмотрим пример реализации исторического моделирования для оценки параметров Критерия Келли:

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from datetime import datetime

plt.style.use('seaborn-v0_8-whitegrid')

# Параметры стратегии и загрузки данных
df = yf.Ticker('IBM').history(interval='1h', start='2024-01-01', end='2025-07-01')
df.head()

if df.empty:
    raise ValueError("Данные не загружены. Проверьте символ, даты или таймфрейм.")

# Подготовка данных
df['Returns'] = df['Close'].pct_change()
df['SMA12'] = df['Close'].rolling(window=12).mean()
df['SMA36'] = df['Close'].rolling(window=36).mean()

# Сигнал на покупку, когда SMA12 > SMA36 (вход с задержкой в 1 свечу)
df['Signal'] = 0
df.loc[df['SMA12'] > df['SMA36'], 'Signal'] = 1
df['Signal'] = df['Signal'].shift(1)
df = df.dropna()

# Доходность стратегии
df['Strategy_Returns'] = df['Returns'] * df['Signal']

# Критерий Келли
wins = df[df['Strategy_Returns'] > 0]
losses = df[df['Strategy_Returns'] < 0]
total_trades = len(wins) + len(losses)

if total_trades == 0 or losses['Strategy_Returns'].mean() == 0:
    print("Недостаточно данных для оценки критерия Келли.")
    kelly_f = np.nan
else:
    p = len(wins) / total_trades
    avg_win = wins['Strategy_Returns'].mean()
    avg_loss = abs(losses['Strategy_Returns'].mean())
    r = avg_win / avg_loss
    kelly_f = p - (1 - p) / r

    print(f"Вероятность выигрыша (p): {p:.4f}")
    print(f"Средняя прибыль: {avg_win:.4%}")
    print(f"Средний убыток: {avg_loss:.4%}")
    print(f"Отношение прибыли к убытку (r): {r:.4f}")
    print(f"Оптимальная доля капитала по Келли (f*): {kelly_f:.4f}")

# Визуализация
plt.figure(figsize=(10, 6))
plt.hist(df['Strategy_Returns'], bins=50, color='steelblue', alpha=0.7)
plt.axvline(x=0, color='black', linestyle='--', linewidth=1)
plt.title(f'Распределение доходностей стратегии (интервал: {interval}) для {symbol}')
plt.xlabel('Доходность')
plt.ylabel('Частота')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Вероятность выигрыша (p): 0.5456
Средняя прибыль: 0.3482%
Средний убыток: 0.3671%
Отношение прибыли к убытку (r): 0.9485
Оптимальная доля капитала по Келли (f*): 0.0666

Гистограмма доходностей торговой стратегии, основанной на пересечении скользящих средних (SMA12 и SMA36) для акций IBM на часовом таймфрейме за период с 1 января 2024 года по 1 июля 2025 года. Вертикальная пунктирная линия соответствует нулевому значению доходности, разделяя убыточные и прибыльные сделки

Рис. 1: Гистограмма доходностей торговой стратегии, основанной на пересечении скользящих средних (SMA12 и SMA36) для акций IBM на часовом таймфрейме за период с 1 января 2024 по 1 июля 2025 года. Вертикальная пунктирная линия соответствует нулевому значению доходности, разделяя убыточные и прибыльные сделки

В этом примере мы загружаем исторические данные IBM, создаем простую стратегию на основе пересечения скользящих средних и оцениваем параметры для критерия Келли. После выполнения кода мы получаем количественную оценку — оптимальную долю капитала, которую следует вкладывать в каждую сделку с учетом исторических показателей стратегии.

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

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

Фракционный Критерий Келли

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

Фракционный Критерий Келли предполагает использование только части от рекомендуемой доли капитала. Формула его расчета следующая:

Формула фракционного критерия Келли

где c — коэффициент, обычно в диапазоне от 0.3 до 0.5.

Например, если полный Критерий Келли рекомендует вложить 25% капитала, то при использовании половинного Критерия Келли (c = 0.5) мы вложим только 12.5%.

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

# Продолжаем предыдущий пример
# Симулируем результаты торговли с разными стратегиями управления капиталом

initial_capital = 10000
strategies = {
    'Full Kelly': kelly_f,
    'Half Kelly': kelly_f * 0.5,
    'Quarter Kelly': kelly_f * 0.25,
    'Fixed 1%': 0.01
}

results = {}

for name, fraction in strategies.items():
    capital = initial_capital
    capitals = [capital]
    
    for ret in df['Strategy_Returns']:
        # Ограничиваем максимальную долю капитала до 100%
        position_size = min(fraction, 1.0)
        capital *= (1 + ret * position_size)
        capitals.append(capital)
    
    results[name] = capitals

# Визуализация результатов
plt.figure(figsize=(12, 6))

for name, capitals in results.items():
    plt.plot(capitals, label=name, linewidth=2)

plt.title('Сравнение стратегий управления капиталом')
plt.xlabel('Торговые дни')
plt.ylabel('Капитал ($)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# Рассчитываем метрики для каждой стратегии
metrics = {}

for name, capitals in results.items():
    capitals_array = np.array(capitals)
    returns = capitals_array[1:] / capitals_array[:-1] - 1
    
    cagr = (capitals[-1] / capitals[0]) ** (252 / len(returns)) - 1
    volatility = np.std(returns) * np.sqrt(252)
    max_drawdown = np.max(np.maximum.accumulate(capitals_array) - capitals_array) / np.max(np.maximum.accumulate(capitals_array))
    sharpe = cagr / volatility
    
    metrics[name] = {
        'CAGR': cagr,
        'Volatility': volatility,
        'Max Drawdown': max_drawdown,
        'Sharpe Ratio': sharpe,
        'Final Capital': capitals[-1]
    }

# Выводим результаты в таблице
metrics_df = pd.DataFrame(metrics).T
print(metrics_df.round(4))

Динамика капитала при использовании различных стратегий управления объемом позиции на основе торговой стратегии для акций IBM. Моделируются четыре подхода: полное, половинное и четвертное применение критерия Келли, а также фиксированная ставка 1% от капитала. По оси абсцисс — количество торговых периодов (часов), по оси ординат — накопленный капитал в долларах США

Рис. 2: Динамика капитала при использовании различных стратегий управления объемом позиции на основе торговой стратегии для акций IBM. Моделируются четыре подхода: полное, половинное и четвертное применение критерия Келли, а также фиксированная ставка 1% от капитала. По оси абсцисс — количество торговых периодов (часов), по оси ординат — накопленный капитал в долларах США

                 CAGR  Volatility  Max Drawdown  Sharpe Ratio  Final Capital
Full Kelly     0.0022      0.0060        0.0162        0.3761     10230.9517
Half Kelly     0.0011      0.0030        0.0082        0.3774     10115.2737
Quarter Kelly  0.0006      0.0015        0.0041        0.3780     10057.5854
Fixed 1%       0.0003      0.0009        0.0025        0.3783     10034.5728

Что делает этот код? Он эмулирует торговлю при разных стратегиях управления капиталом (Full Kelly, Half Kelly, Quarter Kelly, Fixed 1%). Использует доходности по сделкам (Strategy_Returns), чтобы рассчитать, как менялся бы капитал при каждом подходе. Затем строит график изменения капитала и вычисляет ключевые метрики:

  • CAGR (среднегодовая доходность);
  • Волатильность;
  • Максимальная просадка;
  • Коэффициент Шарпа;
  • Итоговый капитал (на последнюю дату).
👉🏻  Фьючерсы: назначение, виды контрактов, сроки, маржа

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

Многомерный Критерий Келли для портфельной оптимизации

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

Многомерный Критерий Келли для портфеля из n активов можно записать в матричной форме:

Формула многомерного критерия Келли

где:

  • f* — вектор оптимальных весов для каждого актива;
  • Σ — ковариационная матрица доходностей;
  • μ — вектор ожидаемых доходностей.

Обратите внимание на сходство с формулой для тангенциального портфеля (tangency portfolio) в теории Марковица. Фактически, многомерный Критерий Келли приводит к тангенциальному портфелю, когда безрисковая ставка равна нулю.

Давайте реализуем многомерный Критерий Келли на Python и сравним его с другими подходами к портфельной оптимизации:

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from scipy.optimize import minimize

# Загрузка данных для нескольких активов
symbols = ['JNJ', 'PG', 'UNH', 'XOM', 'KO']
start_date = '2022-07-01'
end_date = '2025-07-01'

# Загружаем данные
dfs = {}
for symbol in symbols:
    df = yf.download(symbol, start=start_date, end=end_date)
    dfs[symbol] = df['Close']

# Объединяем в один DataFrame
prices = pd.concat(dfs, axis=1)
prices.columns = symbols

# Рассчитываем ежедневные доходности
returns = prices.pct_change().dropna()

# Рассчитываем ожидаемые доходности и ковариационную матрицу
mu = returns.mean().values
Sigma = returns.cov().values

# Многомерный критерий Келли
kelly_weights = np.linalg.solve(Sigma, mu)

# Нормализуем веса, чтобы их сумма была равна 1
kelly_weights = kelly_weights / np.sum(np.abs(kelly_weights))

# Функция для расчета доходности и риска портфеля
def portfolio_stats(weights, mu, Sigma):
    portfolio_return = np.sum(weights * mu) * 252  # Годовая доходность
    portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(Sigma, weights))) * np.sqrt(252)  # Годовая волатильность
    sharpe_ratio = portfolio_return / portfolio_volatility
    return portfolio_return, portfolio_volatility, sharpe_ratio

# Максимизация коэффициента Шарпа (для сравнения)
def negative_sharpe(weights, mu, Sigma):
    return -portfolio_stats(weights, mu, Sigma)[2]

# Ограничения: сумма весов должна быть равна 1
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})

# Границы: запрещаем короткие позиции
bounds = tuple((0, 1) for asset in range(len(symbols)))

# Начальное приближение: равные веса
initial_guess = np.array([1./len(symbols)] * len(symbols))

# Оптимизация по Шарпу
result = minimize(negative_sharpe, initial_guess, args=(mu, Sigma), method='SLSQP',
                 bounds=bounds, constraints=constraints)

sharpe_weights = result['x']

# Рассчитываем метрики для обоих подходов
kelly_stats = portfolio_stats(kelly_weights, mu, Sigma)
sharpe_stats = portfolio_stats(sharpe_weights, mu, Sigma)

# Выводим результаты
print("Многомерный критерий Келли:")
for symbol, weight in zip(symbols, kelly_weights):
    print(f"{symbol}: {weight:.4f}")
print(f"Ожидаемая годовая доходность: {kelly_stats[0]:.4f}")
print(f"Ожидаемая годовая волатильность: {kelly_stats[1]:.4f}")
print(f"Коэффициент Шарпа: {kelly_stats[2]:.4f}\n")

print("Оптимизация по коэффициенту Шарпа:")
for symbol, weight in zip(symbols, sharpe_weights):
    print(f"{symbol}: {weight:.4f}")
print(f"Ожидаемая годовая доходность: {sharpe_stats[0]:.4f}")
print(f"Ожидаемая годовая волатильность: {sharpe_stats[1]:.4f}")
print(f"Коэффициент Шарпа: {sharpe_stats[2]:.4f}")

# Визуализация эффективной границы и позиций портфелей
num_portfolios = 10000
results = np.zeros((3, num_portfolios))
weights_record = []

for i in range(num_portfolios):
    weights = np.random.random(len(symbols))
    weights = weights / np.sum(weights)
    weights_record.append(weights)
    portfolio_return, portfolio_volatility, sharpe_ratio = portfolio_stats(weights, mu, Sigma)
    results[0, i] = portfolio_volatility
    results[1, i] = portfolio_return
    results[2, i] = sharpe_ratio

plt.figure(figsize=(12, 8))
plt.scatter(results[0, :], results[1, :], c=results[2, :], cmap='viridis', marker='o', s=10, alpha=0.3)
plt.colorbar(label='Коэффициент Шарпа')
plt.scatter(kelly_stats[1], kelly_stats[0], c='black', marker='*', s=300, label='Критерий Келли')
plt.scatter(sharpe_stats[1], sharpe_stats[0], c='red', marker='*', s=300, label='Максимальный Шарп')
plt.title('Эффективная граница и оптимальные портфели')
plt.xlabel('Ожидаемая волатильность')
plt.ylabel('Ожидаемая доходность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Многомерный критерий Келли:
JNJ: -0.2205
PG: 0.1613
UNH: -0.1251
XOM: 0.2009
KO: 0.2922
Ожидаемая годовая доходность: 0.0747
Ожидаемая годовая волатильность: 0.0851
Коэффициент Шарпа: 0.8779

Оптимизация по коэффициенту Шарпа:
JNJ: 0.0000
PG: 0.2254
UNH: 0.0000
XOM: 0.3903
KO: 0.3843
Ожидаемая годовая доходность: 0.0968
Ожидаемая годовая волатильность: 0.1419
Коэффициент Шарпа: 0.6823

Эффективная граница портфелей, построенная на основе исторических данных ежедневных доходностей пяти акций голубых фишек (JNJ, PG, UNH, XOM, KO) за период с июля 2022 по июль 2025 года. Точки отображают случайные портфели с различными весами активов, окрашенные по значению коэффициента Шарпа. Черная звезда обозначает портфель, оптимизированный по многомерному критерию Келли, а красная — портфель с максимальным коэффициентом Шарпа, рассчитанный методом численной оптимизации. График иллюстрирует компромисс между ожидаемой доходностью и риском (волатильностью) для различных распределений капитала

Рис. 3: Эффективная граница портфелей, построенная на основе исторических данных ежедневных доходностей пяти акций голубых фишек (JNJ, PG, UNH, XOM, KO) за период с июля 2022 по июль 2025 года. Точки отображают случайные портфели с различными весами активов, окрашенные по значению коэффициента Шарпа. Черная звезда обозначает портфель, оптимизированный по многомерному критерию Келли, а красная — портфель с максимальным коэффициентом Шарпа, рассчитанный методом численной оптимизации. График иллюстрирует компромисс между ожидаемой доходностью и риском (волатильностью) для различных распределений капитала

Давайте разберем что делает этот код:

  1. Загрузка данных дневных цен закрытия акций пяти голубых фишек: JNJ, PG, UNH, XOM, KO за период с 2022-07-01 по 2025-07-01;
  2. Формирование единого датафрейма всех активов;
  3. Расчет доходностей — ежедневные процентные изменения цен;
  4. Расчет статистик — векторы средних доходностей (μ) и ковариационные матрицы доходностей (Σ);
  5. Расчет многомерного критерия Келли и вычисление оптимальных весов портфеля. Нормализация весов таким образом, чтобы сумма абсолютных значений = 1;
  6. Определение функций для оценки портфеля — вычисления годовой доходности, волатильности и коэффициента Шарпа портфеля;
  7. Оптимизация по коэффициенту Шарпа — с помощью метода численной оптимизации (SLSQP) мы ищем вектора весов, максимизирующих коэффициент Шарпа, при ограничениях: веса неотрицательны и сумма весов равна 1;
  8. Расчет метрик для обоих портфелей — ожидаемая доходность, волатильность и коэффициент Шарпа для портфелей, полученных по Келли и максимальному Шарпу;
  9. Генерация случайных портфелей и построение эффективной границы;
  10. Визуализация эффективной границы.
👉🏻  Stock Sell-Off или массовые сбросы акций: причины, кейсы, стратегии трейдинга

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

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

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

Анализ полученных результатов

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

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

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

👉🏻  Показатели TWR (Time-Weighted Return), MWR (Money-Weighted Return) и MDR (Modified Dietz Return)

Ограничения и модификации Критерия Келли

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

Проблема оценки параметров

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

Для иллюстрации этой проблемы давайте рассмотрим, как ошибки в оценке параметров влияют на рекомендуемый размер позиции:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm

# Предположим, что истинные параметры следующие:
true_p = 0.55  # Вероятность выигрыша
true_r = 1.0  # Отношение выигрыш/проигрыш

# Рассчитываем истинное значение f* по критерию Келли
true_kelly = true_p - (1 - true_p) / true_r

# Генерируем ряд оценок p с ошибками
p_estimates = np.linspace(0.45, 0.65, 100)
# Генерируем ряд оценок r с ошибками
r_estimates = np.linspace(0.5, 1.5, 100)

# Создаем сетку для всех комбинаций оценок
p_grid, r_grid = np.meshgrid(p_estimates, r_estimates)
kelly_grid = p_grid - (1 - p_grid) / r_grid

# Визуализация
fig = plt.figure(figsize=(10, 8))

# 3D поверхность
ax1 = fig.add_subplot(221, projection='3d')
surf = ax1.plot_surface(p_grid, r_grid, kelly_grid, cmap='viridis', alpha=0.8)
ax1.set_xlabel('Оценка p')
ax1.set_ylabel('Оценка r')
ax1.set_zlabel('f* по Келли')
ax1.set_title('Зависимость f* от оценок параметров')
fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=5)

# Контурный график
ax2 = fig.add_subplot(222)
contour = ax2.contourf(p_grid, r_grid, kelly_grid, 20, cmap='viridis')
ax2.set_xlabel('Оценка p')
ax2.set_ylabel('Оценка r')
ax2.set_title('Контурный график f* по Келли')
plt.colorbar(contour, ax=ax2)

# Срез по p при фиксированном r = true_r
ax3 = fig.add_subplot(223)
idx = np.abs(r_estimates - true_r).argmin()
ax3.plot(p_estimates, kelly_grid[idx, :], 'k-', linewidth=2)
ax3.axhline(y=true_kelly, color='r', linestyle='--', linewidth=1.5)
ax3.axvline(x=true_p, color='r', linestyle='--', linewidth=1.5)
ax3.set_xlabel('Оценка p')
ax3.set_ylabel('f* по Келли')
ax3.set_title(f'Срез при фиксированном r = {true_r:.2f}')
ax3.grid(True, alpha=0.3)

# Срез по r при фиксированном p = true_p
ax4 = fig.add_subplot(224)
idx = np.abs(p_estimates - true_p).argmin()
ax4.plot(r_estimates, kelly_grid[:, idx], 'k-', linewidth=2)
ax4.axhline(y=true_kelly, color='r', linestyle='--', linewidth=1.5)
ax4.axvline(x=true_r, color='r', linestyle='--', linewidth=1.5)
ax4.set_xlabel('Оценка r')
ax4.set_ylabel('f* по Келли')
ax4.set_title(f'Срез при фиксированном p = {true_p:.2f}')
ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# Рассчитываем ошибку оценки f* в зависимости от ошибок в параметрах
p_errors = np.linspace(-0.1, 0.1, 100)  # Ошибки в оценке p
r_errors = np.linspace(-0.5, 0.5, 100)  # Ошибки в оценке r

p_grid_err, r_grid_err = np.meshgrid(p_errors, r_errors)
true_p_grid = true_p + p_grid_err
true_r_grid = true_r + r_grid_err

# Рассчитываем f* с ошибками
kelly_grid_err = true_p_grid - (1 - true_p_grid) / true_r_grid
# Рассчитываем относительную ошибку в f*
rel_error = (kelly_grid_err - true_kelly) / true_kelly * 100

# Визуализация относительной ошибки
plt.figure(figsize=(9, 6))
contour = plt.contourf(p_grid_err, r_grid_err, rel_error, 20, cmap='RdBu_r')
plt.colorbar(contour, label='Относительная ошибка в f* (%)')
plt.xlabel('Ошибка в оценке p')
plt.ylabel('Ошибка в оценке r')
plt.title('Влияние ошибок в оценке параметров на f* по Келли')
plt.axhline(y=0, color='k', linestyle='--', linewidth=0.5)
plt.axvline(x=0, color='k', linestyle='--', linewidth=0.5)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

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

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

Оценка чувствительности оптимального размера ставки по критерию Келли к ошибкам в параметрах

Рис. 5: Оценка чувствительности оптимального размера ставки по критерию Келли к ошибкам в параметрах

Анализируя результаты этого кода, мы видим, что даже небольшие ошибки в оценке вероятности успеха или отношения выигрыш/проигрыш могут привести к значительным отклонениям в рекомендуемом размере позиции. Например, переоценка вероятности успеха на 5 процентных пунктов может привести к тому, что Критерий Келли рекомендует вложить в 2 раза больше капитала, чем следовало бы.

👉🏻  Что такое хеджирование и как оно работает?

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

Модификации Критерия Келли для повышения устойчивости

Чтобы уменьшить чувствительность к ошибкам в оценке параметров, были разработаны различные модификации Критерия Келли. Рассмотрим некоторые из них:

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

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

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import pymc as pm
import arviz as az

# Загрузка данных
df = yf.Ticker('IBM').history(interval='1h', start='2024-01-01', end='2025-07-01')

# Проверка структуры данных
if isinstance(df.index, pd.MultiIndex):
    df = df.reset_index(level=0, drop=True)

# Рассчитываем дневные доходности
df['Returns'] = df['Close'].pct_change()
df = df.dropna()

# Создаем простую стратегию - покупаем, если RSI-14 < RSI-30 df['RSI'] = df['Returns'].rolling(14).apply( lambda x: 100 - (100 / (1 + (x[x > 0].mean() / -x[x < 0].mean())))
)
df.dropna(inplace=True)
df['Signal'] = 0
df.loc[df['RSI'] < 30, 'Signal'] = 1 df['Signal'] = df['Signal'].shift(1) df = df.dropna() # Рассчитываем доходность стратегии df['Strategy_Returns'] = df['Returns'] * df['Signal'] # Подготовка данных для байесовской оценки strategy_returns = df['Strategy_Returns'].values wins = strategy_returns > 0
losses = strategy_returns < 0
neutral = strategy_returns == 0

n_wins = np.sum(wins)
n_losses = np.sum(losses)
n_neutral = np.sum(neutral)
n_total = len(strategy_returns)

# Создаем байесовскую модель
with pm.Model() as model:
    # Prior для вероятности выигрыша
    p = pm.Beta('p', alpha=2, beta=2)

    # Prior для средней доходности в случае выигрыша
    mu_win = pm.TruncatedNormal('mu_win', mu=0.01, sigma=0.01, lower=0)

    # Prior для средней доходности в случае проигрыша
    mu_loss = pm.TruncatedNormal('mu_loss', mu=-0.01, sigma=0.01, upper=0)

    # Likelihood для количества выигрышей/проигрышей
    win_loss = pm.Binomial('win_loss', n=n_wins + n_losses, p=p, observed=n_wins)

    # Likelihood для доходностей в случае выигрыша
    win_returns = pm.Normal('win_returns', mu=mu_win, sigma=0.01,
                            observed=strategy_returns[wins])

    # Likelihood для доходностей в случае проигрыша
    loss_returns = pm.Normal('loss_returns', mu=mu_loss, sigma=0.01,
                             observed=strategy_returns[losses])

    # Вычисляем Критерий Келли как детерминистическую переменную
    r = pm.Deterministic('r', -mu_win / mu_loss)
    kelly_f = pm.Deterministic('kelly_f', p - (1 - p) / r)

    # Запускаем сэмплирование
    trace = pm.sample(2000, tune=1000, return_inferencedata=True)

# Анализируем результаты
az.plot_posterior(trace, var_names=['p', 'r', 'kelly_f'],
                  kind='hist', figsize=(12, 8))
plt.tight_layout()
plt.show()

# Получаем среднее значение и доверительные интервалы для f*
kelly_samples = trace.posterior['kelly_f'].values.flatten()
kelly_mean = np.mean(kelly_samples)
kelly_median = np.median(kelly_samples)
kelly_lower = np.percentile(kelly_samples, 5)
kelly_upper = np.percentile(kelly_samples, 95)

print(f"Байесовская оценка Критерия Келли:")
print(f"Среднее значение f*: {kelly_mean:.4f}")
print(f"Медианное значение f*: {kelly_median:.4f}")
print(f"90% доверительный интервал: [{kelly_lower:.4f}, {kelly_upper:.4f}]")

# Моделируем результаты торговли с разными уровнями Критерия Келли
initial_capital = 10000
percentiles = [5, 25, 50, 75, 95]
kelly_values = [np.percentile(kelly_samples, p) for p in percentiles]

results = {}
for p, kelly_f in zip(percentiles, kelly_values):
    capital = initial_capital
    capitals = [capital]

    for ret in df['Strategy_Returns']:
        position_size = min(kelly_f, 1.0)
        capital *= (1 + ret * position_size)
        capitals.append(capital)

    results[f'Kelly {p}%'] = capitals

# Визуализация результатов
plt.figure(figsize=(12, 6))

for name, capitals in results.items():
    plt.plot(capitals, label=name, linewidth=2)

plt.title('Сравнение стратегий с разными уровнями Критерия Келли')
plt.xlabel('Торговые дни')
plt.ylabel('Капитал ($)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
                                                                                                                   
  Progress                   Draws   Divergences   Step size   Grad evals   Sampling Speed    Elapsed   Remaining  
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── 
  ━━━━━━━━━━━━━━━━━━━━━━━━   3000    0             0.62        3            1103.09 draws/s   0:00:02   0:00:00    
  ━━━━━━━━━━━━━━━━━━━━━━━━   3000    0             0.84        3            602.04 draws/s    0:00:04   0:00:00  

Байесовская оценка Критерия Келли:
Среднее значение f*: -0.0962
Медианное значение f*: -0.0283
90% доверительный интервал: [-0.6703, 0.2741]

Постериорные распределения параметров стратегии. График показывает байесовские оценки: вероятность положительной доходности сделки (p), соотношение средней прибыли к среднему убытку (r), оптимальную долю капитала по критерию Келли (f*). Каждое распределение отражает уровень неопределенности модели, учитывая исторические данные. Широкие распределения указывают на высокую степень неопределенности, тогда как узкие — на уверенность модели в оценках

Рис. 6: Постериорные распределения параметров стратегии. График показывает байесовские оценки: вероятность положительной доходности сделки (p), соотношение средней прибыли к среднему убытку (r), оптимальную долю капитала по критерию Келли (f*). Каждое распределение отражает уровень неопределенности модели, учитывая исторические данные. Широкие распределения указывают на высокую степень неопределенности, тогда как узкие — на уверенность модели в оценках

Рост капитала при разных уровнях ставки по критерию Келли от 5-го до 95-го процентиля. График показывает, как выбор степени агрессивности (величины ставки) влияет на итоговый капитал инвестора. Чрезмерно агрессивные стратегии (верхние процентильные значения) потенциально обеспечивают более высокую доходность, однако сопровождаются повышенными рисками. Напротив, консервативные стратегии характеризуются более плавной, но устойчивой динамикой капитала

Рис. 7: Рост капитала при разных уровнях ставки по критерию Келли от 5-го до 95-го процентиля. График показывает, как выбор степени агрессивности (величины ставки) влияет на итоговый капитал инвестора. Чрезмерно агрессивные стратегии (верхние процентильные значения) потенциально обеспечивают более высокую доходность, однако сопровождаются повышенными рисками. Напротив, консервативные стратегии характеризуются более плавной, но устойчивой динамикой капитала

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

👉🏻  Основы диверсификации биржевых портфелей

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

Критерий Келли и психология трейдинга

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

Представьте себе ситуацию, когда Критерий Келли рекомендует вложить 30% вашего капитала в одну сделку. Если эта сделка окажется убыточной, вы потеряете значительную часть своего капитала, что может привести к паническим решениям и отказу от следования стратегии.

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

Заключение

Критерий Келли — мощный математический инструмент для оптимального управления капиталом в алгоритмической торговле. Он дает теоретически обоснованный ответ на вопрос «сколько инвестировать?» в условиях неопределенности.

Ключевые выводы:

  1. Критерий Келли максимизирует логарифмический рост капитала в долгосрочной перспективе, что делает его привлекательным для алгоритмических трейдеров с долгосрочным горизонтом;
  2. На практике рекомендуется использовать фракционный Критерий Келли (обычно половинный или четвертичный), чтобы учесть неопределенность в оценке параметров и снизить риск психологических ошибок;
  3. Многомерный Критерий Келли может быть использован для оптимизации портфеля из нескольких активов или стратегий, и имеет прямую связь с теорией портфельного выбора Марковица;
  4. Байесовский подход к оценке параметров Критерия Келли позволяет учесть неопределенность в оценках и выбрать более консервативную стратегию, соответствующую нашей склонности к риску.

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