Как стать автором
Обновить

Что недоговаривают Тинькофф Инвестиции. Вытаскиваем все данные по портфелю через API в большую таблицу Excel

PythonAPIФинансы в IT
Из песочницы

Тинькофф Инвестиции - популярный российский брокер с передовым клиентским приложением для мобильных устройств и браузеров. Приложение призвано упростить процесс торговли и снизить порог входа в инвестиции до такого минимума, чтобы захватить максимально широкую аудиторию.

Однако, когда инвестиции приобретают серьёзный характер, инвестору нужны точные и подробные данные по его портфелю, в частности, для оценки эффективности инвестирования. И вот здесь с приложением возникают неоднозначности.

Описание проблемы

Рассмотрю на примере своего портфеля (не ИИС) в мобильном приложении. (С даты публикации приложение может обновиться).

На главном экране видим ободряющие значения:

У меня здесь сразу возникают вопросы:

  • Каким образом была посчитана эта зелёная сумма и 12,21%? Причём, несколько дней назад у меня было что-то около +17%, потом я зафиксировал одну бумагу с профитом, стоимость портфеля почти не изменилась, а вот этот зелёный "общий процент" сразу упал до 12,21.

  • За всё время - это за несколько лет инвестиций? Ещё есть опция: за сегодня. А какой процент у меня, например, за год?

  • Мой портфель почти полностью в иностранных бумагах и USD. Каким образом это было переведено в рубли: по курсу ЦБ или по рынку?

  • Сколько от этой суммы у меня реально останется после уплаты налогов и комиссий, если я продам весь портфель и выведу деньги?

Заходим в раздел Портфельная аналитика, и находим там уже другие значения:

Почему на главной странице было +955 644, а здесь почти на 2 миллиона больше?

Кстати, здесь уже можно посмотреть результаты за год, приложение выводит сумму, но не процент.

Пока всё выглядит весьма оптимистично, открываю профиль в Пульсе.

Вот это результат! Посмотрим по-подробнее.

Здесь приведены результаты по месяцам. За 4 месяца текущего года +6,67%, а если посмотреть на 2020 год, там у меня +31,41%. Для сравнения, если не ошибаюсь, S&P 500 за 2020 год вырос на 16,26%. Не совсем понимаю, как я мог его так обогнать, если только дело не в курсовой разнице. В любом случае, это не вяжется с обозначенными на главном экране +12,21% за всё время, т.к. 2019 и 2018 года тоже зелёные. В общем, не понятно, как и в какой валюте они считают, надо разбираться.

Поиск решения

Чтобы внести больше ясности в процесс инвестирования, сделать этот процесс более осознанным, мне нужно:

  • Разобраться, как вычисляются значения, отображаемые в мобильном приложении

  • Выяснить реальные показатели эффективности портфеля

  • Узнать общие суммы налога и комиссий, которые я уплатил за всё время

  • Вычислить сумму, которую я могу вывести со счёта, после уплаты налогов при продаже портфеля

Это особенно актуально в свете того, что портфель у меня, в основном, валютный, а налог считается в рублях относительно официального курса на день покупки актива. Поскольку я начал покупать бумаги несколько лет назад, когда USD был гораздо дешевле, налог при фиксации прибыли может оказаться огромным сюрпризом и съесть существенную часть отображаемой на главном экране суммы.

  • Собрать все возможные данные по портфелю и визуализировать их в удобной для меня форме, такой как таблица Excel, с которой я смогу дальше работать средствами самого Excel или Google Sheets.

Есть вариант пытать персонального менеджера и службу поддержки, но переписка с ними бывает утомительна, и они не помогут с визуализацией данных. Лучше написать программное средство, чтобы формировать результат в удобной для меня форме и делать это автоматически.

Здесь на выручку приходит Tinkoff API - средство для разработчиков ПО, позволяющее взаимодействовать с Тинькофф Инвестициями автоматизированными средствами.

Проблема только в том, что я не программист и с банковскими API раньше не работал. Видимо, пришла пора попробовать.

Знакомство с API

Находим официальную страницу Open API от Тинькофф:

https://tinkoffcreditsystems.github.io/invest-openapi/

На странице предлагаются SDK: Java, C#, Go, NodeJS.

Приведены и неофициальные: Python @daxartio,Python @Awethon, Python @Fatal1ty, PHP, Ruby.

Ничего из того, что я умею. В основном, я делал DIY проекты на Arduino-подобных контроллерах с WiFi, проектировал и заказывал для своих электронных устройств печатные платы, делал небольшие одностраничные WEB-интерфейсы и телеграм-боты для взаимодействия с этими устройствами. Т.е., в основном я работал с железом и писал прошивки на Arduino Wiring (на основе C++).

Из представленного списка мне больше всего импонировал Python, я писал на нём что-то на уровне print('Hello World') и давно хотел познакомиться поглубже. Поэтому, я решил, что буду делать проект с Тинькофф API на Python.

Для начала я нашёл статью на Хабре.

И видео от профессионального разработчика.

Это сильно помогло мне продвинуться на начальном этапе: понять, как получать данные с API.

Если коротко, работает это так:

Открываем редактор кода, например Idle, импортируем установленную библиотеку:

import tinvest

Создаём объект для дальнейшей работы с API:

client = tinvest.SyncClient(account_data['my_token'])

В my_token выше подставлем свой API key, который получаем в личном кабинете брокера.

Я не стал пробовать в демо-счёте, сразу указал API своего реального портфеля.

Создаём объект с позициями портфеля:

positions = client.get_portfolio() Это сложный массив, который содержит информацию по каждой бумаге.

Создаём объект с операциями. Здесь указывается дата начала инвестирования и текущая дата в определённом формате.

operations = client.get_operations(from_=account_data['start_date'], to=account_data['now_date'])

Получаем рыночные курсы валют (понадобятся для дальнейших расчётов):

course_usd = client.get_market_orderbook(figi='BBG0013HGFT4', depth=20

course_eur = client.get_market_orderbook(figi='BBG0013HJJ31', depth=20)

И чтобы в нашей будущей таблице появились не только бумаги, но и валюты кэшем, запросим и их тоже:

currencies = client.get_portfolio_currencies()

Данные можно выводить сразу же через print, например:

for pos in positions.payload.positions:

print('name:', pos.name)

print('ticker:', pos.ticker)

print('balance:', pos.balance)

print('currency:', pos.average_position_price.currency)

print('price:', pos.average_position_price.value)

print(' ')

Но наша цель - это Excel таблица. Для её формирования я выбрал библиотеку XlsxWriter:

https://xlsxwriter.readthedocs.io/

import xlsxwriter

Файлик с excel появляется в папке с программной. Если файл с таким названием уже есть в папке - он перезапишется. Поэтому, я сделал, чтобы в названии файла была текущая дата, так удобно потом сравнивать отчёты за разные дни / месяцы:

ecxelFileName = 'tinkoffReport_' + today + '.xlsx'

workbook = xlsxwriter.Workbook(ecxelFileName)

worksheet = workbook.add_worksheet()

Так, на этом этапе, у меня получилась небольшая программка, создающая таблицу с базовой информацией по бумагам, получаемой по API, а именно:

Наименование, тикер, валюта бумаги, количество бумаг, средняя цена покупки, ожидаемая выручка.

Из имеющихся данных простой арифметикой высчитывались: текущая рыночная цена одного лота и суммарная стоимость всей позиции.

Писалось всё в Idle, выглядело как-то так: (НЕ ПОВТОРЯТЬ!)

НЕ ПОВТОРЯТЬ
НЕ ПОВТОРЯТЬ

Подглядывая в проекты других разработчиков на Python, я тогда ещё мало что понимал, но было очевидным, что их проекты пишутся совсем по-другому, и мне придётся менять структуру в корне.

Я решил переписать всё заново, как положено, и в более удобной среде разработки.

Знакомство с Python

В качестве более продвинутой среды, ребята с работы посоветовали PyCharm.

С ним дело пошло гораздо продуктивнее, среда автоматически дрессирует писать в соответствии с PEP8 (стандарт оформления кода).

Общие знания по Python я брал из своего любимого справочника: https://www.w3schools.com/

Просто прошерстил все его статьи по питону сверху вниз, и потом периодически обращался за подробностями.

Вообще, чисто субъективно, мне этот язык сразу понравился. Порадовала краткость путей решения задач. Сложилось впечатление, что если просто нужно, чтобы что-то заработало, оно здесь заработает в два счёта, без лишних заморочек. Гуглится всё элементарно, по крайней мере, на моём уровне сложности. Чаще всего, решения находил на https://stackoverflow.com/

Структура программы

Я решил разбить проект на 3 модуля (файлика .py) и отдельный файлик .txt с данными аккаунта.

Модуль main.py - основной модуль программы, который мы запускаем. В нём создаются классы объектов, происходят вычисления и формируются объекты, заполняясь полученными данными. Парсит только курсы валют по разным датам с API ЦБ РФ.

Модуль data_parser.py - содержет всего две функции, первая парсит данные аккаунта из текстового файла, а вторая из Тинькофф Инвестиций и больше программа к этому не возвращается.

Модуль excel_builder.py - большой модуль, который ничего не считает, только берёт данные из main.py и строит огромную красивую таблицу.

Структура таблицы

- Позиции

В левой части таблицы выводится информация по текущему портфелю:

Параметры (базовые, из API):

name - название бумаги

ticker - тикер

balance - количество бумаг в портфеле

currency - валюта

ave.price - средняя цена покупки в валюте

exp.yield - ожидаемая прибыль с продажи (без учёта налогов и комиссий)

Тут, кстати, стало понятно, откуда взялась сумма 955 644 руб (+12,21%) на главном экране приложения - это как раз и есть суммарный exp.yield в рублях по рыночному курсу.

Параметры, посчитанные на основе базовых:

market price - текущая рыночная цена одной бумаги (ave.price + exp.yield)

% change - процент изменения стоимости актива (market_price / ave.price) * 100) - 100)

market value - текущая рыночная стоимость всей позиции (market_price * balance)

market value RUB - рыночная стоимость всей позиции в пересчёте на рубли по текущему рыночному (не ЦБ) курсу

ave. % - среднее арифметическое всех % change. Сейчас я понимаю, что это бесполезный параметр. Надо было считать по-другому, возможно исправлю в следующих версиях программы.

total value: - рыночная стоимость портфеля в рублях (сумма всех market value RUB)

Как видим из скриншота, total value составляет 8 782 836, что близко к значению на главном экране приложения: 8 782 160р, но не соответствует ему точь-в-точь. Отклонение составляет 0,0077%. Не существенно, но чем вызвано, я не совсем понимаю. Если есть идеи по исправлению - напишите, пожалуйста, в комментариях.

Дальше - интереснее!

Сразу после блока с рыночными ценами, располагается самый сложный, с точки зрения расчётов, блок: стоимость активов по ЦБ и расчёт ожидаемого налога при продаже.

CB value RUB - стоимость позиции в рублях по курсу ЦБ на сегодня.

Внизу считается сумма, которая у меня составила 8 749 045 - это оценка стоимости моего портфеля, с точки зрения ЦБ, и она отличается от рыночной стоимости на 33 791 руб, что вполне нормально.

ave.buy in RUB - средняя стоимость покупки в рублях по курсу ЦБ на дату покупки. Это важный параметр для последующего расчёта налога.

Сложность в том, что бумага могла приобретаться частями, в разные дни, а могла частично продаваться. Здесь действует такое правило, что первой продаётся та бумага, которая первой покупалась.

Чтобы решить эту задачу, я придумал сделать для каждой позиции упорядоченный список (массив). Программа пробегает по всем операциям покупки, находя операции с figi данной бумаги, и каждая покупка добавляет в список количество ячеек, соответствующее количеству приобретённых бумаг. Каждая ячейка содержит значение, соответствующее цене покупки в рублях по курсу ЦБ на дату операции. А каждая продажа удаляет нужное количество ячеек из начала списка. Затем считается среднее значение по оставшимся ячейкам, так получается средняя цена покупки в рублях по курсу ЦБ.

Был небольшой подвох, связанный с тем, что в списке операций от Tinkoff API есть не только выполненные, но и нулевые операции, которые пришлось отсеивать.

Чтобы API ЦБ РФ не решил, что мы его ддосим, я поставил небольшую задержку. В итоге, всё считается как надо, но этот этап обрабатывается ощутимо медленно. Чтобы обработались мои 15 позиций и 430 операций, приходится ждать около 1 минуты.

Наверняка это можно как-то оптимизировать, но, в принципе, мы не торопимся.

Разобравшись с самым сложным этапом, можно составить следующие колоночки:

sum.buy in RUB - сумма покупки позиции в рублях по курсу ЦБ (ave.buy in RUB * balance)

Внизу считается сумма, которая составила 7 178 123 - на такую сумму, по мнению ЦБ, я приобрёл текущие активы.

tax base - налоговая база (sum.buy in RUB - CB value RUB)

expected tax - ожидаемый налог по ставке 13%, который нам насчитают, если продать бумагу сейчас.

Внизу получилась сумма: 207 145 руб. - такой налог будет удержан, если я продам весь портфель сейчас.

- Операции

Справа от раздела с позициями, выводим колоночки со всем типами операций, которые может нам предоставить Tinkoff API. Их много, на один скриншот не влезают, но на большом мониторе помещается:

Под каждой колоночкой считается сумма. Это как раз то, что нам не покажет брокер. И здесь есть кое-что интересное.

Теперь мы можем сравнить сумму внесённых средств и сумму выведенных (в переводе на рубли по курсу ЦБ)

Ещё можно посчитать сумму всех купонов и дивидендов, а также, внимание:

все комиссии, уплаченные брокеру, и все налоги, удержанные брокером!

Итак, вот мы получили и разложили перед глазами все имеющиеся данные.

Теперь можно сделать из них выводы.

- Аналитика

Я впихнул этот маленький раздел прямо под таблицей с позициями.

Investing period - период инвестирования с даты, которую мы указали в текстовом файлике до сегодняшней даты. По идее, должен пригодиться для последующих расчётов эффективности инвестирования.

Кстати, что касается дат, я не учитывал часовые пояса, и это может где-то выплыть.

PayIn - PayOut - разница между внесёнными на счёт средствами и выведенными по курсу ЦБ на дату операции. У меня это получилось 5 843 172 руб - столько средств я задонатил на свой счёт.

Commissions payed - сумма всех комиссий, уплаченных брокеру. У меня она составила 17 315 руб, из которых 2200 руб - это 2% побор за участие в IPO Сегежа, в которое я впутался пару дней назад. Если не принимать его во внимание, то за всё время я заплатил всего около 15 000 руб комиссий, что, я считаю, достаточно мало. На тарифе Премиум при покупке $1000 я плачу всего 18,22 руб комиссии.

Кстати, из-за появления в портфеле позиции "Сегежа" в первый день после IPO программа не могла выполниться и выдавала ошибку. На следующий день по бумаге с API стали приходить нормальные данные и программа снова заработала.

Taxes payed - сумма налогов, уплаченных брокером только в российский бюджет. Налог с дивидендов, уплаченный в других странах, здесь не учитывается, его надо заказывать отдельной справкой в личном кабинете.

Если что, форма W8BEN, у меня, на данный момент, по некоторым причинам, не действует.

Итого, в российский бюджет я уже уплатил 117 631 руб, и, как было посчитано выше, мне предстоит уплатить ещё порядка 207К, если я зафиксирую портфель сейчас.

Здесь ещё раз уточню: налог считается в рублях, и если мы купили бумагу за $100 при курсе ЦБ 60р за доллар, а через год, когда доллар стал стоить 80р, продали эту бумагу за те же $100, мы должны будем заплатить налог 260р за счёт курсовой разницы, потому что, с точки зрения ЦБ, мы купили бумагу за 6000р, а продали за 8000р, хотя ни одного $ мы не заработали.

Clean portfolio - стоимость нашего портфеля по текущему рыночному курсу за вычетом предстоящего налога. Это те деньги, которые мы реально сможем вывести, если зафиксируем весь портфель сейчас. Сюда ещё можно накинуть комиссию брокера, но она варьируется, и я не стал её хардкодить. Навскидку, если у меня портфель примерно $116000 и тариф Премиум, комиссия за продажу всех активов получится где-то примерно 2000р. И ещё почти столько же, если я захочу поменять в рубли. Если не ошибаюсь, в Тинькофф Инвестициях комиссия на всё одинаковая.

Profit - сумма, которую мы заработаем при фиксации портфеля сегодня. Считается как Clean portfolio - (PayIn - PayOut), т.е., полученные средства, очищенные от налогов, минус вложенные средства. У меня получилась сумма 2 732 519, что похоже на сумму "Доход за всё время" из раздела "Портфельная аналитика" в мобильном приложении, только за вычетом налога 207К. Расхождение около 2500 руб. Не знаю, насколько это здесь существенно.

В общем-то, это пока всё, что я сделал.

Заключение

Осталась неподсчитанной эффективность вложений. Мы знаем точный период инвестирования, знаем профит. Однако, сложность в том, что вложенные средства не были зачислены разово, а поступали на счёт периодически, разными суммами, а иногда выводились. Для расчёта эффективности инвестирования нужно что-то вроде формулы XIRR в Excel, но я пока не сообразил, как вкрутить её в эту программу. Если у вас есть идеи на этот счёт - поделитесь, пожалуйста.

Зато, мы разобрались, откуда получаются значения, приводимые в клиентском приложении.

Также, мы узнали суммы комиссий и налогов, как уплаченные, так и ожидаемые.

Нашли реальную сумму, которую сможем вывести со счёта при фиксации портфеля, после удержания налогов.

А главное: получили возможность одним кликом собирать все данные по портфелю со всеми операциями в одну большую таблицу Excel, с которой дальше можем работать средствами самого Excel, можем экспортировать в Google Sheets, или просто сохранить как архив для анализа в будущем.

Проект опубликован на GitHub

В планах сделать оконную версию программы с интерфейсом в виде приложения .exe для тех, кто не хочет возиться с установкой Python на свою машину, и при этом ещё не боится вводить данные своего аккаунта в приложение неизвестного разработчика (я бы, наверно, не рискнул).

Это мой первый проект на Python и первая публикация на Хабре.

Надеюсь, информация окажется полезной для улучшения взаимодействия с приложениями Тинькофф, работы с API и разработки подобных программ, а также поспособствует более осознанному инвестированию, а следовательно, повысит ваше благосостояние. Благодарю, что дочитали до конца.

Теги:тинькоффинвестицииtinkoff investapipythonexcel
Хабы: Python API Финансы в IT
Всего голосов 158: ↑155 и ↓3 +152
Просмотры124.9K

Похожие публикации

Senior Backend Engineer (Rest API, NodeJS, Python)
от 3 500 до 4 500 $DataDirect Networks Inc. (DDN)Можно удаленно
Python разработчик (Middle / Senior)
от 3 000 до 6 500 $CordlessМожно удаленно
Developer (Python/Django)
от 70 000 ₽SkyDNSМожно удаленно
Инженер по автоматизации тестирования Python (Seller API / Доставка)
от 150 000 до 250 000 ₽OZONМоскваМожно удаленно
Python Backend Developer (Team Lead)
от 250 000 ₽OutstreamСанкт-ПетербургМожно удаленно

Лучшие публикации за сутки