Введение в библиотеку tcsinvest

Tinkoff Invest API

Для доступа к инвестиционному счету в Тинькофф Инвестиции существует API с использованием которого, можно автоматизировать различные рутинные процессы. В Tinkoff API реализовано 2 основных способа работы:

На текущий момент для использования доступна самая первая версия API, которая не поддерживает:

Библиотека tcsinvest

tcsinvest - это неофициальная библиотека R для работы с API Тинькофф Инвестиции. Библиотека дает доступ к функциональности API привычными функциями. Библиотека использует в своей основе библиотеку data.table как один из наиболее производительных способов работы с большими объемами информации в R.

В случае, если вы уже имеете токен и знаете как с ним работать - можно пропустить следующую главу (Получение токена)

Получение токена

Токен — это специальный набор символов, представляющий собой зашифрованную информацию о владельце, уровне прав доступа и прочей необходимой для авторизации в Tinkoff OpenAPI информации (из официальной документации).

Если коротко: токен - это строчка, которая позволяет получить доступ к операциям со счета.

Существует 2 типа счетов:

Мы настоятельно рекомендуем все проверки с программным кодом делать в песочнице. Все функции реализованные в библиотеке tcsinvest по умолчанию подключаются к песочнице (требуется специальное указание, что вы подключаетесь к торговому счету).

Получить токен можно из личного кабинета во вкладке Настройки. В разделе “Token для Open API” имеются 2 кнопки: для песочницы и для живой торговли.

При нажатии одной из кнопок мы попадаем на страницу где будет указан Ваш токен. Сохраните этот токен. Как гласит официальная документация “Токен отображается только один раз, просмотреть его позже не получится. Тем не менее вы можете выпускать неограниченное количество токенов”. Ранее выпущенные токены можно отзывать, токены живут 3 месяца с момента их последнего использования.

P.S. важное замечание для торговли на живом счете: функция “Подтверждение сделок кодом” должна быть отключена.

В случае, если вы уже установили библиотеку tcsinvest - можно пропустить следующую часть.

Установка библиотеки tcsinvest

Для использования библиотеки в R, ее нужно сперва установить. Можно это сделать 2 способами:

В репозиторий CRAN обычно загружены наиболее стабильные версии библиотек, их легко установить, но достаточно часто они не учитывают последние изменения в коде, исправления возникающих ошибок и изменения в самом API. Хотя для большинства пользователей достаточно и рекомендуется использование библиотеки загруженной на CRAN. Для установки с CRAN достаточно найти библиотеку в списке общих пакетов или установить с использованием команды:

install.packages("tcsinvest")

В случае загрузки библиотеки с github необходима установленная библиотека devtools (в случае если ее нет - нужно установить).

devtools::install_github("arbuzovv/tcsinvest",build_vignettes = TRUE)

Иногда при таком методе возникает ошибка “Error in utils::download.file(url, path….” которая лечится следующей строчкой кода:

options(download.file.method = "libcurl")

Если в процессе установки возникает ошибка “Error in strptime(xx, f, tz = tz) : (converted from warning) unable to identify current timezone”, то ее можно устранить указанием временной зоны

Sys.timezone()
Sys.setenv(TZ='GMT')
Sys.timezone()

В случае успешной загрузки и компиляции библиотеки, вам станет доступна библиотека tcsinvest в списке доступных библиотек. Теперь подключим данную библиотеку к пространству имен:

library(tcsinvest)

Поздравляем! Вы стали на один шаг ближе к алготрейдингу. Теперь можно переходить к использованию функций библиотеки. В случае, если видите возникающие ошибки при подключения библиотеки - вернитесь на шаг назад и попробуйте установить библиотеку еще раз.

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

К текущему шагу библиотека установлена и мы получили токен. Запомним токен в нашем пространстве имен и помимо этого создадим объект live, который будет указывать на то, работаем ли мы в песочнице или торгуем на реальном счету. По умолчанию во всех функциях для параметра live установлено значение FALSE.

token = 'my_token'
live = FALSE

Попробуем вызвать список доступных акций:

getStocks(token,live)

Список доступных ETF:

getETFs(token,live)

Список доступных валют:

getCurrencies(token,live)

Список доступных облигаций:

getBonds(token,live)

При каждом вызове функции происходит 1 запрос. Необходимо помнить, что любое API имеет ограничение по списку запросов в промежуток времени. Для каждого запроса имеются свои ограничение пропускной способности.

Функции Количество запросов для пользователя Количество запросов на IP Интервал ограничения
sandbox^ 120 1000 1 минута
getPortfolio 120 1000 1 минута
market info* 240 500 1 минута
orders ^^ 120 1000 1 минута
getOperations 120 1000 1 минута

^sandbox - sandboxRegister, sandboxDeleteAccount, sandboxBalance, sandboxPositions, sandboxDeletePositions

*market information - getStocks,getETFs,getCurrencies,getBonds,getSymbolInfo,getTickerInfo,getQuotes, getHistoricalData,getOrderBook

^^orders - marketOrder,limitOrder,cancelOrder,getOrders

Крайне не рекомендуется пытаться проверять какое количество запросов на практике проходит до бана.

Иногда на практике необходимо выгрузить все доступные инструменты для торговли (в алготрейдинге это называют universe). Для этого была написана функция, которая позволяется выполнить все 4 запроса одной командой:

universe = getUniverse(token,live)

Каждый вызов данной функции будет генерировать 4 запроса к серверу, поэтому рекомендуем не злоупотреблять количеством вызовов этой функции и просто сохранить ее в какую-либо переменную.

Данные функции возвращают объект data.table со следующим списком полей:

Попробуем выбрать из списка информацию об акциях Сбербанка:

universe[ticker=='SBER']

Или достанем список доступных ОФЗ:

universe[substr(name,1,3)=="ОФЗ"]

Возможно, если вы работаете в RStudio, то более привычным будет искать данные и анализировать в окне просмотра View:

View(universe)

Иногда для того, чтобы не загружать всю информацию о всех инструментах - можно использовать специальные функции которые ищут по figi или по ticker информацию об инструменте:

getSymbolInfo(token,live,figi = 'BBG005HLTYH9')
getTickerInfo(token,live,'SBER')

Получение рыночной информации

Рыночная информация - это основной ингридиент, который нужен в любой алгоритмической стратегии. В текущей версии API доступны только 3 типа информации: котировки, исторические данные и книга заявок. В последующих версия (выяснено в issues основного репозиротия API) возможно добавление фундаментальной информации, рыночного сантимента, наличия и условий шортов, информации о дивидендах и т.д.

Котировки

Наиболее простой тип команды, который позволяет получить базовую информацию о текущем состоянии инструмента является команда getQuotes. С помощью этой информации мы получаем основную информацию о котировках инструмента:

getQuotes(token,live,figi = 'BBG005HLTYH9')

Данные функции возвращают объект data.table со следующим списком полей:

Исторические данные

Исторические данные - это наиболее используемый тип информации в любой алгоритмической системе. Для загрузки исторических данных по инструменту используется команда getHistoricalData, которая содержит в себе ряд важных параметров:

Очень важным момент является соотношение выгружаемой истории с тем, какие бары выгружаются. У Tinkoff API имеются собственные ограничение на это. Выяснить их предлагается только опытным путем.

getHistoricalData(token,live,figi = 'BBG005HLTYH9',from = Sys.Date() - 2,to = Sys.Date(),interval = "hour")

Книга заявок

Наиболее полная информация о спросе и предложении на рынке содержится в книге заявок. Операция getOrderBook позволяет загружать такого рода информацию. Параметр depth отвечает за то, какое количество уровней в книге заявок загружать. В качестве результата данной функции выдается информации: цена,объем (заявки на продажу кодируются отрицательным объемом). Хотя в документации и описано, что глубина должна быть от 1 до 20, но на данный момент Тинькофф не отслеживает информацию по данному параметру и позволяет выгружать гораздо больше количество доступных уровней книги заявок.

getOrderBook(token,live,figi = 'BBG005HLTYH9',depth = 10)

Взаимодействие со счетом

Предыдущая глава была посвящена взаимодействию с внешней информацией (или рыночной), но наиболее важным является взаимодействие с внутренней информацией или торговым счетом. В этой главе попробуем осветить вопросы связанные с этим взаимодействием.

Информация о счете

Как заявлено в официальной документации - на данный момент идет процесс разработки поддержки мультисчетов. Сейчас можно получить информацию о доступных на данный момент аккаунтах:

getAccounts(token,live)

Получить информацию о балансе аккаунта можно с помощью команды getBalance, которая выдает информацию о доступных средствах в 3 валютах (рубли, доллары и евро)

getBalance(token,live)

По каждому счету могут проходить достаточно большое количество различных (в том числе и неторговых операций). Ежемесячное снятие комиссии, комиссии за сделки, начисления дивидендов и т.д., все это достуно при помощи команды getOperations. Получить информацию о всех операциях совершенных на счету за последние 5 дней:

getOperations(token,live)

В случае необходимости более длительной истории - используйте параметры from и to.

Для того, чтобы получить информацию о текущем портфеле используется команда getPortfolio:

getPortfolio(token,live)

Заявки на покупку или продажу инструментов могут иметь 3 статуса: отменена, исполнена, активна. По умолчанию API (и функция getOrders) возвращает только активные заявки. В случае, если нам необходима история заявок необходимо указать only_live_orders = FALSE - вернется история за последние 5 дней. В случае необходимости более длительной истории - используйте параметры from и to.

getOrders(token,live,only_live_orders = F)

Для получения информации о сделках, используйте функцию getTrades. Контролировать глубину истории можно параметрами from и to. Параметр symbol_info = TRUE позволяет дополнительно подгрузить информацию об инструменте по которому была совершена сделка. По умолчанию выгружается информация за последние 5 дней. Получить информацию о сделках:

getTrades(token,live)

Работа с заявками

Работа с заявками является важнейшей частью любого алгоритма. Глобально можно выделить 2 типа заявок: рыночные и лимитные. В заявках указывается инструмент, объем (в лотах, а не в штуках!), направление и цена (только для лимитных заявок). Поставить рыночную заявку

marketOrder(token,live,figi='BBG005HLTYH9',direction='Buy',lots=1)

Поставить лимитную заявку

limitOrder(token,live,figi='BBG005HLTYH9',direction='Buy',lots=1,price=1)

Результатом выполнения каждой команды является ответ системы о том, была ли поставлена заявка. Рекомендуется в своих алгоритмах использовать проверку на успешность выставленной заявки. Иногда требуется отмена выставленной заявки. Для этого используется функция cancelOrder. Для того, чтобы понять какую заявку нужно отменить, необходимо указать ее orderId, который можно найти с помощью функции getOrders.

cancelOrder(token,live,'orderId')

Работа с потоковой информацией

Tinkoff API позволяет работать с потоковой информацией (через WebSocket). Особенностью работы с потокой информацией является ее асинхронность - информация с сервера приходит когда появились какие-то изменения. Очень важным моментом является факт того, что мы не знаем момента времени, когда придет следующее событие от сервера. Для взаимодействия с потоковой информацией необходимо писать обработчик событий. В API на данный момент поддерживаются 3 типа потоковых данных:

На потоковые данные можно как подписываться, так и отписываться от них.

Для работы с потоковыми данными необходимо создать объект “клиент”, который в последующем будет использован для создания подключения.

client = streamClient(token)

После создания клиента можно посмотреть статус этого клиента. Статус Pre-connecting означает, что подключение готово к использованию и можно подписываться на данные.

streamStatus(client)

Создадим подписку на данные об инструменте:

streamSubscribe(client,subscribe = TRUE,type='instrument_info',figi='BBG004730N88')

Для отмены подписки вызываем опять метод streamSubscribe c параметром subscribe = FALSE

streamSubscribe(client,subscribe = FALSE,type='instrument_info',figi='BBG004730N88')

Создадим подписку на данные о книге заявок:

streamSubscribe(client,subscribe = TRUE,type='orderbook',figi='BBG004730N88',depth=1)

Отменить подписку на данные о книге заявок:

streamSubscribe(client,subscribe = FALSE,type='orderbook',figi='BBG004730N88',depth=1)

Создадим подписку на цену:

streamSubscribe(client,subscribe = TRUE,type='candle',figi='BBG004730N88',interval='1min')

Отменить подписку на цену:

streamSubscribe(client,subscribe = FALSE,type='candle',figi='BBG004730N88',interval='1min')

Для того, чтобы разорвать соединение используйте функцию streamClose:

streamClose(client)

Обработка событий

При подписке на данные, включается обработчик событий, который по умолчанию является функцией print выводящей в консоль информацию о вновь поступившей рыночной информации. Для создания более сложной логики необходимо задать свой собственный обработчик событий. Для этой задачи обычно пишется пользовательская функция с необходимой логикой. В качестве примера попробуем написать обработчик событий, который по инфомации из книги заявок (orderbook) будет вычислять разницу между лучшей ценой спроса и предложения (бид-аск спред).

bid_ask_spread = function(x)
{
  spread = min(x$asks[,1])-max(x$bids[,1])
  print(spread)
}

Теперь, когда у нас имеется пользовательская функция, можно приступить к обработке с ее помощью событий. Для этого в качестве агрумента FUN передадим название нашей новой функции bid_ask_spread:

streamStatus(client)
streamSubscribe(client,subscribe = TRUE,type='orderbook',FUN = 'bid_ask_spread',figi='BBG004730N88',depth=5)

Для отмены подписки на данный поток, используйте команду с указанием той функции, с помощью которой подписывались на поток:

streamSubscribe(client,subscribe = FALSE,type='orderbook',FUN = 'bid_ask_spread',figi='BBG004730N88',depth=5)
streamClose(client)

В качестве функции обрабатывающей события может выступать любая логика, в том числе и торговая. Для обращения к показателям в потоковых данных внутри функции function(x), можно использовать следующую таблицу в качестве подсказки:

Тип потока Обращение в функции Тип Описание
orderbook x$depth numeric Глубина стакана
orderbook x$bids data.frame Массив [Цена, количество] предложений цены
orderbook x$asks data.frame Массив [Цена, количество] запросов цены
orderbook x$figi string FIGI
candle x$o numeric Цена открытия свечи
candle x$с numeric Цена закрытия свечи
candle x$h numeric Наибольшая цена свечи
candle x$l numeric Наименьшая цена свечи
candle x$v numeric Объем торгов
candle x$time string Время в формате RFC3339
candle x$interval string Интервал свечи
candle x$figi string FIGI
instrument_info x$trade_status string Статус торгов
instrument_info x$min_price_increment numeric Шаг цены
instrument_info x$lot numeric Размер лота
instrument_info x$accrued_interest numeric НКД (только для бондов)
instrument_info x$limit_up numeric Верхняя граница заявки
instrument_info x$limit_down numeric Нижняя граница заявки
instrument_info x$figi string FIGI

Управление песочницей

Для отладки кода, лучше всего не рисковать своими деньгами и использовать специально созданную песочницу. В API присутствуют команды, которые позволяют управлять и настраивать песочницу. Стоит обратить внимание, что команды по регистрации новой песочницы и удалению старой проходят с определенным лагом. Зарегистрировать счет:

sandboxRegister(token)

Удалить счет:

sandboxDeleteAccount(token)

Для того, чтобы торговать в песочнице, необходимо внести “песочных денег”. Каждым выполнением команды sandboxBalance можно задавать необходимое количество валюты в песочнице:

sandboxBalance(token,balance = 10000,currency = 'USD')

Иногда необходимо работать с уже готовым портфелем на счету, поэтому имеется команда, которая позволяет задать количество определенного инструмента в портфеле. Каждый вызов функции представляет собой запись в портфель одной позиции:

sandboxPositions(token,balance = 100,figi = 'BBG000BMFNP4')

Для того, чтобы очистить портфель и обнулить баланс песочницы выполните команду sandboxDeletePositions

sandboxDeletePositions(token)

Торговые роботы

Логика торговых роботов может быть абсолютно разной и реагировать они могут на абсолютно разные событий. В этой главе мы попробуем дать представление о том, каким образом можно использовать библиотеку tcsinvest для построения торговых роботов.

Мой первый торговый робот

Здесь представлен тестовый робот, который имеет простейшую логику:

Простейшая логика этой трендовой стратегии:

# задаем баланс в песочнице на который будем торговать
token = 'your_sandbox_token_from_tcs_account'
live = FALSE
sandboxBalance(token,balance = 100000,currency = 'RUB')
getBalance(token,live)

# информация об инструменте
capital = 100000
ticker_info = getTickerInfo(token,live,ticker = 'SBER' )
lots = ticker_info$lot
figi_code = ticker_info$figi

# бесконечный цикл торговли
while(2==2)
{
# инфомация о сигнале и необходимой позиции (теоретической)
history = getHistoricalData(token,live,figi_code,from = Sys.Date()-1,interval = '1min')
last_ret = tail(history$c/history$o-1,1)
size = floor(capital/getQuotes(token,live,figi_code)$lastPrice/lots)
theor_position = ifelse(last_ret>0,size,0)
  
# фактическое состояние портфеля
my_portfolio = getPortfolio(token,live)
if(length(my_portfolio)>0)
  current_position = my_portfolio[figi==figi_code]$lots
current_position = ifelse(length(current_position)==0,0,current_position)  

# приводим теоретическое состояние к фактическому
if(theor_position!=current_position)
{
  direction = ifelse(theor_position-current_position>0,'Buy','Sell')
  marketOrder(token,live,figi_code,direction=direction,lots=abs(theor_position-current_position))
}

# печать совершенных сделок и ожидание следующей минуты  
print(getTrades(token,live))
Sys.sleep(60)
}

P.S.

Данная документация не является полной и всеобъемлющей. Это лишь небольшая попытка дать представление о первоначальных шагах по работе с Tinkoff API. Библиотека tcsinvest является свободным ПО, создана энтузиастами и никак не связана с официальной командой разработки Тинькофф Инвестиции. Используя данную библиотеку вы берете на себя все риски связанные с потенциальными багами, ошибками и обработкой сообщений официального API. Вы всегда можете сообщить об ошибках возникающих в библиотеке tcsinvest в репозиторий на gihub. Stay tuned!