Dalam artikel ini, kita akan membuat dan menggunakan bilangan simpanan penguat untuk belajar bagaimana membuat bot perdagangan Bitcoin. Dalam tutorial ini, kita akan menggunakan gim OpenAI dan bot PPO dari perpustakaan stabil-baselines, sebuah cabang dari perpustakaan asas OpenAI.
Terima kasih kepada OpenAI dan DeepMind atas perisian sumber terbuka yang mereka berikan kepada penyelidik pembelajaran mendalam selama beberapa tahun terakhir. Jika anda belum melihat pencapaian luar biasa yang mereka capai dengan teknologi seperti AlphaGo, OpenAI Five dan AlphaStar, anda mungkin telah hidup di luar pengasingan tahun lalu, tetapi anda juga harus melihatnya.
Latihan AlphaStarhttps://deepmind.com/blog/alphastar-mastering-real-time-strategy-game-starcraft-ii/
Walaupun kita tidak akan membuat apa-apa yang mengagumkan, perdagangan robot Bitcoin dalam perdagangan sehari-hari masih menjadi sesuatu yang sukar.
Oleh itu, bukan sahaja belajar untuk berdagang sendiri... tetapi juga membiarkan robot berdagang untuk kita.
1.为我们的机器人创建gym环境以供其进行机器学习
2.渲染一个简单而优雅的可视化环境
3.训练我们的机器人,使其学习一个可获利的交易策略
Jika anda belum terbiasa dengan cara membuat persekitaran gim dari awal, atau cara sederhana untuk memvisualisasikan penyamaran persekitaran tersebut. Sebelum meneruskan, sila google artikel seperti ini. Kedua-dua tindakan ini tidak akan sukar dilakukan walaupun sebagai pengaturcara peringkat awal.
在本教程中,我们将使用Zielak生成的Kaggle数据集。如果您想下载源代码,我的Github仓库中会提供,同时也有.csv数据文件。好的,让我们开始吧。
Pertama, mari kita mengimport semua perpustakaan yang diperlukan. Pastikan anda memasang perpustakaan yang hilang dengan pip.
import gym
import pandas as pd
import numpy as np
from gym import spaces
from sklearn import preprocessing
Seterusnya, mari kita buat kelas kita untuk persekitaran tersebut. Kita perlu masukkan bilangan tetapan data pandas, serta pilihan initial_balance dan lookback_window_size, yang akan menunjukkan bilangan langkah masa lalu yang diperhatikan oleh bot dalam setiap langkah. Kita akan menetapkan purata bayaran setiap transaksi sebagai 0.075%, iaitu kadar pertukaran semasa Bitmex, dan menetapkan parameter siri sebagai purata palsu, yang bermaksud bahawa secara lalai, bilangan tetapan data kita akan melintasi satu petak secara rawak.
Kami juga memanggil data dropna (,) dan reset_index (), terlebih dahulu menghapuskan baris yang mempunyai nilai NaN, dan kemudian meletakkan semula indeks dengan bilangan senar, kerana kami telah menghapuskan 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)
Aktiviti_space kami di sini ditunjukkan sebagai satu kumpulan 3 pilihan (beli, jual atau pegang) dan satu lagi kumpulan 10 jumlah (1/10, 2/10, 3/10 dan sebagainya). Apabila memilih tindakan membeli, kami akan membeli jumlah * self.balance worth of BTC. Untuk tindakan menjual, kami akan menjual jumlah * self.btc_held value of BTC.
Observation_space kami ditakrifkan sebagai satu set titik terapung berturut-turut antara 0 dan 1, yang berbentuk sebagai ((10, lookback_window_size + 1)); + 1 digunakan untuk mengira jangka masa semasa. Untuk setiap jangka masa dalam tetingkap, kami akan melihat nilai OHCLV. Nilai bersih kami sama dengan jumlah BTC yang dibeli atau dijual, dan jumlah dolar yang kami belanjakan atau terima pada BTC ini.
Seterusnya, kita perlu menulis kaedah reset untuk memulakan persekitaran.
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 tentukan. Mari kita tentukan terlebih dahulu.
我们环境的一个重要部分是交易会话的概念。如果我们将这个机器人部署到市场外,我们可能永远不会一次运行它超过几个月。出于这个原因,我们将限制self.df中连续帧数的数量,也就是我们的机器人连续一次能看到的帧数。
Dalam kaedah _reset_session kami, kami mula-mula menetapkan semula current_step kepada 0; seterusnya kami akan menetapkan steps_left sebagai nombor rawak antara 1 hingga MAX_TRADING_SESSION, bahagian yang akan kami tentukan di atas program.
MAX_TRADING_SESSION = 100000 # ~2个月
Seterusnya, jika kita mahu melintasi bilangan senar secara berturut-turut, kita mesti menetapkannya untuk melintasi bilangan senar keseluruhan, jika tidak, kita menetapkan frame_start sebagai titik rawak dalam self.df dan mencipta bilangan senar data baru yang dinamakan 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]
Satu kesan sampingan yang penting untuk melawat bilangan data dalam sekeping rawak adalah bahawa robot kita akan mempunyai lebih banyak data yang unik untuk digunakan dalam latihan yang panjang. Sebagai contoh, jika kita hanya melawat bilangan data secara berturut-turut (iaitu urutan dari 0 hingga len (df)), maka kita hanya akan mempunyai titik data tunggal yang sama banyak dengan bilangan data. Ruang pemerhatian kita bahkan hanya boleh menggunakan jumlah keadaan yang tidak menentu pada setiap langkah masa.
Namun, dengan menyemak serpihan dataset secara rawak, kita boleh mencipta satu set hasil dagangan yang lebih bermakna untuk setiap jangka masa dalam dataset awal, iaitu gabungan tingkah laku dagangan dan tingkah laku harga yang dilihat sebelum ini untuk membuat lebih banyak dataset yang unik.
Pada langkah masa 10 selepas penyesuaian semula persekitaran siri, robot kita akan sentiasa berjalan secara serentak dalam set data, dan mempunyai tiga pilihan selepas setiap langkah masa: beli, jual, atau tahan. Untuk setiap tiga pilihan ini memerlukan satu lagi pilihan: 10%, 20%,... atau 100% kuantiti tertentu. Ini bermaksud bahawa robot kita mungkin menghadapi salah satu daripada 10 daripada 103 keadaan, dengan jumlah 1030 keadaan.
Sekarang kembali ke persekitaran potongan rawak kita. Pada langkah masa 10, robot kita mungkin berada dalam langkah masa len (df) mana-mana dalam bilangan datanya. Mengandaikan membuat pilihan yang sama selepas setiap langkah masa, ini bermaksud bahawa robot itu boleh mengalami keadaan unik dalam 30 detik dari mana-mana len (df) dalam 10 langkah masa yang sama.
Walaupun ini mungkin menimbulkan bunyi bising yang cukup besar untuk set data yang besar, saya percaya bahawa robot harus dibenarkan untuk belajar lebih banyak daripada jumlah data yang terhad. Kami masih akan melalui data ujian kami secara berturut-turut untuk mendapatkan data terkini, yang kelihatan seperti yang segar dan nyata, dengan harapan untuk mendapatkan pemahaman yang lebih tepat mengenai keberkesanan algoritma.
Dengan melihat persekitaran visual yang berkesan, ia sering membantu untuk mengetahui jenis fungsi yang akan digunakan oleh robot kita. Sebagai contoh, ini adalah visualisasi ruang yang dapat dilihat menggunakan rendering OpenCV.
Pengamatan terhadap persekitaran visual OpenCV
Setiap baris dalam imej mewakili satu baris dalam observation_space kita; 4 baris pertama yang mempunyai frekuensi yang serupa dengan garis merah mewakili data OHCL, dan titik oren dan kuning di bawah mewakili jumlah transaksi; bar biru yang berfluktuasi di bawah adalah nilai bersih bot, dan bar yang lebih ringan di bawah mewakili transaksi bot.
Jika anda melihat dengan teliti, anda juga boleh membuat grafik sendiri. Di bawah bar kuantiti transaksi adalah antara muka seperti kod Morse yang menunjukkan sejarah urus niaga. Nampaknya robot kita harus dapat belajar sepenuhnya dari data dalam observation_space kami, jadi mari kita teruskan. Di sini kita akan menentukan kaedah _next_observation, dan data yang kita amati akan berskala dari 0 hingga 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
Kami telah menubuhkan ruang pemerhatian kami, sekarang adalah masa untuk menulis fungsi tangga kami dan mengambil tindakan yang ditetapkan oleh bot. Setiap kali kami berada dalam self.steps_left == 0 pada waktu dagangan semasa, kami akan menjual BTC yang kami pegang 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, {}
Melakukan tindakan dagangan adalah semudah mendapatkan harga semasa, menentukan tindakan yang perlu dilakukan, dan jumlah yang dibeli atau dijual. Mari kita menulis tindakan cepat supaya kita boleh menguji persekitaran 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 kini boleh memulakan persekitaran baru, menyelesaikan persekitaran itu secara beransur-ansur, dan mengambil tindakan yang mempengaruhi persekitaran.
Kaedah rendering kami boleh menjadi mudah seperti memanggil print ((self.net_worth), tetapi ini tidak cukup menyeronokkan. Sebaliknya, kami akan melukis grafik yang mudah, yang mengandungi grafik berasingan mengenai jumlah transaksi dan nilai bersih kami.
我们将从我上一篇文章中获取StockTradingGraph.py中的代码,并重新设计它以适应比特币环境。你可以从我的Github中获取代码。
Perubahan pertama yang akan kita lakukan ialah mengemas kini self.df [
from datetime import datetime
Mula-mula, kita mengimport perpustakaan datetime, dan kemudian kita akan menggunakan utcfromtimestampmethod untuk mendapatkan tali UTC dari setiap tali masa dan strftime, sehingga formatnya menjadi: Y-m-d H: M format tali.
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 [
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)
Kami boleh melihat robot kami berdagang Bitcoin sekarang.
Menggunakan Matplotlib untuk memvisualisasikan transaksi robot kami
Tag bayangan hijau mewakili pembelian BTC, dan tag bayangan merah mewakili penjualan. Label putih di sudut kanan atas adalah nilai bersih bot semasa, dan tag kanan bawah adalah harga Bitcoin semasa. Sederhana dan elegan. Sekarang, sudah tiba masanya untuk melatih robot kita dan lihat berapa banyak wang yang boleh kita hasilkan!
Salah satu kritikan yang saya terima dalam artikel sebelumnya adalah kekurangan pengesahan silang, tidak membahagikan data kepada set latihan dan set ujian. Tujuan melakukan ini adalah untuk menguji ketepatan model akhir pada data baru yang tidak pernah dilihat sebelumnya. Walaupun ini bukan tumpuan artikel itu, ia sangat penting. Oleh kerana kami menggunakan data siri masa, kami tidak mempunyai banyak pilihan dalam hal pengesahan silang.
Sebagai contoh, satu bentuk pengesahan silang yang biasa dipanggil pengesahan k-fold, di mana anda membahagikan data ke dalam k kumpulan yang sama, masing-masing membahagikan satu kumpulan sebagai kumpulan ujian, dan menggunakan data yang lain sebagai kumpulan latihan. Walau bagaimanapun, data urutan masa sangat bergantung pada masa, yang bermaksud data kemudian sangat bergantung pada data sebelumnya. Oleh itu, k-fold tidak akan berfungsi, kerana robot kita akan belajar dari data masa depan sebelum berdagang, yang merupakan kelebihan yang tidak adil.
Apabila digunakan untuk data siri masa, kelemahan yang sama berlaku untuk kebanyakan strategi pengesahan silang yang lain. Oleh itu, kita hanya perlu menggunakan sebahagian daripada bilangan senar data lengkap sebagai satu set latihan yang bermula dari bilangan senar hingga beberapa indeks rawak, dan menggunakan sisa data sebagai satu set ujian.
slice_point = int(len(df) - 100000)
train_df = df[:slice_point]
test_df = df[slice_point:]
Seterusnya, kerana persekitaran kami hanya disiapkan untuk memproses bilangan tetapan data tunggal, kami akan mencipta dua persekitaran, satu untuk data latihan dan satu untuk data ujian.
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, kami menggunakan papan ketegangan, jadi kami boleh dengan mudah memvisualisasikan carta aliran ketegangan kami dan melihat beberapa metrik kuantitatif mengenai robot kami. Sebagai contoh, berikut adalah carta ganjaran diskaun di mana banyak robot melebihi 200,000 langkah masa:
Wow, nampaknya robot kami sangat menguntungkan! Robot terbaik kami bahkan dapat mencapai keseimbangan 1000x dalam 200,000 langkah, dengan rata-rata selebihnya meningkat sekurang-kurangnya 30 kali!
Pada masa itu, saya sedar ada kesilapan dalam persekitaran... selepas membetulkan kesilapan itu, ini adalah carta ganjaran baru:
Seperti yang anda lihat, beberapa robot kami melakukan dengan baik, yang lain akan gagal sendiri. Walau bagaimanapun, robot yang berprestasi tinggi boleh mencapai 10 kali ganda atau 60 kali ganda baki awal. Saya mesti mengakui bahawa semua robot yang menguntungkan dilatih dan diuji tanpa bayaran, jadi ia tidak praktikal untuk robot kami mendapatkan wang sebenar. Tetapi sekurang-kurangnya kami mendapat arah!
Mari kita uji robot kita dalam persekitaran ujian (menggunakan data baru yang tidak pernah dilihat sebelum ini) dan lihat bagaimana ia akan bertindak.
Robot kita yang terlatih akan rosak apabila berdagang dengan data ujian baru.
Jelas sekali, kita masih mempunyai banyak kerja yang perlu dilakukan. Dengan hanya menukar model untuk menggunakan A2C pada garis asas yang stabil, dan bukan robot PPO2 semasa, kita boleh meningkatkan prestasi kita pada dataset ini dengan ketara. Akhirnya, mengikut cadangan Sean O'Gorman, kita boleh mengemas kini fungsi ganjaran kita sedikit supaya kita dapat menambah ganjaran dalam nilai bersih, dan bukan hanya mencapai nilai bersih yang tinggi dan tinggal di sana.
reward = self.net_worth - prev_net_worth
Hanya dua perubahan ini dapat meningkatkan prestasi kumpulan data ujian dengan ketara, dan seperti yang anda lihat di bawah, kami akhirnya dapat memanfaatkan data baru yang tidak ada dalam kumpulan latihan.
Namun, kita boleh berbuat lebih baik. Untuk meningkatkan hasil ini, kita perlu mengoptimumkan hiperparameter kita dan melatih robot kita untuk masa yang lebih lama.
Setakat ini, artikel ini agak panjang, dan kita masih mempunyai banyak butiran untuk dipertimbangkan, jadi kita berniat untuk berehat di sini. Dalam artikel seterusnya, kita akan menggunakan pengoptimuman Bayes untuk menentukan parameter super terbaik untuk ruang masalah kita dan bersiap sedia untuk latihan / ujian menggunakan CUDA pada GPU.
Dalam artikel ini, kami mula menggunakan pembelajaran penguat untuk membuat bot perdagangan Bitcoin yang menguntungkan dari awal. Kami dapat menyelesaikan tugas berikut:
1.使用OpenAI的gym从零开始创建比特币交易环境。
2.使用Matplotlib构建该环境的可视化。
3.使用简单的交叉验证对我们的机器人进行训练和测试。
4.略微调整我们的机器人以实现盈利
Walaupun robot dagangan kami tidak begitu menguntungkan seperti yang kami harapkan, kami telah bergerak ke arah yang betul. Lain kali, kami akan memastikan bahawa robot kami selalu mengalahkan pasaran, dan kami akan melihat bagaimana robot dagangan kami memproses data masa nyata. Sila teruskan menonton artikel seterusnya, dan selamat tinggal Bitcoin!