javascript

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

  • вторник, 12 ноября 2024 г. в 00:00:05
https://habr.com/ru/articles/855742/

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

  • Время работы биржи через 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.
Новые модули будут загружаться по мере написания и тестирования.

Автор: Михаил Шардин

11 ноября 2024 г.