Трейдеры на финансовом рынке обрабатывают большие объемы информации и принимают решения максимально быстро, чтобы не упустить возможность и избежать рисков. Получить преимущество можно, если умеешь хотя бы немного программировать. Это особенно важно там, где время — деньги.
Я Александр Парфенов, бэкенд-разработчик в Тинькофф Инвестициях и автор InvestAPI SDK для языка Go. Расскажу о том, как автоматизировать торговые стратегии при помощи Tinkoff INVEST API и языка Go.
Интервальный алгоритм торговли
Алготрейдинг — процесс торговли на бирже, при котором трейдер прибегает к помощи разных средств автоматизации для достижения прибыли. Мои коллеги уже рассказывали о том, как писать торговых роботов:
Можно разработать свою стратегию или взять готовую и по ней реализовать робота, торгующего в автоматическом режиме. Именно этим мы и займемся.
Рассмотрим интервальный алгоритм. Есть бумаги, цены которых часто ходят в рамках коридора. Цель интервального бота — найти подходящие бумаги и значения границ этого коридора, чтобы автоматически покупать по нижней границе и продавать по верхней.
Японская свеча — вид интервального графика и технический индикатор, применяемый для отображения изменений биржевых котировок акций, цен на сырье.
Алгоритм можно разделить на несколько логических этапов
Подготовка — анализ финансовых инструментов на основе исторических котировок, выбор подходящих для торговли и расчет интервала цены для каждого инструмента. Например, можем посмотреть на подходящий график минутных свечей, разница между максимальной и минимальной ценой составляет около 1,5%. В таком случае наша стратегия будет работать хорошо.
Для начала определяем начальный перечень инструментов, которые потенциально доступны для торговли, например k
акций. Для каждого из k
инструментов рассматриваем его исторические минутные свечи за n
дней и находим такой интервал цены, при котором достигается максимум функции volatility.
Где width
— ширина интервала в процентах, crosses
— количество свечей, которые этот интервал пересекают. Свеча пересекает интервал, если high-price
свечи выше верхней границы интервала, а low-price
ниже нижней границы интервала. На выходе получаем отсортированные по убыванию значения volatility
, список всех k
инструментов, берем топ t
лучших из них и начинаем торговать.
Торговля — запуск непрерывной торговли инструментами и пересчет интервала с учетом новых свечей.
Теперь необходимо в реальном времени покупать и продавать t
инструментов по границам их интервалов и с некоторой периодичностью пересчитывать эти интервалы.
Для защиты от резкого падения цены открытых позиций бот умеет выставлять stop-loss-заявки на продажу и stop-limit-заявки на покупку. А про полностью автоматизированный процесс была статья в нашем блоге:
Пример сделок на интервалах
Посмотрим на реальном инструменте график цен закрытия минутных свечей и два интервала.
Фиолетовая пунктирная линия (3652) — цена, которая встречается чаще всего в этом наборе, назовем ее M
. Синие точки, соединенные пунктирной линией, — сделки по покупке или продаже инструмента.
В первом случае определим границы интервала так: low = 3650
, high = 3654
, ширина первого интервала составляет около 0,11% от M
, что позволяет совершить пять сделок с общим профитом в 20 ₽.
Во втором случае определим границы интервала так: low = 3648
, high = 3656
, ширина второго интервала составляет около 0,22% от M
, что позволяет провести три сделки с общим профитом в 24 ₽.
Ширина интервала влияет сразу на два параметра: прибыльность единичной сделки и количество сделок. Задача — подобрать оптимальные значения для получения максимального произведения ширины интервала и количества сделок. Забегая вперед: как показывают тесты, наиболее выгодная ширина интервала находится в диапазоне 0,2—0,6%
.
Реализация интервального бота
Реализуем бота на Go, поэтому воспользуемся официальным sdk. В директории examples этого репозитория есть основные сценарии взаимодействия с API, а еще примеры двух торговых роботов, один из которых мы сейчас более детально рассмотрим. Исходный код доступен на GitHub.
Структурная схема интервального робота выглядит так:
Найдем интервал цены с максимальным значением волатильности, сделать это можно несколькими способами.
SIMPLEST
— полный перебор. Набор свечей за определенный интервал времени имеет максимальное и минимальное значение цены. Нужно перебором найти цену, которая встречается чаще всего. На графике свечей это горизонтальная линия с максимальным количеством пересеченных свечей. Далее от этого значения цены расширяемся минимум до ширины s
и более, если значение волатильности не убывает при расширении.
BEST_WIDTH
— разница с SIMPLEST
в том, что начальная цена для расширения находится не перебором, а берется медиана выборки средних цен набора свечей. Средняя цена свечи = (high+low+open+close)/4
.
MATH_STAT
— границы интервала находятся как процентили распределения средних цен набора свечей.
Как показывает моя личная практика, вариант № 2 наиболее выгодный.
На успех стратегии влияет ряд параметров настройки бота.
Instruments
— список инструментов, по которому будет проводиться анализ и отбор лучших по волатильности. В примере реализован отбор из рублевых фондов и акций московской биржи, также можно задать этот список вручную.PreferredPositionPrice
— предпочтительная стоимость открытия позиции в валюте. Например,PreferredPositionPrice
= 1000 ₽. При стоимости одного лота 100 ₽ и лотности инструмента 1 будет куплено 10 лотов.MaxPositionPrice
— если цена покупки одного лота инструмента выше этого значения, инструмент отбрасывается.MinProfit
— минимальный профит для совершения сделки, в нашем случае — минимальная ширина коридора в процентах.TopInstrumentsQuantity
— количество лучших по значению максимальной волатильности инструментов, с которыми мы начинаем торговать.DaysToCalculateInterval
— количество дней, на которых рассчитывается интервал цен для торговли.StopLossPercent
— процент изменения цены для stop-loss-заявки. Например, еслиStopLossPercent
= 1, то при падении цены бумаги на 1% относительно цены открытия позиции выставляется рыночное торговое поручение на продажу.AnalyseLowPercentile
— нижний процентиль для расчета интервала. Учитывается только приAnalyse
=MATH_STAT
.AnalyseHighPercentile
— верхний процентиль для расчета интервала. Учитывается только приAnalyse
=MATH_STAT
.Analyse
— тип анализа исторических свечей при расчете интервала.
Подобрать выгодную конфигурацию можно при помощи бэктеста — специального режима работы бота, при котором алгоритм прогоняется на исторических данных. Например, за последнюю неделю, месяц или даже год. Работа бота ориентирована на торговлю внутри одного дня, результат проверки конкретной конфигурации — это значение среднего процента профита в день.
В роботе реализовано два варианта бэктеста:
Проверка конкретной конфигурации на исторических данных. Результат — значение среднего процента профита в день.
Генерация множества конфигураций и их проверка на исторических данных. Результат — список отчетов, отсортированный по возрастанию среднего процента профита в день.
Режим работы бэктеста управляется переменной mode
, которая может принимать два значения: TEST_WITH_CONFIG
(вариант 1) и TEST_WITH_MULTIPLE_CONFIGS
(вариант 2).
Пример отчета о проверке конфига:
report 8:
// Результат
average day profit percent = 0.416
total profit = 943.944
// Значения конфигурации
analyse = 1
minProfit = 0.6
lowPercentile = 0
highPercentile = 0
days = 1
stopLoss = 200.000
Важно понимать, что проверка на исторических данных не гарантирует таких же результатов в реальном времени.
Запуск бота
Для запуска интервального бота на компьютере должны быть:
Нужно быть клиентом Тинькофф Инвестиций и выпустить токен. Далее следуем инструкции ниже или инструкции из репозитория.
Инструкция по подготовке к запуску бэктеста.
Клонируйте репозиторий:
git clone https://github.com/tinkoff/invest-api-go-sdk
Перейдите в папку с ботом:
cd invest-api-go-sdk/examples/interval_bot
Создайте файл
config.yaml
:
touch "config.yaml"
Заполните его по примеру
example.yaml
:
AccountId: ""
APIToken: <your_token>
EndPoint: sandbox-invest-public-api.tinkoff.ru:443
AppName: invest-api-go-sdk
DisableResourceExhaustedRetry: false
DisableAllRetry: false
MaxRetries: 3
Загрузите исторические свечи по инструментам. Для этого нужно воспользоваться загрузчиком, который будет по частям запрашивать свечи и сохранять их в sqlite. Можно указать свой набор инструментов, но для быстрого старта рекомендуется просто запустить загрузчик с настройками по умолчанию, и он загрузит все минутные свечи по рублевым фондам и акциям с Московской биржи за последние полгода. Этот процесс займет около 30 минут.
Создайте папку для свечей:
mkdir candles
Запустите загрузчик:
go run cmd/candles_downloader/download_candles.go
Запустите бота на песочнице, проде или проверьте бэктест.
После загрузки истории можно приступить к проверке стратегии. Для этого перейдем в файл cmd/backtest/backtest.go
, сконфигурируем бэктест и проверим на истории несколько конфигураций.
В конфигурации № 1 используется тип анализа свечей BEST_WIDTH
, интервал рассчитывается на основе предыдущих 4 торговых дней, а проверка проходит с 22.05.23 по 22.07.23.
var (
// Интервал для проверки
initDate = time.Date(2023, 5, 22, 0, 0, 0, 0, time.Local)
stopDate = time.Date(2023, 7, 22, 0, 0, 0, 0, time.Local)
// Критерий для отбора бумаг. Акции, фонды, акции и фонды
selection = SHARES_AND_ETFS
// Режим запуска теста, на одном конфиге или перебор сгенерированных конфигов
mode = TEST_WITH_CONFIG
...
// Конфиг бэктеста для режима TEST_WITH_CONFIG
configToTest = bot.BacktestConfig{
Analyse: bot.BEST_WIDTH,
LowPercentile: 0,
HighPercentile: 0,
MinProfit: 0.3,
DaysToCalculateInterval: 4,
StopLoss: 1.8,
// Для тарифа "Трейдер" комиссия за сделку с акцией составляет 0.05% от стоимости сделки
Commission: 0.05,
}
...
)
Сохраним изменения и запустим бэктест:
go run cmd/backtest/backtest.go
Результат: средний профит в день = 0,104%, с учетом комиссии брокера — около 2,4% прибыли в месяц.
В конфигурации № 2 используется тип анализа свечей BEST_WIDTH
, интервал по-прежнему рассчитывается на основе предыдущих 4 торговых дней, но проверка проходит с 22.02.23 по 22.07.23. Отключены (=1000,0%) stop-loss'ы
и DISABLE_INFO_LOGS = false
— это позволяет увидеть в консоли подробные сообщения о ходе выполнения программы.
const DISABLE_INFO_LOGS = false
var (
// Интервал для проверки
initDate = time.Date(2023, 2, 22, 0, 0, 0, 0, time.Local)
stopDate = time.Date(2023, 7, 22, 0, 0, 0, 0, time.Local)
// Критерий для отбора бумаг. Акции, фонды, акции и фонды
selection = SHARES_AND_ETFS
// Режим запуска теста, на одном конфиге или перебор сгенерированных конфигов
mode = TEST_WITH_CONFIG
...
// Конфиг бэктеста для режима TEST_WITH_CONFIG
configToTest = bot.BacktestConfig{
Analyse: bot.MATH_STAT,
LowPercentile: 0,
HighPercentile: 0,
MinProfit: 0.3,
DaysToCalculateInterval: 4,
StopLoss: 1000.0,
// Для тарифа "Трейдер" комиссия за сделку с акцией составляет 0.05% от стоимости сделки
Commission: 0.05,
}
...
)
Сохраним изменения и запустим бэктест:
go run cmd/backtest/backtest.go
Результат: средний профит в день = 0,14%, с учетом комиссии брокера — около 3,2% прибыли в месяц.
Изменяя конфигурацию, можно добиться различных значений профита. Как показывает практика, не бывает одного стабильно прибыльного конфига на все случаи. Очень прибыльная стратегия на этой неделе может оказаться убыточной на следующей, поэтому нужно проводить много разных тестов при выборе конфигурации для стратегии.
Статистика и реальные торги
Немного статистики по тестам, которые проводились на одних и тех же данных, а параметры конфигурации не менялись.
По форме кривой сложно определить закономерность, потому что профитность определенного интервала напрямую зависит от графика цены, а он, в свою очередь, непредсказуем. В большинстве случаев нет смысла рассматривать интервал шире 1%.
На графике зависимости от количества дней видно, как влияет на средний профит количество дней для расчета интервала. На этот раз зависимость более закономерная, потому что тест проходит внутри одного дня. Зачастую для расчета достаточно пары предыдущих дней, а большее количество начинает мешать.
Не стоит считать, что лучшее значение ширины интервала = 0,7, а лучшее количество дней для расчета = 1, так как нужно обязательно проверять эти параметры вместе в определенный промежуток времени.
Для запуска торгов на реальной бирже необходимо изменить config.yaml
:
AccountId: "<id брокерского счета>"
APIToken: <токен с полным доступом>
EndPoint: invest-public-api.tinkoff.ru:443
AppName: invest-api-go-sdk
DisableResourceExhaustedRetry: false
DisableAllRetry: false
MaxRetries: 3
Удобно ID реального брокерского счета получить через графический gRPC-клиент.
В конфигурации используется тип анализа свечей BEST_WIDTH
, интервал рассчитывается на основе одного предыдущего дня.
intervalConfig = bot.IntervalStrategyConfig{
PreferredPositionPrice: 250,
MaxPositionPrice: 700,
TopInstrumentsQuantity: 10,
MinProfit: 0.3,
DaysToCalculateInterval: 1,
StopLossPercent: 2.9,
AnalyseLowPercentile: 0,
AnalyseHighPercentile: 0,
Analyse: bot.BEST_WIDTH,
...
}
При запуске бота будет распечатан лог, в котором можно увидеть баланс денежных средств и начальную сумму, которая будет задействована в процессе торговли:
Результат: торги проводились в течение половины рабочего дня, профит = 5,1 ₽ (около 0,13%). Значение, близкое к тем, что мы получали ранее при помощи бэктеста.
Заключение
Мы рассмотрели одну из простейших алгоритмических стратегий и ее реализацию на языке Go, используя публичное API Тинькофф Инвестиций. Этот пример позволяет попробовать различные конфигурации стратегии, проанализировать инструменты, модифицировать или оптимизировать алгоритм и многое другое.
Если у вас есть своя стратегия и вы хотите ее автоматизировать, в коде этого робота и директории с примерами собрана базовая инфраструктура для работы с рыночными данными и торговыми поручениями, которая может помочь вам в написании собственного робота.
У нас есть телеграм-чат с разработчиками API — присоединяйтесь или оставляйте свои вопросы в комментариях. Документация по InvestAPI.
Комментарии (7)
LaRN
05.12.2023 19:46А есть возможность прогнать эту стратегию на реальных данных, а не исторических. Но при этом в демо режиме, без реальной торговли? Так можно было бы реально её оценить, погоняв пару дней например.
jstalex Автор
05.12.2023 19:46Здравствуйте!
Да, можно. У InvestAPI есть тестовый контур, чтобы изменить этот параметр нужно в файлеconfig.yaml
нужно изменитьEndPoint
:EndPoint: sandbox-invest-public-api.tinkoff.ru:443 - тестовый контур
EndPoint: invest-public-api.tinkoff.ru:443 - реальный контур
Однако тестовый контур не полностью отражает ситуацию на реальном рынке, поэтому результат может отличаться.LaRN
05.12.2023 19:46Спасибо. И ещё тогда вопрос: Как учитывается комиссия брокера в расчёте доходности в этой стратегии. Ведь по факту за эти три сделки можно заплатить 25 р комисси при 24 р дохода.
jstalex Автор
05.12.2023 19:46Если ширина интервала в процентах больше чем процент комиссии*2, то такого не может быть. А в бэктесте через руками задается комиссия. Про 24 рубля это просто пример для наглядности)
chernish2
Расскажите пожалуйста, если я хочу добиться максимальной скорости обмена данными с вашими endpoints, какие действия следует предпринять? Начать наверное с того, чтобы разместить сервер максимально близко-где именно? И как ещё можно ускорить обмен? В идеале хотелось бы делать десятки запросов в секунду. Спасибо.
comerc
https://tinkoff.github.io/investAPI/speedup/
Тинькофф Инвестиции осуществляют pre-trade контроль рисков. Это значит, что сначала на стороне брокера проверяется достаточность средств для исполнения поручения и позиций для покупки или продажи, соответствие цен и после этого заявка уходит на биржу.
Плюс такого подхода — нельзя купить «лишних» бумаг и получить margin call. Но минус — дополнительные задержки при исполнении ордеров, которые в среднем составляют 200—400 мс.
Еще у брокера есть ограничение на количество выставленных заявок в единицу времени — на момент написания статьи ограничение составляет 300 поручений в минуту. Поэтому HFT-стратегии, требующие минимальных задержек и большого количества поручений, скорее не подходят для работы через Tinkoff API.
jstalex Автор
Здравствуйте!
Про сетевые задержи и датацентры можно прочитать тут
Лимитная политика по количеству запросов на пользователя тут
Задержки при работе через InvestAPI измеряются сотнями миллисекунд, если вы рассчитываете на HFT торговлю, вам это может не подойти.