Комплексный проект: получить данные, обучить модель, оформить заказ, получить уведомление

Пару недель назад я случайно болтал с другом, в масках, на социальной дистанции, как обычно. Он рассказывал мне, как пытался - я цитирую - детоксикацию из брокерского приложения, которое он использовал. Я спросил его о значении слова «детокс» в данном конкретном контексте, опасаясь, что он может разориться, но нет: он сказал мне, что постоянно торгует. «Если определенная акция росла более часа или около того, и я уже превысил порог прибыли в 1%, то я продаю, - сказал он, - среди других личных правил, которым я следовал». Оставив в стороне небольшой псевдонаучный аспект этих правил, я понял, что он имел в виду под детоксом: следование им означало астрономически большое количество проверок телефона.

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

  1. Получайте подробные данные о ценах на акции в режиме реального времени: в идеале, с интервалом в одну минуту. Чем богаче, тем лучше - мы будем использовать Yahoo! Финансирование для этого, подробности будут позже.
  2. Вместо личного набора правил мы собираемся добавить в систему немного искусственного интеллекта. Полное раскрытие информации: я ни в коем случае не эксперт в анализе временных рядов, там уже есть много руководств о том, как обучить нейронные сети торговле, и я действительно не хочу чрезмерно разрабатывать такую ​​игрушечную систему, так что давайте будьте проще: на данный момент подойдет очень простая модель ARIMA.
  3. На этом этапе у нас будут данные и прогноз, полученные от алгоритма, поэтому мы сможем решить, продавать, покупать или держать; нам нужно связаться с нашим брокером, чтобы действительно выполнить действие. Мы собираемся использовать Робин Гуд и Альпаку.
  4. Вот и все - система закончена. Последнее, что нам нужно, - это развернуть его где-нибудь, в нашем случае AWS, и отслеживать активность. Я решил отправлять сообщение Telegram группе каждый раз, когда моя система выполняет какое-либо действие.

А что нам понадобится?

  • Python 3.6 с некоторыми библиотеками.
  • Учетная запись AWS с правами администратора для хранения и развертывания.
  • Node.js, просто чтобы настроить бессерверную структуру для развертывания.
  • Учетная запись Telegram для мониторинга.

Все, что я закодировал, доступно здесь. Хорошо! Итак, без лишних слов, приступим к первой части: получению данных.

Получение данных

Получить данные непросто. Несколько лет назад был официальный Yahoo! Finance API, а также альтернативы, такие как Google Finance - к сожалению, оба были прекращены уже много лет. Но не волнуйтесь, на рынке по-прежнему есть множество альтернатив. Мои личные требования были:

  • Бесплатно: для производственной системы я определенно поменяю этот пункт на дешевые альтернативы, но для игрушечной системы или подтверждения концепции, как бы вы это ни называли, я хочу, чтобы это было бесплатно.
  • Высокий лимит скорости: в идеале без лимита, но все, что выше 500 ударов в минуту, более чем достаточно.
  • Данные в реальном времени: некоторые API предоставляют данные с небольшой задержкой, скажем, 15 минут. Я хочу реальную сделку - максимально приближенную к текущей цене акций.
  • Простота использования: Опять же - это просто POC. Я хочу самый простой.

Имея в виду этот список, я выбрал yfinance - неофициальную альтернативу старому Yahoo Finance API. Имейте в виду, что для реальной системы и на основе потрясающего списка, предоставленного Патриком Коллинзом, я определенно выбрал бы Alpha Vantage API, но пока давайте не будем усложнять.

Библиотека yfinance была разработана Раном Арусси для доступа к Yahoo! Финансовые данные после закрытия официального API. Цитата из репозитория GitHub,

С тех пор, как Yahoo! финансы прекратили работу API исторических данных, многие программы, которые полагались на него, перестали работать.

yfinance стремится решить эту проблему, предлагая надежный, многопоточный и питонический способ загрузки исторических рыночных данных с Yahoo! финансы.

Сладкий, достаточно для меня. Как это работает? Для начала нам нужно его установить:

$ pip install yfinance --user

И тогда мы можем получить доступ ко всему с помощью объекта Ticker:

import yfinance as yf
google = yf.Ticker(“GOOG”)

Этот метод довольно быстрый, в среднем чуть более 0,005 секунды, и возвращает МНОГО информации об акции; например, google.info содержит 123 поля, включая следующие:

52WeekChange: 0.3531152
SandP52WeekChange: 0.17859101
address1: 1600 Amphitheatre Parkway
algorithm: None
annualHoldingsTurnover: None
annualReportExpenseRatio: None
ask: 1815
askSize: 1100
...
twoHundredDayAverage: 1553.0764
volume: 1320946
volume24Hr: None
volumeAllCurrencies: None
website: http://www.abc.xyz
yield: None
ytdReturn: None
zip: 94043

Дополнительная информация доступна несколькими способами: dividends, splits, balance_sheet или earnings среди других. Большинство этих методов возвращают данные в объекте pandas DataFrame, поэтому нам нужно немного поиграть с ним, чтобы получить то, что мы хотим. На данный момент мне просто нужна информация о цене акций во времени; history метод лучше всего подходит для этой цели. Мы можем выбрать как период, так и даты интервалов, а также частоту данных с точностью до одной минуты - обратите внимание, что внутридневная информация доступна только в том случае, если период меньше 60 дней, и что данные с детализацией в 1 миллион разрешены только за 7 дней. получать по запросу. Транспонированные данные последней записи с интервалом в 1 м выглядят следующим образом:

df = google.history(period='1d', interval="1m")
print(df.head())

Мы можем видеть, как он индексируется по дате и времени, и каждая запись имеет семь функций: четыре фиксированных точки цены акции в течение этой минуты (открытие, максимум, минимум и закрытие) плюс объем, дивиденды и дробление акций. Я собираюсь использовать только низкий уровень, поэтому давайте сохраним эти данные:

df = google.history(period='1d', interval="1m")
df = df[['Low']]
df.head()

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

df['date'] = pd.to_datetime(df.index).time
df.set_index('date', inplace=True)
df.head()

Хорошо выглядеть! Мы уже знаем, как получить самую свежую информацию из yfinance - позже мы накормим этим наш алгоритм. Но для этого нам нужен алгоритм, который нужно кормить: перейдем к следующей части.

Добавление ИИ

Я уже говорил это раньше, но повторю еще раз: не пытайтесь повторить это дома. Что я собираюсь сделать здесь, так это подобрать ОЧЕНЬ простую модель ARIMA для прогнозирования следующего значения цены акции; думайте об этом как о фиктивной модели. Если вы хотите использовать это для реальной торговли, я бы порекомендовал искать лучшие и более сильные модели, но имейте в виду: если бы это было легко, все бы это сделали.

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

X = df.index.values
y = df['Low'].values
# The split point is the 10% of the dataframe length
offset = int(0.10*len(df))
X_train = X[:-offset]
y_train = y[:-offset]
X_test  = X[-offset:]
y_test  = y[-offset:]

Если построить график, то получим:

plt.plot(range(0,len(y_train)),y_train, label='Train')
plt.plot(range(len(y_train),len(y)),y_test,label='Test')
plt.legend()
plt.show()

Теперь давайте сопоставим модель с данными обучения и получим прогноз. Обратите внимание, что гиперпараметры модели являются фиксированными, тогда как в реальном мире вы должны использовать перекрестную проверку, чтобы получить оптимальные - ознакомьтесь с этим замечательным руководством о Как искать в сетке гиперпараметры ARIMA с помощью Python. Я использую конфигурацию 5, 0, 1 и получаю прогноз на данный момент сразу после завершения данных обучения:

from statsmodels.tsa.arima.model import ARIMA
model = ARIMA(y_train, order=(5,0,1)).fit()
forecast = model.forecast(steps=1)[0]

Посмотрим, насколько хорошо работает наша фиктивная модель:

print(f'Real data for time 0: {y_train[len(y_train)-1]}')
print(f'Real data for time 1: {y_test[0]}')
print(f'Pred data for time 1: {forecast}')
---
Real data for time 0: 1776.3199462890625
Real data for time 1: 1776.4000244140625
Pred data for time 1: 1776.392609828666

Это неплохо - с этим можно поработать. С помощью этой информации мы можем определить набор правил на основе того, что мы хотим сделать, например, удерживать, если цена растет, или продавать, если она падает. Я не буду вдаваться в подробности по этой части, потому что я не хочу, чтобы вы подали на меня в суд, говоря, что вы потеряли все свои деньги, поэтому, пожалуйста, определите свой собственный набор правил :) А пока я собираюсь чтобы объяснить следующую часть: подключение к брокеру.

Подключение к брокеру

Как вы, наверное, догадались, эта часть сильно зависит от брокера, которого вы используете. Я говорю о двух брокерах, Робин Гуде и Альпаке; причина в том, что они оба:

  • Иметь общедоступный API (официальный или нет).
  • Не взимайте комиссию за торговлю.

В зависимости от типа вашей учетной записи у вас могут быть некоторые ограничения: например, RobinHood разрешает всего 3 сделки в течение 5-дневного периода, если баланс вашего счета ниже 25000 $; Альпака разрешает гораздо больше запросов, но по-прежнему имеет ограничение в 200 запросов в минуту на ключ API.

Робин Гуд

Есть несколько библиотек, в которые входит RobinHood API, но, к сожалению, насколько мне известно, ни одна из них не является официальной. Библиотека Санко была самой большой, с 1,5 тыс. Звезд на GitHub, но ее поддержка прекращена; LichAmnesia’s продолжила путь Санко, но пока у нее всего 99 звезд. Я собираюсь использовать библиотеку robin_stocks, у которой на момент написания этой статьи чуть более 670 звезд. Установим:

$ pip install robin_stocks

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

import pyotp
import robin_stocks as robinhood
RH_USER_EMAIL = <<<YOUR EMAIL GOES HERE>>>
RH_PASSWORD = <<<YOUR PASSWORD GOES HERE>>>
RH_MFA_CODE = <<<THE ALPHANUMERIC CODE GOES HERE>>>
timed_otp = pyotp.TOTP(RH_MFA_CODE).now()
login = rh.login(RH_USER_EMAIL, RH_PASSWORD, mfa_code=totp)

Купить или продать довольно просто:

# Buying 5 shares of Google
rh.order_buy_market('GOOG', 5)
# Selling 5 shares of Google
rh.order_sell_market('GOOG', 5)

Ознакомьтесь с документацией для расширенного использования и примеров.

Альпака

Для Альпаки мы будем использовать alpaca-trade-api библиотеку, у которой более 700 звезд в GitHub. Установить:

$ pip install alpaca-trade-api

После входа в учетную запись вы получите идентификатор ключа API и секретный ключ; оба необходимы для входа в систему:

import alpaca_trade_api as alpaca
ALPACA_KEY_ID = <<<YOUR KEY ID GOES HERE>>>
ALPACA_SECRET_KEY = <<<YOUR SECRET KEY GOES HERE>>>
# Change to https://api.alpaca.markets for live
BASE_URL = 'https://paper-api.alpaca.markets'
api = alpaca.REST(
    ALPACA_KEY_ID, ALPACA_SECRET_KEY, base_url=BASE_URL)

Отправка заказов немного сложнее, чем с РобинГудом:

# Buying 5 shares of Google
api.submit_order(
    symbol='GOOG', 
    qty='5',
    side='buy',
    type='market',
    time_in_force='day'
)
# Selling 5 shares of Google
api.submit_order(
    symbol='GOOG', 
    qty='5',
    side='sell',
    type='market',
    time_in_force='day'
)

Вот и все! Обратите внимание, что оставлять учетные данные в виде обычного текста - это очень, ОЧЕНЬ плохой поступок, но не волнуйтесь, на следующем шаге мы переключимся на переменные среды, что намного безопаснее. Теперь давайте развернем все в облаке и будем следить за ним.

Развертывание и мониторинг

Мы собираемся развернуть все в AWS Lambda. Очевидно, это не лучший вариант для производственной системы, поскольку у Lambda нет хранилища, и мы хотели бы хранить обученную модель где-нибудь, например, в S3. Однако на данный момент этого достаточно - мы запланируем ежедневное выполнение лямбды, обучая модель каждый раз с данными за текущий день. Для мониторинга мы настроим бота Telegram, который будет отправлять сообщение с указанием действия, которое необходимо предпринять, и его результатов. Обратите внимание, что AWS Lambda предоставляется бесплатно до определенного предела, но помните о квотах, если вы хотите отправлять много сообщений.

Первое, что нужно сделать - это создать бота. Я выполнил официальную инструкцию Telegram:

  • Найдите пользователя @BotFather в Telegram.
  • Используйте команду \newbot и выберите имя и логин для вашего бота.
  • Возьмите токен и храните его в надежном месте, он вам скоро понадобится.

Следующий шаг: развертывание. Есть несколько способов развертывания в Lambda. Я собираюсь использовать бессерверный фреймворк, поэтому давайте установим его и создадим шаблон:

$ npm install serverless --global
$ serverless create --template aws-python3 --path ai_trading_system

Это создаст папку scheduled_tg_bot с тремя файлами: .gitignore, serverless.yml и handler.py. Бессерверный файл определяет развертывание: что, когда и как оно будет запускаться. Файл обработчика будет содержать код для запуска:

import telegram
import sys
import os
CHAT_ID = XXXXXXXX
TOKEN = os.environ['TELEGRAM_TOKEN']

# The global variables should follow the structure: 
#       VARIABLE = os.environ['VARIABLE']
# for instance: 
#       RH_USER_EMAIL = os.environ['RH_USER_EMAIL]
def do_everything():
    # The previous code to get the data, train the model
    # and send the order to the broker goes here.
    return 'The action performed'
def send_message(event, context):
    bot = telegram.Bot(token=TOKEN)
    action_performed = do_everything()
    bot.sendMessage(chat_id=CHAT_ID, text=action_performed)

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

Теперь мы собираемся определить, как запускать код. Откройте serverless.yml и напишите:

org: your-organization-name
app: your-app-name
service: ai_trading_system
frameworkVersion: “>=1.2.0 <2.0.0”
provider:
  name: aws
  runtime: python3.6
  environment:
    TELEGRAM_TOKEN: ${env:TELEGRAM_TOKEN}
    # If using RobinHood    
    RH_USER_EMAIL: ${env:RH_USER_EMAIL}
    RH_PASSWORD: ${env:RH_PASSWORD}
    RH_MFA_CODE: ${env:RH_MFA_CODE}
    # If using Alpaca
    ALPACA_KEY_ID: ${env:ALPACA_KEY_ID}
    ALPACA_SECRET_KEY: ${env:ALPACA_SECRET_KEY}
functions:
  cron:
    handler: handler.send_message
    events:
      # Invoke Lambda function at 21:00 UTC every day
      - schedule: cron(00 21 * * ? *)

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

Осталось только получить учетные данные AWS и установить их вместе с токеном и остальными переменными в качестве переменных среды перед развертыванием. Получить учетные данные довольно просто:

На консоли AWS:

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

Вот и все. Теперь давайте экспортируем учетные данные AWS и токен Telegram. Откройте терминал и напишите:

$ export AWS_ACCESS_KEY_ID=[your key goes here]
$ export AWS_SECRET_ACCESS_KEY=[your key goes here]
$ export TELEGRAM_TOKEN=[your token goes here]
# If using RobinHood
$ export RH_USER_EMAIL=[your mail goes here]
$ export RH_PASSWORD=[your password goes here]
$ export RH_MFA_CODE=[your mfa code goes here]
    
# If using Alpaca
$ export ALPACA_KEY_ID=[your key goes here]
$ export ALPACA_SECRET_KEY=[your key goes here]

Установите необходимые пакеты локально и, наконец, разверните все на AWS:

$ pip3 install -r requirements.txt -t . --system
$ serverless deploy

Были сделаны! Бот будет торговать за нас каждый день в 21:00 по всемирному координированному времени и сообщит нам о выполненном действии. Неплохо для проверки концепции - теперь я могу сказать своему другу, что он может перестать лихорадочно проверять свой телефон, чтобы торговать :)

Обратите внимание, что все ресурсы, которые мы использовали в этом руководстве, имеют собственную документацию: я призываю вас углубиться в то, что вы считаете интересным - помните, что это всего лишь игрушечная система! Однако, как игрушечная система, я считаю, что это хорошая отправная точка для более богатого и сложного продукта. Удачного кодирования!

Вы можете проверить код в GitHub.

Примечание редакторам Data Science. Хотя мы разрешаем независимым авторам публиковать статьи в соответствии с нашими правилами и рекомендациями, мы не поддерживаем вклад каждого автора. В этом случае, как указывает сам автор: не пытайтесь торговать, не посоветовавшись с профессионалами. См. Подробности в наших Условиях для читателей.

Использованная литература:

[1] П. Коллинз, Лучшие биржевые API и отраслевой ландшафт в 2020 году (2020), средний

[2] Р. Арусси, Надежно загружайте исторические рыночные данные с Yahoo! Финансы с Python (2019), Aroussy.com

[3] Дж. Браунли, Как искать в сетке гиперпараметры модели ARIMA с помощью Python (2017 г.), Machine Learning Mastery

[4] Дж. Браунли, Как делать вневыборочные прогнозы с ARIMA в Python (2017 г.), Machine Learning Mastery

[5] Бессерверная команда, Пример расписания Cron AWS Python, GitHub