돈을 잃지 않는 비트코인 거래 로봇을 만들자

저자:리디아, 창작: 2023-02-01 11:52:21, 업데이트: 2023-09-18 19:40:25

img

돈을 잃지 않는 비트코인 거래 로봇을 만들자

인공지능에서 강화 학습을 사용하여 디지털 통화 거래 로봇을 만들어 봅시다.

이 문서에서는 비트코인 거래 로봇을 만드는 방법을 배우기 위해 향상된 학습 프레임 번호를 만들고 적용할 것입니다. 이 튜토리얼에서는 OpenAI의 체육관과 OpenAI 기본 라이브러리의 지점인 안정적인 기본 라이브러리에서 PPO 로봇을 사용할 것입니다.

지난 몇 년 동안 Deep Learning 연구자들을 위해 OpenAI와 DeepMind이 제공한 오픈 소스 소프트웨어에 감사드립니다. AlphaGo, OpenAI Five, AlphaStar 등으로 그들의 놀라운 업적을 보지 못한다면, 작년에 고립된 삶을 살았을 수도 있지만, 그들을 확인해야 합니다.

img

알파스타 훈련:https://deepmind.com/blog/alphastar-mastering-real-time-strategy-game-starcraft-ii/

비록 우리가 인상적 인 것을 만들지는 않지만, 일상적인 거래에서 비트코인 로봇을 거래하는 것은 여전히 쉽지 않습니다.

너무 단순한 것은 가치가 없습니다.

따라서 우리는 스스로 거래하는 것을 배워야 할 뿐만 아니라 로봇이 우리를 대신 거래하도록 해야 합니다.

계획

img

  1. 로봇이 기계 학습을 할 수 있도록 체육관 환경을 만들어

  2. 단순하고 우아한 시각 환경을 만들어

  3. 우리의 로봇이 수익성 있는 거래 전략을 배우도록 훈련시켜라

만약 당신이 처음부터 체육관 환경을 만드는 방법, 또는 단순히 이러한 환경의 시각화를 렌더링하는 방법에 익숙하지 않다면. 계속하기 전에, 이러한 종류의 기사를 구글에 자유롭게하십시오. 이 두 가지 작업은 가장 초보적인 프로그래머에게도 어렵지 않을 것입니다.

시작 하는 일

이 튜토리얼에서는 Zielak에서 생성한 Kaggle 데이터 세트를 사용하겠습니다. 소스 코드를 다운로드하려면..csv 데이터 파일과 함께 Github 저장소에 제공됩니다.

먼저, 필요한 모든 라이브러리를 가져오자. pip를 사용하여 놓친 라이브러리를 설치하십시오.

import gym
import pandas as pd
import numpy as np
from gym import spaces
from sklearn import preprocessing

다음으로, 환경을 위해 우리의 클래스를 만들자. 우리는 판다 데이터 프레임 번호와 선택적 인 initial_balance와 lookback_window_size를 전달해야하며, 이는 로봇이 각 단계에서 관찰한 과거 시간 단계의 수를 나타냅니다. 우리는 각 거래의 수수료를 0.075%로 기본 설정합니다. 즉, 현재 Bitmex의 환율이며, 기본 설정 된 일련 매개 변수를 거짓으로 설정합니다. 이는 우리의 데이터 프레임 번호가 기본적으로 무작위 조각으로 통과된다는 것을 의미합니다.

또한 데이터에 dropna() 와 reset_index() 를 호출하고 NaN 값의 행을 먼저 삭제하고, 프레임 번호의 인덱스를 다시 설정합니다. 왜냐하면 데이터를 삭제했기 때문입니다.

class BitcoinTradingEnv(gym.Env):
  """A Bitcoin trading environment for OpenAI gym"""
  metadata = {'render.modes': ['live', 'file', 'none']}
  scaler = preprocessing.MinMaxScaler()
  viewer = None
def __init__(self, df, lookback_window_size=50, 
                         commission=0.00075,  
                         initial_balance=10000
                         serial=False):
    super(BitcoinTradingEnv, self).__init__()
self.df = df.dropna().reset_index()
    self.lookback_window_size = lookback_window_size
    self.initial_balance = initial_balance
    self.commission = commission
    self.serial = serial
# Actions of the format Buy 1/10, Sell 3/10, Hold, etc.
    self.action_space = spaces.MultiDiscrete([3, 10])
# Observes the OHCLV values, net worth, and trade history
    self.observation_space = spaces.Box(low=0, high=1, shape=(10, lookback_window_size + 1), dtype=np.float16)

우리의 액션 공간은 여기에 3개의 옵션 (구매, 판매 또는 보유) 과 10개의 금액 (1/10, 2/10, 3/10 등) 의 그룹으로 나타납니다. 우리가 구매를 선택할 때, 우리는 BTC의 금액 * self.balance 단어를 구매합니다. 판매를 위해, 우리는 BTC의 금액 * self.btc_held 가치를 판매합니다. 물론, 보유는 금액을 무시하고 아무것도하지 않습니다.

우리의 관측 공간은 0과 1 사이에 설정된 연속적인 부동 소수점으로 정의되며 그 형태는 (10, lookback_window_size+1) 이다. + 1은 현재 시간 단계를 계산하는 데 사용됩니다. 창문 내의 각 시간 단계에 대해 우리는 OHCLV 값을 관측할 것입니다. 우리의 순 자산은 우리가 구매하거나 판매하는 BTC의 수와, 우리가 이 BTC에 지출하거나 받는 총 달러의 양에 같습니다.

다음으로, 환경을 초기화하기 위해 리셋 메소드를 작성해야 합니다.

def reset(self):
  self.balance = self.initial_balance
  self.net_worth = self.initial_balance
  self.btc_held = 0
self._reset_session()
self.account_history = np.repeat([
    [self.net_worth],
    [0],
    [0],
    [0],
    [0]
  ], self.lookback_window_size + 1, axis=1)
self.trades = []
return self._next_observation()

여기서 우리는 self._reset_session와 self._next_observation를 사용하는데, 아직 정의하지 않았습니다. 먼저 정의해 보겠습니다.

거래 세션

img

우리의 환경의 중요한 부분은 거래 세션의 개념입니다. 우리가 이 로봇을 시장 밖에서 배포한다면, 우리는 한 번에 몇 달 이상 실행하지 않을 수 있습니다. 이 이유로, 우리는 self.df에서 연속 프레임의 수를 제한합니다. 이것은 우리의 로봇이 한 번에 볼 수있는 프레임의 수입니다.

_reset_session 방법에서는, 먼저 current_step를 0로 리셋합니다. 다음으로, 우리는 1에서 MAX_TRADING_SESSIONS 사이의 무작위 숫자로 steps_left를 설정합니다. 이 숫자는 프로그램 상단에 정의됩니다.

MAX_TRADING_SESSION = 100000 # ~2 months

다음으로, 우리가 연속적으로 프레임의 수를 가로질러 원한다면, 우리는 프레임의 전체 수를 가로질러 설정해야합니다. 그렇지 않으면 우리는 프레임_스타트를 self.df의 무작위 지점으로 설정하고 active_df라는 새로운 데이터 프레임을 만듭니다. 그것은 단지 self.df의 조각이고, 그것은 frame_start에서 frame_start + steps_left로 이동합니다.

def _reset_session(self):
  self.current_step = 0
if self.serial:
    self.steps_left = len(self.df) - self.lookback_window_size - 1
    self.frame_start = self.lookback_window_size
  else:
    self.steps_left = np.random.randint(1, MAX_TRADING_SESSION)
    self.frame_start = np.random.randint(self.lookback_window_size, len(self.df) - self.steps_left)
self.active_df = self.df[self.frame_start - self.lookback_window_size:self.frame_start + self.steps_left]

무작위 슬라이스의 데이터 프레임 수를 가로질러 중요한 부작용은 우리의 로봇이 장기 훈련에 사용할 수있는 더 독특한 데이터를 갖게 된다는 것입니다. 예를 들어, 우리가 데이터 프레임 수를 일련의 방식으로만 가로질러 (즉 0에서 len ((df))), 우리는 데이터 프레임 수만큼 많은 고유 데이터 포인트를 가질 것입니다. 우리의 관찰 공간은 각 시간 단계에 단독 상태 수만을 사용할 수 있습니다.

그러나, 데이터 세트의 슬라이스를 무작위로 가로질러, 우리는 초기 데이터 세트의 각 시간 단계에 대해 더 의미있는 거래 결과를 만들 수 있습니다. 즉, 더 독특한 데이터 세트를 만들기 위해 이전에 본 거래 행동과 가격 행동의 조합입니다. 설명하기 위해 예를 들어보겠습니다.

연쇄 환경 재설정 후 시간 단계가 10일 때, 우리의 로봇은 항상 데이터 세트에서 동시에 실행되며, 각 시간 단계 후에 세 가지 옵션이 있습니다: 구매, 판매 또는 보유. 세 가지 옵션의 각각에 대해 다른 옵션이 필요합니다: 10%, 20%,... 또는 특정 구현 금액의 100%. 이것은 우리의 로봇이 103개의 경우의 10가지 상태 중 하나를 발견할 수 있음을 의미합니다. 총 1030개의 경우입니다.

이제 우리의 무작위 썰기 환경으로 돌아갑니다. 시간 단계가 10일 때, 우리의 로봇은 데이터 프레임의 수 내의 모든 len(df) 시간 단계에 있을 수 있습니다. 동일한 선택이 각 시간 단계 후에 이루어지는 것을 가정하면 로봇이 동일한 10 시간 단계에서 모든 len(df) 의 30 제곱의 고유 상태를 경험할 수 있습니다.

이것은 큰 데이터 세트에 상당한 소음을 가져올 수 있지만, 저는 로봇이 우리의 제한된 데이터로부터 더 많은 것을 배울 수 있도록 허용되어야 한다고 믿습니다. 우리는 여전히 알고리즘의 효과에 의해 더 정확한 이해를 얻기 위해 가장 신선하고 겉보기에는 '실시간' 데이터를 얻기 위해 일련의 방식으로 테스트 데이터를 통과 할 것입니다.

로봇의 눈으로 관찰

효과적인 시각적 환경 관찰을 통해 로봇이 사용할 기능의 종류를 이해하는 것이 종종 유용합니다. 예를 들어 OpenCV를 사용하여 렌더링 된 관찰 가능한 공간의 시각화입니다.

OpenCV 시각화 환경 관찰

이미지의 각 선은 우리의 관찰 공간의 행을 나타냅니다. 비슷한 주파수를 가진 빨간 선의 첫 네 선은 OHCL 데이터를 나타냅니다. 그리고 바로 아래에 있는 오렌지색과 노란색 점들은 거래량을 나타냅니다. 아래의 변동하는 파란색 막대는 로봇의 순 가치를 나타냅니다. 아래의 밝은 막대는 로봇의 거래를 나타냅니다.

주의 깊게 관찰하면 촛불 지도를 직접 만들 수도 있습니다. 거래 볼륨 바 아래에 거래 역사를 표시하는 모서 코드 인터페이스가 있습니다. 우리의 로봇이 우리의 관찰 공간의 데이터에서 충분히 배울 수 있어야 할 것 같습니다. 그래서 계속하자. 여기 우리는 _next_observation 방법을 정의하고 관찰 데이터를 0에서 1으로 확장합니다.

  • 로봇이 지금까지 관찰한 데이터만을 확장하는 것이 중요합니다.
def _next_observation(self):
  end = self.current_step + self.lookback_window_size + 1
obs = np.array([
    self.active_df['Open'].values[self.current_step:end],  
    self.active_df['High'].values[self.current_step:end],
    self.active_df['Low'].values[self.current_step:end],
    self.active_df['Close'].values[self.current_step:end],
    self.active_df['Volume_(BTC)'].values[self.current_step:end],])
scaled_history = self.scaler.fit_transform(self.account_history)
obs = np.append(obs, scaled_history[:, -(self.lookback_window_size + 1):], axis=0)
return obs

행동하라

우리는 우리의 관측 공간을 설정하고, 이제 우리의 사다리 함수를 작성하고, 로봇의 예정된 행동을 취해야 할 때입니다. 현재 거래 세션에 대한 self.steps_left == 0이 될 때마다, 우리는 BTC를 판매하고 _reset_session(를 호출합니다. 그렇지 않으면 현재 순 가치로 보상을 설정합니다. 자금이 부족하면 True로 설정됩니다.

def step(self, action):
  current_price = self._get_current_price() + 0.01
  self._take_action(action, current_price)
  self.steps_left -= 1
  self.current_step += 1
if self.steps_left == 0:
    self.balance += self.btc_held * current_price
    self.btc_held = 0
    self._reset_session()
obs = self._next_observation()
  reward = self.net_worth
  done = self.net_worth <= 0
return obs, reward, done, {}

트레이딩 액션을 취하는 것은 current_price를 얻는 것만큼 간단하며 실행해야 할 액션과 구매 또는 판매 양을 결정합니다. 우리의 환경을 테스트 할 수 있도록 빠르게 _take_action를 작성해 봅시다.

def _take_action(self, action, current_price):
  action_type = action[0]
  amount = action[1] / 10
btc_bought = 0
  btc_sold = 0
  cost = 0
  sales = 0
if action_type < 1:
    btc_bought = self.balance / current_price * amount
    cost = btc_bought * current_price * (1 + self.commission)
    self.btc_held += btc_bought
    self.balance -= cost
elif action_type < 2:
    btc_sold = self.btc_held * amount
    sales = btc_sold * current_price  * (1 - self.commission)
    self.btc_held -= btc_sold
    self.balance += sales

마지막으로, 같은 방법으로, 우리는 거래 자체에 연결하고 우리의 순 가치와 계좌 역사를 업데이트합니다.

if btc_sold > 0 or btc_bought > 0:
    self.trades.append({
      'step': self.frame_start+self.current_step,
      'amount': btc_sold if btc_sold > 0 else btc_bought,
      'total': sales if btc_sold > 0 else cost,
      'type': "sell" if btc_sold > 0 else "buy"
    })
self.net_worth = self.balance + self.btc_held * current_price
  self.account_history = np.append(self.account_history, [
    [self.net_worth],
    [btc_bought],
    [cost],
    [btc_sold],
    [sales]
  ], axis=1)

우리의 로봇은 이제 새로운 환경을 시작하고, 점차적으로 환경을 완성하고, 환경에 영향을 미치는 행동을 할 수 있습니다.

우리 로봇 무역을 지켜봐

우리의 렌더링 방법은 프린트 (self.net_word) 를 호출하는 것만큼 간단할 수 있지만 충분히 흥미롭지 않습니다. 대신, 우리는 거래량 열과 당사의 순자산의 별도의 차트를 포함하는 간단한 촛불 차트를 그리겠습니다.

코드를 입력할게요StockTrackingGraph.py제 마지막 기사에서 가져와서 비트코인 환경에 적응하도록 재설계합니다.

우리가 해야 할 첫 번째 변화는 self.df ['Date ']를 self.df [Timestamp]로 업데이트하고, 우리의 날짜가 이미 유닉스 타임스테ంపు 형식이기 때문에 date2num에 대한 모든 호출을 삭제하는 것입니다. 다음으로, 우리의 렌더링 방법으로, 우리는 숫자가 아닌 사람이 읽을 수 있는 날짜를 인쇄하기 위해 날짜 태그를 업데이트합니다.

from datetime import datetime

먼저, 날짜 시간 라이브러리를 가져오면, 우리는 각 시간표와 strftime에서 UTC 문자열을 얻기 위해 utcfromtimestamp 메소드를 사용할 것입니다.

date_labels = np.array([datetime.utcfromtimestamp(x).strftime('%Y-%m-%d %H:%M') for x in self.df['Timestamp'].values[step_range]])

마지막으로, df['Volume ']을 self. df[Volume_ (BTC) ) 로 변경하여 데이터 세트와 일치합니다. 이 작업을 완료한 후, 준비되었습니다. 다시 BitcoinTradingEnv로 돌아가서, 이제 차트를 표시하는 렌더링 방법을 작성할 수 있습니다.

def render(self, mode='human', **kwargs):
  if mode == 'human':
    if self.viewer == None:
      self.viewer = BitcoinTradingGraph(self.df,
                                        kwargs.get('title', None))
self.viewer.render(self.frame_start + self.current_step,
                       self.net_worth,
                       self.trades,
                       window_size=self.lookback_window_size)

이제 로봇들이 비트코인을 거래하는 것을 볼 수 있습니다.

Matplotlib과 거래하는 우리의 로봇을 시각화하십시오.

녹색 유령 라벨은 BTC의 구매를 나타내고 빨간 유령 라벨은 판매를 나타냅니다. 오른쪽 상단에있는 흰색 라벨은 로봇의 현재 순액이며 오른쪽 하단에있는 라벨은 비트코인의 현재 가격입니다. 간단하고 우아합니다. 이제 로봇을 훈련시키고 우리가 얼마나 돈을 벌 수 있는지 볼 때입니다!

훈련 시간

전 기사에서 받은 비판 중 하나는 크로스 검증의 부족과 데이터를 훈련 세트와 테스트 세트로 나누지 못한 것입니다. 이것은 이전에는 볼 수 없었던 새로운 데이터에 대한 최종 모델의 정확성을 테스트하는 것입니다. 이 기사의 초점이 아니지만 실제로 매우 중요합니다. 시간 시리즈 데이터를 사용하기 때문에 우리는 크로스 검증에서 많은 선택지가 없습니다.

예를 들어, 크로스 검증의 일반적인 형태는 k-fold 검증이라고 불립니다. 이 검증에서는 데이터를 k 개의 동등한 그룹으로 하나씩 개별적으로 테스트 그룹으로 나누고 나머지 데이터를 훈련 그룹으로 사용합니다. 그러나 시간 계열 데이터는 시간에 많이 의존하며, 이는 후속 데이터가 이전 데이터에 많이 의존한다는 것을 의미합니다. 따라서 k-fold는 작동하지 않을 것입니다. 왜냐하면 우리의 로봇은 거래하기 전에 미래의 데이터에서 배우기 때문에 불공평한 이점입니다.

시간 계열 데이터에 적용되면, 같은 결함이 대부분의 다른 교차 검증 전략에 적용됩니다. 따라서, 우리는 프레임 번호에서 일부 임의 인덱스에 대한 훈련 세트로 전체 데이터 프레임 번호의 일부를 사용해야하고 나머지 데이터를 테스트 세트로 사용해야합니다.

slice_point = int(len(df) - 100000)
train_df = df[:slice_point]
test_df = df[slice_point:]

다음으로, 우리의 환경은 단 하나의 데이터 프레임을 처리하도록 설정되어 있기 때문에, 우리는 두 개의 환경을 만들 것입니다. 하나는 훈련 데이터, 하나는 테스트 데이터입니다.

train_env = DummyVecEnv([lambda: BitcoinTradingEnv(train_df, commission=0, serial=False)])
test_env = DummyVecEnv([lambda: BitcoinTradingEnv(test_df, commission=0, serial=True)])

이제, 우리의 모델을 훈련시키는 것은 우리의 환경을 이용한 로봇을 만들어서 모델.러닝을 호출하는 것만큼 간단합니다.

model = PPO2(MlpPolicy,
             train_env,
             verbose=1, 
             tensorboard_log="./tensorboard/")
model.learn(total_timesteps=50000)

여기서 우리는 텐서 플라이트를 사용해서 텐서 흐름 차트를 쉽게 시각화하고 로봇에 대한 몇 가지 양적 지표를 볼 수 있습니다. 예를 들어, 아래는 200,000 단계 이상의 시간 단계를 가진 많은 로봇의 할인된 보상 차트입니다.

img

우리 로봇이 매우 수익성이 있는 것 같네요! 우리 최고의 로봇은 200만 걸음으로 1000배의 균형을 이룰 수 있고 나머지는 평균 30배 이상 증가할 것입니다!

이 때, 저는 환경의 오류가 있음을 깨달았습니다.

img

보시다시피, 우리 로봇 중 일부는 잘 하고 있고, 다른 것들은 파산하고 있습니다. 그러나, 좋은 성능을 가진 로봇은 초기 잔액의 10배 또는 최대 60배까지 도달할 수 있습니다. 나는 모든 수익성 있는 기계가 수수료 없이 훈련되고 테스트된다는 것을 인정해야 합니다. 그래서 우리 로봇이 실제 돈을 벌 수 있는 것은 비현실적입니다. 하지만 적어도 우리는 방법을 찾았습니다!

우리의 로봇을 테스트 환경에서 테스트해 보도록 합시다 (이전에는 본 적이 없는 새로운 데이터를 사용하여)

img

우리의 훈련된 로봇들은 새로운 테스트 데이터를 거래할 때 파산할 것입니다.

분명히, 우리는 여전히 많은 일을 해야 합니다. 단순히 현재 PPO2 로봇 대신 안정적인 기본 라인 A2C를 사용하는 모델을 전환함으로써, 우리는 이 데이터 세트에 대한 성능을 크게 향상시킬 수 있습니다. 마지막으로, 션 오 고르만의 제안에 따르면, 우리는 보상 기능을 약간 업데이트 할 수 있습니다.

reward = self.net_worth - prev_net_worth

이 두 가지 변화만으로도 테스트 데이터 세트의 성능을 크게 향상시킬 수 있습니다. 그리고 아래에서 볼 수 있듯이, 우리는 마침내 훈련 세트에서 사용할 수 없었던 새로운 데이터에서 이익을 얻을 수 있었습니다.

img

하지만 우리는 더 잘 할 수 있습니다. 이러한 결과를 개선하기 위해, 우리는 우리의 슈퍼 매개 변수를 최적화하고 더 긴 시간 동안 우리의 로봇을 훈련해야합니다. GPU가 작동하고 모든 실린더에 발사하기 시작하는 시간입니다!

지금까지, 이 기사는 조금 길었고, 우리는 아직 많은 세부 사항을 고려해야 합니다, 그래서 우리는 여기서 휴식을 취할 계획입니다. 다음 기사에서, 우리는 우리의 문제 공간에 대한 최고의 하이퍼 파라미터 분할을 위해 베이시안 최적화를 사용하여 CUDA를 사용하여 GPU에 대한 훈련 / 테스트를 준비 할 것입니다.

결론

이 문서에서는, 우리는 처음부터 수익성 있는 비트코인 거래 로봇을 만들기 위해 강화 학습을 사용하기 시작합니다. 우리는 다음 작업을 완료할 수 있습니다:

  1. OpenAI의 체육관을 사용하여 처음부터 비트코인 거래 환경을 만들 수 있습니다.

  2. Matplotlib를 사용하여 환경의 시각화를 구축합니다.

  3. 간단한 교차 검증을 사용하여 로봇을 훈련하고 테스트합니다.

  4. 이윤을 얻기 위해 로봇을 약간 조정하세요.

우리의 거래 로봇이 우리가 기대했던만큼 수익성이 높지는 않았지만, 우리는 이미 올바른 방향으로 움직이고 있습니다. 다음 번에, 우리는 우리의 로봇이 지속적으로 시장을 이길 수 있도록 보장 할 것입니다. 우리는 우리의 거래 로봇이 실시간 데이터를 처리하는 방법을 보게 될 것입니다. 다음 기사를 계속 따르고 Viva Bitcoin!


관련

더 많은