Новые релизы okama

Версия 1.4.4 - Новые классы для стратегий изъятий и пополнения портфеля

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

Новые функции

3 новых класса для настройки стратегий Cash Flow

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

  • IndexationStrategy для стратегий с регулярными индексируемыми снятиями/пополнениями
  • PercentageStrategy для стратегий с регулярными снятиями/пополнениями с фиксированным процентом
  • TimeSeriesStrategy для стратегий с произвольным количеством снятий и/или пополнений

Все 3 класса наследуются от родительского класса CashFlow.
У класса Portfolio больше нет параметров Cash Flow (initial_amount, cashflow, discount_rate). Идея в том, чтобы старый класс Portfolio как был так и остался для “простых” стратегий без промежуточных снятий и пополнений.

Новый класс для настройки параметров моделирования Монте-Карло

Класс MonteCarlo имеет несколько свойств:

  • distribution - тип распределения для генерации временных рядов доходности
  • period - длина прогнозируемого периода в годах
  • number - количество случайных временных рядов

Все параметры Монте-Карло связаны с экземпляром PortfolioDCF и могут быть доступны с помощью конструкции Portfolio().dcf.mc. Например, тип распределения случайной величины доступен через Portfolio().dcf.mc.disctribution.

Новые методы и свойства в PortfolioDCF

PortfolioDCF имеет новый параметр use_discounted_values (по умолчанию False). Он определяет, использовать ли дисконтированные значения при бэктестинге (значения начальных инвестиций, размеры снятий и пополнений). Параметр discount_rate перемещен из Portfolio в PortfolioDCF.

Новые методы и свойства

  • find_the_largest_withdrawals_size - найти наибольший размер снятия средств в рамках стратегии. Для поиска максимального размера снятий используется моделирование Монте-Карло в соответствии со стратегией денежного потока. Этот метод работает с IndexationStrategy и PercentageStrategy
  • initial_investment_fv используется для расчета будущей стоимости (FV) начальных инвестиций в конце прогнозируемого периода
  • initial_investment_pv используется для расчета дисконтированной стоимости (PV) начальных инвестиций на дату first_date из параметров Portfolio()
  • wealth_index_with_assets работает как в Portfolio(), но учитывает денежные потоки (вклады и изъятия)
  • метод set_mc_parameters используется для добавления параметров моделирования Монте-Карло

Изменения в старых методах и свойствах в PortfolioDCF

  • методы monte_carlo_survival_period, survival_date_hist и survival_period_hist теперь имеют новый параметр threshold. Это “порог”, который определяет когда мы считаем баланс портфеля равным нулю. Считается как процент начальных инвестиций
  • количество параметров plot_forecast_monte_carlo сокращено. Остались только backtest и figsize.

Новый ноутбук с примерами использования стратегий снятия/пополнения

Новые примеры использования стратегий IndexationStrategy, PercentageStrategy и TimeSeriesStrategy доступны в ноутбуке 04 investment portfolios with DCF.ipynb.


Обновлена документация библиотеки okama.
Теперь там можно подробно познакомиться с новыми классами семейства CashFlow и MonteCarlo.

Во всех случаях есть примеры применения.

О новой архитектуре

Немного “философии” и размышлений о будущем в работе со стратегиями семейства CashFlow…

В версии 1.4.4 пришлось сильно “перекроить” архитектуру объектов. Теперь каждая из стратегий снятий или пополнений настраивается в своём классе с определенными параметрами.

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

Еще мне хотелось упростить модуль portfolio.py, который разросся и содержит всё, что касается портфелей. Было бы здорово разместить все классы семейства CashFlow в отдельном модуле. Но, оказалось, что не так всё просто…

В версии 1.4.1 появилось кеширование полученных “тяжелые” данных. Например, результатов симуляции Монте-Карло. Когда компьютер считает тысячи временных рядов доходностей с учетом ребалансировок, на это уходит время. Потом с помощью этих рядов считаются какие-то вспомогательные параметры (например, “период дожития”) или строятся графики. “Выбрасывать” и генерировать всё с нуля каждый раз, когда что-то хочешь посчитать или нарисовать… это расточительство :slight_smile:

Кеширование, конечно, полезно. НО! Когда меняется что-то мало-мальски значимое в настройках классов (размер изъятия, количество случайных сценариев в Моне-Карло, даты и т.п.) необходимо почистить кэш, ибо он из добра превращается в зло. А значит это, что каждый из новых классов CashFlow и MonteCarlo должен иметь возможность изменять состояние класса PortfolioDCF. Получаются взаимосвязанные классы. Само по себе отслеживание состояний – довольно сложный процесс. Кроме того, теперь не получится разместить классы CashFlow в отдельном модуле из-за возникающих круговых ссылок.

Получается своего рода развилка.

  • Простота кода и устойчивая конструкция без отслеживания статусов, но отсутствие кеша и более медленная работа.
  • Взаимосвязанные классы. Потенциальные ошибки. Сложный модуль portfolio.py. Быстрая работа.

Я пока склоняюсь все-таки поддерживать вариант 2. Python и так медленный язык. Идти дальше в сторону замедления не хочется. Но, не факт что получится…

Если есть идеи по архитектуре, дайте знать.

Версия 1.5.0 - Стратегии ребалансировки

Большой релиз. В нем много нового. Но основное - это продвинутые стратегии ребалансировки для классов Portfolio и EfficientFrontierReb.

Теперь выбирать можно среди 3 вариантов ребалансировки:

  • Календарная ребалансировка (как было раньше)
  • Ребалансировка по условию (абсолютное отклонение веса или относительное)
  • Смешанная стратегия

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

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

Новая версия окамы уже доступна: pip install okama

Подробности релиза 1.5.0

Кроме стратегий ребалансировок в классе EfficientFrontierReb появилась возможность учитывать ограничения по весам (параметр bands). Устранена важная ошибка, которая мешала правильно строить границы эффективности с учетом ребалансировок.
Большое спасибо @Artt за участие в работе над релизом!

В нескольких классах добавлены новые методы. Есть важные исправления ошибок.

Класс Rebalance

rs = ok.Rebalance(
    period="year",
    abs_deviation=0.10,
    rel_deviation=0.30
)
pf = ok.Portfolio(['SPY.US', 'AGG.US'], rebalancing_strategy=rs)

ВНИМАНИЕ:
Старый способ инициализации Portfolio устарел:

# Устаревший вариант (не работает!)
ok.Portfolio(rebalancing_period="year")

В тоже время всё еще можно инициализировать класс Portfolio вообще без указания стратегии ребалансировки (по умолчанию используется календарная ежемесячная ребалансировка):

# Рабочий вариант (ребалансировка по умолчанию - ежемесячная)
ok.Portfolio(['SPY.US', 'AGG.US'], weights=[.6, .4])

Класс Rebalance

  • Новый класс Rebalance имеет 3 параметра: period, abs_deviation, rel_deviation
  • Rebalance.wealth_ts() рассчитывает временной ряд для баланса ребалансируемого портфеля (опционально с активами)
  • Rebalance.assets_weights_ts() вычисляет месячные временные ряды весов активов портфеля с учетом ребалансировки
  • Rebalance.return_ror_ts() возвращает временной ряд месячной доходности портфеля с учетом ребалансировок

Новые методы и свойства класса Portfolio

  • новое свойство rebalancing_strategy для настройки периодичности, абсолютных и относительных отклонений весов в стратегии ребалансировки
  • новое свойство rebalancing_events возвращает временной ряд событий ребалансировки и их тип

Изменения в классе PortoflioDCF

  • PortfolioDCF.monte_carlo_wealth_pv генерирует дисконтированные случайные временные ряды балансов портфелей с учетом пополнений/изъятий
  • PortfolioDCF.monte_carlo_wealth_fv генерирует номинальные (не дисконтированные) случайные временные ряды балансов портфелей с учетом пополнений/изъятий
  • PortfolioDCF.find_the_largest_withdrawals_size() теперь использует метод бисекции для поиска максимального размера изъятий
  • PortfolioDCF.find_the_largest_withdrawals_size() имеет 3 возможные цели: ‘maintain_balance_pv’, ‘maintain_balance_fv’ и ‘survival_period’
  • PortfolioDCF.find_the_largest_withdrawals_size() имеет новый атрибут withdrawals_range определяет возможный диапазон изъятий для ускорения расчетов
  • в PortfolioDCF.find_the_largest_withdrawals_size() атрибут iter_max ограничивает количество промежуточных итераций при поиске решения
  • PortfolioDCF.find_the_largest_withdrawals_size() метод возвращает класс Result. Если решение не найдено, остаются доступными промежуточные результаты

Новые методы и свойства класса EfficienFrontierReb

Класс EfficienFrontierReb теперь использует параметр rebalancing_strategy при инициализации (аналогично Portfolio).
Поддерживает ограничения весов активов через параметр bounds.

  • новый параметр rebalancing_strategy для определения стратегии ребалансировки
  • новый параметр bounds задает ограничения на веса активов
  • новый метод EfficienFrontierReb.plot_pair_ef() строит Границу эффективности для каждой пары активов

Изменения в классе EfficientFrontier

  • метод EfficientFrontier.get_monte_carlo генерирует случайные ребалансируемые портфели методом Монте-Карло с учетом ограничений bounds.

Новый Jupyter Notebook с примерами стратегий ребалансировки

Добавлен новый Jupyter Notebook с примерами инвестиционных портфелей с различными стратегиями ребалансировки:
10 rebalancing portfolio.ipynb

Обновленная документация с подробным описаниями всех классов доступна на ReadTheDocs.


Скоро обновление библиотеки okama до версии 1.5.1

В новой версии будет много важного и полезного:
:black_small_square:Продвинутые стратегии снятия денег: Vanguard Dynamic Spending (VDS) и
Cut Withdrawals If Drawdown (CWID)
:black_small_square: Возможность добавлять произвольные снятия и пополнения по датам в любой стратегии (extra cash flow)
:black_small_square: Возможность самостоятельной настройки параметров распределения в прогнозировании с помощью Монте-Карло (нормальное, логнормальное и Стьюдента)
:black_small_square: skew-t распределения Стьюдента для прогнозов стратегий с высоким коэффициентом асимметрии

90% работы над новой версией уже завершено. Обновление ожидается в сентябре.

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

1 лайк

Okama 2.0.0

Вот и дождались юбилейного релиза :grinning_face_with_smiling_eyes:

В этот раз изменений накопилось много… Основные темы релиза такие:

  • EfficientFrontier и EfficientFrontierReb объединены в единый класс EfficientFrontier
  • Существенно ускорены расчёты Границы эффективности благодаря кэшированию, параллельным вычислениям и оптимизации целевой функции
  • Добавлены новые продвинутые стратегии изъятия средств: Vanguard Dynamic Spending (VDS) и CutWithdrawalsIfDrawdown (CWD). О них будет отдельная статья.

image

Подробности изменений

Единый класс EfficientFrontier с многопериодной оптимизацией

EfficientFrontier и EfficientFrontierReb были объединены в единый класс EfficientFrontier. Теперь любая Граница эффективости по умолчанию рассчитывается с ребалансировкой (многопериодный подход). Оптимизация ребалансируемых портфелей теперь поддерживает ограничения на веса (bounds), позволяя пользователям задавать минимальные и максимальные пределы распределения для каждого актива.

Скорость расчётов была значительно увеличена благодаря:

  • кэшированию промежуточных результатов
  • параллельным вычислениям
  • оптимизации целевой функции
  • быстрым векторизованным методам в классе Rebalance

ПРЕДУПРЕЖДЕНИЕ:
Устаревший класс с классической оптимизацией Марковица (без ребалансировки) был переименован в EfficientFrontierSingle. Если вы использовали старый EfficientFrontier (однопериодный), перейдите на EfficientFrontierSingle.

import matplotlib.pyplot as plt

y = ok.EfficientFrontier(
    assets="SPY.US", "GLD.US"],
    first_date="2004-12",
    last_date="2020-10",
    ccy="USD",
    rebalancing_strategy=ok.Rebalance(period="year"),  # календарная ребалансировка раз в год
    ticker_names=True,  # использовать наименование ценных бумаг вместо тикеров
    n_points=40,  # количество точек на Границе эффективности (детализация графика)
)
fig, ax = plt.subplots(figsize=(12, 10))

# Plot the Efficient Frontier
ax.plot(df_reb_year.Risk, df_reb_year.CAGR, label="Annually rebalanced")
ax.plot(df_not_reb.Risk, df_not_reb.CAGR, label="Not rebalanced")

# Отрисовка точек активов
y.plot_assets(kind="cagr")

ax.set_xlabel("Risk (Standard Deviation)")
ax.set_ylabel("CAGR")
ax.legend();

image

Продвинутые стратегии изъятия средств из портфеля

Две новые стратегии денежного потока для пенсионного планирования и управления целевыми фондами:

Vanguard Dynamic Spending (VDS)

Реализация правила Vanguard Dynamic Spending, которое корректирует изъятия средств в зависимости от результатов портфеля с использованием нижних и верхних ограничителей.

pf = ok.Portfolio(['SPY.US', 'BND.US'], weights=[.6, .4], first_date='2010-01', last_date='2024-10', ccy='USD', inflation=True)
vds = ok.VanguardDynamicSpending(
    parent=pf,
    initial_investment=1_000_000,
    frequency="year",
    percentage=-0.05,
    floor_ceiling=(-0.05, 0.10),  # -5% floor / +10% ceiling
)
pf.dcf.cashflow_parameters = vds

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

Подробное описание новых стратегий изъятия средств приведено в 04 investment portfolios with DCF.ipynb.

Новые классы и методы

Параметры first_date и last_date в Asset

  • Параметры first_date и last_date доступны в классе Asset. Использование этих параметров может значительно ускорить загрузку исторических данных.

Улучшения в классе EfficientFrontier

  • EfficientFrontier.plot_transition_map() — визуализация границы переходов
  • EfficientFrontier.plot_cml() — построение линии рынка капитала
  • EfficientFrontier.get_most_diversified_portfolio() — оптимизация Most Diversified Portfolio (MDP) на основе коэффициента диверсификации
  • EfficientFrontier.get_monte_carlo() генерирует случайные портфели с помощью моделирования Монте-Карло с учётом ограничения по весам (bounds)
  • EfficientFrontier.plot_assets() теперь принимает **kwargs для matplotlib scatter()

AssetList

  • AssetList.get_monthly_geometric_mean_return() — новый метод для расчёта геометрической средней доходности за месяц

MonteCarlo

  • MonteCarlo.plot_forecast_monte_carlo() теперь возвращает объект matplotlib Axes
  • MonteCarlo.get_parameters_for_distribution() — подбор распределения с параметрами для нормального, логнормального распределений и распределения Стьюдента
  • MonteCarlo.kstest() и MonteCarlo.kstest_for_all_distributions() — тесты согласия Колмогорова — Смирнова
  • MonteCarlo.plot_hist_fit() — гистограмма с наложением подобранного распределения
  • MonteCarlo.plot_qq() — QQ-график для визуальной оценки качества подгонки распределения

Архитектура портфеля

  • Новый класс MonteCarlo для продвинутого моделирования Монте-Карло и статистического анализа распределений (перенесён из Portfolio)
  • Новый класс PortfolioDCF для анализа дисконтированных денежных потоков, анализа выживаемости портфеля и тестирования стратегий изъятия средств
  • Новый вспомогательный класс Rebalance, инкапсулирующий логику ребалансировки, с методами wealth_ts_ef и return_ror_ts_ef для быстрой оптимизации

Документация и примеры

Новые и обновлённые примеры Jupyter Notebook в каталоге /examples:

  • 04 investment portfolios with DCF.ipynb — множество новых примеров для PortfolioDCF, VanguardDynamicSpending и стратегии CutWithdrawalsIfDrawdown
  • 07 efficient frontier multi-period.ipynb — существенно переработан, добавлены примеры для единого класса EfficientFrontier и многопериодной оптимизации
  • 10 forecasting.ipynb — обновлённые примеры прогнозирования методом Монте-Карло, включая методы подгонки распределений MonteCarlo
  • 11 rebalancing portfolio.ipynb — несколько новых примеров

Все существующие ноутбуки были обновлены в соответствии с последними изменениями API в okama 2.0.0.

Исправления ошибок

  • Исправлен расчёт капитализации годовой средней арифметической доходности в AssetList.annual_return_ts и Portfolio.annual_return_ts
  • Исправлен расчёт PortfolioDCF.wealth_index(discounting="pv")
  • Исправлена проблема, при которой PortfolioDCF.cashflow_parameters изменялся после plot_forecast_monte_carlo()
  • Исправлено поведение PortfolioDCF.find_the_largest_withdrawals_size, из-за которого не сохранялись исходные параметры CashFlow
  • Исправлены корректировки денежного потока PortfolioDCF в первом и последнем году для неполных периодов
  • Исправлен расчёт Portfolio.mean_return, теперь используется формула ror.mean()
  • Исправлен расчёт параметров MonteCarlo._get_params_for_lognormal
  • Исправлена подгонка логнормального распределения в MonteCarlo.kstest(), теперь используются валовые доходности
  • Исправлен FutureWarning Pandas для concat с пустым DataFrame в классе Rebalance
  • Исправлено имя параметра сеттера MonteCarlo.mc_number
  • Исправлен расчёт PortfolioDCF._target_cagr_range_left

Прочие изменения

  • Python 3.10 больше не поддерживается
  • Подключение к API переведено на HTTPS с проверкой SSL
  • Обновлены все примеры Jupyter Notebook в каталоге /examples
  • Существенно увеличено покрытие тестами для модулей DCF, Monte Carlo и стратегий денежного потока

Полный список изменений: v1.5.0…v2.0.0

Okama 2.0.1

2.0.1 - это обещанный технический релиз, который обеспечил поддержку Python 3.14 и Pandas 3.0.

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

Подробности изменений

  • PortfolioDCF.wealth_index() и PortfolioDCF.cash_flow_ts() теперь используют классическое дисконтирование. Раньше было “обратное”, которое многих ставило в тупик.
  • Исправили баг, при котором терялся один месяц исторической доходности в случае применения ребалансировки портфеля
  • Добавлен CHANGELOG.md с подробностями всех изменений

Удобство для разработчиков

  • перешли от связки black + flake8 к современному ruff check
  • начали использовать pre-commit для автоматической проверки коммитов
  • обновлен CONTRIBUTING.md с инструкциями для разработчиков

Okama 2.1.0

Версия 2.1.0 доступна для установки через pip.

В этом релизе идёт подготовка к глубокой модернизации виджетов на okama.io. Добавлены и обновлены полезные методы, которые в ближайшее время будут использоваться для визуализации накопленной доходности на сайте. Главное изменение — get_cumulative_return и get_cagr теперь возвращают расширяющуюся временную серию (expanding window) вместо одного скалярного значения на конец периода. Это удобнее для построения графиков «как менялась накопленная доходность во времени».

Ниже пример использования нового метода — сравнение накопленной доходности золота (GC.COMM) и серебра (SI.COMM) с 2005 по 2026 год в долларах США:

import okama as ok

al = ok.AssetList(['GC.COMM', 'SI.COMM'], ccy='USD',
                  first_date='2005-01', last_date='2026-04')
al.get_cumulative_return().plot()

Изменения

  • AssetList.get_cumulative_return() и Portfolio.get_cumulative_return() теперь возвращают расширяющийся временной ряд накопленной доходности, а не одно значение на конец периода. Ноутбук 03 investment portfolios.ipynb обновлён под новое поведение.
  • AssetList.get_cagr() и Portfolio.get_cagr() тоже считают CAGR на расширяющемся окне — поведение синхронизировано с get_cumulative_return().

Новые возможности

  • URL и таймаут запросов к API okama теперь настраиваются через переменные окружения OKAMA_API_URL и OKAMA_API_TIMEOUT (модули okama.settings и okama.api.api_methods). Полезно, если нужно направить запросы на собственный экземпляр сервера данных.

Исправление ошибок

  • Frame.get_semideviation() (модуль okama.common.helpers) теперь использует выборочное среднее доходностей вместо «популяционного» — это соответствует стандартному определению полу-отклонения (semideviation). Изменение проброшено во всех потребителей — AssetList и Portfolio.
  • AssetList.recovery_periods устойчив к ситуациям, когда last_date не приходится на начало месяца.
  • EfficientFrontier и EfficientFrontierReb теперь поднимают RuntimeError, если SLSQP-оптимизация не сошлась, вместо молчаливого возврата некорректных весов.
  • AssetList.plot_assets() и Portfolio.plot_assets() больше не передают невалидный аргумент axis="year" в autoscale.
  • Inflation.cumulative_inflation (модуль okama.macro) использует .iloc[-1] вместо позиционного [-1] — убирает pandas FutureWarning и потенциальный баг.
  • В PortfolioDCF атрибут переименован: monlthly_discount_ratemonthly_discount_rate (исправлена опечатка).
  • Helpers, порождающие NaN-строки, используют np.nan в dict.fromkeys(...) — DataFrame сохраняет тип float.

Удалено

  • Мёртвый метод Portfolio._clear_cf_cache.

Инструменты разработки

  • Включены ruff UP (pyupgrade) правила и применены автоисправления к модулям и ноутбукам.
  • Поддерживаемые версии Python в pyproject.toml синхронизированы; AGENTS.md теперь требует TDD-workflow для production-кода.
  • .gitignore исключает .env.

pip install --upgrade okama

Okama 2.1.1

Версия 2.1.1 доступна для установки через pip.

В этом релизе добавлен систематический (сеточный) перебор весов портфелей на эффективной границе — детерминированная альтернатива случайной выборке Монте-Карло. Выводы методов выборки портфелей также дополнены столбцами с весами по каждому активу.

Ниже — пример: эффективная граница из четырёх активов с наложенной сеткой портфелей get_grid_portfolios (шаг 10%):

import okama as ok

ef = ok.EfficientFrontier(['SPY.US', 'GLD.US', 'OKID.INDX', 'MCFTR.INDX'],
                          ccy='RUB', rebalancing_strategy=ok.Rebalance(period='year'))
grid = ef.get_grid_portfolios(step=0.10)  # все портфели на сетке весов с шагом 10%

Новые возможности

  • EfficientFrontier.get_grid_portfolios() (многопериодные ребалансируемые портфели) и EfficientFrontierSingle.get_grid_portfolios() (однопериодные) перебирают все портфели, веса которых лежат на фиксированной процентной сетке (step, по умолчанию 0.10), с учётом индивидуальных ограничений весов bounds. Это дополняет случайный get_monte_carlo() воспроизводимой и исчерпывающей выборкой допустимой области.
  • Вспомогательная функция Float.get_grid_weights() в okama.common.helpers — генератор всех векторов весов, суммирующихся в 1.0 на заданном шаге сетки, с учётом ограничений по активам. Шаг step валидируется: должен лежать в [0.01, 1.0] и нацело делить 1.0.
  • Столбцы с весами по каждому активу в выводах EfficientFrontier.get_monte_carlo() и EfficientFrontier.get_grid_portfolios() (многопериодный вариант) — теперь по структуре совпадают с тем, что уже выдавал EfficientFrontierSingle.get_monte_carlo().

Инструменты разработки

  • Для сборки документации зафиксирован sphinx < 9: переписанный autodoc в Sphinx 9.x падает с ValueError: The truth value of a DataFrame is ambiguous на атрибутах-классах pandas DataFrame (например, в okama.common.make_asset_list).
  • Pre-commit хук ruff поднят до v0.15.14 — в соответствие с версией ruff в poetry/CI, чтобы он перестал повторно применять исправления, которые проект уже подавляет через # noqa: UP0xx.

pip install --upgrade okama

Grid портфели - это конечно, хорошо. Но не всегда… :grinning_face_with_smiling_eyes:
Бывает и такое…

Okama 2.2.0

Версия 2.2.0 доступна для установки через pip.

Главное в релизе — денежно-взвешенная доходность (IRR/MWRR) для портфелей с денежными потоками: и на исторических данных, и в виде распределения по сценариям в прогнозах. Кроме того, Монте-Карло симуляции теперь векторизованы — одна полная симуляция стала быстрее на 3–4 порядка, а случайная выборка теперь кешируется и воспроизводима через seed.

Ниже — пример: распределение IRR по 500 путям Монте-Карло для портфеля 60/40 с ежегодным выводом 4% и стратегией сокращения выводов в просадки (CutWithdrawalsIfDrawdown или CWD):

import okama as ok

pf = ok.Portfolio(["SPY.US", "AGG.US"], weights=[0.6, 0.4], ccy="USD")
pf.dcf.cashflow_parameters = ok.CutWithdrawalsIfDrawdown(
    parent=pf,
    initial_investment=1_000_000,
    frequency="year",
    amount=-40_000,  # вывод 4% в год
    crash_threshold_reduction=[(0.20, 0.40), (0.50, 1)],  # просадка >20% — вывод меньше на 40%
)
pf.dcf.set_mc_parameters(distribution="norm", period=20, mc_number=500, seed=42)
irr_dist = pf.dcf.monte_carlo_irr()  # pd.Series с IRR каждого пути
irr_dist.median()

Изменения

  • Случайная выборка доходностей Монте-Карло (MonteCarlo.monte_carlo_returns_ts) теперь генерируется один раз и кешируется: monte_carlo_wealth, monte_carlo_cash_flow, monte_carlo_survival_period, monte_carlo_irr и методы распределения CAGR видят один согласованный набор сценариев (раньше каждое обращение генерировало новую случайность). Кеш сбрасывается при изменении любого параметра Монте-Карло.
  • Генерация прогнозов для Wealth Index теперь векторизована: Portfolio.dcf.monte_carlo_wealth() и всё, что на нём построено, считает все случайные пути за один проход вместо пути-за-путём через pandas apply. Результаты не изменились (закреплены сеткой тестов эквивалентности по стратегиям и частотам); измеренное ускорение одной полной симуляции — ×1400 для годовой и ×6800 для месячной частоты выводов на 1000 путей × 30 лет.
  • Аналогичным образом работает новый метод monte_carlo_irr().
  • Portfolio.dcf.find_the_largest_withdrawals_size() тоже работает на много быстрее: бисекция заменена методом Брента (scipy.optimize.brentq)

Новые возможности

  • Portfolio.dcf.irr() — номинальная годовая денежно-взвешенная доходность (IRR/MWRR) денежного потока портфеля за весь исторический период с учётом настроенной стратегии (IndexationStrategy, PercentageStrategy, VanguardDynamicSpending, CutWithdrawalsIfDrawdown, TimeSeriesStrategy). Без промежуточных денежных потоков совпадает с Portfolio.get_cagr() за период.
  • Portfolio.dcf.monte_carlo_irr() — распределение (pandas.Series) денежно-взвешенных IRR по сценариям Монте-Карло, прогнозный аналог Portfolio.dcf.irr().
  • Параметр seed для воспроизводимых выборок Монте-Карло: MonteCarlo.seed и новый аргумент seed в PortfolioDCF.set_mc_parameters().

Исправление ошибок

  • monte_carlo_cash_flow() с remove_if_wealth_index_negative=True маскировал выборку денежного потока по выборке богатства, сгенерированной из другой случайности; с общей кешированной выборкой маска истощения теперь согласована по каждому пути.
  • В симуляциях с периодической частотой первый месяц периода с дополнительными денежными потоками (time_series) пропускал доходность; рекурсия теперь единообразна для каждого месяца. Индексы богатства и серии денежных потоков с дополнительными потоками на частотах year/half-year/quarter изменились соответственно.
  • Лимиты floor_ceiling стратегии VanguardDynamicSpending не срабатывали при расчёте wealth_index и monte_carlo_wealth. Предыдущий вывод всегда считался равным 0. Индексы богатства для VDS-стратегий с floor_ceiling изменились и теперь согласованы с cash_flow_ts.

ВНИМАНИЕ: численные результаты Монте-Карло методов без фиксированного seed отличаются от версии 2.1.1 — выборка теперь общая и кешированная. Для воспроизводимых расчётов задавайте set_mc_parameters(..., seed=...).


pip install --upgrade okama

Okama 2.2.2

Версия 2.2.2 доступна для установки через pip.

В этом релизе добавлена аналитика ошибки следования (tracking error) для портфелей и два новых вида просадок — с поправкой на инфляцию и по ценам без учёта дивидендов. Заодно собраны исправления, накопившиеся с версии 2.2.0, включая важные правки эффективной границы из 2.2.1.

Один из новых методов — real_drawdowns: просадки не номинального, а скорректированного на инфляцию индекса благосостояния. Они показывают потерю покупательной способности, которую скрывает номинальный рост. Наглядный пример — портфель ОФЗ (RGBITR.INDX) в рублях: номинально просадки выглядят умеренными (до −22.8% в 2022 году), но в реальном выражении доходят до −38% и к концу периода ещё далеки от восстановления:

import okama as ok

pf = ok.Portfolio(["RGBITR.INDX"], ccy="RUB", inflation=True)
pf.real_drawdowns.plot()   # реальные просадки (с поправкой на инфляцию)
pf.drawdowns.plot()        # для сравнения — номинальные

Новые возможности

  • Portfolio.tracking_error(benchmark, rolling_window, method) — ex-post ошибка следования портфеля относительно бенчмарка. Бенчмарк задаётся тикером-строкой или объектом (Asset, Portfolio); метод делегирует расчёт в AssetList.
  • Параметр method для AssetList.tracking_error и Index.tracking_error (okama.common.helpers): "rms" (по умолчанию, прежний нецентрированный корень из среднего квадрата отклонений — включает систематическое отставание от бенчмарка) или "std" (центрированное выборочное стандартное отклонение с поправкой Бесселя).
  • AssetList.real_drawdowns и Portfolio.real_drawdowns — просадки скорректированного на инфляцию индекса благосостояния; показывают потери покупательной способности, скрытые за номинальным ростом (требуется inflation=True).
  • AssetList.price_drawdowns и Portfolio.price_drawdowns — просадки по ценам закрытия без учёта дивидендов; для активов с высокими дивидендами заметно отличаются от полнодоходных drawdowns.

Исправление ошибок

  • PortfolioDCF.find_the_largest_withdrawals_size() для целей maintain_balance_pv и maintain_balance_fv ошибочно поднимал ValueError про target_survival_period на любом периоде Монте-Карло ≤ 27 лет — хотя эти цели target_survival_period не используют, и вызывающий код его не передавал. Теперь параметр проверяется только для цели survival_period.
  • EfficientFrontier.ef_points выдавал дублирующиеся точки правой части границы на thread-бэкенде joblib (воркер и добавлял строку в общий список, и возвращал её). Теперь воркер только возвращает строку.
  • Frame.kstest_series (используется в kstest_for_all_distributions и в свойствах подбора распределения у AssetList / Portfolio) падал с TypeError: ndtr() takes from 1 to 2 positional arguments but 3 were given под scipy 1.18.0 на Python ≥ 3.12. Тест Колмогорова — Смирнова теперь использует CDF «замороженного» распределения — численно эквивалентно и совместимо со scipy 1.17 и 1.18.

Сюда же вошли ключевые исправления эффективной границы из версии 2.2.1, которая на форуме отдельно не анонсировалась:

  • Правая часть EfficientFrontier.ef_points возле угловой (одноактивной) точки больше не рисует доминируемый «крючок» и не пропадает молча: граница теперь всегда заканчивается ровно на угловом активе с монотонно растущим риском.
  • Парные границы EfficientFrontier.plot_pair_ef() теперь доходят до точек активов и учитывают rebalancing_strategy родительского портфеля (раньше всегда считались с ежегодной ребалансировкой по умолчанию).
  • Сетка целевых значений ef_points теперь проходит точно через каждую одноактивную точку, лежащую на границе диапазона (число строк в ef_points / mdp_points может немного превышать n_points).
  • Убраны Pandas4Warning на pandas 3.

Документация

  • Уточнено, что значения tracking_error — это десятичные доли, а не проценты (0.05 — это 5% годовых).
  • В докстринге find_the_largest_withdrawals_size() отмечено, что поддерживаются подклассы стратегий IndexationStrategy / PercentageStrategy (например, CutWithdrawalsIfDrawdown, VanguardDynamicSpending).

pip install --upgrade okama