Вступление

Предыстория

Все началось несколько лет назад со школьного проекта по Computer science. Моя идея была сделать компьютерную программу которая проанализирует историю рынка, определит комбинации из 4х свечей в кластеры по схожести, запомнит какая свеча шла после этой комбинации и в дальнейшем сможет найти кластер для реальной ситуации на рынке. На странице должна быть отображена ситуация на рынке, найденый кластер комбинаций и его статистика. Если в кластере большой процент комбинаций с последующей зеленой свечей - мы ставим на рост, и соответственно наоборот.

Изначально в качестве API для программы был выбран Forex Oanda. На тот момент это был единственный найденный нами брокер с Open API и кое-какой документацией. В планах сервер который работает с API и фронт для отображения работы
(на тот момент) индикатора, поэтому пишем на Node JS. Проект был доведен до логического завершения, он исправно находил похожие комбинации и собирал их в кластеры, был сделан интерфейс который изображал полу-статичную информацию. Однако протестировать все это мы так и не успели, забросив все после сдачи проекта.

Перерождение

Летом этого года я наконец скачал, так рекламируемое всеми, приложение Тинькофф Инвестиции, но не особо заходил туда, пока осенью я не наткнулся на их API и вспомнил про то что мы уже имели дело с брокерскими API. Окончательно сломило меня наличие готовой SDK для Node JS и протоколом Streaming.

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

Первые шаги

Соединение с API

Сразу оговорюсь - просто копируя код из этой статьи собрать целый проект не получится, однако, при наличии логики и желания, вполне реально сделать нечто похожее.

Пишем все на Typescript. Для начала определяем все необходимые переменные для соединения, а точнее: apiURL, secretToken, socketURL. Подробности авторизации и остальные детали работы с API описаны в Документации.

import OpenAPI from '@tinkoff/invest-openapi-js-sdk';

// определяем apiURL, secretToken, socketURL

const api = new OpenAPI({ apiURL, secretToken, socketURL});

export default api;

В другом файле импортируем api и уже можем работать с рынком. Обратите внимание что для Production и Sandbox окружений используются разные ссылки и настройки.

import api from './api';
let unSub = function (){};
unSub = api.candle({ figi, interval},async (candle) => {
  console.log(candle);
});

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

Сбор базы данных для дальнейшего анализа

Приступим к созданию метода по сбору данных, на ходу было решено записывать все в JSON и установить лимит по количеству объектов в файле, при переполнении создавать новый файл и записывать уже туда. Этот модуль разделен на 2 части: первая часть собирает свечи непосредственно из Tinkoff партиями максимум в 1 день, вторая используется внутри первой и принимает в себя свечи из партии, формирует комбинации и записывает следующую свечу для формирования статистики в дальнейшем. После формирования массива комбинаций она записывает их в нужный файл в зависимости от наполнения. В новый модуль, отвечающий за работу с API пишем первую функцию.

import { CandleResolution } from '@tinkoff/invest-openapi-js-sdk';
import api from './api';

export async function getCandles( 
	// возвращает свечи с даты from до даты to
  from: string,
  to: string,
  figi: string,
  interval?: CandleResolution,
) {
  try {
    return await api.candlesGet({
      from,
      to,
      figi,
      interval,
    });
  } catch (e) {
    return { tr: 1, Error: e };
  }
}

У Tinkoff установлен лимит: максимум 1 день данных по методу api.candlesGet именно поэтому мы пишем свечи такими партиями. Даты - очень капризная вещь, везде разные форматы + разные временные зоны, поэтому убеждаемся что мы передаём в api дату в правильном формате, мной была написана простая функция по преобразованию даты в нужный формат. Пишем ее в модуль с различными часто используемыми функциями.

export function preoDate(date: Date) {
  date.setDate(date.getDate() + 1);
  const rA = date.toISOString().split(':');
  rA.pop();
  const rC = rA.join(':');
  // addToD - GMT+addToD
  return rC + ':00+0' + addtoD + ':00';
}

Непосредственная запись свечей после получения не особо интересна, к тому же если вы решите использовать сразу базу данных вместо JSON файлов, то работать все будет абсолютно по-другому. В моем случае готовые комбинации это папка файлов в которой каждый файл состоит из таких объектов:

{
  "mLength": 2013,
  "main": [
    {
      "candles": [
        {
          "o": 44.4375,
          "c": 44.4125,
          "h": 44.4375,
          "l": 44.4125,
          "v": 8820,
          "time": "2018-01-23T13:25:00Z",
          "interval": "5min",
          "figi": "BBG000B9XRY4"
        },
        {
          // candle
        },
        {
          // candle
        },
        {
          // candle
        }
      ],
      "afterCandle": {
        // candle
      }
    },
    ...

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

Заключение

В следующей части мы детальнее рассмотрим структуру проекта и постепенно начнем смотреть самый главный модуль, ответственный за сборку кластеров.