Торговые роботы на Golang

feaa760f4a5562010fa7206e5cb2eb55.png

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

Я Александр Парфенов, бэкенд-разработчик в Тинькофф Инвестициях и автор InvestAPI SDK для языка Go. Расскажу о том, как автоматизировать торговые стратегии при помощи Tinkoff INVEST API и языка Go.

Интервальный алгоритм торговли

Алготрейдинг — процесс торговли на бирже, при котором трейдер прибегает к помощи разных средств автоматизации для достижения прибыли. Мои коллеги уже рассказывали о том, как писать торговых роботов:

API для Инвестиций, или Как написать торгового робота

На связи команда Тинькофф Инвестиций. В этой статье разберем, как клиенты с минимальными навыками пр…

habr.com

Можно разработать свою стратегию или взять готовую и по ней реализовать робота, торгующего в автоматическом режиме. Именно этим мы и займемся.

Рассмотрим интервальный алгоритм. Есть бумаги, цены которых часто ходят в рамках коридора. Цель интервального бота — найти подходящие бумаги и значения границ этого коридора, чтобы автоматически покупать по нижней границе и продавать по верхней.

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

faf4ec1931c5464b28d9220e73d583f8.png

Алгоритм можно разделить на несколько логических этапов

Подготовка — анализ финансовых инструментов на основе исторических котировок, выбор подходящих для торговли и расчет интервала цены для каждого инструмента. Например, можем посмотреть на подходящий график минутных свечей, разница между максимальной и минимальной ценой составляет около 1,5%. В таком случае наша стратегия будет работать хорошо.

График минутных свечей

График минутных свечей

Для начала определяем начальный перечень инструментов, которые потенциально доступны для торговли, например k акций. Для каждого из k инструментов рассматриваем его исторические минутные свечи за n дней и находим такой интервал цены, при котором достигается максимум функции volatility.

volatility = width * crosses

Где width — ширина интервала в процентах, crosses — количество свечей, которые этот интервал пересекают. Свеча пересекает интервал, если high-price свечи выше верхней границы интервала, а low-price ниже нижней границы интервала. На выходе получаем отсортированные по убыванию значения volatility, список всех k инструментов, берем топ t лучших из них и начинаем торговать.

Торговля —запуск непрерывной торговли инструментами и пересчет интервала с учетом новых свечей.

Демонстрация сделок по интервалу. Желтая и зеленая линии — границы интервала, по которым совершаются сделки, а красная линия — stop-loss-цена

Демонстрация сделок по интервалу. Желтая и зеленая линии — границы интервала, по которым совершаются сделки, а красная линия — stop-loss-цена

Теперь необходимо в реальном времени покупать и продавать t инструментов по границам их интервалов и с некоторой периодичностью пересчитывать эти интервалы. 

Для защиты от резкого падения цены открытых позиций бот умеет выставлять stop-loss-заявки на продажу и stop-limit-заявки на покупку. А про полностью автоматизированный процесс была статья в нашем блоге:  

Тинькофф Инвестиции про новую версию API и конкурс торговых роботов

Привет! На связи команда Тинькофф Инвестиций. Мы запустили новую версию программного интерфейса для …

habr.com

Пример сделок на интервалах

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

Ширина интервала 0,11%

Ширина интервала 0,11%

Фиолетовая пунктирная линия (3652) — цена, которая встречается чаще всего в этом наборе, назовем ее M. Синие точки, соединенные пунктирной линией, — сделки по покупке или продаже инструмента.

В первом случае определим границы интервала так: low = 3650, high = 3654, ширина первого интервала составляет около 0,11% от M, что позволяет совершить пять сделок с общим профитом в 20 ₽.

Во втором случае определим границы интервала так: low = 3648, high = 3656, ширина второго интервала составляет около 0,22% от M, что позволяет провести три сделки с общим профитом в 24 ₽.

Ширина интервала 0,22%

Ширина интервала 0,22%

Ширина интервала влияет сразу на два параметра: прибыльность единичной сделки и количество сделок. Задача — подобрать оптимальные значения для получения максимального произведения ширины интервала и количества сделок. Забегая вперед: как показывают тесты, наиболее выгодная ширина интервала находится в диапазоне 0,2—0,6%.

Реализация интервального бота

Реализуем бота на Go, поэтому воспользуемся официальным [sdk]. В директории examples этого репозитория есть основные сценарии взаимодействия с API, а еще примеры двух торговых роботов, один из которых мы сейчас более детально рассмотрим. Исходный код доступен на GitHub.

Структурная схема интервального робота выглядит так:

Bot реализует сбор и обработку рыночных данных, методы для локальной проверки и конфигурации стратегии и расчет интервалов цен. CandlesStorage заполняется историческими данными один раз перед началом работы бота, потом бот регулярно добавляет в него новые данные. Executor отвечает за выставление торговых поручений на покупку или продажу инструментов по границам интервалов, полученных от Bot’a. Все взаимодействие с Tinkoff INVEST API происходит через пакет investgo

Bot реализует сбор и обработку рыночных данных, методы для локальной проверки и конфигурации стратегии и расчет интервалов цен. CandlesStorage заполняется историческими данными один раз перед началом работы бота, потом бот регулярно добавляет в него новые данные. Executor отвечает за выставление торговых поручений на покупку или продажу инструментов по границам интервалов, полученных от Bot«a. Все взаимодействие с Tinkoff INVEST API происходит через пакет investgo

Найдем интервал цены с максимальным значением волатильности, сделать это можно несколькими способами.

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

Важно понимать, что проверка на исторических данных не гарантирует таких же результатов в реальном времени.

Запуск бота

Для запуска интервального бота на компьютере должны быть:

Нужно быть клиентом Тинькофф Инвестиций и выпустить токен. Далее следуем инструкции ниже или инструкции из репозитория.

Инструкция по подготовке к запуску бэктеста.

  1. Клонируйте репозиторий:

 git clone https://github.com/tinkoff/invest-api-go-sdk
  1. Перейдите в папку с ботом:

 cd invest-api-go-sdk/examples/interval_bot
  1. Создайте файл config.yaml:

touch "config.yaml"
  1. Заполните его по примеру example.yaml:

AccountId: ""
APIToken: 
EndPoint: sandbox-invest-public-api.tinkoff.ru:443
AppName: invest-api-go-sdk
DisableResourceExhaustedRetry: false
DisableAllRetry: false
MaxRetries: 3
  1. Загрузите исторические свечи по инструментам. Для этого нужно воспользоваться загрузчиком, который будет по частям запрашивать свечи и сохранять их в sqlite. Можно указать свой набор инструментов, но для быстрого старта рекомендуется просто запустить загрузчик с настройками по умолчанию, и он загрузит все минутные свечи по рублевым фондам и акциям с Московской биржи за последние полгода. Этот процесс займет около 30 минут.

  2. Создайте папку для свечей:

 mkdir candles
  1. Запустите загрузчик:

 go run cmd/candles_downloader/download_candles.go
  1. Запустите бота на песочнице, проде или проверьте бэктест.

После загрузки истории можно приступить к проверке стратегии. Для этого перейдем в файл 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% прибыли в месяц.

Результат бэктеста с конфигурацией № 1

Результат бэктеста с конфигурацией № 1

В конфигурации № 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% прибыли в месяц.

Результат бэктеста с конфигурацией № 2

Результат бэктеста с конфигурацией № 2

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

Статистика и реальные торги

Немного статистики по тестам, которые проводились на одних и тех же данных, а параметры конфигурации не менялись.

Зависимость среднего профита от ширины интервала

Зависимость среднего профита от ширины интервала

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

Зависимость среднего профита от количества дней

Зависимость среднего профита от количества дней

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

Не стоит считать, что лучшее значение ширины интервала = 0,7, а лучшее количество дней для расчета = 1, так как нужно обязательно проверять эти параметры вместе в определенный промежуток времени.

Для запуска торгов на реальной бирже необходимо изменить config.yaml:

AccountId: ""
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 — присоединяйтесь или оставляйте свои вопросы в комментариях.

© Habrahabr.ru