Если вы пришли с tinkoff-investments
Справка для тех, у кого бот уже написан на SDK tinkoff-investments (gRPC API Т-Банка, T-Invest API) и кто переносит его на brokapi. Цель: показать, что в каком
месте меняется, чтобы перенос не превратился в двойную заявку. Левая колонка описывает API Т-Банка, на котором стоит SDK, названия методов сверяйте с версией своего SDK.
Идея одна: brokapi берет на себя то, что в SDK делал ваш код (сверка, лимиты, перевод единиц), и взамен требует порядка, который в SDK был по желанию. Поэтому перенос - это не замена вызовов один к одному.
Таблица соответствий
| Тема | В SDK | В brokapi |
|---|---|---|
| Количество в заявке | лоты: quantity=2 значит два лота | штуки: qty=params.lot_size * 2. Не кратное лоту, дробное, нулевое - Rejected с кодом invalid_qty до отправки |
| Как назвать инструмент | figi (устаревает) или instrument_id (uid), строкой | await client.instrument("SBER") возвращает InstrumentId (ключ SBER@TQBR и uid внутри). Везде передается он, а не строка. У Т-Банка instrument делает один запрос к справочнику |
| Номер заявки | order_id: строка, ключ идемпотентности, вы его придумываете | cid: brokapi.new_cid(), UUID, в журнал на диск до place. Повтор того же cid библиотека не отправит: Rejected с кодом duplicate_place |
| Потоки событий | отдельный поток на рыночные данные, на сделки, на статусы заявок, на портфель, каждый со своей подпиской | один поток client.events: async for envelope in client.events. В конверте source и типизированный payload (стакан, OrderSnapshot, Execution, Reconnected…). Подписки - subscribe_* |
| Сверка после старта и обрыва | своя, как вы напишете | обязательный reconcile(журнал) до первой торговой команды, иначе NotReconciled. После обрыва потока своих заявок (Reconnected), Overflow и ошибки Degraded торговля закрыта до новой сверки. Подписка на статусы - до reconcile |
| Цена и деньги | Quotation(units, nano) и MoneyValue(currency, units, nano), переводы своими функциями | Decimal везде. float отвергается (TypeError). Цена округляется до шага функцией round_to_step |
| Стоп-заявки | post_stop_order, get_stop_orders, cancel_stop_order, количество в лотах | place_stop(kind=, qty=, stop_price=, limit_price=, expiry=), active_stop_orders(), cancel_stop(...), количество в штуках. Событий о срабатывании нет: узнавать только опросом. После рестарта cid стопа Т-Банк не возвращает, снимать надо по broker_id (Стоп-заявки, Python) |
| Ошибки | исключение с кодом gRPC и номером Т-Банка, разбор на вас | иерархия по исходу: Rejected (точно не сделано, поле code), Unknown (мог сделать, не повторять, спросить order_state(cid)), RateLimited (подождать retry_after), Transport (сеть, для чтения можно повторить), Config (настройка). Автоповтора команд нет и не будет (Ошибки) |
| Лимиты запросов | считаете сами, ждете ответа об ошибке | один счетчик на пользователя Т-Банка (BrokerLimiter), команда без квоты не уходит: RateLimited. Несколько клиентов одного брокера делят счетчик через limiter= |
| Счет | account_id в каждом вызове | клиент привязан к одному счету (account_id= в connect). Несколько счетов - несколько клиентов с общим limiter= (Несколько брокеров и счетов) |
| Песочница | свой клиент или адрес песочницы, счет открываете OpenSandboxAccount | connect("tbank", sandbox=True), счет песочницы по-прежнему открываете сами (Т-Банк, где взять токен) |
| Длинная история свечей | постраничные запросы, лимит периода на вас | candle_pages и load_candles режут период на окна по лимитам брокера (История свечей). Один candles на длинный период - Rejected |
| Синхронный клиент | есть синхронный и асинхронный | только asyncio |
Что и как переносить
Два преобразования, без которых не обходится ни один перенос. Лоты в штуки: старый код ставил quantity=lots, новому нужны единицы инструмента.
Quotation в Decimal: пригодится для данных, которые вы хранили в файлах (в самом brokapi цены уже Decimal).
async def place_lots(
client: brokapi.Client, ticker: str, lots: int, price: Decimal, side: brokapi.Side = brokapi.Side.BUY
) -> uuid.UUID:
"""The order of the old code was `quantity=lots`. Here the quantity is in units of the instrument: lots times the lot size."""
instrument = await client.instrument(ticker) # an InstrumentId (the uid inside), not a figi string
params = await client.trading_params(instrument)
cid = brokapi.new_cid() # the old `order_id`: made by the bot, in its journal before the call
await client.place(cid=cid, instrument=instrument, side=side, qty=params.lot_size * lots, price=price)
return cid
def quotation_to_decimal(units: int, nano: int) -> Decimal:
"""`Quotation(units, nano)` and the `units` and `nano` of `MoneyValue` of the old code, for the data you keep in files.
Exact: no float on the way. Negative prices have both parts negative, as in the API of the broker."""
return Decimal(units) + Decimal(nano) / Decimal(1_000_000_000)
Порядок переноса, который не оставляет дыр:
- Подключите клиента к песочнице и прогоните старую логику через
connect("tbank", sandbox=True). Первая ступень вообще без токена: Быстрый старт. - Замените номер заявки:
order_id->cidиз журнала, записанного до вызова. Это главное отличие, на нем держится безопасность повторов. - Добавьте подписку на статусы и
reconcileв начале работы. Бот, у которого в журнале есть живыеcid, передает их вreconcile. - Разберите исходы
placeпо таблице из торгового бота:UnknownиTransportна команде не повторяются. - Если у бота есть стопы, добавьте проверку на старте: Бот со стопом: старт и рестарт.
- Перед боевым счетом - Перед боевым счетом и Аварийная остановка бота.
Если вы пришли с QUIK
brokapi не подключается к QUIK и не заменяет терминал: он говорит с API брокера напрямую (БКС, Т-Банк, Финам, АЛОР, Bybit), и ни терминал, ни запущенный на машине QUIK не нужны.
Скрипты QUIK (Lua, QUIK# и им подобные) живут внутри терминала и реагируют на его события (OnOrder, OnTrade, OnTransReply) и таблицы, заявка там - транзакция с номером
TRANS_ID. В brokapi то же самое делают поток client.events (статусы заявок и сделки), portfolio() с позициями и cid, который бот придумывает сам и пишет в журнал
до place. Переносится логика стратегии, а слой связи с терминалом заменяется целиком. Что изменится по порядку работы, описано в таблице выше: сверка до торговли, единицы в штуках,
Decimal вместо float.
Листать можно клавишами Влево и Вправо.