Что такое тренажерный зал OpenAI?

Gym — это библиотека Python с открытым исходным кодом для разработки и сравнения алгоритмов обучения с подкреплением, предоставляющая стандартный API для связи между алгоритмами обучения и средами. Недавно тренажерный зал больше не поддерживается OpenAI, а другой командой, и последние версии можно найти здесь.

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

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

Выполнение

Понимание класса gym.Env

Все среды, созданные в спортзале OpenAI, должны наследоваться от класса спортзала.Env. Класс gym.Env определяет API, необходимый для среды. API, на котором нам нужно сосредоточиться, следующие:

  • __init__: инициализация выполняется при создании экземпляра среды.
  • reset: эта функция сбрасывает среду в исходное состояние. Это вызывается всякий раз, когда среда достигает терминального состояния или состояния, которое является недопустимым. Он возвращает исходное состояние.
  • рендеринг: это для визуализации данных в визуальной форме. Мы не будем использовать эту функцию.
  • шаг: это ядро ​​среды, реализующее то, что происходит, когда агент «воздействует» на среду. Функция step возвращает кортеж из четырех переменных — (наблюдение, вознаграждение, выполнено, информация).

Требуемый импорт

import gym
from gym.envs.registration import register
from gym import error, spaces, utils
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import pandas_datareader.data as web
import arrow
import random
import sys

Реализация __init__

В нашем примере функция __init__ принимает

  • start_date и end_date : это даты начала и окончания, используемые для данных в среде.
  • tc и тикер: они представляют собой стоимость транзакции и тикер, для которого мы будем загружать данные. по умолчанию установлено значение «^DJI», т. е. индекс Доу-Джонса.

Помимо параметров мы также инициализируем self.action_space и self.observation_space. Эти переменные фиксируют форму/тип действия и диапазон/размер пространства наблюдения. Они необходимы, если вы собираетесь использовать среду с готовыми модулями для обучения.

Следующие несколько строк загружают набор данных и создают функции, которые мы будем использовать в нашем пространстве состояний и вознаграждении. Мы также инициализируем self.curr_index и self.action, чтобы отслеживать текущую дату активности в среде и предыдущее действие.

class TradingEnv(gym.Env):
    def __init__(self, start_date, end_date, tc=0.05/100, ticker='^DJI'):
        self.start = start_date
        self.end = end_date
        self.tc = tc
        self.ticker = ticker

        self.action_space = spaces.Discrete(1)
        self.observation_space = spaces.Box(low=-1,high=1,dtype=np.float32)
        returns = self.load_dataset()
        self.data_df = self.create_features(returns)
        self.curr_index = 0
        self.data_len = self.data_df.shape[0]
        self.action = 0

Реализация load_dataset и create_features

load_dataset: эта функция загружает данные со stooq.com с помощью средства чтения данных pandas, выбирается соответствующий временной интервал, а доходность рассчитывается на основе цены акции. Эту функцию можно изменить для использования любого источника данных, такого как YahooFinance, или любых других пользовательских источников данных (включая загрузку данных из файла).

create_features : эта функция принимает возвраты и вычисляет возвраты в виде запаздывающих доходностей до 5 дней. Он также вычисляет возврат метки, который является возвратом на следующий день для дня. Мы будем использовать возврат метки для вычисления вознаграждения в нашей среде.

def load_dataset(self):
    df = web.DataReader(self.ticker, 'stooq')
    mask = ( self.start <= df.index ) & ( df.index <= self.end )
    df = df[mask]
    df = df.sort_values(by='Date')
    returns = df['Close'].pct_change()
    return returns

def create_features(self, returns):
    dfs = []
    for i in range(5):
        dfs.append(returns.shift(i).rename('r%d'%i))
    dfs.append(returns.shift(-1).rename('Y'))
    df_net = pd.concat(dfs, axis=1)
    df_net = df_net.dropna()
    return df_net

Осуществление сброса

def reset(self):
    self.curr_index = 0
    return self.extract_state(self.data_df, self.curr_index).values

Шаг реализации

Эта функция является основной логикой среды. Логика здесь следующая:

  • получить действие от агента и вычислить изменение позиции (т. е. количество акций, которыми владеет агент)
  • использовать изменение позиции для расчета фактических затрат на транзакцию (нам нужно сделать это, поскольку транзакция на складе всегда влечет за собой затраты, которые могут быть либо комиссией, уплаченной за транзакцию, либо затратами, понесенными с точки зрения проскальзывания)
  • вычислить вознаграждение как функцию действия и стоимости
  • обновить счетчик и получить следующее наблюдение
  • вернуть значения агенту
def step(self, action):

    done = False
    stock_return = self.extract_return(self.data_df, self.curr_index)
    change_in_position = np.abs(self.action - action)
    cost = change_in_position * self.tc
    reward = action*stock_return - cost

    if self.curr_index == self.data_len - 2:
        done = True
    self.curr_index += 1
    self.action = action

    obs = self.extract_state(self.data_df, self.curr_index).values

    info = { 'date' : self.data_df.index[self.curr_index], 'return' : stock_return }

    return obs, reward, done, info

Регистрация env в спортзале

Среда должна быть зарегистрирована в структуре спортзала, чтобы ее можно было создать с именем. Параметры __init__ передаются как kwargs в функцию регистрации.

ENV_NAME = 'TradingEnv-v0'

reg = register(
    id=ENV_NAME,
    entry_point='__main__:TradingEnv',
    kwargs={
        'start_date' : '2019-01-01',
        'end_date' : '2022-01-10'
    }
)

Тестирование env с помощью случайного агента

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

if __name__ == '__main__':

    env = gym.make(ENV_NAME)
    obs = env.reset()
    done = False

    reward_returns = []
    while not done:
        action = random.uniform(-1,1)
        obs, reward, done, info = env.step(action)
        reward_returns.append( (reward,info['return']) )

    i = list(range(len(reward_returns)))
    agent_returns = np.cumprod([ 1 + x[0] for x in reward_returns ])
    stock_returns = np.cumprod([ 1 + x[1] for x in reward_returns ])

    plt.plot(i, agent_returns, label='random_agent_return')
    plt.plot(i, stock_returns, label='actual_stock_return')
    plt.legend()
    plt.show()

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

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

Полный код можно найти здесь.

Рекомендации