В продолжение статьи о вреде избыточной диверсификации создадим полезный инструментарий🛠️ по подбору акций. После этого сделаем простую ребалансировку⚖️ и добавим уникальные условия технических индикаторов📈, которых так часто не хватает в популярных сервисах. А затем сравним доходность отдельных активов и различных портфелей💼. Во всем этом задействуем Pandas и минимизируем количество циклов. Погруппируем времянные ряды и порисуем графиков. Познакомимся с мультииндексами и их поведением. И всё это в Jupyter на Python 3.6🐍.Если хочешь сделать что-то хорошо, сделай это сам. Фердинанд Порше Описанный инструмент позволит подобрать оптимальные активы для портфеля и исключить инструменты, навязываемые консультантами. Но мы увидим лишь общую картину - без учёта ликвидности, времени набора позиций, комиссий брокера и стоимости одной акции. В целом, при ежемесячной или ежегодной ребалансировке у крупных брокеров это будут незначительные затраты. Однако перед применением выбранную стратегию всё же стоит проверить в event-driven бэктестере, например, Quantopian (QP), дабы исключить потенциальные ошибки. Почему не сразу в QP? Время. Там самый простой тест длится около 5 минут. А текущее решение позволит вам за минуту проверить сотни разных стратегий с уникальными условиями. 📥Загрузка сырых данных Для загрузки данных возьмем метод, описанный в этой статье. Для хранения дневных цен я использую PostgreSQL, но сейчас полно бесплатных источников, из которых можно сформировать необходимый DataFrame. Код загрузки истории цен из БД доступен в репозитории. Ссылка будет в конце статьи. 📦Структура DataFrame При работе с историей цен, для удобной группировки и доступа ко всем данным, лучшим решением является использование мильтииндекса (MultiIndex) с датой и тикерами.Код доступен на Quantrum.me Используя мультииндекс, мы можем легко получить доступ ко всей истории цен для всех активов и можем группировать массив отдельно по датам и активам. Также можем получить историю цен для одного актива. Вот пример, как можно легко группировать историю по неделям, месяцам и годам. И всё это показать на графиках силами Pandas:Код доступен на Quantrum.me Для корректного отображения области с легендой графика мы переносим уровень индекса с тикерами на второй уровень над колонками, используя команду Series().unstack(1). С DataFrame() такой номер не пройдёт, но решение есть ниже. При группировке по стандартным периодам Pandas использует в индексе последнюю календарную дату группы, которая часто отличается от фактических дат. Для того чтобы это исправить, обновим индекс.Код доступен на Quantrum.meПример получения истории цен определённого актива (берём все даты, тикер QQQ и все колонки). 🎢Ежемесячная волатильность активов Теперь мы в несколько строк можем посмотреть на графике изменение цены каждого актива за интересующий нас период. Для этого получим процент изменения цены, группируя dataframe по уровню мультииндекса с тикером актива.Код доступен на Quantrum.me 📊Сравним доходность активов Теперь воспользуемся оконным методом Series().rolling() и выведем доходность активов за определённый период:Код доступен на Quantrum.me 💼Методы ребалансировки портфелей Вот мы и подобрались к самому вкусному. В примерах мы посмотрим результаты портфлеля при распределении капитала по заранее определённым долям между несколькими активами. А также добавим уникальные условия, по которым будем отказываться от некоторых активов в момент распределения капитала. Если подходящих активов не будет, то будем считать, что капитал лежит у брокера в кэше. Для того чтобы при ребалансировке использовать методы Pandas, нам необходимо хранить доли распределения и условия ребалансировки в DataFrame с группированными данными. Теперь рассмотрим функции ребалансировок, которые будем передавать в метод DataFrame().apply(): Много кода, доступно на Quantrum.me По порядку: rebalance_simple - самая простая функция, которая будет распределять доходность каждого актива по долям.rebalance_sma - функция, распределяющая капитал по активам, у которых скользящая средняя за 50 дней выше значения за 200 дней на момент ребалансировки.rebalance_rsi - функция, распределяющая капитал по активам, у которых значение индикатора RSI за 100 дней выше 50.rebalance_custom - самая медленная и самая универсальная функция, где мы будем высчитывать значения индикатора из дневной истории цен актива на момент ребалансировки. Здесь можно использовать любые условия и данные. Даже загружать каждый раз из внешних источников. Но без цикла уже не обойтись.drawdown - вспомогательная фукция, показывающая максимальную просадку по портфелю. В функциях ребалансировки нам необходим массив всех данных на дату в разрезе активов. Метод DataFrame().apply(), которым мы будем рассчитывать результаты портфелей, передаст в нашу функцию массив, где колонки станут индексом строк. А если мы сделаем мультииндекс, где нулевым уровнем будут тикеры, то к нам придёт мультииндекс. Этот мультииндекс мы сможем развернуть в двумерный массив и получить на каждой строке данные соответствующего актива. ⚖️Ребалансировка портфелей Теперь достаточно подготовить необходимые условия и сделать в цикле расчёт для каждого портфеля. Первым делом рассчитаем индикаторы на дневной истории цен:Код доступен на Quantrum.meТеперь сгруппируем историю под нужный период ребалансировки, используя методы, описанные выше. Будем брать при этом значения индикаторов в начале периода, чтобы исключить заглядывание в будущее. Опишем структуру портфелей и укажем нужную ребалансировку. Портфели рассчитаем в цикле, так как нам необходимо указывать уникальные доли и условия: Много кода, доступно на Quantrum.me В этот раз нам потребуется провернуть хитрость с индексами колонок и строк, чтобы получить нужный мультииндекс в функции ребалансировки. Добьёмся этого, вызвав последовательно методы DataFrame().stack().unstack([1, 2]). Данный код перенесет колонки в строчный мультииндекс, а затем вернет обратно мультииндекс с тикерами и колонками в нужном порядке. 📈Готовые портфели на графики Теперь осталось всё нарисовать. Для этого ещё раз запустим цикл по портфелям, который выведет данные на графики. В конце нарисуем SPY в качестве бенчмарка для сравнения. Много кода, доступно на Quantrum.me 🏁Заключение Рассмотренный код позволяет подбирать различные структуры портфелей и условия ребалансировок. С его помощью можно быстро проверить, стоит ли, например, держать в портфеле золото (GLD) или развивающиеся рынки (EEM). Попробуйте его сами, добавьте свои условия индикаторов или подберите параметры уже описанных. (Но помните об ошибке выжившего и о том, что подгонка под прошлые данные, может не оправдать ожидания в будущем.) А после этого решите, кому вы доверите свой портфель - Python-у или финкосультантy? 💬В комментариях напишите, какие портфели, обгоняющие SPY, вам удалось найти. Предложите условия, которые могут улучшить результаты ребалансировки. Задавайте вопросы. Александр РумянцевАвтор на Quantrum.meПодписывайтесь на telegram-канал: @quantikiИнтересуетесь алготрейдингом на Python? Присоединяйтесь к команде.