Определение времени покупки и продажи

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

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

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

Ознакомьтесь с моей предыдущей статьей, чтобы получить подробное объяснение этого и увидеть реальный пример того, как собака ловит мяч.

Представляем MaskablePPO для стратегий торговли акциями

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

В этой статье мы представим другой подход к обучению модели с помощью MaskablePPO. Причина, по которой мы используем MaskablePPO, заключается в том, что мы хотим избежать покупки актива, который уже куплен, или продажи актива, который не был куплен ранее.

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

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

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

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

Реализация Среда «Продавать-Держать-Покупать»

В статье «Тестирование стратегий торговли акциями с использованием Python (подготовка данных)» мы узнали, как получить биржевые данные OIH из Интернета и как создать файл, содержащий данные через разные промежутки времени.

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

Шаг 1: Импорт библиотек

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

import numpy as np
import gym
from gym import spaces

Шаг 2: Определение констант, класса среды и конструктора класса

Мы определяем константы, необходимые для нормализации и наказания. Затем мы определяем класс SellHoldBuyEnv, который наследуется от тренажерного зала. Класс env. Мы также определяем конструктор класса, который принимает два параметра, observation_size и closes, которые представляют размер пространства наблюдения и цены закрытия актива соответственно.

В конструкторе мы настраиваем пространства наблюдения и действия. В этом случае пространство действий будет дискретным, так как действие, которое вернет агент, — это SELL (0), HOLD (1) и BUY (2). Мы также инициализируем некоторые переменные, которые мы будем использовать позже в методах reset() и step().

# Normalization & Penalization 
OBS_MIN_MAX = 0.05
NOOP_PENALIZATION = 0.01

# Operations
SELL = 0
HOLD = 1
BUY = 2

class SellHoldBuyEnv(gym.Env):

def __init__(self, observation_size, closes):

# Data
self.__features = closes
self.__prices = closes

# Spaces
self.observation_space = spaces.Box(low=np.NINF, high=np.PINF, shape=(observation_size,), dtype=np.float32)
self.action_space = spaces.Discrete(3)

# Episode Management
self.__start_tick = observation_size
self.__end_tick = len(self.__prices)
self.__current_tick = self.__end_tick

# Position Management
self.__current_action = HOLD
self.__current_profit = 0

Шаг 3: Определение метода сброса

В методеreset() сбрасываем переменные текущего действия и текущей прибыли. Мы также устанавливаем текущий указатель тика на начальный тик и возвращаем новое наблюдение с помощью __get_observation()

    def reset(self):

# Reset the current action and current profit
self.__current_action = HOLD
self.__current_profit = 0

# Reset the current tick pointer and return a new observation
self.__current_tick = self.__start_tick

return self.__get_observation()

Шаг 4: Определение метода шага

В методе step() мы сначала проверяем, превышает ли текущий тик последний индекс в массиве признаков. Если это так, мы создаем исключение, потому что среду необходимо сбросить.

Затем мы вычисляем вознаграждение за шаг на основе текущего действия и действия, переданного агентом в качестве параметра. Если действие КУПИТЬ, мы устанавливаем цену открытия на текущую цену и меняем текущее действие на ПОКУПКА. Если действие SELL, мы рассчитываем вознаграждение за шаг как разницу между текущей ценой и ценой открытия, добавляем ее к текущей прибыли и меняем текущее действие на HOLD. Если текущим действием является HOLD, мы наказываем агента небольшим значением, чтобы агент не застрял, ничего не делая.

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

    def step(self, action):

# If current tick is over the last index in the feature array, the environment needs to be reset
if self.__current_tick > self.__end_tick:
raise Exception('The environment needs to be reset.')

# Compute the step reward (Penalize the agent if it is stuck doing anything)
step_reward = 0
if self.__current_action == HOLD and action == BUY:
self.__open_price = self.__prices[self.__current_tick]
self.__current_action = BUY
elif self.__current_action == BUY and action == SELL:
step_reward = self.__prices[self.__current_tick] - self.__open_price
self.__current_profit += step_reward
self.__current_action = HOLD
elif self.__current_action == HOLD:
step_reward = -NOOP_PENALIZATION

# Generate the custom info array with the real and predicted values
info = {
'current_action': self.__current_action,
'current_profit': self.__current_profit
}

# Increase the current tick pointer, check if the environment is fully processed, and get a new observation
self.__current_tick += 1
done = self.__current_tick >= self.__end_tick
obs = self.__get_observation()

# Returns the observation, the step reward, the status of the environment, and the custom information
return obs, step_reward, done, info

Шаг 5: Определение метода масок действий

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

def action_masks(self):

mask = np.ones(self.action_space.n, dtype=bool)

# If current action is Buy, only allow to hold or sell
if self.__current_action == BUY:
mask[BUY] = False

# If current action is Hold, only allow to hold or buy
if self.__current_action == HOLD:
mask[SELL] = False

return mask

Шаг 6: Определение метода наблюдения Get

Метод __get_observation() возвращает текущее наблюдение за окружающей средой. Он генерирует новое наблюдение, беря окно исторических цен из текущего тика и нормализуя его до значений от -1 до 1.

def __get_observation(self):

# If current tick over the last value in the feature array, the environment needs to be reset
if self.__current_tick >= self.__end_tick:
return None

# Generate a copy of the observation to avoid changing the original data
obs = self.__features[(self.__current_tick - self.__start_tick):self.__current_tick].copy()

# Calculate values between -1 and 1 for the new observation without leak any data
avg = np.mean(obs)
obs = np.clip((obs / avg - 1) / OBS_MIN_MAX, -1, 1)

# Return the calculated observation
return obs

Использование среды «Продавать-Держать-Покупать» для обучения и прогнозирования того, когда покупать и продавать актив

В этом разделе мы предоставим пошаговое руководство по реализации настройки и выполнения нашей модели для обучения и прогнозирования. Мы будем использовать Python и библиотеку Stable Baselines3 (версия < 2.0, так как среда реализована с помощью OpenAI gym, а не gymnasium) для создания модели RL на основе алгоритма Maskable Proximal Policy Optimization (MaskablePPO). Мы также предоставим фрагменты кода для каждого шага, чтобы облегчить процесс реализации. Модель будет обучена на 15-минутных данных фондового рынка OIH, чтобы предсказать, когда покупать и продавать актив, и мы покажем, как оценить производительность модели на тестовом наборе данных.

Шаг 1: Импортируйте необходимые библиотеки и классы

import math
import numpy as np
import pandas as pd

from sb3_contrib import MaskablePPO
from stable_baselines3.common.env_util import make_vec_env

from sell_hold_buy_env import SellHoldBuyEnv

На этом шаге импортируются необходимые библиотеки и классы, которые будут использоваться в реализации, такие как mathnumpypandasMaskablePPOmake_vec_env и SellHoldBuyEnv.

Шаг 2: Чтение данных и создание наборов данных для обучения и тестирования

df = pd.read_csv('OIH_15T.csv.gz', compression='gzip')
train = df[df['date'] <= '2022-01-01']
test = df[df['date'] > '2022-01-01']

На этом шаге считываются данные из OIH_15T.csv.gz, который содержит исторические цены OIH. Затем данные разбиваются на train и test на основе даты.

Шаг 3: Создайте 4 среды параллельного обучения

env = make_vec_env(SellHoldBuyEnv, seed=42, n_envs=4, env_kwargs={'observation_size': 26, 'closes': train['close'].values})

На этом шаге создаются 4 параллельные среды с помощью функции make_vec_env из stable_baselines3SellHoldBuyEnv передается в качестве аргумента make_vec_env для создания сред с параметром seed, равным 42 (для обеспечения повторяемости). Параметр observation_size установлен на 26, что является количеством объектов в пространстве наблюдения. Параметр closes устанавливается в столбец close набора данных train.

Шаг 4: Обучите модель

model = MaskablePPO("MlpPolicy", env, verbose=1)
model.learn(total_timesteps=10000000)

На этом шаге создается экземпляр класса MaskablePPO и модель обучается с помощью метода learn. Параметр total_timesteps установлен равным 10 миллионам, то есть количеству временных шагов, на которые будет обучаться модель. Обучение может занять некоторое время, поэтому вы можете уменьшить это число, если это необходимо.

Шаг 5: Сохранение, удаление и перезагрузка модели

model.save("maskableppo_sell_hold_buy")
del model
model = MaskablePPO.load("maskableppo_sell_hold_buy")

На этом шаге обученная модель сохраняется на диске с помощью метода save. Оператор del удаляет экземпляр модели из памяти. Наконец, модель перезагружается с диска с помощью метода load, чтобы убедиться, что она была сохранена и может быть успешно загружена.

Шаг 6: Прогнозирование тестовых значений с помощью обученной модели

# Create a test environmant
env = SellHoldBuyEnv(observation_size=26, closes=test['close'].values)

# Create the required variables for calculation
done = False

# Predict the test values with the trained model
obs = env.reset()
while not done:
action, _states = model.predict(obs, deterministic=True))
obs, rewards, done, info = env.step(action)

print(f"Action: {info['current_action']} - Profit: {info['current_profit']:6.3f}")

В приведенном выше коде мы сначала создаем новую среду env с помощью класса SellHoldBuyEnv и передаем аргументы observation_size и closes. Затем мы инициализируем переменную done в False.

Затем мы вызываем метод reset() в среде, чтобы получить начальное наблюдение. Затем мы запускаем цикл, который выполняется до тех пор, пока флаг done не будет установлен в True. Внутри цикла мы вызываем метод predict() в модели, передавая текущее obs, чтобы получить следующее действие. Затем мы передаем это действие методу step() среды, который возвращает следующее наблюдение, награды, флаг done и информационный словарь.

Затем мы выводим текущее действие, предпринятое моделью, и текущую прибыль, полученную моделью, используя info словарь, возвращаемый методом step()

Шаг 7: Отображение результатов

print(' RESULT '.center(56, '*'))
print(f"* Profit/Loss: {info['current_profit']:6.3f}")

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

После того, как мы сделали прогнозы, нам нужно оценить производительность нашей модели. Мы получим прибыль/убыток модели из custom_infoданных.

Вот результат прибыли/убытка после исполнения

************************ RESULT ************************
* Profit/Loss: 72.870

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

Вот и все! Мы успешно внедрили и выполнили базовую модель для прогнозирования того, когда покупать и продавать, используя обучение с подкреплением и алгоритм MaskablePPO.

Если вам понравилась история, пожалуйста, не стесняйтесь потратить пару секунд, чтобы оставить комментарий и дать мне 10 хлопков. Это очень мотивирует меня продолжать писать. 🙂

Загрузите полный исходный код этой статьи отсюда.

Источник