Что не показывает аналитика в Тинькофф Инвестициях. Подробно разбираем свой портфель
У меня, как у пользователя Тинькофф Инвестиций, часто возникали вопросы: какие акции за весь период торговли принесли мне наибольший доход; сколько всего я заплатил за обслуживание тарифа и прочие комиссии; какие акции в портфеле давно лежат и не приносят мне доход; как отсортировать акции в своем портфеле по какому-либо критерию. Текущая аналитика брокера показывает только открытые позиции, а профиль в «пульсе» подсчитывает лишь общий процент, без конкретики. Это не дает возможность увидеть «настоящую» картину своего портфеля. К примеру, покупаем 1 акцию по 1000, после падения до 900 усредняем еще одной акцией. Затем при отскоке до 950 продаем 1 акцию. В итоге оставшаяся акция в портфеле «горит зеленым» (образуется плюс +50 по правилу fifo), хотя фактический результат бумаги на данный момент будет 0 (без учета комиссий). Такое отображение бумаг в приложении может сбить с толку, и привести к неправильным решениям и большим потерям, при совершении множества сделок. Идея вести дневник сделок в excel, или периодически копаться в налоговом/брокерском отчете, мне не понравилась. Зато приглянулась мысль — обрабатывать данные полученные из официального API. Результатом стал онлайн-инструмент, в который легко загрузить историю операций. После чего она отображается в табличном виде, с которыми удобно работать. В статье я продемонстрирую, как пользоваться созданным инструментом, и какие задачи решаются. Подсвечу некоторые особенности данных полученных из API.
Об инструменте, немного лирики
Мои первые шаги по анализу истории были сделаны года два назад. Результатом были таблицы, которые помогали мне закрыть любые вопросы: Я даже периодически делал разного роды выгрузки друзьям и знакомым. Передавать VM/doсker образ со скриптами людям без навыков в IT было сложно, поэтому делалось все мною вручную. Зародилась мысль автоматизировать и вывести процесс в онлайн. По акции от AWS достался бесплатный сервер на год, куда я перенес базу и продолжил изучение python, придумывая себе различные задачи, позволяющие автоматизировать торговлю. Позже к разработке подключился друг. И вот мы больше года держим сервер, где крутятся наши наработки на python (Telegram-боты, скринеры и прочее). К примеру, боты уведомляют о различных событиях по акциям наших портфелей. Разработка велась нерегулярно, в свободное время. Различных скриптов со временем стало много, для этого мы создали единый модуль с веб-интерфейсом для управления всеми наработками, заодно изучили React. Начали с отображения табличных данных. Результатом полуторамесячной работы по выходным стал сайт https://tstocks.ru/, который использует:
- Front: ReactJS (Nginx)
- Backend: Python Flask
- СУБД: Postgres
Немного внутренностей
Все запросы и параметры хранятся в БД. Получился достаточно удобный конструктор таблиц и прочих методов
Сайт должен работать во всех современных браузерах. Публично пока доступен лишь анализ истории.
Ближе к делу, как загрузить данные для анализа
Для анализа требуется история сделок, которую можно забрать в формате json из API Тинькофф. Пример для curl, а ниже будут команды и для Windows.
Следует ознакомиться с правилами использования OpenApi Тинькофф. Никому не передавайте токен и не оставляйте его в истории команд, т.к. с помощью него можно совершать операции. Если вы считаете, что токен мог быть скомпрометирован, то отзовите его на странице банка https://id.tinkoff.ru/account
Если вы ранее не работали с консолью, и у вас вопросы по использованию команд, то лучше спросите в ЛС/комментариях перед использованием.
curl -X GET "https://api-invest.tinkoff.ru/openapi/operations?from=2015-01-01T00%3A00%3A00%2B03%3A00&to=2025-01-01T00%3A00%3A00%2B03%3A00" -H "accept: application/json" -H "Authorization: Bearer t.TOKEN" > _my_invest_operations.json
Параметры запроса CURL
Можно изменить параметры запроса в url к api-invest.tinkoff.ru/openapi/operations:
from=2015-01-01 — период выгрузки ОТ
to=2025-01-01 — период выгрузки ДО* лучше указывать максимальный период, для консистентности данных
&figi=******* — необязательный параметр, выгрузка операций по конкретному FIGI
&brokerAccountId=****** — необязательный параметр, указываем конкретный брокерский счет, если у вас их несколько
brokerAccountId можно получить командой:
curl -X 'GET' 'https://api-invest.tinkoff.ru/openapi/user/accounts' -H 'accept: application/json' -H 'Authorization: Bearer t.TOKEN' # -- УКАЗЫВАЕМ СВОЙ ТОКЕН
Json не содержит API токен и прочую личную информацию. Исключить передачу токена было главной целью.
Некоторые операции имеют вложенный элемент «trades»
< "trackingId": "9e46ad76c68c9a37", "payload": < "operations": [ < "operationType": "Dividend", "date": "2021-12-13T06:00:00+03:00", "isMarginCall": false, "instrumentType": "Stock", "figi": "BBG000BR2B91", "payment": 1.37, "currency": "USD", "status": "Done", "id": "2825259975" >, < "operationType": "BrokerCommission", "date": "2021-12-10T19:18:32.273+03:00", "isMarginCall": false, "instrumentType": "Stock", "figi": "BBG006L8G4H1", "quantity": 0, "quantityExecuted": 0, "payment": -5.63, "currency": "RUB", "status": "Done", "id": "2826624962" >, < "operationType": "Buy", "date": "2021-12-10T19:18:31.273+03:00", "isMarginCall": false, "instrumentType": "Stock", "figi": "BBG006L8G4H1", "quantity": 3, "quantityExecuted": 3, "price": 4690.8, "payment": -14072.4, "currency": "RUB", "commission": < "currency": "RUB", "value": -5.63 >, "trades": [ < "tradeId": "498456613", "date": "2021-12-10T19:42:09.441+03:00", "quantity": 3, "price": 4690.8 >], "status": "Done", "id": "29072082626" > ] >, "status": "Ok" >
Далее эти данные загружаются на сервер, в json ответе возвращается sha1 hash ваших операций (именуемый id).
Получение и импорт сделок можно поместить в одну команду:
#Загружаем операции одной командой через pipeline, не забыв указать токен в первом curl curl -X GET "https://api-invest.tinkoff.ru/openapi/operations?from=2015-01-01T00%3A00%3A00%2B03%3A00&to=2025-01-01T00%3A00%3A00%2B03%3A00" -H "accept: application/json" -H "Authorization: Bearer t.TOKEN" | curl -H "Content-Type: application/json" -X POST --data-binary @- https://tstocks.ru/api/operations/upload
повторный импорт
При повторном импорте, если данные по сделкам не изменились, то будет выдан тот же id, а загрузка пропущена.
*Nginx имеет небольшие лимиты на повторный импорт
Пример для Windows:
- Сохраняем историю сделок в файл Дополнительные параметры
Можно изменить параметры запроса в url к api-invest.tinkoff.ru/openapi/operations: from=2015-01-01 — период выгрузки ОТ to=2025-01-01 — период выгрузки ДО* лучше указывать максимальный период, для консистентности данных &figi=******* — необязательный параметр, выгрузка операций по конкретному FIGI &brokerAccountId=****** — необязательный параметр, указываем конкретный брокерский счет, если у вас их несколько brokerAccountId можно получить командой:
(Invoke-WebRequest -Uri "https://api-invest.tinkoff.ru/openapi/user/accounts" -Headers @< "method" = "GET" "accept"="application/json" "authorization"="Bearer t.TOKEN" # -- УКАЗЫВАЕМ СВОЙ ТОКЕН >).Content
(Invoke-WebRequest -Uri "https://api-invest.tinkoff.ru/openapi/operations?from=2015-01-01T00%3A00%3A00%2B03%3A00&to=2025-01-01T00%3A00%3A00%2B03%3A00" -Headers @ < "method" = "GET" "accept"="application/json" "authorization"="Bearer t.TOKEN" # -- УКАЗЫВАЕМ СВОЙ ТОКЕН >).Content > _my_invest_operations.json # название файла в который сохранятся операции
- Отправляем файл на сервер
Invoke-RestMethod -ContentType 'application/json' -uri https://tstocks.ru/api/operations/upload -method POST -InFile _my_invest_operations.json
Как пользоваться
После загрузки операций и получения id, вставляем его на сайт. Не путайте с токеном TinkoffApi. Имеется демонстрационный id, для тех, кому интересно посмотреть функционал, не загружая свои сделки.
На сайте нет авторизации в привычном виде, т.к. цель максимально упростить процесс, избежать регистраций и сбора личной информации.
Id это sha1 хэш, который достаточно уникальный и длинный для перебора. Просмотреть сделки сможет любой, кто знает id, что может быть удобно для передачи его другу-инвестору.
Имеется кнопка удаления данных по id, а также в планах ввести время жизни загруженного портфеля.
Если у вас есть сомнения по поводу описываемых операций, уточните подробности у нас, если есть идеи как сделать лучше — сообщите.
При нажатии на кнопки открываются соответствующие таблицы, я покажу несколько кейсов их использования, чтобы было понятно, как с ними работать. Постараюсь воздержаться от комментариев самих сделок и результата.
Статистика по инструментам
Таблица отображает данные сгруппированные по FIGI. В наименованиях столбцов кратко описан смысл, по ходу статьи дополнительно уточню значения некоторых столбцов.
а) Смотрим доходность торговли по конкретной бумаге
Почти два года назад я наивно игрался с «шортами Tesla», и мне было интересно сколько дохода это приносит. С этого кейса родилась идея извлекать данные из API.
Итог сделок плачевный, хотя начиналось все неплохо. Опыт, сын ошибок трудных..
Поясню значения столбцов:
- Фикс разница — зафиксированный доход/убыток. Купоны/дивиденды/комиссии не включены, они отображаются в отдельных одноименных столбцах
Оказывается, что это не так легко посчитать, если на руках не 0 бумаг.
Бумаги покупаются и продаются частями в разное время, требуется раскрывать и упорядочивать сделки, соблюдая правило FIFO, еще у некоторых акций огромные лоты (*в TGKB миллион шт.)
Все расчеты ведутся в БД, т.ч. пришлось повозиться с оптимизацией sql запросов и функций, чтобы это работало очень быстро. Для оптимизации использовал https://explain.dalibo.com/
- Доход текущий — это примерно то, что мы видим в приложении по текущим позициям, купоны/дивиденды/комиссии не включены
- Доход — это сумма двух предыдущих столбцов, купоны/дивиденды/комиссии не включены
- Общий доход — это предыдущий столбец + налоги(див/купон)/купоны/дивиденды/комиссии
- Figi содержит ссылку, по которой открывается вся история по бумаге:
*для демонстрационного портфеля некоторые подробности недоступны
**комиссия за маржинальную торговлю не привязана к конкретному figi, ее можно посмотреть на вкладке «Прочие платежи»
б) Смотрим то, что не видно в приложении при фиксации убытков, или активной торговле
Скоро конец года, и для снижения налога на доход я зафиксировал убыток по FEES, и снова выкупил акции. Открыв приложение брокера, я даже увижу прибыль (столбец «Доход текущий»). А если так зафиксировать все убытки, то можно увидеть «зеленый» портфель в приложении.
Однако, мне интересно видеть и не забывать про реальный итог торговли по тикеру.
в) Ищем бумаги, которые принесли наибольшую прибыль за все время
Столбцы таблицы поддерживают фильтрацию, сортировку и группировку по нескольким столбцам. Сортировка осуществляется кликом по нескольким столбцам через SHIFT, группировка осуществляется через меню-ПКМ в требуемой последовательности. При группировке некоторые числовые столбцы показывают итог с суммой, минимальным и максимальным значением
Группируем или фильтруем по валюте и сортируем по «Общему доходу»
Наиболее прибыльной стратегией для меня было — купить несколько голубых фишек, и забыть про них:)
г) Считаем дивиденды
Некоторые компании торгуются в рублях, но платят дивиденды в валюте (например «Полиметалл»). Поэтому для группировки введен отдельный столбец — «валюта дивидендов». Не буду повторяться и показывать, как группировать и сортировать.
В примере покажу некий парадокс, у меня приложение Тинькофф не отображает дивиденды по своим же акциям.
Обратил внимание, что TCS($) принес большие дивиденды, и огромную доходность (* считаю как отношение всего оборота доходов/расходов).
Понимаю, что этот доход скорее всего относится к рублевой акции, т.к. сумма достаточно большая. Почти все, кто использовал Тинькофф API знают, что TCS и TCSG раньше висели на одном FIGI, и создавали головную боль. Пришлось ввести простой костыль, который меняет FIGI в зависимости от валюты.
Но особенность в том, что даже в терминале по этим тикерам не видно информации по данным выплатам:
История в терминале
В ленте приложения эти событие отображается, и при клике переводит в TCS ($)
Но и в истории тикеров данной информации нет:
е) Еще один необычный пример. По STX статистика показывает отрицательное количество акций.
*в данном случае эмулируется шорт-позиция. Доход рассчитывается, словно я продал по 98, а выкупить сейчас можно по 104
Видимо у Seagate когда-то сменился figi. Такая же история в официальном терминале, т.е. я словно из воздуха получил и продал эти акции, никакого наследования не видно. Техподдержка отправила в брокерский отчет смотреть сумму покупки по старому isin (IE00B58JVZ52):
Есть еще минусы анализа данных из API, которые опишу в конце статьи.
е) Ищем «залежавшиеся» акции через столбец «дней с последней сделки»
Это была одна из первых покупок, оказавшаяся падающим ножом, которую я почему-то наивно храню. Акция не приносит дивидендов, и лежит в долгосроке почти 3 года. Возможно пора зафиксировать убыток..
Отклоненные операции
Таблица может быть полезна для обнаружения несработавших тейк-профитов/стоп-лосс, попыток купить/продать акцию.
Например, у меня стоял тейпрофит на TGKN, выставивший заявку, которая не исполнилась.
Несработавший тейк/стоп придется переставлять вручную.
Еще можно вспомнить, какие акции вы хотели купить/продать, и как повела себя акция по отношению к текущей цене.
В примере ниже видны мои попытки купить ISKJ, который взлетел в 3.5 раза за короткий промежуток времени.
Есть и обратные примеры, куда к счастью не удалось войти. Мне такой анализ позволяет вспомнить, чем я руководствовался и немного порефлексировать:)
Прочие операции
Данные таблицы содержат операции, не связанные напрямую с инструментами, например, ввод/вывод средств, комиссию за маржинальную торговлю, плату за тариф и т.п.
Дополнительные столбцы с датами позволяют удобнее фильтровать/группировать данные по периодам, а одна из таблиц уже сгруппирована по месяцам, валюте и типу.
Анализируя траты на оплату тарифа «Трейдер», я заметил, что в августе 2021 год плата списалась с меня 2 раза. Я даже подумал, что это глюк API:
Пример ручной группировки
Ответ тех. поддержки я не очень понял. Судя по всему, я с каких-то пор стал привязан к расчетному периоду и дате платежа (22-е). Хотя мне казалось, что если я заплатил 05.08.2021, то могу торговать до 04.09.2021.
UPD. В комментариях представители банка написали, что двойное списание осуществлено из-за технических проблем https://habr.com/ru/post/589865/comments/#comment_23875149
Этот пример показывает, что необходимо всегда сверять ожидание с реальностью.
Проблемы анализа через API
- API не возвращает историю по бумагам, которые больше не обращаются на бирже. Некоторых погашенных облигаций я тоже не нашел в ответе API. Видимо поэтому, чтобы узнать сколько я потратил на упомянутый выше Seagate, мне нужно скачивать отчет брокера. По этой же причине не берусь считать суммарный итог портфеля.
- При сплите/консолидации бумаги непонятен коэффициент, и я не нашел способа получить его от брокера. Видимо надо мониторить какие-то ресурсы/новости. Или пытаться высчитывать самому по разнице дневных свечей, но не уверен, что это надежно. На данный момент бумаги, прошедшие сплит/консолидацию могут неправильно отображать доход в таблице. Если кто знает, как это решить — напишите.
- Пока что не учитываю налоги, т.к. цель показать общую картину без тонкостей: учета ИИС; W-8BEN; сроков владения и т.д.
Итог
За последние годы приложение Тинькофф стало лучше, появились скринеры, группы, заметки. Не знаю есть ли такое у других брокеров. Но я до сих пор не нашел способа фильтровать/сортировать инструменты своего портфеля. Не раз писал предложения по улучшению, в том числе на Хабре. Как раз статья @softandiron вдохновила меня оживить забытый инструмент.
Я показал лишь часть ярких примеров использования. В будущем планируется расширить функционал сайта https://tstocks.ru/. Не откажемся от помощи грамотных инвесторов и программистов.
Как вам в целом такой формат загрузки, преобразования и отображения данных. Пользуйтесь ли вы сторонними инструментами, и помогают ли они пролить свет на портфель? Хватает ли вам штатного функционала брокера?
Таблицы несложно расширить, пишите, чего не хватает, сообщайте о недочетах и делитесь общим впечатлением.
Источник https://habr.com/ru/articles/589865/