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

Официальный веб-сайт NASDAQ предоставляет надежный и бесплатный ресурс на странице проверки акций, которая позволяет вам получить полный список акций, торгуемых на всех трех основных фондовых биржах: NASDAQ, NYSE и AMEX. Вы можете получить доступ к странице, используя этот URL:

https://www.nasdaq.com/market-activity/stocks/screener

Для иллюстрации я загрузил список из 23 крупных компаний, акции которых торгуются на NYSE, и сохранил его как файл с именем nyse.csv. Данные в этом файле включают символы акций, которые мы можем использовать для получения исторических данных о торговле всеми этими акциями, используя yfinance для обучения моделей машинного обучения.

Чтение CSV-файла

Читать CSV-файл с помощью Pandas очень просто:

import pandas as pd

df = pd.read_csv('nyse.csv')

Скачать торговые данные

Столбец Символ файла csv содержит нужные нам биржевые символы. Используя цикл, исторические торговые данные каждой акции можно получить с помощью yfinance, как описано в Получение данных, первый шаг к использованию машинного обучения для торговли акциями (drl4t-01). Приведенный ниже скрипт получает ежедневные торговые данные за последние пять лет для каждой акции, исключая дивиденды и сплиты. Данные были сохранены в каталоге.

import yfinance as yf

data = {}
for symbol in df['Symbol']:
    try:
        ticker = yf.Ticker(symbol)
        hist = ticker.history(interval='1d', period='5y', actions=False)
        if len(hist) > 0:
            data[symbol] = hist
    except:
        print(f'Failed to download data for {symbol}')

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

data.keys()

Расчет технических индикаторов

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

for symbol, hist in data.items():
    hist['SMA10'] = hist['Close'].rolling(window=10).mean()
    hist['SMA20'] = hist['Close'].rolling(window=20).mean()
    
    ema_short = hist['Close'].ewm(span=12, adjust=False).mean()
    ema_long = hist['Close'].ewm(span=26, adjust=False).mean()
    hist['MACD_DIF'] = ema_short - ema_long
    hist['MACD_SIGNAL'] = hist['MACD_DIF'].ewm(span=9, adjust=False).mean()
    hist['MACD'] = hist['MACD_DIF'] - hist['MACD_SIGNAL']
    
    sma = hist['Close'].rolling(window=20).mean()
    std = hist['Close'].rolling(window=20).std()
    hist['UB'] = sma + 2 * std
    hist['LB'] = sma - 2 * std
    
    mfm = ((hist['Close'] - hist['Low']) - (hist['High'] - hist['Close'])) / (hist['High'] - hist['Low'])
    mfv = mfm * hist['Volume']
    hist['CMF'] = mfv.rolling(21).sum() / hist['Volume'].rolling(21).sum()

Давайте посмотрим на данные первой акции с помощью следующего скрипта. Во-первых, давайте получим символ первой акции:

symbol = list(data.keys())[0]

Затем давайте проверим технические индикаторы этой акции:

data[symbol][['SMA10', 'SMA20', 'MACD_DIF', 'MACD_SIGNAL', 'MACD', 'UB', 'LB', 'CMF']]

Из этого фрагмента данных можно увидеть две очевидные проблемы: значение NaN и данные разных масштабов.

Нормализация данных

Как видно из приведенных выше данных, значения простых скользящих средних (SMA) и полос Боллинджера (BB) коррелируют с ценами акций. Они будут сильно различаться от одной акции к другой и не могут использоваться напрямую для машинного обучения.

Нормализация данных заключается в корректировке значений в разных масштабах до условного общего масштаба, часто [0,1]. Таким образом, можно проводить достоверные сравнения между различными диапазонами данных.

Для простой скользящей средней (SMA) самым простым подходом является использование отношения Close к SMA.

for symbol, hist in data.items():
    hist['SMARatio10'] = hist['Close'] / hist['SMA10']
    hist['SMARatio20'] = hist['Close'] / hist['SMA20']

Для полос Боллинджера (BB) мы можем использовать положение закрытия относительно верхней и нижней полос. Это также известно как положение полос Боллинджера (BBP).

for symbol, hist in data.items():
    hist['BBP'] = (hist['Close'] - hist['LB']) / (hist['UB'] - hist['LB'])

Удалить данные со значениями NaN

Расчет технических индикаторов часто включает усреднение скользящих данных. Это приведет к тому, что результаты первых нескольких периодов будут NaN, поскольку данных недостаточно.

Эти торговые данные, содержащие значения NaN, необходимо удалить. Это очень просто с Python:

for symbol, hist in data.items():
    data[symbol] = hist.dropna()

Данные готовы

Теперь давайте посмотрим на торговые данные первой акции после завершения всей предварительной обработки данных:

data[symbol][['SMARatio10', 'SMARatio20', 'MACD', 'BBP', 'CMF']]

Разделить данные для проверки модели

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

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

Следующий скрипт создает два новых каталога: train_data и test_data. Затем торговые данные каждой акции разбиваются на две части: до split_date и равные или более поздние, чем split_date. Здесь split_date — торговый день, который мы можем указать.

import datetime as dt

train_data = {}
test_data = {}

split_date = dt.date.today() - pd.DateOffset(days=100)

for symbol, hist in data.items():
    train_data[symbol] = hist[:split_date - pd.Timedelta(days=1)]
    test_data[symbol] = hist[split_date:]

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

train_data[symbol][['SMARatio10', 'SMARatio20', 'MACD', 'BBP', 'CMF']]

test_data[symbol][['SMARatio10', 'SMARatio20', 'MACD', 'BBP', 'CMF']]