Package: tinkoff-api
tinkoff-apiPython Tinkoff API client for asyncio and humans.Table of contensCovered APIsFeaturesInstallationUsage examplesREST API clientStreaming clientDynamic subscriptions in runtimeComplete simple botHistorical dataTODOCovered APIsTinkoff Investments (official docs)FeaturesClients for bothRESTandStreamingprotocols in Tinkoff InvestmentsPresence of data classes for all interaction with APIAutomatic reconnection and keep-alive connectionsInternal exclusive rate limiter for every resource in REST protocolFriendly exceptions for API errorsInstallationUse pip to install:$pipinstalltinkoff-apiUsage examplesREST API client:importasynciofromdatetimeimportdatetimefromtinkoff.investmentsimport(TinkoffInvestmentsRESTClient,Environment,CandleResolution,)fromtinkoff.investments.client.exceptionsimportTinkoffInvestmentsErrorasyncdefshow_apple_year_candles():try:asyncwithTinkoffInvestmentsRESTClient(token="TOKEN",environment=Environment.SANDBOX)asclient:candles=awaitclient.market.candles.get(figi="BBG000B9XRY4",dt_from=datetime(2019,1,1),dt_to=datetime(2019,12,31),interval=CandleResolution.DAY,)forcandleincandles:print(f"{candle.time}:{candle.h}")exceptTinkoffInvestmentsErrorase:print(e)asyncdefjackpot():try:asyncwithTinkoffInvestmentsRESTClient(token="TOKEN",environment=Environment.SANDBOX)asclient:instruments=awaitclient.market.instruments.search("AAPL")apple=instruments[0]account=awaitclient.sandbox.accounts.register()awaitclient.sandbox.accounts.positions.set_balance(figi=apple.figi,balance=100,broker_account_id=account.brokerAccountId,)print("We created the following portfolio:")positions=awaitclient.portfolio.get_positions()forpositioninpositions:print(f"{position.name}:{position.lots}lots")exceptTinkoffInvestmentsErrorase:print(e)asyncio.run(jackpot())Streaming Client:importasynciofromdatetimeimportdatetimefromtinkoff.investmentsimport(CandleEvent,CandleResolution,TinkoffInvestmentsStreamingClient,)client=TinkoffInvestmentsStreamingClient(token="TOKEN")@client.events.candles("BBG009S39JX6",CandleResolution.MIN_1)@client.events.candles("BBG000B9XRY4",CandleResolution.MIN_1)asyncdefon_candle(candle:CandleEvent,server_time:datetime):print(candle,server_time)asyncio.run(client.run())Dynamic subscriptions in runtime:importasynciofromdatetimeimportdatetimefromtinkoff.investmentsimport(CandleEvent,CandleResolution,TinkoffInvestmentsStreamingClient,)client=TinkoffInvestmentsStreamingClient(token="TOKEN")@client.events.candles("BBG000B9XRY4",CandleResolution.HOUR)asyncdefon_candle(candle:CandleEvent,server_time:datetime):ifcandle.h>1000:awaitclient.events.candles.subscribe(callback=on_candle,figi=candle.figi,interval=CandleResolution.MIN_1,)elifcandle.h<1000:awaitclient.events.candles.unsubscribe(candle.figi,CandleResolution.MIN_1)asyncio.run(client.run())Complete simple bot:importasynciofromdatetimeimportdatetimefromtinkoff.investmentsimport(CandleEvent,CandleResolution,OperationType,TinkoffInvestmentsRESTClient,TinkoffInvestmentsStreamingClient,)streaming=TinkoffInvestmentsStreamingClient("TOKEN")rest=TinkoffInvestmentsRESTClient("TOKEN")@streaming.events.candles("BBG000B9XRY4",CandleResolution.MIN_1)asyncdefbuy_apple(candle:CandleEvent,server_time:datetime):ifcandle.c>350:awaitrest.orders.create_market_order(figi="BBG000B9XRY4",lots=1,operation=OperationType.BUY,broker_account_id=123,)asyncio.run(streaming.run())Historical data:importasynciofromdatetimeimportdatetimefromtinkoff.investmentsimport(CandleResolution,Environment,TinkoffInvestmentsRESTClient,)fromtinkoff.investments.utils.historical_dataimportHistoricalDataasyncdefget_minute_candles():# show 1 minute candles for AAPL in 1 year period of timeasyncwithTinkoffInvestmentsRESTClient(token="TOKEN",environment=Environment.SANDBOX)asclient:historical_data=HistoricalData(client)asyncforcandleinhistorical_data.iter_candles(figi="BBG000B9XRY4",dt_from=datetime(2019,1,1),dt_to=datetime(2020,1,1),interval=CandleResolution.MIN_1,):print(candle)asyncio.run(get_minute_candles())TODOallow to provide str constants along with specific enum objectsadd ability to unsubscribe by patternrename some fieldsmake some fields in snake casegenerate documentation