Menciptakan robot perdagangan Bitcoin yang tidak akan kehilangan uang

Penulis:Kebaikan, Dibuat: 2019-06-27 10:58:40, Diperbarui: 2023-10-30 20:30:00

img

Mari kita gunakan pembelajaran penguatan di AI untuk membuat robot perdagangan mata uang digital.

Dalam artikel ini, kita akan membuat dan menerapkan algoritma penguatan untuk belajar bagaimana membuat robot perdagangan Bitcoin. Dalam tutorial ini, kita akan menggunakan OpenAI's gym dan PPO robot dari stable-baselines library, sebuah cabang dari database dasar OpenAI.

Terima kasih banyak OpenAI dan DeepMind atas perangkat lunak sumber terbuka yang mereka berikan kepada peneliti pembelajaran dalam beberapa tahun terakhir. Jika Anda belum melihat hal-hal menakjubkan yang mereka lakukan dengan teknologi seperti AlphaGo, OpenAI Five, dan AlphaStar, Anda mungkin telah hidup di luar isolasi dunia tahun lalu, tetapi Anda harus melihat mereka.

img

Pelatihan AlphaStarhttps://deepmind.com/blog/alphastar-mastering-real-time-strategy-game-starcraft-ii/

Meskipun kita tidak akan membuat apa pun yang mengesankan, perdagangan robot Bitcoin dalam transaksi sehari-hari masih merupakan hal yang tidak mudah.

Jika Anda tidak memiliki uang, Anda tidak akan memiliki apa-apa.

Oleh karena itu, kita tidak hanya harus belajar berdagang sendiri... tapi juga membiarkan robot berdagang untuk kita.

Perencanaan

img

1.为我们的机器人创建gym环境以供其进行机器学习

2.渲染一个简单而优雅的可视化环境

3.训练我们的机器人,使其学习一个可获利的交易策略

Jika Anda belum terbiasa dengan cara membuat lingkungan gym dari awal, atau cara sederhana untuk memvisualisasikannya. Sebelum melanjutkan, silakan googling artikel seperti ini. Kedua tindakan ini tidak akan sulit dilakukan bahkan sebagai programmer tingkat awal.

Masuk

在本教程中,我们将使用Zielak生成的Kaggle数据集。如果您想下载源代码,我的Github仓库中会提供,同时也有.csv数据文件。好的,让我们开始吧。

Pertama-tama, mari kita impor semua pustaka yang diperlukan. Pastikan untuk menginstal setiap pustaka yang Anda hilangkan dengan pip.

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

Selanjutnya, mari kita buat kelas kita untuk lingkungan tersebut. Kita perlu masukkan angka puck data panda, serta initial_balance opsional dan lookback_window_size, yang akan menunjukkan berapa banyak langkah waktu masa lalu yang diamati robot dalam setiap langkah. Kita akan membuat komisi per transaksi menjadi standar 0.075%, yaitu nilai tukar saat ini di Bitmex, dan parameter seri akan menjadi default false, yang berarti bahwa pada kondisi default, angka puck data kita akan melintasi segmen acak.

Kita juga memanggil data dropna (,) dan reset_index (), terlebih dahulu menghapus baris dengan nilai NaN, dan kemudian menempatkan kembali indeks dengan jumlah titik, karena kita telah menghapus data.

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)

Action_space kami di sini diwakili sebagai satu set 3 opsi (membeli, menjual atau memegang) dan satu lagi set 10 jumlah (1/10, 2/10, 3/10, dll). Ketika memilih untuk membeli tindakan, kita akan membeli jumlah * self.balance worth of BTC. Untuk tindakan jual, kita akan menjual jumlah * self.btc_held value of BTC.

Observation_space kami didefinisikan sebagai kumpulan titik terapung berturut-turut antara 0 dan 1, yang berbentuk seperti ((10, lookback_window_size + 1)); + 1 digunakan untuk menghitung durasi waktu saat ini. Untuk setiap durasi waktu di jendela kami akan mengamati nilai OHCLV.

Selanjutnya, kita perlu menulis metode reset untuk menginisialisasi lingkungan.

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()

Di sini kita menggunakan self._reset_session dan self._next_observation, yang belum kita definisikan. Mari kita definisikan terlebih dahulu.

Percakapan

img

我们环境的一个重要部分是交易会话的概念。如果我们将这个机器人部署到市场外,我们可能永远不会一次运行它超过几个月。出于这个原因,我们将限制self.df中连续帧数的数量,也就是我们的机器人连续一次能看到的帧数。

Dalam metode _reset_session kami, kami pertama-tama menetapkan kembali current_step menjadi 0; selanjutnya kami akan menetapkan steps_left menjadi angka acak antara 1 dan MAX_TRADING_SESSION, bagian yang akan kami definisikan di bagian atas program.

MAX_TRADING_SESSION = 100000 # ~2个月

Selanjutnya, jika kita ingin terus menerus menjelajahi bilangan bulat, kita harus mengaturnya untuk menjelajahi seluruh bilangan bulat, jika tidak, kita mengatur frame_start sebagai titik acak di self.df dan membuat bilangan bulat data baru yang disebut active_df, yang hanya merupakan sepotong dari self.df dan berasal dari frame_start ke 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]

Sebuah efek samping yang penting dari menjelajahi jumlah data dalam potongan acak adalah bahwa robot kita akan memiliki lebih banyak data yang unik untuk digunakan dalam pelatihan jangka panjang. Misalnya, jika kita hanya menjelajahi jumlah data secara serial (yaitu urutan dari 0 ke len (df)) maka kita hanya akan memiliki satu-satunya titik data yang sama banyaknya dengan jumlah data. Ruang pengamatan kita bahkan hanya dapat menggunakan jumlah negara yang berbeda pada setiap langkah waktu.

Namun, dengan mengevaluasi potongan-potongan data secara acak, kita dapat membuat koleksi hasil transaksi yang lebih bermakna untuk setiap langkah waktu dalam dataset awal, yaitu kombinasi dari perilaku transaksi dan perilaku harga yang dilihat sebelumnya untuk membuat lebih banyak dataset yang unik.

Pada langkah waktu 10 setelah pengaturan ulang lingkungan seri, robot kita akan selalu berjalan secara bersamaan di dalam dataset, dan setelah setiap langkah waktu ada tiga pilihan: beli, jual, atau tahan. Untuk masing-masing dari tiga pilihan tersebut, diperlukan pilihan lain: 10%, 20%,... atau 100% dari jumlah praktis tertentu. Ini berarti bahwa robot kita mungkin mengalami salah satu dari 103 dari 10 keadaan, total 1030 situasi.

Sekarang kembali ke lingkungan potongan acak kita. Pada langkah waktu 10, robot kita mungkin berada dalam langkah waktu len (df) manapun dalam jumlah data. Dengan asumsi bahwa pilihan yang sama dibuat setelah setiap langkah waktu, robot ini dapat mengalami satu-satunya keadaan dalam 30 detik dari setiap langkah waktu len (df) dalam 10 langkah waktu yang sama.

Meskipun hal ini mungkin menimbulkan kebisingan yang cukup besar untuk dataset besar, saya percaya bahwa robot harus diizinkan untuk belajar lebih banyak dari data terbatas kita. Kami masih akan menelusuri data pengujian kami secara berurutan untuk mendapatkan data terbaru, yang tampaknya sangat segar dan real-time, dengan harapan untuk mendapatkan pemahaman yang lebih akurat tentang efektivitas algoritma.

Dilihat dari mata robot

Pengamatan melalui lingkungan visual yang efektif sering membantu dalam memahami jenis fungsi yang akan digunakan robot kita. Misalnya, ini adalah visualisasi ruang yang dapat diamati dengan menggunakan rendering OpenCV.

img

Pengamatan pada lingkungan visualisasi OpenCV

Setiap baris dalam gambar mewakili satu baris dalam observation_space kita. Empat baris pertama yang memiliki frekuensi serupa, garis merah, mewakili data OHCL, dan titik oranye dan kuning di bawahnya mewakili transaksi. Bar biru yang berfluktuasi di bawahnya adalah nilai bersih robot, sedangkan bar yang lebih ringan di bawahnya mewakili transaksi robot.

Jika Anda melihat dengan seksama, Anda bahkan dapat membuat grafik sendiri. Di bawah bar volume transaksi adalah antarmuka yang mirip kode Morse yang menunjukkan sejarah transaksi. Tampaknya robot kita harus dapat sepenuhnya belajar dari data di observation_space kami, jadi mari kita lanjutkan. Di sini kita akan mendefinisikan metode_next_observation, dan data yang kita amati akan berskala dari 0 ke 1

  • Yang penting adalah hanya memperluas data yang diamati robot sejauh ini untuk mencegah pergeseran yang melampaui.
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

Tindakan

Kami telah membangun ruang pengamatan kami, sekarang saatnya untuk menulis fungsi tangga kami dan mengambil tindakan yang ditentukan oleh robot. Setiap kali kita sel.steps_left == 0 dari waktu perdagangan saat ini, kami akan menjual BTC yang kami miliki dan memanggil_reset_session ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((

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, {}

Mengambil tindakan perdagangan adalah sesederhana mendapatkan current_price, menentukan tindakan yang perlu dilakukan, dan jumlah yang harus dibeli atau dijual. Mari kita cepat menulis_take_action sehingga kita dapat menguji lingkungan kita.

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

最后,在同一方法中,我们会将交易附加到self.trades并更新我们的净值和账户历史。

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)

Robot kita sekarang dapat memulai lingkungan baru, menyelesaikan lingkungan itu secara bertahap, dan mengambil tindakan yang mempengaruhi lingkungan.

Lihatlah robot kami berdagang

Metode rendering kita bisa sesederhana memanggil print (self.net_worth), tetapi itu tidak cukup menarik. Sebaliknya, kita akan menggambar grafik yang sederhana yang berisi grafik terpisah dari volume transaksi dan nilai bersih kita.

我们将从我上一篇文章中获取StockTradingGraph.py中的代码,并重新设计它以适应比特币环境。你可以从我的Github中获取代码。

Perubahan pertama yang akan kita lakukan adalah memperbarui self.df [Date tag] menjadi self.df [Timestamp tag] dan menghapus semua panggilan ke date2num karena tanggal kita sudah dalam format Unix time tag. Selanjutnya, dalam metode rendering kami, kami akan memperbarui tag tanggal untuk mencetak tanggal yang dapat dibaca manusia, bukan angka.

from datetime import datetime

Pertama, kita mengimpor datetime, lalu kita akan menggunakan utcfromtimestampmethod untuk mengambil strings UTC dari setiap timestamp dan strftime, sehingga formatnya menjadi: Y-m-d H:M format string.

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

Akhirnya, kita mengubah self.df [Volume] menjadi self.df [Volume_(BTC) ] untuk mencocokkan dataset kita, dan setelah selesai, kita siap. Kembali ke Bitcoin TradingEnv kami, kita sekarang dapat menulis metode rendering untuk menampilkan grafik.

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)

Wow! Kita sekarang bisa melihat robot kita berdagang Bitcoin.

img

Menggunakan Matplotlib untuk memvisualisasikan transaksi robot kami

Tanda bayangan hijau mewakili pembelian BTC, dan tanda bayangan merah mewakili penjualan. Tanda bayangan putih di pojok kanan atas adalah nilai bersih saat ini dari robot, dan tanda bayangan di pojok kanan bawah adalah harga bitcoin saat ini. Sederhana dan elegan. Sekarang, saatnya untuk melatih robot kita dan melihat berapa banyak uang yang bisa kita hasilkan!

Waktu Pelatihan

Salah satu kritik yang saya terima dalam artikel sebelumnya adalah kurangnya verifikasi silang, tidak membagi data menjadi training set dan test set. Tujuan dari melakukan ini adalah untuk menguji akurasi model akhir pada data baru yang belum pernah dilihat sebelumnya. Meskipun bukan fokus dari artikel itu, itu sangat penting. Karena kami menggunakan data urutan waktu, kami tidak memiliki banyak pilihan dalam hal verifikasi silang.

Sebagai contoh, satu bentuk verifikasi silang yang umum disebut verifikasi k-fold, di mana Anda membagi data menjadi k kelompok yang sama, masing-masing kelompok secara terpisah menjadi kelompok uji, dan data yang tersisa digunakan sebagai kelompok pelatihan. Namun, data urutan waktu sangat bergantung pada waktu, yang berarti data berikutnya sangat bergantung pada data sebelumnya. Jadi k-fold tidak akan berfungsi, karena robot kita akan belajar dari data masa depan sebelum melakukan transaksi, yang merupakan keuntungan yang tidak adil.

Ketika diterapkan pada data deret waktu, kelemahan yang sama berlaku untuk sebagian besar strategi verifikasi silang lainnya. Oleh karena itu, kita hanya perlu menggunakan sebagian dari jumlah kubus data lengkap sebagai set pelatihan yang dimulai dari jumlah kubus hingga beberapa indeks arbitrer, dan menggunakan sisa data sebagai set uji.

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

Selanjutnya, karena lingkungan kami hanya disetel untuk menangani jumlah batch data tunggal, kami akan membuat dua lingkungan, satu untuk data pelatihan dan satu untuk data pengujian.

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

现在,训练我们的模型就像使用我们的环境创建机器人并调用model.learn一样简单。

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

Di sini, kita menggunakan papan tegangan sehingga kita dapat dengan mudah memvisualisasikan peta aliran tegangan kita dan melihat beberapa indikator kuantitatif tentang robot kita. Sebagai contoh, berikut adalah grafik imbalan diskon di mana banyak robot memiliki lebih dari 200.000 langkah waktu:

img

Wow, sepertinya robot kami sangat menguntungkan! Robot terbaik kami bahkan mampu mencapai keseimbangan 1000x selama 200.000 langkah, dengan rata-rata sisanya meningkat setidaknya 30 kali!

Pada saat itu, saya menyadari ada kesalahan di lingkungan... setelah memperbaiki kesalahan itu, ini adalah grafik hadiah baru:

img

Seperti yang Anda lihat, beberapa robot kami bekerja dengan baik, dan sisanya bangkrut sendiri. Namun, robot yang berkinerja baik dapat mencapai 10 kali lipat atau bahkan 60 kali lipat dari saldo awal. Saya harus mengakui bahwa semua robot yang menguntungkan dilatih dan diuji tanpa komisi, jadi tidak praktis bagi robot kami untuk mendapatkan uang sungguhan.

Mari kita uji robot kita di lingkungan pengujian (menggunakan data baru yang belum pernah mereka lihat sebelumnya) dan lihat bagaimana performa mereka.

img

Robot terlatih kami akan bangkrut saat berdagang dengan data uji baru.

Jelas, kita masih memiliki banyak pekerjaan yang harus dilakukan. Dengan hanya beralih model untuk menggunakan A2C pada basis yang stabil, bukan robot PPO2 saat ini, kita dapat secara signifikan meningkatkan kinerja kita pada dataset ini. Akhirnya, menurut saran Sean O'Gorman, kita dapat sedikit memperbarui fungsi reward kita sehingga kita dapat meningkatkan reward pada net, bukan hanya mencapai net tinggi dan tinggal di sana.

reward = self.net_worth - prev_net_worth

Hanya dua perubahan ini yang dapat secara signifikan meningkatkan kinerja test data set, dan seperti yang Anda lihat di bawah ini, kami akhirnya dapat memanfaatkan data baru yang tidak ada di training set.

img

Namun, kita bisa melakukan yang lebih baik. Untuk meningkatkan hasil ini, kita perlu mengoptimalkan hiperparameter kita dan melatih robot kita lebih lama.

Sampai saat ini, artikel ini sudah agak panjang, dan kami masih memiliki banyak detail untuk dipertimbangkan, jadi kami berniat untuk beristirahat di sini. Pada artikel berikutnya, kami akan menggunakan Bayesian Optimization untuk membedakan parameter super terbaik untuk ruang masalah kami, dan bersiap untuk melatih / menguji CUDA di GPU.

Kesimpulan

Dalam artikel ini, kami mulai membuat robot perdagangan Bitcoin yang menguntungkan dari awal dengan menggunakan pembelajaran penguatan.

1.使用OpenAI的gym从零开始创建比特币交易环境。

2.使用Matplotlib构建该环境的可视化。

3.使用简单的交叉验证对我们的机器人进行训练和测试。

4.略微调整我们的机器人以实现盈利

Meskipun robot perdagangan kami tidak menguntungkan seperti yang kami harapkan, kami telah bergerak ke arah yang benar. Lain kali, kami akan memastikan bahwa robot kami selalu mengalahkan pasar, dan kami akan melihat bagaimana robot perdagangan kami menangani data real-time.


Berkaitan

Lebih banyak