Муки выбора
В последние год-два появилось много постов, как сделать таблички в Google Sheets/MS Excel для отображения актуальной информации о своем инвестиционном портфеле и т.п. Действительно хочется видеть, что там творится. Особенно актуально для продвинутых подписок/тарифных планов, когда требуется определенная сумма на счетах, и будет обидно, если из-за падения акций или курса доллара сумма снизится ниже пороговой…
Есть много инструментов для отслеживания инвестиционного портфеля. И сервисы, и таблицы… Мне хотелось иметь что-то с одной стороны легкое (не требуется учета купонов и т.п.), а с другой – чтобы легко настроить именно такой вид, такие параметры и срезы, как хочется именно мне.
Так что я попробовал несколько вариантов от электронных таблиц до записи в MySQL скриптом на python, и на текущий момент остановился на отображении моих инвестиционных счетов в Grafana.
MS Excel
Поскольку с ним и по работе сталкиваться приходится, всякие Pivot, графики строить иногда, это для меня самый привычный инструмент, и начал я именно с него.
К счастью, вовремя обнаружил, что на Android ни через приложение (даже при подписке Office Home, что на 5 членов семьи), ни через web, формулы FilterXML не работают.
Пробовал и другие клиенты, и всякие Libre Office, ничего не помогло. А я хотел универсальное решение, и со смартфона, и с планшета…
Google Sheet
Пришлось осваивать. И по сию пору держу там подборку интересных инструментов, вытаскиваю по названию ETF то, что можно. Но некоторые хотелки я реализовать не смог.
Это и «исторические данные» (хотелось смотреть графики изменений, пусть примерные, без свечей, конечно, но все-таки). И возможность сохранения (при экспорте в Excel работоспособность теряется, а я очень привык иметь файл на компьютере, доступный offline, и при этом периодически синхронизирующийся с OneDrive).
И конфиденциальность (в OneDrive я синкаю через GoodSync с шифрованием, т.е. в облаке хранится версия, зашифрованная моим локальным ключом, а в Google Sheet все, включая токен к Тинкофф Инвестициям, было бы в открытом виде).
Python + MS Excel
В рамках осваивания python написал скриптик, дергающий Инвестиции и сохраняющий все (в т.ч. периодически историю баланса аккаунта) в XSLX.
Разумеется, тут можно накрутить много чего. Но все-таки с построением графиков и т.п. были сложности. Частые апдейты через cron на отдельной машине – слишком файл разрастется. Да и на ноут его забирать… С ноута непосредственно скрипт запускать – не всегда в одно и то же время возможно, графики толком не построить…
Пробовал в MySQL писать – из Excel не смог удобно графики строить даже через MySQL Workbench. Можно, наверное, но понял, что усилия для этого потребуются неадекватные (как выбирать временной интервал для отображения - масштаб, смещение...)
Текущий финальный выбор
Поскольку в это же время я разбирался с популярными системами мониторинга, а именно с Prometheus и Grafana, стал делать на них дашборды для домашней лабы и других личных серверов, подумал: «А почему бы и финансовый дашборд не замутить?»
Погуглил, нашел пару проектов по экспорту из Тинькофф Инвестиций в Prometheus, решил, что раз другие так делают, это не совсем дурацкая идея, и стал делать.
Что получилось
Посмотрел пару репозиториев (https://github.com/maksim77/tinkoff_exporter и https://github.com/byumov/tinkoff_investing_exporter). Не совсем мне подошли. Поскольку написаны Go, которого я совсем не знаю, поправить их я не смог, поэтому стал писать свое на python. И воспользовался некоторыми идеями по организации дашборда на Grafana.
В итоге получилось https://github.com/Anrikigai/promTinkoff. Описание там есть, а здесь я больше сфокусируюсь на особенностях и проблемах. Может быть кому-то это поможет сделать что-то более информативное.
Prometheus
В контейнере запускаю скрипт, периодически опрашивающий API Tinkoff Инвестиции и презентующий в формате Prometheus. По умолчанию раз в 10 минут, этот же интервал указан и конфигурации Prometheus:
- job_name: 'tinkoff'
scrape_interval: 600s
static_configs:
- targets: ['promTinkoff:8848']
Поскольку сам экспортер тоже в контейнере на той же машине, указывается просто его имя.
В конечном итоге данные представляются в виде
tcs_item{account="Tinkoff", balance_currency="EUR", currency="EUR", instance="promTinkoff:8848", job="tinkoff", name="Тинькофф Вечный портфель EUR", ticker="TEUR", type="Etf"}
tcs_item{account="Tinkoff", balance_currency="USD", currency="EUR", instance="promTinkoff:8848", job="tinkoff", name="Тинькофф Вечный портфель EUR", ticker="TEUR", type="Etf"}
tcs_item{account="Tinkoff", balance_currency="RUB", currency="EUR", instance="promTinkoff:8848", job="tinkoff", name="Тинькофф Вечный портфель EUR", ticker="TEUR", type="Etf"}
tcs_item{account="Tinkoff", balance_currency="RUB", currency="USD", instance="promTinkoff:8848", job="tinkoff", name="Pfizer", ticker="PFE", type="Stock"}
Причем для удобства дальнейшего использования данные по каждой позиции экспортируются в трех валютах: EUR, USD, RUB (balance_currency
). Соответственно, далее я могу отображать результаты в требуемой валюте не пересчитывая их каждый раз по курсу на тот момент.
При этом currency
получается от Tinkoff. Скажем, для "Тинькофф Вечный портфель EUR" currency=EUR
, для Pfizer - currency=USD
.
Аналогичный подход с дублированием в трех валютах используется для оценки прибыли/убытков в tcs_yield.
tcs_yield{account="Tinkoff", balance_currency="EUR", currency="USD", instance="promTinkoff:8848", job="tinkoff", name="Pfizer", ticker="PFE", type="Stock"} 90.99262579525735
tcs_yield{account="Tinkoff", balance_currency="USD", currency="USD", instance="promTinkoff:8848", job="tinkoff", name="Pfizer", ticker="PFE", type="Stock"} 107.5
tcs_yield{account="Tinkoff", balance_currency="RUB", currency="USD", instance="promTinkoff:8848", job="tinkoff", name="Pfizer", ticker="PFE", type="Stock"} 7866.3125
Также сохраняю курсы в tcs_rate
.
И помимо вывода данных по каждому аккаунту, получаемого от Тинькофф Инвестиций (в данном случае Tinkoff, TinkoffIis), также добавляю автоматически посчитанные записи для фиктивного аккаунта _Total_
tcs_item{account="_Total_", balance_currency="RUB", currency="Multi", instance="promTinkoff:8848", job="tinkoff", name="_Total_", ticker="_Total_", type="_Total_"}
tcs_yield{account="_Total_", balance_currency="RUB", currency="Multi", instance="promTinkoff:8848", job="tinkoff", name="_Total_", ticker="_Total_", type="_Total_"}
Grafana
Суммы брокерского аккаунта скрою, буду показывать на примере недавного перенесенного в Тинькофф ИИС.
На содержимое не обращайте внимания. Хотя там и есть и акции, и облигации, в реальности я консервативен, рассчитываю на долгосрок, и растить его буду за счет ETF/БПИФ.
Ниже опишу основные элементы этой секции.
Portfolio
В левом верхнем углу дашборда я хочу видеть баланс счета крупными цифрами сразу во всех трех валютах (balance_currency
).
Использую обычный виджет Stat и для каждого из полей перекрываю способ отображения (символ валюты):
Поскольку API возвращает среднюю цену позиции и баланс (количество), их произведение, выводимое в tcs_item
, является суммой потраченных (инвестированных) средств.
Для получения текущей стоимости проще всего прибавить ожидаемые прибыли/убытки (tcs_yield
). Поэтому везде, где нужна текущая стоимость, и используется tcs_item() + tcs_yield()
.
Details
Таблица с тремя query (для каждой из валют) типа
Sum(tcs_item{balance_currency="RUB"}) by (account) + Sum(tcs_yield{balance_currency="RUB"}) by (account)
И пришлось добавить Transform (убрать поле Time и поименовать колонки)
Также для красоты я переопределил суммарный аккаунт _Total_ в TOTAL
Profit/Loss
Такая же табличка, только запросы проще:
Sum(tcs_yield{balance_currency="RUB"}) by (account)
Profit/Loss without Currency
Значительная доля на брокерском счету у меня в валюте, и мне интересно отделить результат инвестирования от банальной валютной переоценки кеша. Поэтому еще одна табличка без Currency:
Sum(tcs_yield{balance_currency="RUB",account=~"T.*",type!="Currency"}) by (account)
Для ИИС видно, что портфель целиком в небольшом минусе (там до последнего момента лежали евро). При этом «w/o currency» слегка плюс.
Для долгосрочного инвестирования это все неважно, но мне было интересно посмотреть на данные с разных сторон этим новым для меня способом.
Графики
Portfolio целиком в разных валютах
Две линии: текущая стоимость портфеля (tcs_item + tcs_yield) и штрих-пунктиром инвестированная (в данном случае небольшой минус)
Sum(tcs_item{balance_currency="RUB",account=~"T.*"}) + Sum(tcs_yield{balance_currency="RUB",account=~"T.*"})
Sum(tcs_item{balance_currency="RUB",account=~"T.*"})
Portfolio w/o Currency
То же, но за вычетом кеша. Желтая линия имеет ступеньку, в то время как сам портфель целиком такой не имеет. Это потому что я не вводил дополнительные средства на счет, а просто потратил остаток свободных рублей на покупку ETF на Казначейские облигации США (запарковал кеш в ожидании, что после выборов доллар подрастет).
И раздельно по портфелям
Поскольку курс евро и доллара более-менее близки, их я «прижал» к правой оси.
К сожалению, я не знаю, как задать масштаб с «дельтой» для плавающих значений. Поэтому для валют «дельта» достаточно велика (доллар формирует нижнюю границу графика, евро - верхнюю). И их колебания выглядят незначительными. А вот в рублях «расколбас по осям» всегда от минимума до максимума, хотя на самом деле относительные изменения могут быть совсем невелики. Хотелось бы рубль также вогнать в диапазон 10-20% (как отношение евро/доллар), но не знаю как это сделать.
Currency
tcs_rate{currency=~"USDRUB"}
tcs_rate{currency=~"EURRUB"}
Item info
Вверху дашборда можно выбрать инструмент для получения информации по нему
Для таблички используется 6 query. 3 «суммы» для каждой из валют и 3 просто tcs_yield
sum(tcs_item{name="$Item",balance_currency="RUB"}) + sum(tcs_yield{name="$Item",balance_currency="RUB"})
sum(tcs_yield{name="$Item",balance_currency="RUB"})
Для графиков текущего баланса и «инвестированного»:
sum(tcs_item{name="$Item",balance_currency="RUB"}) by (name) + sum(tcs_yield{name="$Item",balance_currency="RUB"}) by (name)
sum(tcs_item{name="$Item",balance_currency="RUB"}) by (name)
В данном случае отображаю в рублях. Ну, тут на выбор.
Важно отметить параметр $Item. Он как раз задает инструмент, выбранный вверху. Также он используется и при формировании заголовка:
Current position and yield - $Item
Портфель (TinkoffIis)
Для графиков и таблички используется Time series, как обычно. Из интересного – группирую данные по имени инструмента (name) и в легенде укаываю {{ name }}, чтобы в табличке было соответсвующее название. В качестве альтернативы можно использовать {{ticker}}. Как вывести оба типа “PFE (Pfizer)” не знаю.
Sum здесь используется для единообразного подхода для total, если инструмент встретится в обоих.
Можно группировать по разному
Для оценки диверсификации не просто выбираем 3 query в разных валютах, но и разбиваем их по типу актива. Это позволяет отдельно увидеть, например, Currency RUB и Currency EUR.
sum(tcs_item{balance_currency="RUB",account=~"TinkoffIis", currency="RUB"}) by (type) + sum(tcs_yield{balance_currency="RUB",account=~"TinkoffIis", currency="RUB"}) by (type)
sum(tcs_item{balance_currency="RUB",account=~"TinkoffIis", currency="USD"}) by (type) + sum(tcs_yield{balance_currency="RUB",account=~"TinkoffIis", currency="USD"}) by (type)
sum(tcs_item{balance_currency="RUB",account=~"TinkoffIis", currency="EUR"}) by (type) + sum(tcs_yield{balance_currency="RUB",account=~"TinkoffIis", currency="EUR"}) by (type)
При этом в качестве Value используется последнее ненулевое значение (Last *
), а для отображения (Legend) также и доля (Percent
)
Для отдельного обзора каждого типа инструмента (ну если бы их было много разных), можно сделать отдельные Pie chart типа
sum(tcs_item{type="Etf",account=~"TinkoffIis",balance_currency="RUB"}) by (name) + sum(tcs_yield{type="Etf",account=~"TinkoffIis",balance_currency="RUB"}) by (name)
sum(tcs_item{type="Stock",account=~"TinkoffIis",balance_currency="RUB"}) by (name) + sum(tcs_yield{type="Stock",account=~"TinkoffIis",balance_currency="RUB"}) by (name)
и т.п.
Немного о грустном
Диверсификация
Хотя я и привел пример с диверсификацией портфеля, в реальности не стоит особо на нее полагаться. Это ведь обычно делается для оценки риска (скажем, 90% акции / 10% облигации – портфель с высоким риском). Однако, ETF на облигации достаточно консервативны, по идее стоит отнести их к Bond, но не представляю, как это сделать, как их отделить от ETF на акции, относящиеся все же к более рискованным активам.
Впрочем, ETF FXFA (FinEx Fallen Angels UCITS ETF - Высокодоходные корпоративные облигации развитых стран) хоть и облигации, но я бы не стал относить их к консервативным. Уверен, что в кризис они ощутимо обвалятся. Так что я не стал заморачиваться. Для этой цели все равно что-то специализированное нужно (да хоть intelinvest не так давно научился ETF по секторам раскидывать).
Другая проблема - валюты Условный Пфайзер я купил за доллары, и он реально котируется в долларах. Он значится в Stock, USD. Но есть много зарубежных инструментов, купленных за рубли. И они попадут в "рублевую часть", потому что Тинькофф вернет currency="RUB"
, хотя на самом деле это валютные активы, "застрахованные" от падения курса рубля.
Банковские счета и вклады
Хочется видеть в одном дашборде также и другие средства. Уж как минимум из того же Тинькофф, только уже банка. Но увы, этот API доступен только бизнес аккаунтам. Физикам вроде его могут подключать, но в индивидуальном порядке.
Кое-кто (к примеру, Дзен-мани) умеют эту информацию извлекать. Но, насколько я понял, они используют API от мобильного клиента. Сделать reverse engineering далеко выходит за пределы моих возможностей.
Так что пока увы.
Впрочем, если когда-то и получится такое сделать, это будет отдельный контейнер для Prometheus. А Grafana все объединит :)
Заключение
Мне хотелось показать, что в Grafana можно делать довольно разнообразные и информативные дашборды даже для не совсем стандартных применений. Экспортер в Prometheus пишется легко. Даже у меня, ни разу не программиста, с этим сложностей не возникло.
Буду рад комментариям. Интересен кому-то такой подход, или это извращение? Я его затеял, чтобы на чем-то практическом потренироваться в Python/Prometheus/Grafana, и своей цели достиг. Но в практической применимости в свете проблем с диверсификацией не очень уверен. Посматривать на текущий баланс, конечно, буду. Но вот принимать решения, чего прикупить исходя из этих графиков - вряд ли.