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

Версия 0.98
Новые методы для класса Portfolio

  • get_cumulative_return - расчет накопленной доходности портфеля
  • get_rolling_cumulative_return - расчет накопленной скользящей доходности портфеля

'YTD' - больше не является аргументом для get_cagr. По смыслу доход с начал года (YTD) - это не CAGR (среднегодовая доходность), а накопленная (за несколько месяцев) доходность. Поэтому аргумент ‘YTD’ перенесен в get_cumulative_return.

В репозитории библиотеки появилась ветка предрелиза dev.
Установить предрелизную версию можно через:

pip install git+https://github.com/mbk-dev/okama@dev

На сегодняшний день в предрелизной версии доступны улучшения нескольких методов AssetList и Portfolio для расчета реальной доходности:

  • get_cagr(real=True)
  • get_rolling_cagr(real=True)
  • get_cumulative_return(real=True)
  • get_rolling_cumulative_return(real=True)

Внимание: предрелизная версия может быть нестабильной

1 лайк

Версия 0.99
Улучшения для классов Portfolio и AssetList:

  • Portfolio.get_return_ts() имеет аргумент rebalancing_period
  • Portfolio.get_rebalanced_portfolio_return_ts упразднен и больше не используется
  • get_rolling_cagr, get_rolling_cumulative_return, get_cagr и cumulative_return методы в классах AssetList и Portfolio используют аргумент real=True для расчета реальной доходности

Добавлены строки документации в классах Portfolio and AssetLists.

Добавлена Дорожная карта развития и инструкция Contributing Guidelines для желающих участвовать в проекте.

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

Ключевые изменения

  • Портфель теперь - это тип актива (такой же как акции, ETF, комодитиз и др.). Портфели можно добавлять в AssetList. Это позволяет, например, создавать портфели и сравнивать их между собой или с бенчмарками…
  • У портфеля теперь есть тикер (так же как у любого другого актива). Для портфелей зарезервировано расширение .PF. Тикер присваивается портфелю автоматически, но может быть изменен при помощи .symbol
  • Период ребалансировки (rebalancing_period) теперь является атрибутом портфеля. Он указывается при инициализации, по умолчанию равен одному месяцу. Доступны следующие периоды ребалансировки: год (year), месяц (month), без ребалансировки (none)
  • Asset получил методы .close и .adj_close. Теперь история цен закрытия доступна непосредственно из класса
  • Значения исторических данных по дивидендам приводятся к базовой валюте в классах AssetList и Portfolio
  • Дивидендная доходность портфеля .dividend_yield считается как взвешенная сумма дивидендных доходностей активов, в него входящих.
  • Портфель получил метод .dividends. Как в других активах, этот метод показывает историю дивидендов

Jupyter Notebook с примерами для инвестиционных портфелей

В классе Portfolio произошло много изменений. Поэтому для портфелей создан отдельный файл с примерами.
Все другие примеры применения библиотеки находятся в папке examples.

Изменения в системе абстракций

Новый абстрактный класс ListMaker является родительским для AssetList и Portfolio.

После изменений в версии 1.0.0 в шапке всех блокнотов с примерами находится ссылка, которая позволяет запустить блокнот в Google Colab. Это позволяет запускать Jupyter Notebook без локальной установки библиотеки.

Например, 01 howto.ipynb можно запустить в Google Colab по ссылке:

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

ВНИМАНИЕ: В этом релизе исправлены некоторые неприятные ошибки. Рекомендую всем обновиться до последней версии!

pip install --upgrade okama

Улучшение работы поиска в базе данных: .search и .symbols_in_namespace

  • ok.search() принимает namespace в качестве параметра (по умолчанию None).
  • ok.search() и ok.symbols_in_namespace() по умолчанию возвращают результаты в DataFrame. Тип выдачи можно изменить с помощью параметра response_format (может быть ‘frame’ или ‘json’)

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

  • длина временного ряда .inflation_ts ограничена значениями ‘first_date’ и ‘last_date’. Актуально для Portfolio и AssetList. Ошибка в некоторых случаях приводила к некорректным результатам расчета реальной доходности.
  • np.nan и np.inf заменены на 0 в расчетах среднегодового роста дивидендной доходности AssetList.get_dividend_mean_grow()

Пример работы нового поиска

Для поиска по тикерам конкретной биржи (или любого другого namespace) задаем параметр namespace=. Например для поиска всех ETF компании FinEx на Московской бирже:

Тоже самое для Лондонской биржи (LSE):

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

Коэффициент Шарпа, Tangency Portfolio и Линия рынка капитала (CML)

  • get_sharpe_ratio позволяет рассчитать Коэффициент Шарпа в Portfolio и AssetList (для всех активов, включенных в список)
  • .get_tangency_portfolio позволяет оптимизировать портфели по Коэффициенту Шарпа. Портфель с максимальным Коэффициентом Шарпа называется Tangency Portfolio. Пока новый метод работает только в классе EfficientFrontier
  • .plot_cml() рисует Линию рынка капитала (Capital Market Line) и Tangency Portfolio вместе с Границей эффективности (EfficientFrontier класс)

Изменения в классах:
Соблюдая принцип “Бритвы Оккамы”, мы избавились от лишнего класса Plots. Теперь “графические методы”, облегчающие жизнь, при рисовании сложных объектов, доступны прямо из традиционных классов (AssetList, Portfolio и т.д.).

  • метод .plot_assets перемещен в родительский абстрактный класс ListMaker и теперь доступен во всех дочерних классах: AsstList, Portfolio, EfficientFrontier и EfficientFrontierReb
  • методы .plot_pair_ef и .plot__transition_map доступны из EfficientFrontier

Пример отображения Линии рынка капитала (CML)

image
image
На графике точка MSR - Maximum Sharpe Ratio, он же Tangency Portfolio.
Веса и другие характеристики точки MSR доступны через .get_tangency_portfolio:
image

Документация по новым методам доступна на ReadTheDocs

Сергей, добрый день! При расчёте коэффициента Шарпа что Вы рекомендуете принимать за безрисковый актив в базовой валюте (рубли, доллары США, Евро)?

1 лайк

Извиняюсь, что пропустил вопрос.
Размер безрисковой ставки зависит от инвестора. Если это частный инвестор, то можно брать размер ставки в банке или на госбонды (ОФЗ или трежуриз). Если это институциональный инвестор, то берут например ставку LIBOR или КС.

Версия 1.1.1
Ноябрьский релиз с небольшими дополнениями и исправлениями доступен через pip.

Ежегодное отклонение от индекса

Новый метод .tracking_difference_annual для AssetList позволяет рассчитать историю отклонения от бенчмарка по календарным годам. Метод может быть полезен для проверки стабильности качества управления фондом. Например, это график ежегодных отклонения двух популярных фондов на S&P500: VOO (Vanguard, NYSE) и SPXS (Invesco UCITS, LSE).
okama-AssetList-tracking_difference_annual-1

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

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

Перед инициализацией классы AssetList, Portfolio, EfficientFrontier проверяют активы на достаточность исторического периода данных. Теперь в список активов можно включить только тикеры, имеющие как минимум 3 месяца истории. В противном случае - ValueError.

Версия 1.1.2
Релиз посвящен работе с популярными финансовыми коэффициентами. Обновление доступно через pip.

Коэффициент Сортино

  • метод get_sortiono_ratio() доступен в классах AssetList и Portfolio

Коэффициент Сортино является аналогом Коэффициента Шарпа. Разница в том, что в знаменателе учитывается не волатильность а полуотклонение (среднеквадратичное отклонение ниже целевой доходности, downside deviation):

S = \frac{R_p-R_t}{DR}

R_p - матожидание доходности портфеля (среднее арифметическое доходности)
R_t - целевая доходность портфеля
DR - полутоклонение доходности

Пример сравнения Коэффициента Сортино для различных активов:

al = ok.AssetList(['VOO.US', 'BND.US'], last_date='2021-12')
al.get_sortino_ratio(t_return=0.03)  # целевая доходность равна 3%
VOO.US    1.321951
BND.US    0.028969

Коэффициент Сортино для инвестиционного портфеля:

pf = ok.Portfolio(['VOO.US', 'BND.US'], last_date='2021-12')
pf.get_sortino_ratio(t_return=0.02)
1.4377728903230174

Коэффициент диверсификации

Коэффициент диверсификации показывает на сколько эффективно применяется корреляция для снижения риска в портфеле. В отличие от коэффициентов Шарпа и Сортино в формуле не используется доходность. Речь идет только о снижении риска:

DR(w) = \frac{\sum_{i=1}^{N} w_i \times \sigma_i}{\sigma}

w_i - вес ценной бумаги в портфеле
\sigma_i - риск (среднеквадратическое отклонение) доходности ценной бумаги
\sigma - риск инвестиционного портфеля

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

  • атрибут diversification_ratio доступен для Portfolio
  • оптимизации по КД возможна в классе EfficientFrontier через метод get_most_diversified_portfolio(). Метод позволяет получить как глобальный оптимизированный портфель, где КД достигает максимальной величины, так и локально оптимизированный портфель для заданной доходности портфеля.
  • множество оптимизированных по КД портфелей доступно через атрибут mdp_points в EfficientFrontier. Оптимизация происходит последовательно для промеж

Преимущество оптимизированных по КД портфелей заключается в том, что в отличие от Границы эффективности в них нет “вырожденных” весов. Поэтому для конструирования реальных инвестиционных портфелей распределение по КД - это хорошая подсказка.

ВНИМАНИЕ: использовать в явном виде эти веса нельзя, так же как и веса портфелей на Границе эффективности.

Пример построения кривой максимально диверсифицированных портфелей и Границы эффективности. В примере использованы портфели, составленные из:

  • Индекс S&P500 полной доходности (SP500TR.INDEX)
  • Индекс Мосбиржи полной доходности (MCFTR.INDEX)
  • Индекс облигаций федерального займа Мосбиржи (RGBITR.INDX)
  • Спотовые цены на золото (GC.COMM)
ls4 = ['SP500TR.INDX', 'MCFTR.INDX', 'RGBITR.INDX', 'GC.COMM']
y = ok.EfficientFrontier(assets=ls4, ccy='RUB', n_points=100)  
# n_points=100 задает количество точек на Границе эффективности и на кривой 
# максимально диверсифицированных портфелей

mdp = y.get_most_diversified_portfolio()   # Глобальный максимально диверсифицированный портфель
mdp
{'SP500TR.INDX': 0.18734372897034768,
 'MCFTR.INDX': 0.023902608406034215,
 'RGBITR.INDX': 0.6595979466663215,
 'GC.COMM': 0.12915571595729658,
 'Mean return': 0.12279521203933563,
 'CAGR': 0.12124953230039481,
 'Risk': 0.059120398386235534,
 'Diversification ratio': 1.9671253973462728}

df = y.mdp_points  # множество локально оптимизированных по КД портфелей (100 шт)
ef = y.ef_points  # портфели на Границе эффективности (100 шт)

Строим графики Границы эффективности, Кривой максимально диверсифицированных портфелей, отображаем точку, где КД максимален:

fig = plt.figure()
# Отображение точек риск-доходность для каждой ценной бумаги
y.plot_assets(kind='cagr')  # kind should be set to "cagr" as we take "CAGR" column from the ef_points.
ax = plt.gca()
# Отображение Границы эффективности
ax.plot(ef['Risk'], ef['CAGR'], label='Efficient Frontier')  # на оси Y мы отображаем среднегодовую доходность (CAGR)
# Отображение кривой с максимально диверсифицированными портфелями
ax.plot(df['Risk'], df['CAGR'], linestyle='dashed', label='Most diversified portfolios')
# Отображение точки с максимальным КД (глобальный максимум). Обозначен как MDP
ax.scatter(mdp['Risk'], mdp['CAGR'], s=30, marker='o', label='MDP')
# Название графика и надписи осей
ax.set_title('Efficient Frontier vs Most diversified portfolios frontier')
ax.set_xlabel('Risk (Standard Deviation)')
ax.set_ylabel('Return (CAGR)')
ax.legend();

Версия 1.1.3
Небольшой релиз, связанный с переходом на poetry для управления зависимостями библиотеки (до этого зависимости управлялись с помощью requirements.txt).

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

Обновление доступно через pip.

Версия 1.1.5
Релиз связан с переходом на новый API финансовой базы данных.
Новый сервер расположен по адресу api.okama.io:5000.

Рекомендую обновиться до новой версии. API по старому адресу будет поддерживаться еще примерно месяц. Уже сейчас по старому адресу не доступны новые типы данных (например расширенная база курсов валют ЦБ .CBR)

Пример запроса на новом API:
http://api.okama.io:5000/api/namespaces/

Версия 1.1.6
Параллельные вычисления теперь используются в “тяжелых” расчетах, связанных с оптимизацией портфеля с произвольной ребалансировкой портфеля (расчет Границы эффективности). Спасибо за помощь @alex

Для параллельных вычислений используется библиотека Joblib. Прирост скорости составил примерно 20%, но будет гораздо выше, когда сможем перейти на Pandas версии 1.4 и выше. Пока для совместимости с Python 3.7 используется Pandas версии 1.1.5. Версия Python 3.7 будет поддерживаться еще какое-то время до тех пор пока Google Colab не перейдет но более новую версию.

Другие изменения:

  • библиотека black используется для автоформатирования стиля кода
  • в зависимостях в версии для разработчика по умолчанию используется Python 3.7. При текущих зависимостях okama работает с версиями 3.7, 3.8 и 3.9

Версия 1.2.0

Новый класс для макроэкономических параметров Indicator

Теперь 3 макро-класса доступны (и отображены в Документации):

  • Indicator : Макроэкономические индикаторы. (.RATIO новый namespace)
  • Inflation : Инфляция и свойственные для нее методы (.INFL namespace)
  • Rates : Ставки банков и Центробанков (.RATE namesapce)

Циклический P/E Шиллера (CAPE10) для 20+ стран добавлен в базу данных. Новые тикеры: USA_CAPE10.RATIO, CHN_CAPE10.RATIO, CHN_CAPE10.RATIO и т.д.

Дневные данные в Макроэкономических классах

Rate класс теперь имеет .values_daily атрибут (по аналоги с Asset().daily_close):
ok.Rate("RUONIA.RATE").values_daily

Все макроэкономические классы имеют .values_monthly атрибут.

.describe() метод для все макро-классов

.describe() показывает статистику с начала года и для выбранного списка временных промежутков:

  • среднее (арфметическое)
  • медиана
  • макс. и мин. значения с датами

.describe() в классе Inflation отличается. Он показывает характерные для инфляции статистические величины:

  • Инфляция с начала года (YTD)
  • Среднегодовая инфляция (среднее геометрическое) для выбранных периодов и за весь срок
  • Максимальная 12-месячная инфляция для выбранных периодов и за весь срок
  • Покупательная способность для 1000 денежных единиц (для выбранных периодов и за весь срок)

Скользящие отклонения от индекса для ETF

.tracking_difference() и tracking_difference_annualized() теперь являются методами (были атрибутами). У них появился необязательный параметр rolling_window который задает размер окна для скользящего отклонения в месяцах.
Например, для расчета скользящего 24-месячного отклонения:
x.tracking_difference(rolling_window=24)

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

Сравнение точности следования SPY и VFINX

Сравним отклонение самого старого ETF на S&P500 SPDR c тикером SPY и первого индекса взаимного фонда от Vanguard (тикер VFINX).

x = ok.AssetList(['SP500TR.INDX', 'SPY.US', 'VFINX.US'])
x

image
Общая история у этих фонов доступна за последние 29 лет.
Можно посмотреть, кто из них отклонился больше за весь промежуток:

x.tracking_difference().plot()


Очевидное преимущество у взаимного фонда Vanguard.
Посмотрим тот же результат, но приведенный к готовым значениям:

x.tracking_difference_annualized().plot()


Видно, что в начале периода наблюдения фонд Vanguard несколько опережал индекс (как ему это удавалось - это отдельная история). SPY в среднем отставал от индекса на 1,5% за последние 29 лет.

При выборе между двумя этими фондами информации явно недостаточно. Инвестору важно знать, как фонд управлялся в последние 5-10 лет а не в начале 90х…
Для этого посмотрим скользящие отклонения, например, с окном в 5 лет:

x.tracking_difference_annualized(rolling_window=12*5).plot()


Мы видим, что в последние годы отклонения у индексных фондов не так сильно отличаются. Оба фонда отстают от SP500 TR менее чем на 1% ежегодно. У VFINX показатели более ровные и, начиная с 2020 года, несколько лучше чем у SPY. Секрет, как обычно, может быть в TER, но не только …

Версия 1.2.1
Для функции get_tangency_portfolio() в классе EfficentFrontier добавлен новый параметр cagr. При cagr=True функция рассчитывает точку с максимальный Коэффициентом Шарпа (MSR) для среднегодовой доходности, рассчитанной с помощью среднего геометрического (CAGR). По умолчанию, как и раньше, параметры MSR считаются для среднего арифметического доходности (для матожидания).

Исправления:

  • в классах AssetList и Portfolio теперь правильно считаются first_date и last_date для базовой валюты.

Версия 1.2.2 - последний релиз, поддерживающий Python 3.7

Исправления:

  • баг с совместимостью старой библиотеки importlib-metadata

Обновления:

  • новый формат данных FOREX потребовал обновления классов-наследников ListMaker (Portfolio, AssetList, EfficentFrontier и т.п.)

ВНИМАНИЕ: С новым форматом данных FOREX (он уже реализован в API) старые релизы библиотеки работать не будут. Требуется обновиться до 1.2.2

Разработка следующих версий библиотеки будет вестись с помощью Python 3.8

Версия 1.2.3 - релиз с минимальной версией Python 3.8

Новые версии okama больше не будут поддерживать легаси Python 3.7. Исключение старых версий Python было необходимо для корректной работы с новыми возможностями в Pandas, SciPy и Numpy. Старые версии питона постепенно перестают поддерживаться этими библиотеками.

Обновления:

  • метод EfficentFrontier().get_monte_carlo() теперь кроме риска и доходности возвращает веса сгенерированных портфелей

Исправления:

  • Portfolio().weights_ts возвращает правильный порядок активов, входящих в портфель

ВНИМАНИЕ: Google Colab все еще работает на Python 3.7. Пока они не перейдут на 3.8+ !pip install okama будет устанавливать версию 1.2.2 okama.

Версия 1.3.0 - Новые возможность для скользящих. Sequencies

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

  • параметр rolling_window доступен в функциях AssetList: index_corr(), index_beta(), tracking_error(). Теперь можно строить скользящие для Ошибки следования и для беты.
  • Две функции index_corr() и index_rolling_corr() слиты в одну в AssetList
  • AssetList, Prtfolio, EfficentFrontier и EfficentFrontierReb теперь являются sequences (последовательностями) и обладают методами __getitem__, __iter__.

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

  • get_namspaces() и другие алиасы из init.py больше не совершают удаленных запросов по API при импорте (спасибо за помощь @alex)
  • EfficientFrontier.plot_pair_ef() падала при inflation=False
  • Можно использовать тикеры с точкой. Такие как BRK.B