Добро пожаловать во вторую часть моего руководства по использованию JavaScript и Node.js с Alpaca для алгоритмической торговли. Если вы еще не читали первую часть, обязательно посмотрите ее здесь. К концу этой части вы сможете написать очень простой алгоритм, который будет создавать заказы на покупку и продажу в Alpaca на основе двух простых скользящих средних. Полный код этой части руководства можно найти в index.js в моем репозитории GitHub. Если вы предпочитаете видео-версию этого урока, не стесняйтесь проверить мое видео на YouTube.

Справочная информация о простых скользящих средних

Если вы не знакомы со скользящими средними, ознакомьтесь с этой отличной статьей, доступной на Investopedia. По сути, мы рассмотрим две скользящие средние по ценам закрытия минут за два разных периода: последние 20 минут и последние 50 минут. Когда скользящие средние пересекаются, если 20 идет около 50, мы будем считать это сигналом покупки, а если 20 упадет ниже 50, мы будем считать это сигналом продажи. Это не лучший алгоритм для торговли, но он послужит доказательством концепции написания реальных торговых алгоритмов.

Установить lodash и технические индикаторы

Если вы раньше программировали на JavaScript, велика вероятность, что вы использовали lodash. Он предоставляет несколько чрезвычайно полезных методов, которые упрощают работу с объектами и массивами, поэтому мы будем использовать его в нашей программе. Кроме того, мы будем использовать пакет под названием technicalindicators, который позволит нам провести быстрый технический анализ, а точнее простые вычисления скользящей средней. Чтобы установить эти пакеты, выполните эти две команды:

npm install --save lodash
npm install --save technicalindicators

Как только вы это сделаете, вы должны быть готовы вернуться к коду!

Импортируйте новые пакеты

Затем нам нужно импортировать только что установленные пакеты. Вверху index.js добавьте эти строки вокруг нашего импорта Альпаки:

const _ = require(‘lodash’);
const Alpaca = require(‘@alpacahq/alpaca-trade-api’);
const SMA = require(‘technicalindicators’).SMA;

Мы получаем lodash как _ и SMA из пакета технических индикаторов, чтобы мы могли делать некоторые простые скользящие средние.

Удалить printAccount

Затем вы можете продолжить и удалить следующие строки, которые мы использовали только для тестирования нашего подключения к Альпаке:

async function printAccount() {
  const account = await alpaca.getAccount();
  console.log(account);
}
printAccount();

Добавить переменные

Затем мы инициализируем некоторые переменные, которые мы будем использовать для отслеживания простых скользящих средних и проверки их пересечения. Мы создадим две переменные для отслеживания простых скользящих средних: sma20 и sma50. Затем мы создадим переменную с именем lastOrder, которая будет отслеживать, был ли наш последний ордер ордером на покупку или продажу. Нам нужно отслеживать, какой был последний ордер, чтобы мы покупали / продавали только в том случае, если мы фактически пересекаем средние значения, а не просто остаемся выше или ниже. Эта инициализация должна выглядеть примерно так:

let sma20, sma50;
let lastOrder = ‘SELL’;

Мы устанавливаем lastOrder на ‘SELL’, потому что предполагаем, что у нас нет акций, которыми мы торгуем, и поэтому хотим, чтобы наш первый ордер был ордером на покупку.

Инициализировать простые скользящие средние

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

async function initializeAverages() {
  const initialData = await alpaca.getBars(
    ‘1Min’,
    ‘SPY’,
    {
      limit: 50,
      until: new Date()
    }
  );
}

Это даст нам предыдущие 50 одноминутных баров и позволит нам установить среднее значение по последним 20 и 50 ценам закрытия. Итак, поскольку мы берем среднее значение цен закрытия, мы сопоставим полученные бары с массивом цен закрытия:

async function initializeAverages() {
  const initialData = ...
  const closeValues = _.map(initialData.SPY, (bar) => bar.c);
}

Здесь мы используем lodash, чтобы взять все бары в initialData.SPY и использовать только число, сохраненное с ключом c, который является значением закрытия. Это даст нам массив близких значений, которые мы можем использовать сейчас, чтобы установить наши скользящие средние:

async function initializeAverages() {
  const initialData = ...
  const closeValues = ...

  sma20 = new SMA({ period: 20, values: closeValues });
  sma50 = new SMA({ period: 50, values: closeValues });

  console.log(sma20.getResult());
  console.log(sma50.getResult());
}
initializeAverages();

Здесь мы используем объект SMA нашего пакета технических индикаторов для поиска скользящих средних. Когда эти результаты будут напечатаны, вы заметите, что sma20 напечатает 31 скользящее среднее, а sma50 только одно. Это связано с тем, что существует 31 возможный последовательный набор из 20 точек данных, которые дадут нам скользящее среднее, но только один возможный последовательный набор из 50. Итак, теперь у вас должен быть код, который выглядит следующим образом:

async function initializeAverages() {
  const initialData = await alpaca.getBars(
    ‘1Min’,
    ‘SPY’,
    {
      limit: 50,
      until: new Date()
    }
  );

  const closeValues = _.map(initialData.SPY, (bar) => bar.c);

  sma20 = new SMA({ period: 20, values: closeValues });
  sma50 = new SMA({ period: 50, values: closeValues });

  console.log(`sma20: ${sma20.getResult()}`);
  console.log(`sma50: ${sma50.getResult()}`);
}
initializeAverages();

Создать клиент Alpaca Websocket

Затем мы создадим клиента, который позволит нам использовать веб-узел Alpaca для отслеживания интересных событий, таких как изменения цен на акции. Сначала мы просто создадим client переменную и добавим onConnect функцию:

const client = alpaca.websocket;
client.onConnect(() => {
  client.subscribe([‘AM.SPY’]);
  setTimeout(() => client.disconnect(), 6000*1000);
});

Итак, здесь много чего происходит. Сначала мы создаем клиентскую переменную, которую берем из Альпаки. Затем мы добавляем прослушиватель событий, который происходит всякий раз, когда мы подключаемся к веб-сокету. В этой функции мы можем подписаться на список каналов. В этом случае мы подписываемся на AM.SPY, что означает прослушивание сводных минутных обновлений для SPY. Это позволит нам получать обновления каждую минуту с полосой на SPY, содержащей значения открытия, закрытия, максимума, минимума, объема и других значений. Наконец, мы устанавливаем тайм-аут в 6000 секунд, который отключит нас от веб-сокета, чтобы мы не оставили это соединение открытым.

Добавить слушателя канала

Далее мы добавим слушателя для минутных обновлений. Мы проанализируем полученные данные и возьмем из них близкое значение:

client.onStockAggMin((subject, data) => {
  const nextValue = JSON.parse(data)[0].c;
});

В этом случае данные вернутся в виде JSON с массивом полос. Поскольку у нас есть только один канал, нам будет важна 0-я запись, и мы снова будем использовать ключ c для получения значения закрытия. После этого мы можем пересчитать простые скользящие средние:

client.onStockAggMin((subject, data) => {
  const nextValue = ...

  const next20 = sma20.nextValue(nextValue);
  const next50 = sma50.nextValue(nextValue);

  console.log(`next20: ${next20}`);
  console.log(`next50: ${next50}`);
});

Это позволит получить пересчитанные простые скользящие средние и распечатать их. Теперь мы можем использовать эти пересчитанные простые скользящие средние, чтобы решить, покупать или продавать. Во-первых, мы рассмотрим случай покупки, когда последние 20 скользящих средних пересекают последние 50. Мы можем сделать это с помощью простого оператора if, в котором говорится, что если новые 20 скользящих средних выше 50, и мы не покупали последними, то происходит крест:

client.onStockAggMin((subject, data) => {
  const nextValue = ...
  const next20 = ...
  const next50 = ...

  if (next20 > next50 && lastOrder !== ‘BUY’) {
    // Buy!
  }
});

Мы можем использовать противоположную логику, чтобы проверить, пересекаемся ли мы в противоположном направлении:

client.onStockAggMin((subject, data) => {
  const nextValue = ...
  const next20 = ...
  const next50 = ...

  if (next20 > next50 && lastOrder !== ‘BUY’) {
    // Buy!
  } else if (next20 < next50 && lastORder !== ‘SELL’) {
    // Sell!
  }
});

После этого мы можем передать заказы Альпаке и записать, что произошло. В этом случае я скажу Альпаке купить или продать 300 акций SPY в качестве рыночного ордера, срок действия которого истекает через день. Поскольку на SPY почти наверняка присутствует большой объем, эти заказы следует исполнять быстро. 300 - это несколько произвольно, потому что на моем бумажном счете 100000 долларов, а на создание SPY стоит около 300 долларов. Вот как это будет выглядеть:

client.onStockAggMin((subject, data) => {
  const nextValue = ...
  const next20 = ...
  const next50 = ...
  
  if (next20 > next50 && lastOrder !== ‘BUY’) {
    alpaca.createOrder({
      symbol: ‘SPY’,
      qty: 300,
      side: ‘buy’,
      type: ‘market’,
      time_in_force: ‘day’
    });
    lastOrder = ‘BUY’;
    console.log(‘\nBUY\n’);
  } else if (next20 < next50 && lastOrder !== ‘SELL’) {
    alpaca.createOrder({
      symbol: ‘SPY’,
      qty: 300,
      side: ‘sell’,
      type: ‘market’,
      time_in_force: ‘day’
    });
    lastOrder = ‘SELL’;
    console.log(‘\nSELL\n’);
  }
});

И последнее, но не менее важное: теперь мы должны подключиться к клиенту, который запустит событие прослушивателя onConnect и подпишет нас на канал, который вызовет обработчик onStockAggMin. Для этого мы используем эту простую строку:

client.connect();

Резюме

Вот и все! Теперь ваш код должен выглядеть примерно так: Это. Теперь вы можете запустить это с node index.js и позволить ему работать. В зависимости от того, как движется SPY, вы можете увидеть много или очень мало заказов. Я настоятельно рекомендую вам повозиться с тем, какими акциями вы торгуете, с параметрами, которые влияют на то, покупаете ли вы или продаете, и с количеством, которое вы покупаете или продаете. Это всего лишь простое доказательство того, как мы можем написать очень простой торговый алгоритм. Я надеюсь, что это было полезно, и я с нетерпением жду возможности принести вам больше полезных руководств по мере того, как продолжаю учиться!