Перейти к содержанию

Примеры

Больше примеров доступно здесь.

Получение и вывод в консоль свечей с часовым интервалом за год

examples/all_candles.py

import os
from datetime import timedelta

from tinkoff.invest import CandleInterval, Client
from tinkoff.invest.schemas import CandleSource
from tinkoff.invest.utils import now

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        for candle in client.get_all_candles(
            instrument_id="BBG004730N88",
            from_=now() - timedelta(days=365),
            interval=CandleInterval.CANDLE_INTERVAL_HOUR,
            candle_source_type=CandleSource.CANDLE_SOURCE_UNSPECIFIED,
        ):
            print(candle)

    return 0


if __name__ == "__main__":
    main()

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

examples/async_all_candles.py

import asyncio
import os
from datetime import timedelta

from tinkoff.invest import AsyncClient, CandleInterval
from tinkoff.invest.schemas import CandleSource
from tinkoff.invest.utils import now

TOKEN = os.environ["INVEST_TOKEN"]


async def main():
    async with AsyncClient(TOKEN) as client:
        async for candle in client.get_all_candles(
            instrument_id="BBG004730N88",
            from_=now() - timedelta(days=365),
            interval=CandleInterval.CANDLE_INTERVAL_HOUR,
            candle_source_type=CandleSource.CANDLE_SOURCE_EXCHANGE,
        ):
            print(candle)


if __name__ == "__main__":
    asyncio.run(main())

Асинхронная функция получения и вывода счетов пользователя

examples/async_client.py

import asyncio
import os

from tinkoff.invest import AsyncClient

TOKEN = os.environ["INVEST_TOKEN"]


async def main():
    async with AsyncClient(TOKEN) as client:
        print(await client.users.get_accounts())


if __name__ == "__main__":
    asyncio.run(main())

Асинхронная функция получения и вывода минутных свечей

examples/async_retrying_client.py

import asyncio
import logging
import os
from datetime import timedelta

from tinkoff.invest import CandleInterval
from tinkoff.invest.retrying.aio.client import AsyncRetryingClient
from tinkoff.invest.retrying.settings import RetryClientSettings
from tinkoff.invest.utils import now

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)

TOKEN = os.environ["INVEST_TOKEN"]

retry_settings = RetryClientSettings(use_retry=True, max_retry_attempt=2)


async def main():
    async with AsyncRetryingClient(TOKEN, settings=retry_settings) as client:
        async for candle in client.get_all_candles(
            figi="BBG000B9XRY4",
            from_=now() - timedelta(days=301),
            interval=CandleInterval.CANDLE_INTERVAL_1_MIN,
        ):
            print(candle)


if __name__ == "__main__":
    asyncio.run(main())

Подписка на стрим котировок по минутным свечам и вывод получаемых свечей в консоль

examples/async_stream_client.py

import asyncio
import os

from tinkoff.invest import (
    AsyncClient,
    CandleInstrument,
    MarketDataRequest,
    SubscribeCandlesRequest,
    SubscriptionAction,
    SubscriptionInterval,
)

TOKEN = os.environ["INVEST_TOKEN"]


async def main():
    async def request_iterator():
        yield MarketDataRequest(
            subscribe_candles_request=SubscribeCandlesRequest(
                subscription_action=SubscriptionAction.SUBSCRIPTION_ACTION_SUBSCRIBE,
                instruments=[
                    CandleInstrument(
                        figi="BBG004730N88",
                        interval=SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,
                    )
                ],
            )
        )
        while True:
            await asyncio.sleep(1)

    async with AsyncClient(TOKEN) as client:
        async for marketdata in client.market_data_stream.market_data_stream(
            request_iterator()
        ):
            print(marketdata)


if __name__ == "__main__":
    asyncio.run(main())

Отмена всех выставленных поручений

examples/cancel_orders.py

import logging
import os

from tinkoff.invest import Client

TOKEN = os.environ["INVEST_TOKEN"]

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


def main():
    with Client(TOKEN) as client:
        response = client.users.get_accounts()
        account, *_ = response.accounts
        account_id = account.id
        logger.info("Orders: %s", client.orders.get_orders(account_id=account_id))
        client.cancel_all_orders(account_id=account.id)
        logger.info("Orders: %s", client.orders.get_orders(account_id=account_id))


if __name__ == "__main__":
    main()

Функция получения и вывода счетов клиента

examples/client.py

import os

from tinkoff.invest import Client

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        print(client.users.get_accounts())


if __name__ == "__main__":
    main()

Загрузка и вывод всех минутных свечей по интрументу

examples/download_all_candles.py

import logging
import os
from datetime import timedelta
from pathlib import Path

from tinkoff.invest import CandleInterval, Client
from tinkoff.invest.caching.market_data_cache.cache import MarketDataCache
from tinkoff.invest.caching.market_data_cache.cache_settings import (
    MarketDataCacheSettings,
)
from tinkoff.invest.utils import now

TOKEN = os.environ["INVEST_TOKEN"]
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.DEBUG)


def main():
    with Client(TOKEN) as client:
        settings = MarketDataCacheSettings(base_cache_dir=Path("market_data_cache"))
        market_data_cache = MarketDataCache(settings=settings, services=client)
        for candle in market_data_cache.get_all_candles(
            figi="BBG004730N88",
            from_=now() - timedelta(days=1),
            interval=CandleInterval.CANDLE_INTERVAL_HOUR,
        ):
            print(candle.time, candle.is_complete)

    return 0


if __name__ == "__main__":
    main()

Асинхронная подписка на стрим минутных свечей

examples/easy_async_stream_client.py

import asyncio
import os

from tinkoff.invest import (
    AsyncClient,
    CandleInstrument,
    InfoInstrument,
    MarketDataResponse,
    SubscriptionInterval,
    TradeInstrument,
)
from tinkoff.invest.async_services import AsyncMarketDataStreamManager

TOKEN = os.environ["INVEST_TOKEN"]


async def main():
    async with AsyncClient(TOKEN) as client:
        market_data_stream: AsyncMarketDataStreamManager = (
            client.create_market_data_stream()
        )
        market_data_stream.candles.waiting_close().subscribe(
            [
                CandleInstrument(
                    figi="BBG004730N88",
                    interval=SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,
                )
            ]
        )
        market_data_stream.trades.subscribe(
            [
                TradeInstrument(
                    figi="BBG004730N88",
                )
            ]
        )
        async for marketdata in market_data_stream:
            marketdata: MarketDataResponse = marketdata
            print(marketdata)
            market_data_stream.info.subscribe([InfoInstrument(figi="BBG004730N88")])
            if marketdata.subscribe_info_response:
                market_data_stream.stop()


if __name__ == "__main__":
    asyncio.run(main())

Простая подписка на стрим минутных свечей

examples/easy_stream_client.py

import os

from tinkoff.invest import (
    CandleInstrument,
    Client,
    InfoInstrument,
    SubscriptionInterval,
)
from tinkoff.invest.services import MarketDataStreamManager

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        market_data_stream: MarketDataStreamManager = client.create_market_data_stream()
        market_data_stream.candles.waiting_close().subscribe(
            [
                CandleInstrument(
                    figi="BBG004730N88",
                    interval=SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,
                )
            ]
        )
        for marketdata in market_data_stream:
            print(marketdata)
            market_data_stream.info.subscribe([InfoInstrument(figi="BBG004730N88")])
            if marketdata.subscribe_info_response:
                market_data_stream.stop()


if __name__ == "__main__":
    main()

Получение списка операций и их постраничный вывод

examples/get_operations_by_cursor.py

import os
from pprint import pprint

from tinkoff.invest import Client, GetOperationsByCursorRequest

token = os.environ["INVEST_TOKEN"]


with Client(token) as client:
    accounts = client.users.get_accounts()
    account_id = accounts.accounts[0].id

    def get_request(cursor=""):
        return GetOperationsByCursorRequest(
            account_id=account_id,
            instrument_id="BBG004730N88",
            cursor=cursor,
            limit=1,
        )

    operations = client.operations.get_operations_by_cursor(get_request())
    print(operations)
    depth = 10
    while operations.has_next and depth > 0:
        request = get_request(cursor=operations.next_cursor)
        operations = client.operations.get_operations_by_cursor(request)
        pprint(operations)
        depth -= 1

Функция кэширования инструментов

examples/instrument_cache.py

import logging
import os
from pprint import pprint

from tinkoff.invest import Client, InstrumentIdType
from tinkoff.invest.caching.instruments_cache.instruments_cache import InstrumentsCache
from tinkoff.invest.caching.instruments_cache.settings import InstrumentsCacheSettings

TOKEN = os.environ["INVEST_TOKEN"]


logging.basicConfig(level=logging.INFO)


def main():
    with Client(TOKEN) as client:
        inst = client.instruments.etfs().instruments[-1]
        pprint(inst)

        from_server = client.instruments.etf_by(
            id_type=InstrumentIdType.INSTRUMENT_ID_TYPE_UID,
            class_code=inst.class_code,
            id=inst.uid,
        )
        pprint(from_server)

        settings = InstrumentsCacheSettings()
        instruments_cache = InstrumentsCache(
            settings=settings, instruments_service=client.instruments
        )

        from_cache = instruments_cache.etf_by(
            id_type=InstrumentIdType.INSTRUMENT_ID_TYPE_UID,
            class_code=inst.class_code,
            id=inst.uid,
        )
        pprint(from_cache)

        if str(from_server) != str(from_cache):
            raise Exception("cache miss")


if __name__ == "__main__":
    main()

Функция получения списка инструментов подходящих под строку query

examples/instruments.py

import os

from tinkoff.invest import Client

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        r = client.instruments.find_instrument(query="BBG001M2SC01")
        for i in r.instruments:
            print(i)


if __name__ == "__main__":
    main()

Функция логгирования ошибок

examples/logger.py

import logging
import os

from tinkoff.invest import Client, RequestError

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)


def main():
    with Client(TOKEN) as client:
        _ = client.users.get_accounts().accounts
        try:
            client.users.get_margin_attributes(account_id="123")
        except RequestError as err:
            tracking_id = err.metadata.tracking_id if err.metadata else ""
            logger.error("Error tracking_id=%s code=%s", tracking_id, str(err.code))


if __name__ == "__main__":
    main()

Подписка на стрим портфолио и вывод информации

examples/porfolio_stream_client.py

import os

from tinkoff.invest import Client

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        accounts = client.users.get_accounts()
        for portfolio in client.operations_stream.portfolio_stream(
            accounts=[acc.id for acc in accounts.accounts], ping_delay_ms=60_000
        ):
            print(portfolio)


if __name__ == "__main__":
    main()

Подписка на стрим позиций и вывод информации

examples/positions_stream.py

import os

from tinkoff.invest import Client

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with Client(TOKEN) as client:
        response = client.users.get_accounts()
        accounts = [account.id for account in response.accounts]
        for response in client.operations_stream.positions_stream(accounts=accounts):
            print(response)


if __name__ == "__main__":
    main()

Функция получения и вывода минутных свечей

examples/retrying_client.py

import logging
import os
from datetime import timedelta

from tinkoff.invest import CandleInterval
from tinkoff.invest.retrying.settings import RetryClientSettings
from tinkoff.invest.retrying.sync.client import RetryingClient
from tinkoff.invest.utils import now

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)

TOKEN = os.environ["INVEST_TOKEN"]

retry_settings = RetryClientSettings(use_retry=True, max_retry_attempt=2)

with RetryingClient(TOKEN, settings=retry_settings) as client:
    for candle in client.get_all_candles(
        figi="BBG000B9XRY4",
        from_=now() - timedelta(days=301),
        interval=CandleInterval.CANDLE_INTERVAL_1_MIN,
    ):
        print(candle)

Получение и вывод информации об аккаунте пользователя в песочнице

examples/sandbox_client.py

import os

from tinkoff.invest.sandbox.client import SandboxClient

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    with SandboxClient(TOKEN) as client:
        print(client.users.get_info())


if __name__ == "__main__":
    main()

Подписка на стрим минутных свечей и их вывод

examples/stream_client.py

import os
import time

from tinkoff.invest import (
    CandleInstrument,
    Client,
    MarketDataRequest,
    SubscribeCandlesRequest,
    SubscriptionAction,
    SubscriptionInterval,
)
from tinkoff.invest.schemas import CandleSource

TOKEN = os.environ["INVEST_TOKEN"]


def main():
    def request_iterator():
        yield MarketDataRequest(
            subscribe_candles_request=SubscribeCandlesRequest(
                waiting_close=True,
                subscription_action=SubscriptionAction.SUBSCRIPTION_ACTION_SUBSCRIBE,
                candle_source_type=CandleSource.CANDLE_SOURCE_EXCHANGE,
                instruments=[
                    CandleInstrument(
                        figi="BBG004730N88",
                        interval=SubscriptionInterval.SUBSCRIPTION_INTERVAL_ONE_MINUTE,
                    )
                ],
            )
        )
        while True:
            time.sleep(1)

    with Client(TOKEN) as client:
        for marketdata in client.market_data_stream.market_data_stream(
            request_iterator()
        ):
            print(marketdata)


if __name__ == "__main__":
    main()

Создание тэйк-профит стоп ордера

examples/wiseplat_create_take_profit_stop_order.py

"""Example - How to create takeprofit buy order."""
import logging
import os
from decimal import Decimal

from tinkoff.invest import (
    Client,
    InstrumentIdType,
    StopOrderDirection,
    StopOrderExpirationType,
    StopOrderType,
)
from tinkoff.invest.exceptions import InvestError
from tinkoff.invest.utils import decimal_to_quotation, quotation_to_decimal

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)


def main():
    """Example - How to create takeprofit buy order."""
    with Client(TOKEN) as client:
        response = client.users.get_accounts()
        account, *_ = response.accounts
        account_id = account.id
        logger.info("Orders: %s", client.orders.get_orders(account_id=account_id))

        figi = "BBG004730ZJ9"  # BBG004730ZJ9 - VTBR / BBG004730N88 - SBER

        # getting the last price for instrument
        last_price = (
            client.market_data.get_last_prices(figi=[figi]).last_prices[0].price
        )
        last_price = quotation_to_decimal(last_price)
        print(f"figi, last price = {last_price}")

        # setting the percentage by which the takeprofit stop order
        # should be set below the last price
        percent_down = 5

        # calculation of the price for takeprofit stop order
        calculated_price = last_price - last_price * Decimal(percent_down / 100)
        print(f"calculated_price = {calculated_price}")

        # getting the min price increment and the number of digits after point
        min_price_increment = client.instruments.get_instrument_by(
            id_type=InstrumentIdType.INSTRUMENT_ID_TYPE_FIGI, id=figi
        ).instrument.min_price_increment
        number_digits_after_point = 9 - len(str(min_price_increment.nano)) + 1
        min_price_increment = quotation_to_decimal(min_price_increment)
        print(
            f"min_price_increment = {min_price_increment}, "
            f"number_digits_after_point={number_digits_after_point}"
        )

        # calculation of the price for instrument which is
        # divisible to min price increment
        calculated_price = (
            round(calculated_price / min_price_increment) * min_price_increment
        )
        print(
            f"let's send stop order at price = "
            f"{calculated_price:.{number_digits_after_point}f} divisible to "
            f"min price increment {min_price_increment}"
        )

        # creating takeprofit buy order
        stop_order_type = StopOrderType.STOP_ORDER_TYPE_TAKE_PROFIT
        direction = StopOrderDirection.STOP_ORDER_DIRECTION_BUY
        exp_type = StopOrderExpirationType.STOP_ORDER_EXPIRATION_TYPE_GOOD_TILL_CANCEL
        try:
            response = client.stop_orders.post_stop_order(
                figi=figi,
                quantity=1,
                price=decimal_to_quotation(Decimal(calculated_price)),
                stop_price=decimal_to_quotation(Decimal(calculated_price)),
                direction=direction,
                account_id=account_id,
                expiration_type=exp_type,
                stop_order_type=stop_order_type,
                expire_date=None,
            )
            print(response)
            print("stop_order_id=", response.stop_order_id)
        except InvestError as error:
            logger.error("Posting trade takeprofit order failed. Exception: %s", error)


if __name__ == "__main__":
    main()

Отмена всех выставленных стоп ордеров

examples/wiseplat_cancel_all_stop_orders.py

"""Example - How to cancel all stop orders."""
import logging
import os

from tinkoff.invest import Client
from tinkoff.invest.exceptions import InvestError

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)


def main():
    """Example - How to cancel all stop orders."""
    with Client(TOKEN) as client:
        response = client.users.get_accounts()
        account, *_ = response.accounts
        account_id = account.id

        try:
            stop_orders_response = client.stop_orders.get_stop_orders(
                account_id=account_id
            )
            logger.info("Stop Orders: %s", stop_orders_response)
            for stop_order in stop_orders_response.stop_orders:
                client.stop_orders.cancel_stop_order(
                    account_id=account_id, stop_order_id=stop_order.stop_order_id
                )
                logger.info("Stop Order: %s was canceled.", stop_order.stop_order_id)
            logger.info(
                "Orders: %s", client.stop_orders.get_stop_orders(account_id=account_id)
            )
        except InvestError as error:
            logger.error("Failed to cancel all orders. Error: %s", error)


if __name__ == "__main__":
    main()

Получение figi для тикера

examples/wiseplat_get_figi_for_ticker.py

"""Example - How to get figi by name of ticker."""
import logging
import os

from pandas import DataFrame

from tinkoff.invest import Client, SecurityTradingStatus
from tinkoff.invest.services import InstrumentsService
from tinkoff.invest.utils import quotation_to_decimal

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)


def main():
    """Example - How to get figi by name of ticker."""

    ticker = "VTBR"  # "BRH3" "SBER" "VTBR"

    with Client(TOKEN) as client:
        instruments: InstrumentsService = client.instruments
        tickers = []
        for method in ["shares", "bonds", "etfs", "currencies", "futures"]:
            for item in getattr(instruments, method)().instruments:
                tickers.append(
                    {
                        "name": item.name,
                        "ticker": item.ticker,
                        "class_code": item.class_code,
                        "figi": item.figi,
                        "uid": item.uid,
                        "type": method,
                        "min_price_increment": quotation_to_decimal(
                            item.min_price_increment
                        ),
                        "scale": 9 - len(str(item.min_price_increment.nano)) + 1,
                        "lot": item.lot,
                        "trading_status": str(
                            SecurityTradingStatus(item.trading_status).name
                        ),
                        "api_trade_available_flag": item.api_trade_available_flag,
                        "currency": item.currency,
                        "exchange": item.exchange,
                        "buy_available_flag": item.buy_available_flag,
                        "sell_available_flag": item.sell_available_flag,
                        "short_enabled_flag": item.short_enabled_flag,
                        "klong": quotation_to_decimal(item.klong),
                        "kshort": quotation_to_decimal(item.kshort),
                    }
                )

        tickers_df = DataFrame(tickers)

        ticker_df = tickers_df[tickers_df["ticker"] == ticker]
        if ticker_df.empty:
            logger.error("There is no such ticker: %s", ticker)
            return

        figi = ticker_df["figi"].iloc[0]
        print(f"\nTicker {ticker} have figi={figi}\n")
        print(f"Additional info for this {ticker} ticker:")
        print(ticker_df.iloc[0])


if __name__ == "__main__":
    main()

Получение / установка баланса для песочницы. Получение / закрытие всех песочниц. Создание новой песочницы.

examples/wiseplat_set_get_sandbox_balance.py

""" Example - How to set/get balance for sandbox account.
    How to get/close all sandbox accounts.
    How to open new sandbox account. """

import logging
import os
from datetime import datetime
from decimal import Decimal

from tinkoff.invest import MoneyValue
from tinkoff.invest.sandbox.client import SandboxClient
from tinkoff.invest.utils import decimal_to_quotation, quotation_to_decimal

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)


def add_money_sandbox(client, account_id, money, currency="rub"):
    """Function to add money to sandbox account."""
    money = decimal_to_quotation(Decimal(money))
    return client.sandbox.sandbox_pay_in(
        account_id=account_id,
        amount=MoneyValue(units=money.units, nano=money.nano, currency=currency),
    )


def main():
    """Example - How to set/get balance for sandbox account.
    How to get/close all sandbox accounts.
    How to open new sandbox account."""
    with SandboxClient(TOKEN) as client:
        # get all sandbox accounts
        sandbox_accounts = client.users.get_accounts()
        print(sandbox_accounts)

        # close all sandbox accounts
        for sandbox_account in sandbox_accounts.accounts:
            client.sandbox.close_sandbox_account(account_id=sandbox_account.id)

        # open new sandbox account
        sandbox_account = client.sandbox.open_sandbox_account()
        print(sandbox_account.account_id)

        account_id = sandbox_account.account_id

        # add initial 2 000 000 to sandbox account
        print(add_money_sandbox(client=client, account_id=account_id, money=2000000))
        logger.info(
            "positions: %s", client.operations.get_positions(account_id=account_id)
        )
        print(
            "money: ",
            float(
                quotation_to_decimal(
                    client.operations.get_positions(account_id=account_id).money[0]
                )
            ),
        )

        logger.info("orders: %s", client.orders.get_orders(account_id=account_id))
        logger.info(
            "positions: %s", client.operations.get_positions(account_id=account_id)
        )
        logger.info(
            "portfolio: %s", client.operations.get_portfolio(account_id=account_id)
        )
        logger.info(
            "operations: %s",
            client.operations.get_operations(
                account_id=account_id,
                from_=datetime(2023, 1, 1),
                to=datetime(2023, 2, 5),
            ),
        )
        logger.info(
            "withdraw_limits: %s",
            client.operations.get_withdraw_limits(account_id=account_id),
        )

        # add + 2 000 000 to sandbox account, total is 4 000 000
        print(add_money_sandbox(client=client, account_id=account_id, money=2000000))
        logger.info(
            "positions: %s", client.operations.get_positions(account_id=account_id)
        )

        # close new sandbox account
        sandbox_account = client.sandbox.close_sandbox_account(
            account_id=sandbox_account.account_id
        )
        print(sandbox_account)


if __name__ == "__main__":
    main()

Пример live стратегии для нескольких тикеров. Вывод OHLCV для каждой сформировавшейся свечи.

examples/wiseplat_live_strategy_print_ohlcv.py

"""
    This code is an example of applying Trading Strategy for several Tickers.
    The strategy in this code is for demonstration only purposes
    - it outputs OHLCV values.
    Author: Oleg Shpagin, my github: https://github.com/WISEPLAT
"""

import asyncio
import logging
import os
from datetime import timedelta
from typing import List, Optional

from tinkoff.invest import AioRequestError, AsyncClient, CandleInterval, HistoricCandle
from tinkoff.invest.async_services import AsyncServices
from tinkoff.invest.utils import now

TOKEN = os.environ["INVEST_TOKEN"]

logging.basicConfig(format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)


class LogOnlyCandlesStrategy:
    """This class is responsible for a strategy. You can put here
    your methods for your strategy."""

    def __init__(
        self,
        figi: str,
        timeframe: CandleInterval,
        days_back: int,
        check_interval: int,
        client: Optional[AsyncServices],
    ):
        self.account_id = None
        self.figi = figi
        self.timeframe = timeframe
        self.days_back = days_back
        self.check_interval = check_interval
        self.client = client
        self.candles: List[HistoricCandle] = []

    async def get_historical_data(self):
        """
        Gets historical data for the instrument. Returns list of candles.
        Requests all the candles of timeframe from days_back to now.

        :return: list of HistoricCandle
        """
        logger.debug(
            "Start getting historical data for %s days back from now. figi=%s",
            self.days_back,
            self.figi,
        )
        async for candle in self.client.get_all_candles(
            figi=self.figi,
            from_=now() - timedelta(days=self.days_back),
            to=now(),
            interval=self.timeframe,
        ):
            if candle not in self.candles:
                if candle.is_complete:
                    self.candles.append(candle)
                    logger.debug("Found %s - figi=%s", candle, self.figi)

    async def ensure_market_open(self):
        """
        Ensure that the market is open. Loop until the instrument is available.
        :return: when instrument is available for trading
        """
        trading_status = await self.client.market_data.get_trading_status(
            figi=self.figi
        )
        while not (
            trading_status.market_order_available_flag
            and trading_status.api_trade_available_flag
        ):
            logger.debug("Waiting for the market to open. figi=%s", self.figi)
            await asyncio.sleep(60)
            trading_status = await self.client.market_data.get_trading_status(
                figi=self.figi
            )

    async def main_cycle(self):
        """Main cycle for live strategy."""
        while True:
            try:
                await self.ensure_market_open()
                await self.get_historical_data()

                # put your strategy code here for live
                # to generate signals for buying or selling tickers
                logger.debug(
                    "- live mode: run some strategy code to buy or sell - figi=%s",
                    self.figi,
                )

            except AioRequestError as are:
                logger.error("Client error %s", are)

            await asyncio.sleep(self.check_interval)

    async def start(self):
        """Strategy starts from this function."""
        if self.account_id is None:
            try:
                self.account_id = (
                    (await self.client.users.get_accounts()).accounts.pop().id
                )
            except AioRequestError as are:
                logger.error("Error taking account id. Stopping strategy. %s", are)
                return
        await self.main_cycle()


async def run_strategy(portfolio, timeframe, days_back, check_interval):
    """From this function we are starting
    strategy for every ticker from portfolio.
    """
    async with AsyncClient(token=TOKEN, app_name="TinkoffApp") as client:
        strategy_tasks = []
        for instrument in portfolio:
            strategy = LogOnlyCandlesStrategy(
                figi=instrument,
                timeframe=timeframe,
                days_back=days_back,
                check_interval=check_interval,
                client=client,
            )
            strategy_tasks.append(asyncio.create_task(strategy.start()))
        await asyncio.gather(*strategy_tasks)


if __name__ == "__main__":
    sber_figi = "BBG004730N88"
    vtbr_figi = "BBG004730ZJ9"
    portfolio = {sber_figi, vtbr_figi}
    timeframe = CandleInterval.CANDLE_INTERVAL_1_MIN
    days_back = 1
    check_interval = 10  # seconds to check interval for new completed candle

    loop = asyncio.get_event_loop()
    task = loop.create_task(
        run_strategy(
            portfolio=portfolio,
            timeframe=timeframe,
            days_back=days_back,
            check_interval=check_interval,
        )
    )
    loop.run_until_complete(task)