Локальный сервер для тестирования торговых роботов на Tinkoff Invest API. Предоставляет полностью идентичное API и позволяет быстро прогнать любую стратегию на исторических данных.
- Идея
- Что умеет сервер
- Запуск сервера
- Конфигурация
- Tick
- Примеры
- Разработка
- Планы
- Связанные проекты
- Лицензия
Идея проекта - сделать "локальную" биржу, которая отвечает по Tinkoff Invest API. Подменив в роботе API эндпойнт, можно полноценно его тестировать на истории свечей в условиях приближённых к боевым. При этом язык программирования робота может быть любой.
Процесс выглядит так:
- Вы локально запускаете сервер, который в точности повторяет API Тинькоф инвестиций
- Передаете URL этого сервера в свой код вместо настоящего API
- Конфигурируете сервер: задать шаг и диапазон дат для итерации
- В цикле вызываете у сервера метод
tick()
. На каждом вызове сервер возвращает новую актуальную дату, а также обрабатывает все заявки и позиции в портфеле - После завершения цикла рассчитывается прибыльность стратегии
По сути все то же самое, что делает реальный брокер при взаимодействии с ним через API:
- автоматическая загрузка и кеширование свечей
- прием и исполнение заявок (лимитных и рыночных)
- корректная блокировка средств и расчет баланса
- списание комиссий
- обновление позиций в портфеле
- ведение списка операций
- расчет прибыли
- отправка событий в стрим
Запустить сервер удобнее всего через докер:
docker run --init --rm \
-p 8080:8080 \
-v $(pwd)/.cache:/app/.cache \
-e 'DEBUG=tinkoff-local-broker:*' \
vitalets/tinkoff-local-broker
После скачивания образа и запуска API Тинькофф доступно локально по адресу: localhost:8080
.
Передав этот адрес вместо боевого API, вы сможете тестировать вашего робота.
Перед каждым прогоном на исторических данных сервер необходимо сконфигурировать: задать шаг для свечей и диапазон дат, по которому будет проходить итерация.
Чтобы не добавлять новых методов в API, конфигурация сделана через отправку специальной заявки методом postOrder()
.
Нужно указать в поле accountId=config
, а сам конфиг положить в поле figi
(остальные поля не важны).
Пример - прогон по минутным свечам за 29 апреля (js):
// сам конфиг
const config = {
/** Интервал свечей / шаг */
candleInterval: CandleInterval.CANDLE_INTERVAL_1_MIN,
/** Стартовая дата бэктестинга */
from: '2022-04-29T10:00:00+03:00',
/** Конечная дата бэктестинга */
to: '2022-04-29T19:00:00+03:00',
/** Начальный капитал */
initialCapital: 100000,
/** Комиссия брокера, % от суммы сделки */
brokerFee: 0.3,
};
// отправка специальной заявки с конфигом
api.orders.postOrder({
accountId: 'config',
figi: JSON.stringify(config),
quantity: 0,
direction: 0,
orderType: 0,
orderId: '',
});
После этого сревер готов к итерациям.
При каждом тике сервер перемещает текущую дату и выполняет все действия с заявками.
В ответе приходит новая дата, которую необходимо использовать во всех запросах как текущую.
Вызов тика происходит также с помощью специальной заявки со значением accountId=tick
(остальные поля не важны):
const order = await api.orders.postOrder({
accountId: 'tick',
figi: '',
quantity: 0,
direction: 0,
orderType: 0,
orderId: '',
});
if (order.message) {
// в order.message лежит дата текущей свечи. ее необходимо использовать как текущую для запросов
} else {
// если в message пусто, значит все свечи пройдены, пора считать прибыль :)
}
Удобно завернуть вызов тика в функцию, тогда сам цикл выглядит примерно так:
// конфигурация
while (tick()) {
// вызвать код робота
}
// расчет прибыли
Полный пример тестирования робота можно посмотреть в папке examples.
Запуск сервера из исходников:
npm start
Запуск тестов:
npm t
Релиз новой версии:
./scripts/release.sh latest
# или
./scripts/release.sh x.x
Сейчас на стороне робота приходится после каждого тика выставлять дату. Хочется придумать способ, чтобы этого не делать, а сервер рассчитывал смещение автоматически.
- tinkoff-invest-api - Node.js клиент для работы с Tinkoff Invest API.
MIT @ Vitaliy Potapov