Перед тем как использовать торгового робота на живых деньгах хочется всё протестировать на демо-счете (или «песочнице»). Это когда программные ошибки не имеют особой стоимости.
Я планирую использовать робота на Московской бирже, через АПИ одного из брокеров. Чтобы частному инвестору начать торговать на бирже нужен брокерский счет. Однако минимальное число российских брокеров имеют свои API (на текущий момент я знаю только ФИНАМ, Алор, Тинькофф Инвестиции). По субъективным причинам я выбрал работать с T‑Bank Invest API (это бывший Тинькофф) через среду выполнения JavaScript Node.JS.
В статье разбираюсь как используя песочницу:
Открыть счёт.
Пополнить баланс счёта рублями через специальный запрос.
Посмотреть все свои открытые счета в песочнице.
Купить 1 акцию.
Продать 1 акцию.
Получить все открытые позиции указанного счёта.
Закрыть счёт.
SilverFir-TradingBot\src\sandbox.js
Этот код Node.js взаимодействует с API Tinkoff Invest, позволяя имитировать торговые операции на виртуальном счете, что позволяет протестировать некоторые функции API в ручном режиме. Вот что делает этот код:
1. Импорт модулей
secrets
: импортирует ключи доступа и идентификаторы из внешнего файла конфигурации (secrets
), что помогает защитить конфиденциальную информацию.logger
: импортирует модуль ведения журнала, который записывает журналы в файл или консоль. Это важно для отслеживания активности бота и отладки.logFunctionName
: импортирует утилиту для получения имен функций, что упрощает ведение журнала текущего контекста функции.TinkoffClient
: импортирует клиентский модуль для взаимодействия с API Tinkoff Invest. Этот клиент обрабатывает запросы к API.
2. Настройка клиента
API_TOKEN
: получает токен API (в режиме песочницы) из внешнего файла конфигурации (secrets
) для аутентификации.tinkoffClient
: создает экземплярTinkoffClient
с токеном песочницы, настраивая связь API для среды песочницы.
3. Функции песочницы
sandboxAccount()
: это основная функция, демонстрирующая различные операции с учетной записью песочницы, с несколькими действиями, которые в настоящее время закомментированы.logFunctionName()
: регистрирует имя функции в консоли, что полезно для отслеживания в сложных приложениях.GetSandboxAccounts
: получает все открытые позиции указанного счёта.
Закомментированные операции:
OpenSandboxAccount: регистрирует новый счет в песочнице, что позволит начать тестирование заново.
SandboxPayIn: зачисляет средства на счет в песочнице в российских рублях (RUB). Здесь указанная сумма составляет 30 000 руб.
CloseSandboxAccount: закрывает указанный счет в песочнице, используя его
accountId
, что позволяет выполнить сброс после тестирования.GetSandboxPositions: извлекает и регистрирует все открытые позиции для указанного идентификатора счета.
placeMarketOrder: отправляет рыночные ордера на покупку и продажу указанного инструмента (здесь
BBG004730N88
). Это позволит протестировать функциональность размещения ордеров в песочнице.
Ошибки
sandboxAccount().catch(logger.error)
: запускаетsandboxAccount
асинхронно и регистрирует любые обнаруженные ошибки.
Эта структура кода демонстрирует, как взаимодействовать с виртуальным торговым счетом в API Тинькофф. Закомментированные блоки кода указывают на дополнительные функции, которые можно активировать при необходимости, такие как открытие, пополнение и закрытие счетов песочницы, а также размещение ордеров на покупку/продажу.
// Импорт необходимых модулей
const secrets = require('../config/secrets'); // Ключи доступа и идентификаторы
const logger = require('./services/logService'); // Логирование в файл и консоль
const logFunctionName = require('./services/logFunctionName'); // Получение имени функции
const TinkoffClient = require('./grpc/tinkoffClient'); // модуль для взаимодействия с API Tinkoff Invest
const API_TOKEN = secrets.TbankSandboxMode;
const tinkoffClient = new TinkoffClient(API_TOKEN);
async function sandboxAccount() {
// https://tinkoff.github.io/investAPI/swagger-ui/#/SandboxService/SandboxService_GetSandboxAccounts
logger.info(`Запуск функции ${JSON.stringify(logFunctionName())}\n`);
// // Регистрации счёта в песочнице
// const OpenSandboxAccount = await tinkoffClient.callApi('SandboxService/OpenSandboxAccount');
// logger.info(`Регистрации счёта в песочнице:\n ${JSON.stringify(OpenSandboxAccount, null, '\t')}\n\n`);
// // Пополнение баланса счёта песочницы
// const RUB = {
// "accountId": secrets.AccountID,
// "amount": {
// "nano": 0, // Дробная часть отсутствует
// "currency": "RUB",
// "units": 30000, // Сумма в рублях
// }
// };
// const SandboxPayIn = await tinkoffClient.callApi('SandboxService/SandboxPayIn', RUB);
// logger.info(`Пополнение баланса счёта песочницы:\n ${JSON.stringify(SandboxPayIn, null, '\t')}\n\n`);
// // Закрытие счёта в песочнице
// const accountId = {
// "accountId": secrets.AccountID
// };
// const CloseSandboxAccount = await tinkoffClient.callApi('SandboxService/CloseSandboxAccount', accountId);
// logger.info(`Закрытие счёта в песочнице:\n ${JSON.stringify(CloseSandboxAccount, null, '\t')}\n\n`);
// Посмотреть счета в песочнице
const GetSandboxAccounts = await tinkoffClient.callApi('SandboxService/GetSandboxAccounts');
logger.info(`Список счетов в песочнице:\n ${JSON.stringify(GetSandboxAccounts, null, '\t')}\n\n`);
// // Получить все открытые позиции указанного счёта
// const accountId = {
// "accountId": secrets.AccountID
// };
// const GetSandboxPositions = await tinkoffClient.callApi('OperationsService/GetPositions', accountId);
// logger.info(`Все открытые позиции счёта ${secrets.AccountID}:\n ${JSON.stringify(GetSandboxPositions, null, '\t')}\n\n`);
// // Функция для отправки рыночного ордера
// tinkoffClient.placeMarketOrder('BBG004730N88', 1, 'ORDER_DIRECTION_BUY'); // Купить 1 акцию
// tinkoffClient.placeMarketOrder('BBG004730N88', 1, 'ORDER_DIRECTION_SELL'); // Продать 1 акцию
}
// ======================================================================================
// ============ Запуск функций ===================================================
// ======================================================================================
sandboxAccount().catch(logger.error);
Быстройдействие
Я не ждал какого-то особо быстродействия. Для человека это очень быстро, но вот для робота это медленно. Это придётся учесть при разработке торговой стратегии.
[Running] node "d:\Synology ...\SilverFir-TradingBot_github\src\sandbox.js"
2024-11-01 14:11:57 [INFO]: Запуск функции "sandboxAccount"
2024-11-01 14:11:58 [WARN]: Операция продажи выполнена успешно для Сбер Банк (SBER) (BBG004730N88).
2024-11-01 14:11:58 [INFO]: Детали операции:
{
"orderId": "27a35903-2134-4aaf-XXXX-3b38bc38c5e5",
"executionReportStatus": "EXECUTION_REPORT_STATUS_FILL",
"lotsRequested": "1",
"lotsExecuted": "1",
"initialOrderPrice": {
"currency": "rub",
"units": "2358",
"nano": 100000000
},
"executedOrderPrice": {
"currency": "rub",
"units": "235",
"nano": 810000000
},
"totalOrderAmount": {
"currency": "rub",
"units": "2358",
"nano": 100000000
},
"initialCommission": {
"currency": "rub",
"units": "1",
"nano": 179050000
},
"executedCommission": {
"currency": "rub",
"units": "1",
"nano": 179050000
},
"figi": "BBG004730N88",
"direction": "ORDER_DIRECTION_SELL",
"initialSecurityPrice": {
"currency": "rub",
"units": "235",
"nano": 810000000
},
"orderType": "ORDER_TYPE_MARKET",
"message": "",
"initialOrderPricePt": {
"units": "0",
"nano": 0
},
"instrumentUid": "e6123145-9665-43e0-XXXX-cd61b8aa9b13",
"orderRequestId": "",
"responseMetadata": {
"trackingId": "d059748a138038d3XXXXX93783d61a99",
"serverTime": "2024-11-01T09:11:57.919185435Z"
}
}
2024-11-01 14:11:58 [INFO]: Идентификатор продажи: 27a35903-2134-4aaf-XXXX-3b38bc38c5e5.
2024-11-01 14:11:58 [INFO]: Общая стоимость сделки: 2358.1 руб.
2024-11-01 14:11:58 [INFO]: Цена за 1 шт. Сбер Банк (SBER): 235.81 руб.
2024-11-01 14:11:58 [INFO]: Комиссия за сделку: 1.17905 руб.
[Done] exited with code=0 in 1.146 seconds
Для торгового робота 1,146 секунды от отправки ордера до его исполнения можно считать довольно медленным временем.
В высокочастотной торговле (HFT), где компании конкурируют за время исполнения менее миллисекунды, время обработки ордера более одной секунды будет непозволительно долгим. Стратегии HFT основаны на выполнении тысяч сделок за доли секунды, поэтому 1,146 секунды сделают этого робота неконкурентоспособным.
Напротив, для долгосрочной стратегии, такой как дневной торговый бот или свинг-трейдинг, это время может быть приемлемым. Скорость исполнения остается важной, но не такой критической, как в HFT. В этих случаях компромисс часто склоняется в сторону надежности и экономической эффективности, а не чистой скорости. Задержка в 1 секунду, как правило, не подорвет прибыльность в стратегии, где сделки исполняются с интервалом в несколько минут или даже часов.
Я планирую использовать свинг-трейдинг — это торговая стратегия, ориентированная на захват краткосрочных и среднесрочных ценовых движений, обычно удерживая активы в течение нескольких дней или нескольких недель. Цель — извлечь прибыль из «колебаний» цены, используя рыночный импульс, когда цены колеблются в рамках тренда или между уровнями поддержки и сопротивления.
Итоги
Проект полностью представлен на Гитхабе: https://github.com/empenoso/SilverFir-TradingBot.
Новые модули будут загружаться по мере написания и тестирования.
Автор: Михаил Шардин
5 ноября 2024 г.
Комментарии (10)
qeeveex
05.11.2024 04:15В России на столько не предсказуемый рынок, и доверять торговлю боту весьма опрометчивый поступок.
supercat1337
05.11.2024 04:15А где предсказуемый? Поделитесь секретом. Сразу торговлю с плечом 1 к 1000 будем вести.
supercat1337
Спасибо за публикацию.
Под капотом сокеты? Странно, что так медленно.
Для увеличения скорости обмена может быть снять сервера в Москве? Рассматривали такой вариант?
empenoso Автор
Не хочу арендовать сервер, а сам территориально нахожусь в Перми.
Рассматриваю робота не как основной источник дохода, а как возможность дополнительного канала дохода (по возможности :))) ) и объект исследования.
Поэтому дополнительные финансовые затраты не хочу на себя брать.
hardtop
Рынок не терпит факультативности. Потратил более 2-х лет своей жизни на всех этих роботов (со свечным анализом, уровнями поддержки и сопротивления, фибоначи, мани-менеджментом...) стратегии и пр. Это всё работает очень нестабильно, сколько на исторических данных не учи. Как только начнёте торговать на реальных деньгах - быстрее поседеете и сольёте несколько депозитов, чем что-то заработаете. И даже если даже получится урвать - прибыть испарится быстрее.
Собственно, все эти "успешные" трейдеры больше заработали на книгах, курсах и продаже сигналов. А самый успешный трейдер - это муж Ненси Пелосси, который вовремя прикупил Nvidia.
empenoso Автор
Я раньше этим занимался. Моё мнение что что-то простое может работать. А Фибоначчи никак не формализовать.
supercat1337
Рано закрывали ордера? Или приходилось закрывать с убытком в конце недели?
Когда робот промахивается с краткосрочным брендом, то нужно ориентироваться на среднесрочный рост, чтобы выйти в плюс. Бывает и это занимает продолжительное время. Ну если и тут не угадали, то или держать, или закрывать с убытком )))
mxr
1.16 секунды, это очень много и дело тут явно не в пинге.
Возможно, проблема в самом стороннем API. В том как оно взаимодействует с торговым движком биржи. А может это и вовсе limit order, который заполняется постепенно, в отличии от market-а. Плохо разбираюсь в теме, не читал исходники автора.
P.S,
Указанная сложность статьи не соответствует реальности, тут должна быть "легкая".
empenoso Автор
Здесь маркет ордер
empenoso Автор
Сложность исправил