Как стать автором
Обновить

Инструменты робота, торгующего на Московской бирже через API брокера

Уровень сложностиСложный
Время на прочтение8 мин
Количество просмотров2.1K

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

  • Время работы биржи через InstrumentsService/TradingSchedules.

  • Основную информацию об инструменте через InstrumentsService/GetInstrumentBy.

  • Последнюю котировку по инструменту через MarketDataService/GetLastPrices.

  • Торговые лоты - это определенное количество акций, которые можно купить или продать в рамках одной сделки.

  • Свечи по инструменту для разных временных интервалов через MarketDataService/GetCandles.

  • Технические индикаторы через MarketDataService/GetTechAnalysis.

  • Понятное имя инструмента через InstrumentsService/FindInstrument.

В статье разбираюсь как проделать все эти операции при помощи программного кода.

Частному лицу для начала торговли на бирже частному инвестору необходим брокерский счёт. Но лишь у немногих российских брокеров есть собственные API (точно есть у ФИНАМ, Алор, Тинькофф Инвестиции). По личным предпочтениям я решил использовать API от T-Банк (ранее известный как Тинькофф), работая в среде исполнения JavaScript Node.js.

Время запроса почти 3 секунды - это много
Время запроса почти 3 секунды - это много

SilverFir-TradingBot\src\instruments.js

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

  • secrets и config для конфиденциальной информации и настроек конфигурации.

  • Службы для рисования диаграмм (chart), обработки CSV-файлов (csvHandler), решений о покупке/продаже (buyDecision и sellDecision) и расчета доходности (yieldCalculator).

  • Служба ведения журнала (logger) для отслеживания действий и ошибок.

  • TinkoffClient, модуль для взаимодействия с Tinkoff Invest API, и API_TOKEN для аутентификации.

Основные функции

Функция test():

Цель: Тестирование функциональности API и регистрация данных для конкретных биржевых инструментов.

Примеры операций:

  • Получить основную информацию об инструменте - вызывает InstrumentsService/GetInstrumentBy для получения информации о определенном инструменте с использованием его идентификатора.

  • Получить список всех акций - вызывает InstrumentsService/Shares для составления списка акций и регистрации первых нескольких результатов.

Функция instruments():

Цель: Основная функция для извлечения данных и подготовки к торговле.

Примеры операций:

  • Получение времени работы биржи - получает и регистрирует часы торговли.

  • Найти всю информацию об акциях в списке файла config - отображает всю информацию о каждом из тикеров в JSON формате.

  • Последние цены и торговые лоты - извлекает последние цены акций и проверяет размеры лотов (это определенное количество акций, которые можно купить или продать в рамках одной сделки).

  • Данные свечей - собирает данные свечей (ценовые точки с течением времени) в различные интервалы (5 минут, час, день).

  • Технические индикаторы - извлекает индикаторы, такие как SMA (простая скользящая средняя), для анализа тенденций акций. По выходным данных нет, хотя свечи за это же время присутствуют.

  • Разместить рыночный ордер - строки кода для прямого размещения ордеров на покупку/продажу.

  • Позиции портфеля - перечисляет текущие активы и вычисляет годовую доходность.

В конце код запускает test() и instruments() с обработкой ошибок, регистрируя все возникшие проблемы.

Файл instruments.js это ещё одна часть бота, которая позволяет частному инвестору отслеживать и взаимодействовать с платформой Tinkoff, обрабатывая все: от анализа цен акций и тенденций до размещения сделок. Настройка этого бота подходит для среднесрочной торговли на основе данных, используя Node.js для быстрой обработки данных и взаимодействия с API.

// Импорт необходимых модулей
const secrets = require('../config/secrets'); // Ключи доступа и идентификаторы
const config = require('../config/config'); // Параметры
const chart = require('./services/chartService'); // Отрисовка графиков
const csvHandler = require('./services/csvHandler'); // Работа с CSV файлами
const buyDecision = require('./services/buyDecision'); // Функции покупки
const sellDecision = require('./services/sellDecision'); // Функции продажи
const yieldCalculator = require('./services/yieldCalculator'); // Расчёт годовой доходности от Торгового робота

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 test() {
    logger.info(`Запуск функции ${JSON.stringify(logFunctionName())}\n`);

    // // Получить основную информацию об инструменте InstrumentsService/GetInstrumentBy
    // const testPayload = {
    //     idType: "INSTRUMENT_ID_TYPE_FIGI", // Тип идентификатора INSTRUMENT_ID_TYPE_FIGI / INSTRUMENT_ID_TYPE_UID / INSTRUMENT_ID_TYPE_TICKER
    //     id: "BBG004730N88" // Идентификатор инструмента
    // };
    // const response = await tinkoffClient.callApi('InstrumentsService/GetInstrumentBy', testPayload);
    // logger.info(`InstrumentsService/GetForecastBy: ${JSON.stringify(response, null, 2)}`); // Отображение ответа от API

    // // Получить список акций InstrumentsService/Shares 
    // const testPayload = {
    //     "instrumentStatus": "INSTRUMENT_STATUS_BASE", // https://russianinvestments.github.io/investAPI/instruments/#instrumentsrequest
    //     "instrumentExchange": "INSTRUMENT_EXCHANGE_UNSPECIFIED"
    // };
    // const response = await tinkoffClient.callApi('InstrumentsService/Shares', testPayload);
    // // Отображение ответа от API
    // logger.info(`Ответ: ${JSON.stringify(response, null, 2)}`); // выводится только 3 первых значения .slice(0, 3)

}

async function instruments() {
    logger.info(`Запуск функции ${JSON.stringify(logFunctionName())}\n`);

    // // Получение времени работы биржи
    // const response = await tinkoffClient.callApi('InstrumentsService/TradingSchedules', {});
    // logger.info(`Получение времени работы биржи: ${JSON.stringify(response, null, 2)}`);
    // await tinkoffClient.getExchangeOpen();

    // // Найти всю информацию об акциях в списке файла config
    // for (const stock of config.securitiesToMonitorTikerArray) { // securitiesToMonitorFigiArray или securitiesToMonitorTikerArray
    //     const securitiesToMonitorTikerArrayPayload = {
    //         "query": stock,
    //         "instrumentKind": "INSTRUMENT_TYPE_SHARE"
    //     };
    //     try {
    //         const FindInstrument = await tinkoffClient.callApi('InstrumentsService/FindInstrument', securitiesToMonitorTikerArrayPayload);
    //         logger.info(`Ищем тикер ${stock}:\n${JSON.stringify(FindInstrument, null, 2)}\n\n`);
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}:`, error.message);
    //     }
    // }

    // // Получить последнюю цену для акций из списка в файле config
    // for (const stock of config.securitiesToMonitorFigiArray) {
    //     try {
    //         const quote = await tinkoffClient.getQuote(stock);
    //         const name = await tinkoffClient.getName(stock);
    //         logger.info(`Цена акции ${name.nameCombination} [${stock}]: ${quote} руб.`);
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}:`, error.message);
    //     }
    // }

    // // Получение торговых лотов - это определенное количество акций, которые можно купить или продать в рамках одной сделки
    // for (const stock of config.securitiesToMonitorFigiArray) {
    //     try {
    //         const quote = await tinkoffClient.getLot(stock);
    //         const name = await tinkoffClient.getName(stock);
    //         logger.info(`Торговый лот акции ${name.nameCombination} [${stock}] = ${quote} шт.`);
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}:`, error.message);
    //     }
    // }

    // Получение понятного имени инструмента
    for (const stock of config.securitiesToMonitorFigiArray) {
        try {
            const name = await tinkoffClient.getName(stock);
            const nameUid = name.uid;
            logger.info(`${name.nameCombination} это ${stock} или ${nameUid}.`);
        } catch (error) {
            logger.error(`Ошибка ${stock}:`, error.message);
        }
    }

    // // Тест корректности размера лотов:
    // const figi = 'BBG004730N88'; // Пример ФИГИ
    // const price = await tinkoffClient.getQuote(figi);
    // const quantity = await config.getPurchaseQuantity(price, figi);
    // logger.info(`Тест количества лотов ${figi} для покупки: ${quantity}`);

    // // Получение свечей по инструменту
    // for (const stock of config.securitiesToMonitorFigiArray) {
    //     try {
    //         const name = await tinkoffClient.getName(stock);
    //         const candles5Min = await tinkoffClient.getCandles(stock, "CANDLE_INTERVAL_5_MIN");
    //         logger.info(`5-минутные свечи для ${name.nameCombination}: ${JSON.stringify(candles5Min.slice(0, 3), null, 2)}`); // выводится только 3 первых значения
    //         const candlesHour = await tinkoffClient.getCandles(stock, "CANDLE_INTERVAL_HOUR");
    //         logger.info(`Часовые свечи для ${name.nameCombination}: ${JSON.stringify(candlesHour.slice(0, 3), null, 2)}`); // выводится только 3 первых значения
    //         const candlesDay = await tinkoffClient.getCandles(stock, "CANDLE_INTERVAL_DAY");
    //         logger.info(`Дневные свечи для ${name.nameCombination}: ${JSON.stringify(candlesDay.slice(0, 3), null, 2)}`); // выводится только 3 первых значения
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}:`, error.message);
    //     }
    // }

    // // Получение технических индикаторов по инструменту
    // for (const stock of config.securitiesToMonitorFigiArray) {
    //     try {
    //         const instrument = await tinkoffClient.getName(stock);
    //         const instrumentUid = instrument.uid;
    //         const indicatorType = "INDICATOR_TYPE_SMA"; // Пример типа индикатора (SMA, RSI, MACD и т.д.)
    //         const interval = "INDICATOR_INTERVAL_FIVE_MINUTES"; // Пример интервала (5 минут, час, день) INDICATOR_INTERVAL_ONE_HOUR
    //         const typeOfPrice = "TYPE_OF_PRICE_CLOSE"; // Тип цены (например, закрытие)
    //         const indicators = await tinkoffClient.getTechIndicators(instrumentUid, indicatorType, interval, typeOfPrice);
    //         logger.info(`Индикатор ${indicatorType} для ${instrument.nameCombination}: ${JSON.stringify(indicators.slice(0, 3), null, 2)}`); // выводится только 3 первых значения
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}: ${error.message}`);
    //     }
    // }

    // // Создание графиков пересечения свечей и индикатора для акций из списка в файле config
    // for (const stock of config.securitiesToMonitorFigiArray) {
    //     try {
    //         const charts = chart.generateCandlestickChart(stock);
    //     } catch (error) {
    //         logger.error(`Ошибка ${stock}:`, error.message);
    //     }
    // }

    // // Функция для отправки рыночного ордера
    // tinkoffClient.placeMarketOrder('BBG004730N88', 1, 'ORDER_DIRECTION_BUY'); // Купить акцию
    // tinkoffClient.placeMarketOrder('BBG004730N88', 1, 'ORDER_DIRECTION_SELL'); // Продать акцию

    // // Получить все открытые позиции счёта 
    // const GetSandboxPositions = await tinkoffClient.getPortfolio();
    // logger.info(`Все открытые позиции счёта ${secrets.AccountID}:\n ${JSON.stringify(GetSandboxPositions, null, '\t')}\n\n`);

    // // Расчёт годовой доходности от Торгового робота
    // const SilverFirBotYield = await yieldCalculator.calculateAnnualYield();
    // logger.info(`Годовая доходность от Торгового робота SilverFir Bot: ${SilverFirBotYield}%.`);

    // // Получить прогнозов инвестдомов по инструменту InstrumentsService/GetForecastBy
    // const ForecastPayload = {
    //     "instrumentId": "1c69e020-f3b1-455c-affa-45f8b8049234" // У Аэрофлот (AFLT), BBG004S683W7 [1c69e020-f3b1-455c-affa-45f8b8049234] нет данных аналитиков.
    // };
    // const response = await tinkoffClient.callApi('InstrumentsService/GetForecastBy', ForecastPayload);    
    // logger.info(`InstrumentsService/GetForecastBy: ${JSON.stringify(response, null, 2)}`); // Отображение ответа от API
}


// ======================================================================================
// ============      Запуск функций   ===================================================
// ======================================================================================

test().catch(logger.error);
instruments().catch(err => logger.error(err));

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

Итоги

Проект полностью представлен на Гитхабе: https://github.com/empenoso/SilverFir-TradingBot.
Новые модули будут загружаться по мере написания и тестирования.

Автор: Михаил Шардин
🔗 Моя онлайн-визитка
📢 Telegram «Умный Дом Инвестора»

11 ноября 2024 г.

Теги:
Хабы:
Рейтинг0
Комментарии0

Публикации

Истории

Работа

Ближайшие события

19 марта – 28 апреля
Экспедиция «Рэйдикс»
Нижний НовгородЕкатеринбургНовосибирскВладивостокИжевскКазаньТюменьУфаИркутскЧелябинскСамараХабаровскКрасноярскОмск
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань
20 – 22 июня
Летняя айти-тусовка Summer Merge
Ульяновская область