Целью проекта является автоматизация процесса управления портфелем акций. Используемый подход не предполагает огромных доходностей, а нацелен на получение результата чуть лучше рынка при рисках чуть меньше рынка при относительно небольшом обороте. Портфель ценных бумаг должен быть достаточно сбалансированным, чтобы его нестрашно было оставить без наблюдения на продолжительное время
Большинство частных инвесторов стремиться к быстрому обогащению и, согласно известному афоризму Баффета, "мало кто хочет разбогатеть медленно", поэтому проект является открытым. Стараюсь по возможности исправлять ошибки, выявленные другими пользователями, и буду рад разумным PR(-ам). Особенно приветствуются вопросы и предложения по усовершенствованию содержательной части подхода к управлению портфелем, UI/UX web- и Telegram-интерфейсов
Проект находится в стадии развития и постоянно модифицируется (не всегда удачно), поэтому может быть использован на свой страх и риск
Установка описана для MacOS. Готов принять PR с описанием установки для Linux или Windows.
POptimizer представляет собой приложение на Python. Для его сборки и установки нужны базовые инструменты разработчика, которые требуется установить, если они не установлены ранее:
git clone --filter=blob:none https://github.com/WLM1ke/poptimizer.git && cd poptimizer- запустите команду установки необходимых инструментов и зависимостей
task installДанная команда предназначена для неопытных пользователей, которые не меняют сами код программы:
- проверьте, что последняя версия проходит тесты (зеленый банер в начала этого файла)
- остановите программу
Ctrl-C - запустите команду обновления
task update_ver- так же можно обновить данные о дивидендах по наиболее ликвидным акциям из актуальной версии (лучше вводить дивиденды самому, если в портфеле много малоликвидных позиций)
task update_divПри возникновении ошибок во время обновления можно откатить на предыдущую версию с помощью команды
task revert_verДля работы POptimizer могут потребоваться секретные токены, которые необходимо внести в конфигурационный файл.
Чтобы не хранить секреты в открытом виде:
- сохраните их в хранилище секретных ключей операционной системы
uv run poptimizer keychain save YOUR_SECRET_KEY YOUR_SECRET_VALUE- в конфигурационном файле вместо
YOUR_SECRET_VALUEиспользуйте специальную заглушкуkeychain:YOUR_SECRET_KEY, которая в момент загрузки будет заменять наYOUR_SECRET_VALUEиз хранилища секретов операционной системы - при необходимости можно удалить секрет из хранилища секретов
uv run poptimizer keychain delete YOUR_SECRET_KEY- посмотреть текущее значение
uv run poptimizer keychain get YOUR_SECRET_KEY- так же можно зайти в интерфейс хранилища напрямую - секреты сохраняются для приложения
poptimizer
Настройки приложения хранятся в cfg/cfg.yaml и носят опциональный характер - приложение будет работать без заполнения конфигурационного файла.
При первом запуске будет создан пустой конфигурационный файл, который можно заполнить по мере необходимости на основе шаблона cfg/cfg.yaml.template.
При необходимости можно создать cfg/cfg.yaml самому и выборочно заполнить нужные секции
POptimizer может отправлять оповещения об ошибках, ключевых событиях в работе, изменении стоимости портфеля и рекомендациях по его оптимизации в Telegram.
Так же можно производить ряд операций с помощью бота. Этот функционал опционален, а для его настройки:
- создайте
Telegramбота - сохраните токен в хранилище секретов (смотри выше) и внесите заглушку для него в конфиг
- запустите программу
- создайте чат с ботом - в ответ на любое сообщение он пришлет информацию о
chat_id - внесите
chat_idв конфиг и перезапустите программу с полными настройкамиTelegram
В программе реализовано автоматическое обновление позиций на счетах в Tinkoff, для этого:
- Создайте Read-only token
- Выполните команду для получения id счетов
uv run poptimizer tinkoff YOUR_TOKEN- сохраните токен в хранилище секретов (смотри выше) и внесите заглушку для него в конфиг. Название счета из конфигурационного файла используется для отображения в web-интерфейсе
POptimizerи может состоять из любых английских букв и цифр. При необходимости могут быть внесены несколько счетов для одного токена или разные токены и счета для них - после запуска программы будут созданы соответствующие счета внутри
POptimizer, если они не созданы ранее. По ним подтянутся актуальные данные об открытых позициях и в последствии будут регулярно обновляться
При желании подобный функционал можно поддержать для других брокеров - готов принять ПРы и помочь с доработкой
- запустите
MongoDB, которая указана вcfg.yamlили воспользуйтесь командой для локального запуска с настройками по умолчанию. Данную команду достаточно выполнить один раз. Она зарегистрирует сервис, которые будет запускатьMongoDBпри перезапуске системы
task mongo- запустите программу
task run- перейдите по адресу, указанному
cfg.yamlфайле или http://localhost:5000 по умолчанию - создайте в настройках хотя бы один брокерский счет или дождитесь автоматического создания для счетов
Tinkoff, если они были внесены вcfg.yaml - заполните счета актуальной информацией по имеющимся акциям и денежным средствам
- опционально в настройках можно добавить тикеры в список исключений (например, недоступные у вашего брокера, по которым ожидается делистинг и т.д.)
- дождитесь первого ночного обновления данных (1:00 MSK). Программа удалит из списка доступных малоликвидные бумаги с короткой историей и нулевой позицией на всех счетах, а так же внесенные в список исключений
- следуйте рекомендациям в разделе
Optimization
При первом запуске в портфеле будут доступны все акции и фонды торгуемые на MOEX с достаточной для построения моделей историей. В дальнейшем список доступных бумаг будет меняться на основании следующих факторов:
- включения и исключения ценных бумаг из перечня торгуемых на
MOEX - изменения требования к минимальной истории котировок для построения моделей
- изменения требования к минимальной ликвидности ценных бумаг в зависимости от размера портфеля - чем крупнее портфель, тем меньше бумаг будет доступно
Бумаги, которые не удовлетворяют одному из требований будут автоматически исключаться при ежедневном обновлении данных, если по ним отсутствует позиция на всех счетах
На уровне портфеля отслеживается информация о характерной частоте сделок, которая используется для построения моделей и учета издержек в предложениях по оптимизации - первоначально 1 день
Агрегированные данные по всем счетам выводятся на вкладке Portfolio, а данные по отдельным счетам с возможностью редактирования в разделах соответствующих счетов
При первоначальном запуске формируется база с дивидендами для наиболее ликвидных бумаг, которые реально торговать при портфеле от 200М.
Если у вас менее крупный портфель, необходимо заполнить информацию по остальным акциям. При дальнейшей работе будут поступать сообщения в Telegram о необходимости обновления дивидендов и появится возможность их редактирования разделе Dividends
Используемые для построения прогнозов модели формируются автоматически с помощью эволюционного алгоритма:
- модели с большей доходностью и лучшими статистическими свойствами на тестовой выборке выживают и создают новые модели с похожими характеристиками
- модели с меньшей доходностью и худшими статистическими свойствами на тестовой выборке удаляются
В качестве тестовой выборки используются последние торговые дни, а их количество меняется в зависимости от рыночной конъюнктуры. В результате бумаги с короткой историей могут быть исключены из портфеля. Для тренировки моделей используются все ценные бумаги из текущего портфеля, а прогноз строится на несколько дней вперед в соответствии с актуальной частотой сделок
Прогнозы по всем моделям агрегируюся и выводятся на вкладке Forecast в пересчете в годовое выражение, при этом используется портфельная теория для расчета риска и доходности портфеля на основе характеристик отдельных позиций
На вкладке Optimization выводятся рекомендации о покупке и продаже ценных бумаг. При этом учитывается разброс в прогнозах - нижняя граница доверительного интервала в предложениях на покупку должна быть больше верхней границы доверительного интервала предложений на продажу с учетом транзакционных издержек
В некоторых случаях может сложиться ситуация, когда предложений по оптимизации не будет:
- при малом количестве прогнозов или большом их расхождении - большая ширина доверительных интервалов
- большой частоте сделок - больших транзакционных издержках, которые учитываются при расчете доверительных интервалов
- близости портфеля к оптимальному - все малодоходные и рискованные бумаги проданы, а у остальных не достаточно сильно различаются прогнозные метрики, чтобы перекрыть транзакционные издержки
В этом случае будет выведена одна бумага с максимальной нижней границей доверительного интервала, которую следует покупать при наличии свободных денежных средств
Прогноз и предложение по оптимизации пересчитывается при появлении достаточно большого количества новых моделей и с некоторой задержкой при изменении портфеля. В интерфейсе будет отображаться надпись outdated, если расчеты пока не обновились после последнего изменения портфеля
Оптимизация портфеля построена на базе Modern portfolio theory с учетом неточности имеющихся прогнозов вместо классической mean-variance оптимизации:
- При построении портфеля учитываются все акций и ETF, обращающихся на MOEX, с достаточной ликвидностью и длинной истории для построения прогнозов
- Для оценки корреляционных матриц для большого числа активов используется сжатие Ledoit-Wolf
- Для прогнозирования риска и доходности используются ансамбль нейронных сетей, который формируется с помощью эволюционного алгоритма
Frontend сделан на htmx, а Backend на asyncio Python, в том числе:
- HTTP клиент и сервер
aiohttp - хранение данных
asyncPyMongo - валидация и сериализация данных
Pydantic - обучения моделей
PyTorch - статистические тесты и оптимизация
SciPy
Backend использует event-driven архитектуру - сообщения публикуются в шину, которая на основании типа сообщения выбирает необходимые обработчики и запускает их выполнение. Если обработчик возвращает новые события, то они автоматически отправляются в шину
При запуске приложения публикуется событие AppStarted:
- На него реагирует обработчик
MigrationsHandler, который создает первоначальные данные о дивидендах вMongoDBпри первом запуске и осуществляет их бекап и проводит необходимые миграции данных при последующих - Далее срабатывает
DataHandler, который отслеживает появление новых данных наMOEX ISS. В случае необходимости публикует событиеNewDataPublishedс информацией о последнем торговом дне, которое запускает цикл обновления данных (синий на схеме). Если новых данных нет, то обработчик публикует событиеDataCheckedс информацией о последнем торговом дне, которое запускает цикл тренировки моделей и обновления прогнозов (зеленый на схеме). Оба цикла завершаются отправкой сообщений для обработчикаDataHandlerв результате процесс продолжается бесконечно - или обновляются данные, или тренируются новые модели и обновляются прогнозы