Okama 2.0: продвинутые стратегии изъятий и новая Граница эффективности

7 мин
Okama 2.0: продвинутые стратегии изъятий и новая Граница эффективности

Мы выпустили “юбилейную” версию okama 2.0. В релизе множество важных изменений. Основные темы: новые продвинутые стратегии снятия денег из портфеля и новая логика оптимизации портфелей для Границы эффективности.

Инсталлировать или обновить до последней версии библиотеку можно уже сейчас.

pip install okama -U

В okama 2.0 мы сознательно сделали шаг от учебных и академических кейсов к более прикладным и реалистичным сценариям. Во-первых, набор модулей получила новые инструменты, которые хорошо подходят для разработки “пенсионных стратегий” с регулярными изъятиями.

Построение попарных Границ эффективности с учетом ребалансировки
Построение попарных Границ эффективности с учетом ребалансировки

Во-вторых, изменилась логика работы с Границей эффективности: теперь многопериодная оптимизация с ребалансировкой стала поведением по умолчанию. И этот более сложный вид оптимизации работает кратно быстрее! Теперь не надо оставлять работать компьютер и уходить пить чай, пока идут расчеты… Это сделало okama гораздо пригодной для WEB приложений. Изменения на okama.io не за горами.

Стратегии с денежными потоками и дисконтированием

Новые стратегии изъятий встроены в уже существующую архитектуру анализа денежных потоков с дисконтированием в классе PortfolioDCF. Портфель остаётся отдельным объектом, а стратегия денежных потоков подключается через pf.dcf.cashflow_parameters. Такая схема даёт возможность независимо тестировать структуру портфеля, правила ребалансировки и логику изъятий.

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

import okama as ok pf = ok.Portfolio() # по умолчанию это портфель из одного фонда SPY

Стратегия снятий Vanguard Dynamic Spending (VDS)

В версии 1.5.0 появилась стратегия изъятия портфеля, основанная на процентном снятии (PercentageStrategy). Свежий класс VanguardDynamicSpending (VDS) развивает идею, где снятия определяются фиксированным процентом от баланса портфеля. Но вводит дополнительные ограничения. Это значимый момент: новая стратегия не заменяет старую процентную схема, а расширяет её. Идеи этой стратегии разработаны в исследовании Vangard (прилагается).

Свежий параметр, который вводит VDS, это floor_ceiling. Он ограничивает изменение снятия по сравнению с предыдущего года. Например, пара (-0.025, 0.05) означает, что новое изъятие не может быть больше чем на 5% выше и не может быть больше чем на 2.5% ниже прошлогоднего значения. Благодаря этого стратегия не допускает слишком резких скачков расходов вслед за рынком.

Дополнительно можно задать абсолютные ограничения через min_max_annual_withdrawals. Если включён adjust_min_max=True, эти границы индексируются, в частности на инфляцию. Параметр adjust_floor_ceiling работает похожим образом: сначала прошлогоднее снятие может быть проиндексировано, а затем к нему применяются floor и ceiling. В итоге VDS сочетает процентную привязку к портфелю и контроль над волатильностью потребления.

Ограничения: VDS работает только с регулярными снятиями (без пополнений), частота снятий – всегда раз в год.

Создается стратегия следующим образом:

vds = ok.VanguardDynamicSpending( parent=pf, initial_investment=1_000_000, # размер стартового капитал percentage=-0.08, # процент снятия денег floor_ceiling=(-0.025, 0.05), # процент, на который может изменяться размер снятий
) pf.dcf.cashflow_parameters = vds

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

Стратегия снятий Cut Withdrawals If Drawdown (CWD)

Это некоторое количество другая философия снятий. Размер зависит от реальных потребностей пенсионера и не зависит от размера портфеля. Этот подход был создан в версии 1.5.0 через класс IndexationStrategy.

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

Класс CutWithdrawalsIfDrawdown (CWD) задает параметры такой стратегии через ограничения снятий, которые зависят от глубины просадки портфеля.

Параметр crash_threshold_reduction задаётся списком пар вида (порог просадки, коэффициент сокращения). В частности, запись (0.20, 0.40) означает: если просадка портфеля превышает 20%, текущее снятие сокращается на 40%. А пара (0.35, 1) означает фактическую остановку изъятий при просадке глубже 35%.

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

Разработка CWD:

cwd = ok.CutWithdrawalsIfDrawdown( parent=pf, initial_investment=1_000_000, # стартовый капитал frequency="year", # периодичность снятий (здесь – раз в год) amount=-60_000, # целевой размер снятий в сегодняшних деньгах indexation="inflation", # индексация размера снятий на размер инфляции crash_threshold_reduction=[(.10, .25), (.20, .50), (.35, 1)], # список ограничений
) pf.dcf.cashflow_parameters = cwd

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

В следующих публикациях мы расскажем более подробно, как применять на практике стратегии VDS и CWD.

EfficientFrontier: единый класс для оптимизации с учетом ребалансировки

Следующий ключевой блок релиза — обновление EfficientFrontier. Мы объединили EfficientFrontier и EfficientFrontierReb в единый класс EfficientFrontier. Теперь Граница эффективности по умолчанию строится в многопериодной логике, с ребалансировкой.

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

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

ef = ok.EfficientFrontier( assets=["SPY.US", "GLD.US"], first_date="2004-12", last_date="2020-10", ccy="USD", rebalancing_strategy=ok.Rebalance(period="year"), n_points=40, ticker_names=True, bounds=((0.0, 1.0), (0.0, 0.20)), # процент золота в портфелях ограничен 20%
)

Быстрый поиск решений для заданной доходности:

ef.minimize_risk(target_value=0.11)
{'SPY.US': np.float64(0.9101000447520419), 'GLD.US': np.float64(0.08989995524795805), 'CAGR': 0.11, 'Mean return': np.float64(0.11403419643168483), 'Risk': np.float64(0.15083978371568296), 'Weights': array([0.91010004, 0.08989996]), 'iterations': 5}

Произвольные снятия и пополнения во всех стратегиях

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

time_series_dic = { "2027-02": 2_000, # пополнение портфеля в феврале 2027 года "2030-03": -4_000 # изъятие денег в марте 2030
}

Бэктестинг распределения

Бэктестинг распределения – значимый этап подготовки к прогнозированию. Для его удобства появились новые инструменты.

Автоматический выбор распределения

В версии 2.0 появился метод kstest_for_all_distributions, который “прогоняет” проверка Колмогорова-Смирнова для всех поддерживаемы распределений и определяет, какое из них подходит лучше.

pf.dcf.mc.kstest_for_all_distributions

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

Любой параметр распределения теперь может быть задан в ручном режиме. Полностью или отчасти.

Это особенно важно для распределения Стьюдента, которое обычно подходит лучше всего для прогнозирования “толстых хвостов”.

В частности, параметры распределения Стьюдента можно теперь задать следующим образом:

pf.dcf.mc.distribution_parameters = (4.26, None, None) # df, loc, scale

Уровень свободы (df) задается вручную, а остальные параметры – автоматически.

Более того, теперь уровень свободы можно “подогнать” автоматически под прогноз для определенной перцентили:

pf.dcf.mc.optimize_df_for_students(var_level=5) # подгон уровня свободы для 5й перцентили
4.260007759068121

Q-Q Plot

Точность бэктестинга распределения удобно визуализировать на графике Квантиль-Квантиль (Q-Q Plot), где сравниваются исторические данные и сгенерированные.

Для этого в версии 2.0 появился метод plot_qq:

pf.dcf.mc.plot_qq(var_level=5) # 5 – это доверительный уровень для VAR и CVAR
График Квантиль-Квантиль (Q-Q Plot)
График Квантиль-Квантиль (Q-Q Plot)

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

Документы okama

Документы okama стала опрятнее и содержит подробную информацию по всем новшествам и примеры.

Сам раздел примеров на GitHub существенно доработан:

  • 04 investment portfolios with DCF.ipynb — множество новых примеров для стратегий изъятий VanguardDynamicSpending и CutWithdrawalsIfDrawdown

  • 07 efficient frontier multi-period.ipynb — Добавлены примеры для унифицированного класса EfficientFrontier и оптимизации с учетом ребалансировки

  • 10 forecasting.ipynb — обновлены примеры прогнозирования методом Монте-Карло, включая методы подгонки распределений в Монте-Карло

Дальнейшие планы okama

В ближайшее время мы начнем переносить самые важные изменения на okama.io.

В новых версиях okama появится поддержка Pandas 3 и Python 3.14.

Несколько более далекая перспектива – это создание аналога Личного финансового плана (ЛФП). ЛФП будет состоять из последовательности инвестиционных стратегий. В частности, агрессивный портфель для создания пенсионных накоплений и консервативный портфель для расходования средств на пенсии. Конечно, всю последовательность портфелей надо будет прогнать сквозь прогнозирование Монте-Карло. И мы уже придумали, как это сделать.

Источники

1. Форум проекта на русском языке: Новые релизы okama

2. GitHub okama

3. Документы okama

4. Примеры использования в Jupyter Notebook

Читают сейчас

Sony добавила нейросетевое усовершенствование графики на обычную PS5

11 часов назад

Sony добавила нейросетевое усовершенствование графики на обычную PS5

За нейросетевым улучшением картинки больше не обязательно идти покупать PS5 Pro. Sony представила QSSR — технологию масштабирования изображения для обычной PlayStation 5. Первыми поддержку получили Ma

Неделя OCR для LLM — в Telegram-канале Smart Engines

12 часов назад

Неделя OCR для LLM — в Telegram-канале Smart Engines

Привет, ! Объявляем тематическую неделю, посвященную OCR для LLM и интеллектуальной обработки документов без галлюцинаций и «додумывания» данных. Расскажем, как устроено полнотекстовое распознавание S

Metro 2033 и Last Light получат бесплатное апдейт с улучшенной графикой и поддержкой 120 FPS

12 часов назад

Metro 2033 и Last Light получат бесплатное апдейт с улучшенной графикой и поддержкой 120 FPS

Возвращаться в московское метро скоро станет приятнее, насколько это вообще возможно среди мутантов и радиации. 4A Games и Deep Silver анонсировали бесплатное обновление для Metro 2033 Redux и Metro:

Perfscale news #13. Выжигание WASM модулей, http3 и начальный postmortem

12 часов назад

Perfscale news #13. Выжигание WASM модулей, http3 и начальный postmortem

Всех категорически приветствую, любители больших количеств девяток! Сегодня в выпуске у нас будет интересности, ну а начнем мы как всегда с истории прошлых выпусков Читать далее

Мы прошли все стадии принятия ИИ: вышел свежий сезон подкаста «Свободный слот»

13 часов назад

Мы прошли все стадии принятия ИИ: вышел свежий сезон подкаста «Свободный слот»

Помните, как в 2023 году все спорили, заменят ли нейросети инженеров? Тема дискуссионная, но сейчас, когда ИИ уже заложен в каждый процесс, вопрос ставится иначе: что создавать? Как с ним жить? Мы в А