Tinkov API. Investment. First steps

Almost from the first days I became a client of Tinkov. Investments.

And from that moment on, vague doubts torment me - does the personal account reflect objective reality?

The fact is that I buy securities denominated in dollars, but in LC the prices of all assets are displayed in dollars, and the total value of the portfolio in rubles.

And I don’t understand if the dollar has grown or am I such a successful investor?

But what about commissions, taxes and other dividends?

I’d like to take all my transactions and write them in FIFO, as in stock accounting ... And put the dividends received on top, and then deduct taxes.

And then I’ll see the result that I understand.

It turned out that Tinkov has an API that allows you to write trading robots (I’m not at all interested in this), as well as upload data on his portfolio and operations.

This API has an official description, but not everything was clear to me, I had to understand it.
The results of these showdowns present to your attention.

Useful links:

API
description Another description

Getting a token and installing a library


Before you begin, you need to install the library and get a token.

Library Installation:

pip install -i https://test.pypi.org/simple/ --extra-index-url=https://pypi.org/simple/ tinkoff-invest-openapi-client

I quote the official token receiving instruction:

  1. Log in to your account at tinkoff.ru
  2. Go to the investment section
  3. Go to settings
  4. The function "Confirmation of transactions by code" must be disabled
  5. Issue the OpenApi token for the exchange and Sandbox. Perhaps the system will ask you to log in again, do not worry, this is necessary to connect the robot to the trading platform.
  6. Copy the token and save, the token is displayed only once, you won’t be able to view it later, however, you can issue an unlimited number of tokens.

At the time of writing, the token was issued on the page www.tinkoff.ru/invest/settings , a button at the bottom of the page.

image

I had errors with the token for the sandbox, so I started experimenting with the combat version. What I wish you (Caution: do not buy, sell by accident something extra).

Login


from openapi_client import openapi

token = '    '  
client = openapi.api_client(token)

These two lines do everything we need.

Next we work with the client variable.

What's in our portfolio


Get the contents of our portfolio:

pf = client.portfolio.portfolio_get()

Let's see the basic data of the first element:

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)

In my case, this is:

value: 45.98
currency: USD
balance: 21.0
figi: BBG000BWPXQ8
ticker: BTI
name: British American Tobacco

value - Paper price
balance - The number of securities in the portfolio, value and currency - their monetary value.

figi - the Financial Instrument of Global the Identifier (Global Financial Instrument Identifier)
ticker - Ticker asset.

From this data, we can find out the human-readable name of the asset.

For this request, we do not need this (see the name field), but in other cases it will come in handy.

Get the name of the paper by FIGI and ticket


#    FIGI
instr = client.market.market_search_by_figi_get('BBG000BWPXQ8') 
instr

We get:

{'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'}

This API function works for me as it should. We see that 'BBG000BWPXQ8' -> 'British American Tobacco'.

But the search for the asset name by ticker does not work for me: (((

instr = client.market.market_search_by_ticker_get('BTI' ) 
print(instr)

The developers suggested updating the library, but even after that it didn’t take off.

Download the securities directory


However, I resolved this issue radically. I downloaded from Tinkov a complete directory of traded assets:

#   
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]
got
[{'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'}]

As you can see, figi and name are there. For my purposes - more than enough.

Get a list of operations


But the most interesting thing is to get a list of my operations. The following actions fall into the operation (in my case):

  • PayIn - Brokerage account replenishment
  • PayOut - Withdraw Money
  • BuyCard - Buy from a card
  • Sell ​​- Sale
  • BrokerCommission - Broker Commission
  • Dividend - Dividend Payout
  • Tax - Taxes
  • TaxDividend- Dividend Taxes
  • ServiceCommission - Service Fee

Code for unloading a portfolio:

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())
Let's see what happened. In my case, this element is of interest
ops.payload.operations[217]
That's what he is
{'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'}]}

We are interested in the fields:

  • date - transaction date
  • figi - asset code
  • operation_type - type of operation
  • payment - the amount of the transaction. For taxes or commissions, it is she who is indicated. price while None
  • price - price of one paper
  • quantity - planned amount of securities
  • trades - real exchange deals

The question immediately arose - why do we need some trades if there is price and quantity?

Everything is not so simple (plan and fact)


As I understand it, quantity indicates the number of papers I wanted to buy. And what is actually bought lies in trades [i] .quantity.

Those. if you want to turn to actual transactions, you need to sort out what lies in the trades.

In some cases, there is None - for example, for taxes or deposit / withdrawals of funds.

To get real numbers, you need to look at transactions and exchange transactions:

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('--------------')

All Articles