HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
JustinSun

НОВОСТИ API Тиньков.Инвестиции. Первые шаги

BDFpromo
Оффлайн

BDFpromo

.
.
Регистрация
23.09.18
Сообщения
12.347
Реакции
176
Репутация
0
Практически с первых дней я стал клиентом Тиньков.Инвестиции.
И с этого же момента меня терзают смутные сомнения — отражает ли личный кабинет объективную реальность?
Дело в том, что я покупаю ценные бумаги, номинированные в долларах, но в ЛК цены всех активов отображаются в долларах, а итоговая стоимость портфеля в рублях.
И мне непонятно, это доллар вырос или я такой результативный инвестор?
А как же комиссии, налоги и прочие дивиденды?
Вот бы взять все мои сделки и расписать по ФИФО, как в складском учете… А сверху положить полученные дивиденды, а потом вычесть налоги.
Вот тогда я и увижу понятный мне результат.

Оказалось, у Тинькова есть API, которое позволяет писать торговых роботов (мне это совсем не интересно), а также загружать данные по своему портфелю и операциям.

У этого API есть официальное описание, но мне не все было понятно, пришлось разбираться.
Результаты этих разборок представляю вашему вниманию.

Полезные ссылки:



Получение токена и установка библиотеки


Перед началом работы нужно установить библиотеку и получить токен.
Установка библиотеки
pip install -i --extra-index-url= tinkoff-invest-openapi-client

Цитирую официальную инструкцию по получению токена:
  1. Зайдите в свой аккаунт на tinkoff.ru
  2. Перейдите в раздел инвестиций
  3. Перейдите в настройки
  4. Функция «Подтверждение сделок кодом» должна быть отключена
  5. Выпустите токен OpenApi для биржи и Sandbox. Возможно система попросит вас авторизоваться еще раз, не беспокойтесь, это необходимо для подключения робота к торговой платформе.
  6. Скопируйте токен и сохраните, токен отображается только один раз, просмотреть его позже не получится, тем не менее вы можете выпускать неограниченное количество токенов.


На момент написания статью токен выдавался на странице , кнопка в нижней части страницы.
zzjvysg4cl23e17owhta6ydtemk.jpeg

С токеном для песочницы у меня возникали ошибки, поэтому я начал экспериментировать с боевой версией. Чего и вам желаю (Осторожно: не купите-продайте случайно что-то лишнее).

Авторизация



from openapi_client import openapi

token = 'тут нужно вставить ваш токен'
client = openapi.api_client(token)

Эти две строки делают все, что нам нужно.
Дальше работаем с переменной client.

Что у нас в портфеле


Получим содержимое нашего вашего портфеля

pf = client.portfolio.portfolio_get()

Посмотрим основные данные первого элемента
print('value:', pf.payload.positions[0].average_position_price.value)
print('currency:', pf.payload.positions[0].average_position_price.currency)
print('balance:', pf.payload.positions[0].balance)
print('figi:', pf.payload.positions[0].figi)
print('ticker:', pf.payload.positions[0].ticker)
print('name:', pf.payload.positions[0].name)

В моем случае это
value: 45.98
currency: USD
balance: 21.0
figi: BBG000BWPXQ8
ticker: BTI
name: British American Tobacco

value — Цена бумаги
balance — Кличество бумаг в портфеле, value и currency — их денежное выражение.
figi — (Финансовый Глобальный Идентификатор инструмента)
ticker — актива.
По этим данным мы можем узнать человекочитаемое название актива.
Для данного запроса нам это не нужно (см. поле name), но в других случаях пригодится.
Получаем название бумаги по FIFI и тикету



# Получение инструмента по FIGI
instr = client.market.market_search_by_figi_get('BBG000BWPXQ8')
instr
получаем
{'payload': {'currency': 'USD',
'figi': 'BBG000BWPXQ8',
'isin': 'US1104481072',
'lot': 1,
'min_price_increment': 0.01,
'name': 'British American Tobacco',
'ticker': 'BTI',
'type': 'Stock'},
'status': 'Ok',
'tracking_id': 'a1979917d2141916'}
Эта API-функция у меня работает как надо. Видим, что 'BBG000BWPXQ8' -> 'British American Tobacco'
А вот поиск названия актива по тикеру у меня не работает :(((
instr = client.market.market_search_by_ticker_get('BTI' )
print(instr)
Разработчики предложили обновить библиотеку, но даже после этого не взлетело.
Качаем справочник ценных бумаг


Впрочем, я решил этот вопрос кардинально. Скачал у Тинькова полный справочник торгуемых активов
# Получение списка облигаций
bonds = client.market.market_bonds_get()

# Получение списка ETF
etfs = client.market.market_etfs_get()

# Получение списка акций
stocks = client.market.market_stocks_get()

instr_list = bonds.payload.instruments + etfs.payload.instruments + stocks.payload.instruments

instr_list[:3]
получил
[{'currency': 'RUB',
'figi': 'BBG00844BD08',
'isin': 'RU000A0JU898',
'lot': 1,
'min_price_increment': 0.1,
'name': 'МКБ выпуск 9',
'ticker': 'RU000A0JU898'}, {'currency': 'RUB',
'figi': 'BBG00R05JT04',
'isin': 'RU000A1013Y3',
'lot': 1,
'min_price_increment': 0.1,
'name': 'Черкизово выпуск\xa02',
'ticker': 'RU000A1013Y3'}, {'currency': 'RUB',
'figi': 'BBG00PNLY692',
'isin': 'RU000A100DC4',
'lot': 1,
'min_price_increment': 0.1,
'name': 'МСБ-Лизинг 002P выпуск 2',
'ticker': 'RU000A100DC4'}]
Как видим, figi и name там есть. Для моих целей — более чем достаточно.
Получаем список операций

А вот самое интересное — получить список моих операций. В операции (в моем случае) попадают следующие действия
  • PayIn — Пополнение брокерского счета
  • PayOut — Вывод денег
  • BuyCard — Покупка с карты
  • Sell — Продажа
  • BrokerCommission — Комиссия брокера
  • Dividend — Выплата дивидендов
  • Tax — Налоги
  • TaxDividend- Налоги c дивидендов
  • ServiceCommission — Комиссия за обслуживание
Код для выгрузки портфеля
from datetime import datetime
from pytz import timezone

# Качаем все операции с 30 сентября 2016 (я один из первых клиентов Тиньков Инвестиции)
d1 = datetime(2016, 9, 30, 0, 0, 0, tzinfo=timezone('Europe/Moscow')) # timezone нужно указывать. Иначе - ошибка
d2 = datetime.now(tz=timezone('Europe/Moscow')) # По настоящее время
ops = client.operations.operations_get(_from=d1.isoformat(), to=d2.isoformat())
Посмотрим, что получилось. В моем случае, представляет интерес этот элемент
ops.payload.operations[217]
Вот что он собой являет
{'commission': {'currency': 'USD', 'value': -0.42},
'currency': 'USD',
'date': datetime.datetime(2018, 11, 7, 10, 55, 53, 648913, tzinfo=tzoffset(None, 10800)),
'figi': 'BBG000PSKYX7',
'id': '42281525510',
'instrument_type': 'Stock',
'is_margin_call': False,
'operation_type': 'BuyCard',
'payment': -141.05,
'price': 141.05,
'quantity': 4,
'status': 'Done',
'trades': [{'date': datetime.datetime(2018, 11, 7, 10, 55, 53, 648913, tzinfo=tzoffset(None, 10800)),
'price': 141.05,
'quantity': 1,
'trade_id': '42636800'}]}
Нас интересуют поля:
  • date — дата сделки
  • figi — код актива
  • operation_type — тип операции
  • payment — сумма операции. У налогов или комиссий указана именно она. price при этом None
  • price — цена одной бумаги
  • quantity — плановое количество бумаг
  • trades — реальные биржевые сделки
Сразу возник вопрос — зачем нам какие-то trades, если есть price и quantity?
Все не так просто (план и факт)

Как я понял, в quantity указано то количество бумаг, которые я хотел купить. А то, что фактически куплено, лежит в trades.quantity.
Т.е. если хотите обратится к фактическим сделкам, нужно перебрать то, что лежит в trades.
В ряде случаев, там None — например, для налогов или вводов/выводов средств.

Чтобы получить настоящие цифры, нужно смотреть и в сделки, и в биржевые операции
for op in ops.payload.operations: # Перебираем операции
print(op.figi) # figi всегда берем из операции
print(op.operation_type) # и тип операции тоже
if op.trades == None: # Если биржевых сделок нет
print('price:', op.price) # Берем из операции цену бумаги
print('payment:', op.payment) # Сумму платежа
print('quantity:', op.quantity) # И количество бумаг
else:
for t in op.trades: # А если есть сделки - то перебираем их
print('price:', t.price) # И берем данные из них
print('quantity:', t.quantity)
print('--------------')
 
Сверху Снизу