金銭を失わないビットコイン取引ロボットを 作りましょう

作者: リン・ハーンリディア, 作成日:2023-02-01 11:52:21, 更新日:2023-09-18 19:40:25

img

金銭を損しないビットコイン取引ロボットを 作りましょう

デジタル通貨取引ロボットを 作ろう

この記事では,Bitcoinトレーディングロボットの作成方法を学ぶために拡張学習フレーム番号を作成し,適用します.このチュートリアルでは,OpenAIのジムとOpenAIベースラインライブラリの支部である安定ベースラインライブラリからのPPOロボットを使用します.

OpenAIとDeepMindがここ数年で ディープラーニングの研究者向けに提供したオープンソースソフトウェアに感謝します. AlphaGo,OpenAI Five,AlphaStarなどの技術で 素晴らしい成果を成し遂げたことを 見たことがなければ,去年 孤立して暮らしていたのかもしれませんが, ぜひチェックしてみてください.

img

アルファスター訓練https://deepmind.com/blog/alphastar-mastering-real-time-strategy-game-starcraft-ii/

しかしテディ・ルーズベルトがかつて言ったように ビットコインの取引は簡単ではありません

単純すぎるものは価値がない.

だから 自分で取引を学ぶだけでなく ロボットに代わって取引をさせることも必要です

計画

img

  1. 機械学習を行うためのジム環境を作ります

  2. シンプルでエレガントなビジュアル環境を作ります

  3. ロボットに収益性の高い取引戦略を教える

ジム環境をゼロから作成する方法,またはこれらの環境の視覚化を単純にレンダリングする方法について知りません. 継続する前に,Googleでこのような記事を自由に検索してください. この2つの操作は,最も初級プログラマーにとっても困難ではありません.

スタート

このチュートリアルでは,Zielakによって生成された Kaggle データセットを使用します.ソースコードをダウンロードしたい場合は,.csv データファイルと一緒に私の Github リポジトリに提供されます.OK,始めましょう.

必要なライブラリをすべてインポートします. 欠けているライブラリをインストールするには pip を使用してください.

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

次に,環境のためのクラスを作成します. パンダのデータフレーム番号とオプションの initial_balance とlookback_indow_size を入力し,各ステップでロボットが観測した過去時間ステップの数を示します. 各トランザクションのコミッションを0.075%に,つまりBitmexの現在の為替レートにデフォルトで設定し,シリアルパラメータを false にデフォルトで設定します.

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 word を購入します. 販売のために,BTCの金額 * self.btc_held value を販売します. もちろん,保有は金額を無視し,何もしません.

私たちのobservation_spaceは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 にリセットします.次に, steps_left を 1 から MAX_TRADING_SESSIONS までのランダムな数に設定します.これをプログラムの上部で定義します.

MAX_TRADING_SESSION = 100000 # ~2 months

次に,フレームの数を連続して横断したい場合は,フレームの全数を横断するように設定する必要があります. そうでなければ,フレーム_スタートを self.df のランダムな点に設定し, active_df という新しいデータフレームを作成します. これは単に self.df のスライスで,フレーム_スタートから 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である場合,ロボットは常にデータセットを同時に実行し,各時間ステップの後には3つのオプションがあります. 購入,販売または保持. 3つのオプションのそれぞれには,別のオプションが必要です: 10%, 20%,... または特定の実装金額の100%. これは,私たちのロボットは103の10の状態の1つ,合計1030のケースに出くわす可能性があることを意味します.

ランダムスライス環境に戻ります. タイムステップが10であるとき,ロボットはデータフレームの数内の任意のlen(df) タイムステップに入ることができます. タイムステップごとに同じ選択が行われると仮定すると,ロボットは同じ10つのタイムステップで任意のlen(df) の30乗のユニークな状態を経験することができます.

これは大きなデータセットに相当な騒音をもたらすかもしれませんが,ロボットが私たちの限られたデータからより多くのことを学ぶことを許されるべきだと私は考えています.アルゴリズムの有効性によりより正確な理解を得るために,最も新鮮で"リアルタイム"のように見えるデータを入手するために,私たちはまだ連続的にテストデータを横断します.

ロボットの目を通して観察

効果的な視覚的な環境観察を通じて,ロボットが使用する機能の種類を理解することがしばしば役立ちます.例えば,OpenCVを使用してレンダリングされた観測可能な空間の可視化です.

OpenCVビジュアライゼーション環境の観察

画像の各行は,observation_spaceの行を表しています. 類似の周波数を持つ最初の4行の赤い線は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, {}

トレーディングアクションを行うことは,実行されるアクションと購入または販売する量を決定する,現在の価格を取得するほど簡単です. 環境をテストできるように,迅速に _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)

ロボットが新しい環境を 今から始め 徐々に環境を完成させ 環境に影響を与える行動を起こすことができます

ロボットの取引を見ろ

レンダリング方法は,print (self.net_word) を呼び出すほど簡単ですが,それほど面白くありません.代わりに,簡単なキャンドルチャートを描きます.これは取引量列と私たちの純資産の別々のチャートを含みます.

コードを入れますStockTrackingGraph.py私のGithubからコードを入手できます. 暗号は,

self.df ['Date '] を self.df [Timestamp] に更新し,date2num にすべての呼び出しを削除する必要があります.日付は既に unix 時間スタンプ形式にあります.次に,レンダリング方法では,日付タグを更新して数字ではなく人間で読み取れる日付を印刷します.

from datetime import datetime

まず,datetimeライブラリをインポートし, utcfromtimestampメソッドを使用して,各タイムスタンプとstrftimeから UTC文字列を取得し,文字列としてフォーマットします.

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

最後に,self.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倍検証と呼ばれる.この検証では,データをkつの等しいグループに分割し,テストグループとして個別に,残りのデータをトレーニンググループとして使用します.しかし,時間系列データは時間に依存しています.つまり,次のデータは以前のデータに依存しています.したがって,k倍は機能しません.なぜなら,私たちのロボットは取引する前に将来のデータから学習します.これは不公平な利点です.

タイムシリアルデータに適用すると,同じ欠陥が他のほとんどのクロスバリダーション戦略に適用されます.したがって,私たちはフレーム番号から任意のインデックスへのトレーニングセットとして完全なデータフレーム番号の一部を使用し,残りのデータをテストセットとして使用する必要があります.

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

次に,環境はデータフレームの数を 扱うように設定されているので, 2つの環境を作成します. 訓練データとテストデータです.

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

モデルを訓練するのは 私たちの環境を使って ロボットを作って モデル.learn を呼び出すほど簡単です

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

ここではテンソールプレートを使用します.テンソールフローチャートを簡単に視覚化し,ロボットに関する定量指標を表示できます.例えば,下記は200,000以上の時間ステップを持つ多くのロボットによる割引報酬チャートです.

img

おっと,我々のロボットはとても儲かるようだ!我々の最高のロボットは20万歩で1000倍のバランスを達成できるし,残りは平均で少なくとも30倍増加する!

このとき,私は環境に間違いがあったことに気づきました... バグを修正した後,これが新しい報酬グラフです:

img

ご覧の通り,私たちのロボットの中には,うまくいっているものもあれば,破産しているものもいます.しかし,良い性能を持つロボットは,最初の残高の10倍,あるいは60倍にも達することができます.私は,すべての収益性の高い機械が,佣金なしで訓練され,テストされていることを認めなければなりません.したがって,私たちのロボットが本当のお金を稼ぐことは非現実的です.しかし,少なくとも私たちは方法を見つけました!

ロボットがどのように振る舞うか確認するために テスト環境で (これまで見たことのない新しいデータを使って) ロボットをテストしましょう

img

訓練されたロボットは テストデータ交換で破産する

明らかに,まだ多くの作業があります.現在のPPO2ロボットの代わりに安定したベースラインのA2Cを使用するモデルを単純に切り替えることで,このデータセットでのパフォーマンスを大幅に改善することができます.最後に,ショーン・オゴーマンの提案によると,私たちは報酬関数をわずかに更新することができ,高純資産を実現してそこに留まるのではなく,純資産に報酬を追加することができます.

reward = self.net_worth - prev_net_worth

この2つの変更だけで テストデータセットのパフォーマンスを大幅に向上させることができました 下にご覧のとおり 訓練データセットにはなかった 新しいデータから 利益を得ることができました

img

しかし,我々はもっと良いことができる. これらの結果を改善するために,我々は我々のスーパーパラメータを最適化し,より長い時間のために我々のロボットを訓練する必要があります. GPUがすべてのシリンダーで動作し,発射を開始する時間です!

この記事では,少し長くなりましたが,まだ多くの詳細を考慮する必要がありますので,ここで休憩する予定です. 次の記事では,ベイエス優化を使用して,問題空間のための最高のハイパーパラメータを分割し,CUDAを使用して GPU のトレーニング/テストに準備します.

結論

この記事では,強化学習を使用して,ゼロから収益性の高いビットコイン取引ロボットを作成します.

  1. OpenAIのジムを使って Bitcoinの取引環境をゼロから作ります

  2. 視覚化して表示します.

  3. ロボットを訓練してテストする 簡単なクロスバリダーションを使います

  4. ロボットを少し調整して 利益を得よう

私たちの取引ロボットは,私たちが期待していたほど利益を得なかったが,私たちはすでに正しい方向に進んでいる.次回は,私たちのロボットは一貫して市場を倒せるように保証します.私たちの取引ロボットはリアルタイムデータをどのように処理するか見ていきます.私の次の記事とバイバビットコインをフォローし続けてください!


関連性

もっと