- Регистрация
- 23.09.18
- Сообщения
- 12.347
- Реакции
- 176
- Репутация
- 0
Практически с первых дней я стал клиентом Тиньков.Инвестиции.
И с этого же момента меня терзают смутные сомнения — отражает ли личный кабинет объективную реальность?
Дело в том, что я покупаю ценные бумаги, номинированные в долларах, но в ЛК цены всех активов отображаются в долларах, а итоговая стоимость портфеля в рублях.
И мне непонятно, это доллар вырос или я такой результативный инвестор?
А как же комиссии, налоги и прочие дивиденды?
Вот бы взять все мои сделки и расписать по ФИФО, как в складском учете… А сверху положить полученные дивиденды, а потом вычесть налоги.
Вот тогда я и увижу понятный мне результат.
Оказалось, у Тинькова есть API, которое позволяет писать торговых роботов (мне это совсем не интересно), а также загружать данные по своему портфелю и операциям.
У этого API есть официальное описание, но мне не все было понятно, пришлось разбираться.
Результаты этих разборок представляю вашему вниманию.
Полезные ссылки:
Получение токена и установка библиотеки
Перед началом работы нужно установить библиотеку и получить токен.
Установка библиотеки
pip install -i
Цитирую официальную инструкцию по получению токена:
На момент написания статью токен выдавался на странице
С токеном для песочницы у меня возникали ошибки, поэтому я начал экспериментировать с боевой версией. Чего и вам желаю (Осторожно: не купите-продайте случайно что-то лишнее).
Авторизация
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 там есть. Для моих целей — более чем достаточно.
Получаем список операций
А вот самое интересное — получить список моих операций. В операции (в моем случае) попадают следующие действия
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'}]}
Нас интересуют поля:
Все не так просто (план и факт)
Как я понял, в 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('--------------')
И с этого же момента меня терзают смутные сомнения — отражает ли личный кабинет объективную реальность?
Дело в том, что я покупаю ценные бумаги, номинированные в долларах, но в ЛК цены всех активов отображаются в долларах, а итоговая стоимость портфеля в рублях.
И мне непонятно, это доллар вырос или я такой результативный инвестор?
А как же комиссии, налоги и прочие дивиденды?
Вот бы взять все мои сделки и расписать по ФИФО, как в складском учете… А сверху положить полученные дивиденды, а потом вычесть налоги.
Вот тогда я и увижу понятный мне результат.
Оказалось, у Тинькова есть API, которое позволяет писать торговых роботов (мне это совсем не интересно), а также загружать данные по своему портфелю и операциям.
У этого API есть официальное описание, но мне не все было понятно, пришлось разбираться.
Результаты этих разборок представляю вашему вниманию.
Полезные ссылки:
You must be registered for see links
You must be registered for see links
Получение токена и установка библиотеки
Перед началом работы нужно установить библиотеку и получить токен.
Установка библиотеки
pip install -i
You must be registered for see links
--extra-index-url=
You must be registered for see links
tinkoff-invest-openapi-clientЦитирую официальную инструкцию по получению токена:
- Зайдите в свой аккаунт на tinkoff.ru
- Перейдите в раздел инвестиций
- Перейдите в настройки
- Функция «Подтверждение сделок кодом» должна быть отключена
- Выпустите токен OpenApi для биржи и Sandbox. Возможно система попросит вас авторизоваться еще раз, не беспокойтесь, это необходимо для подключения робота к торговой платформе.
- Скопируйте токен и сохраните, токен отображается только один раз, просмотреть его позже не получится, тем не менее вы можете выпускать неограниченное количество токенов.
На момент написания статью токен выдавался на странице
You must be registered for see links
, кнопка в нижней части страницы.
С токеном для песочницы у меня возникали ошибки, поэтому я начал экспериментировать с боевой версией. Чего и вам желаю (Осторожно: не купите-продайте случайно что-то лишнее).
Авторизация
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 —
You must be registered for see links
(Финансовый Глобальный Идентификатор инструмента)ticker —
You must be registered for see links
актива.По этим данным мы можем узнать человекочитаемое название актива.
Для данного запроса нам это не нужно (см. поле 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 — реальные биржевые сделки
Все не так просто (план и факт)
Как я понял, в 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('--------------')