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

В предыдущих статьях мы показали, как реализовать простые торговые стратегии в Quantiacs. Здесь мы описываем реализацию алгоритма, использующего методы машинного обучения.

Набор инструментов Python Quantiacs позволяет использовать методы машинного обучения для написания торговых алгоритмов. Мы описываем пример, основанный на keras, и используем модель долгосрочной краткосрочной памяти (LSTM) для прогнозирования временных рядов.

Все системные файлы Quantiacs должны содержать определение настроек:

def mySettings():
    settings = {}
    
    settings['markets']  = ['F_ES']
    settings['slippage'] = 0.05
    settings['budget']   = 1000000
    settings['lookback'] = 504
    settings['beginInSample'] = '20140101'
    settings['endInSample']   = '20170101'
    return settings

Эти настройки определяют один актив для торговли, фьючерс на индекс E-Mini S&P 500, устанавливают проскальзывание равным 5% дневного диапазона актива, фиксируют начальный капитал на уровне 1 млн долларов США, определяют период ретроспективного анализа в 504 дня и установите начальный и конечный дни симуляции.

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

import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
import tensorflow as tf

Далее мы определяем функцию:

def createAndTrain(DATE, CLOSE, settings):
    lst_dates  = DATE.tolist()
    lst_dates  = lst_dates[1:]
    price_data = CLOSE[1:, :]
    average    = np.average(price_data)
    std_dev    = np.std(price_data)
    price_data = (price_data - average) / std_dev
    return_data = (CLOSE[1:, :] - CLOSE[:- 1, :]) / CLOSE[:- 1, :]
    # define training set and target:
    trainX = np.reshape(price_data, (price_data.shape[0], 1, price_data.shape[1]))
    trainY = return_data
    # create and fit the LSTM network:
    model = Sequential()
    model.add(LSTM(4, input_dim=1))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=0)
    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model
    return

Функция выполняет контролируемое обучение, определяя обучающий набор, в котором функции являются нормализованными ценами ЗАКРЫТИЯ активов. Нормализация происходит путем вычитания из CLOSE среднего значения и деления на стандартное отклонение. Целевая переменная представлена ​​относительной доходностью активов.

Затем он создает сеть LSTM и согласовывает ее с обучающим набором. Функция расширяет настройки торговой системы, определяя три новых объекта:

    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model

которые используются торговой системой.

Торговая логика определяется функцией торговой системы:

def myTradingSystem(DATE, CLOSE, exposure, equity, settings):
    lookBack = settings['lookback']
    if 'model' not in settings:
        createAndTrain(DATE[:lookBack - 2], CLOSE[:lookBack - 2], settings)
    model   = settings['model']
    average = settings['mean']
    std_dev = settings['std']
    testX = (CLOSE[lookBack-1:] - average) / std_dev
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
    testY = model.predict(testX)
    newDelta = testY[0]
    nMarkets = CLOSE.shape[1]
    pos = np.ones((1, nMarkets))
    if newDelta >= 0:
        pos[0] = 1
    else:
        pos[0] = -1
    return pos, settings

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

Основной вызов функции:

if __name__ == '__main__':
    from quantiacsToolbox import runts
    inter_op_parallelism_threads = 1
    
    tf.random.set_seed(1) 
    results = runts(__file__)

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

Если вас интересует статистика и машинное обучение, и вы хотите проверить свои идеи на мировых финансовых рынках, отправьте свой код до конца 2020 года и примите участие в конкурсе Q14 на Quantiacs!

Полная стратегия

import numpy as np
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
import tensorflow as tf
def createAndTrain(DATE, CLOSE, settings):
    lst_dates  = DATE.tolist()
    lst_dates  = lst_dates[1:]
    price_data = CLOSE[1:, :]
    average    = np.average(price_data)
    std_dev    = np.std(price_data)
    price_data = (price_data - average) / std_dev
    return_data = (CLOSE[1:, :] - CLOSE[:- 1, :]) / CLOSE[:- 1, :]
    # define training set and target:
    trainX = np.reshape(price_data, (price_data.shape[0], 1, price_data.shape[1]))
    trainY = return_data
    # create and fit the LSTM network:
    model = Sequential()
    model.add(LSTM(4, input_dim=1))
    model.add(Dense(1))
    model.compile(loss='mean_squared_error', optimizer='adam')
    model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=0)
    settings['mean']  = average
    settings['std']   = std_dev
    settings['model'] = model
    return
def myTradingSystem(DATE, CLOSE, exposure, equity, settings):
    lookBack = settings['lookback']
    if 'model' not in settings:
        createAndTrain(DATE[:lookBack - 2], CLOSE[:lookBack - 2], settings)
    model   = settings['model']
    average = settings['mean']
    std_dev = settings['std']
    testX = (CLOSE[lookBack-1:] - average) / std_dev
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
    testY = model.predict(testX)
    newDelta = testY[0]
    nMarkets = CLOSE.shape[1]
    pos = np.ones((1, nMarkets))
    if newDelta >= 0:
        pos[0] = 1
    else:
        pos[0] = -1
    return pos, settings
def mySettings():
    settings = {}
    
    settings['markets']  = ['F_ES']
    settings['slippage'] = 0.05
    settings['budget']   = 1000000
    settings['lookback'] = 504
    settings['beginInSample'] = '20140101'
    settings['endInSample']   = '20170101'
    return settings
if __name__ == '__main__':
    from quantiacsToolbox import runts
    inter_op_parallelism_threads = 1
    
    tf.random.set_seed(1)
    results = runts(__file__)