Sumber dimuat naik... memuat...

Buat robot perdagangan Bitcoin yang tidak akan kehilangan wang

Penulis:FMZ~Lydia, Dicipta: 2023-02-01 11:52:21, Dikemas kini: 2023-09-18 19:40:25

img

Buat robot perdagangan Bitcoin yang tidak akan kehilangan wang

Mari gunakan pembelajaran penguat dalam AI untuk membina robot perdagangan mata wang digital.

Dalam artikel ini, kita akan membuat dan menggunakan nombor bingkai pembelajaran yang dipertingkatkan untuk belajar bagaimana membuat robot perdagangan Bitcoin. Dalam tutorial ini, kita akan menggunakan gim OpenAI dan robot PPO dari perpustakaan asas stabil, yang merupakan cawangan perpustakaan asas OpenAI.

Terima kasih banyak untuk perisian sumber terbuka yang disediakan oleh OpenAI dan DeepMind untuk penyelidik pembelajaran mendalam dalam beberapa tahun kebelakangan ini. Jika anda belum melihat pencapaian luar biasa mereka dengan AlphaGo, OpenAI Five, AlphaStar dan teknologi lain, anda mungkin hidup dalam pengasingan tahun lalu, tetapi anda harus memeriksanya.

img

Latihan AlphaStar:https://deepmind.com/blog/alphastar-mastering-real-time-strategy-game-starcraft-ii/

Walaupun kita tidak akan mencipta apa-apa yang mengagumkan, ia masih tidak mudah untuk berdagang robot Bitcoin dalam transaksi harian.

Tiada nilai dalam apa-apa yang terlalu mudah.

Oleh itu, bukan sahaja kita perlu belajar untuk berdagang sendiri, tetapi juga membiarkan robot berdagang untuk kita.

Rancangan

img

  1. Buat persekitaran gimnasium untuk robot kita untuk melakukan pembelajaran mesin

  2. Membuat persekitaran visual yang mudah dan elegan

  3. Latih robot kita untuk belajar strategi perdagangan yang menguntungkan

Jika anda tidak biasa dengan bagaimana untuk membuat persekitaran gim dari awal, atau bagaimana untuk hanya membuat visualisasi persekitaran ini. Sebelum meneruskan, sila berasa bebas untuk google artikel jenis ini. kedua-dua tindakan ini tidak akan sukar walaupun untuk pengaturcara yang paling junior.

Memulakan

Dalam tutorial ini, kita akan menggunakan dataset Kaggle yang dihasilkan oleh Zielak. Jika anda ingin memuat turun kod sumber, ia akan disediakan di repositori Github saya, bersama dengan fail data.csv. Ok, mari kita mulakan.

Pertama, mari kita import semua perpustakaan yang diperlukan.

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

Seterusnya, mari kita mencipta kelas kita untuk persekitaran. Kita perlu lulus dalam nombor bingkai data Pandas dan pilihan initial_balance dan lookback_window_size, yang akan menunjukkan bilangan langkah masa lalu yang diperhatikan oleh robot dalam setiap langkah. Kami lalai komisen setiap transaksi kepada 0.075%, iaitu kadar pertukaran semasa Bitmex, dan lalai parameter siri kepada palsu, yang bermaksud bahawa nombor bingkai data kita akan dilalui oleh serpihan rawak secara lalai.

Kami juga memanggil dropna() dan reset_index() pada data, terlebih dahulu memadam baris dengan nilai NaN, dan kemudian menetapkan semula indeks nombor bingkai, kerana kami telah memadam 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 diwakili sebagai kumpulan 3 pilihan (beli, jual atau tahan) di sini dan kumpulan lain 10 jumlah (1/10, 2/10, 3/10, dan lain-lain). Apabila kita memilih untuk membeli, kita akan membeli jumlah * self.balance kata BTC. Untuk menjual, kita akan menjual jumlah * self.btc_held nilai BTC. Sudah tentu, memegang akan mengabaikan jumlah dan tidak melakukan apa-apa.

Observation_space kami ditakrifkan sebagai titik terapung berterusan yang ditetapkan antara 0 dan 1, dan bentuknya adalah (10, lookback_window_size+1). + 1 digunakan untuk mengira langkah masa semasa. Untuk setiap langkah masa di tingkap, kami akan memerhatikan nilai OHCLV. Nilai bersih kami sama dengan jumlah BTC yang kami beli atau jual, 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 mereka terlebih dahulu.

Sesi Dagangan

img

Satu bahagian penting dalam persekitaran kita adalah konsep sesi dagangan. Jika kita menggunakan robot ini di luar pasaran, kita mungkin tidak pernah menjalankannya selama lebih dari beberapa bulan pada satu masa. Atas sebab ini, kita akan mengehadkan bilangan bingkai berturut-turut dalam self.df, yang merupakan bilangan bingkai yang robot kita boleh lihat pada satu masa.

Dalam kaedah _reset_session kami, kami menetapkan semula current_step kepada 0 terlebih dahulu. Seterusnya, kami akan menetapkan steps_left kepada nombor rawak antara 1 hingga MAX_TRADING_SESSIONS, yang akan kami tentukan di bahagian atas program.

MAX_TRADING_SESSION = 100000 # ~2 months

Seterusnya, jika kita mahu melintasi bilangan bingkai secara berturut-turut, kita mesti menetapkannya untuk melintasi keseluruhan bilangan bingkai, jika tidak kita menetapkan frame_start ke titik rawak dalam self.df dan membuat bingkai data baru bernama active_df, yang hanya sepotong self.df dan ia mendapatkan 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]

Kesan sampingan yang penting dari melintasi bilangan bingkai data dalam kepingan rawak adalah bahawa robot kita akan mempunyai data yang lebih unik untuk digunakan dalam latihan jangka panjang. Sebagai contoh, jika kita hanya melintasi bilangan bingkai data secara siri (iaitu, dari 0 hingga len(df)), kita hanya akan mempunyai banyak titik data unik seperti bilangan bingkai data. Ruang pemerhatian kita hanya boleh menggunakan bilangan keadaan diskrit pada setiap langkah masa.

Walau bagaimanapun, dengan melintasi kepingan set data secara rawak, kita boleh membuat satu set hasil perdagangan yang lebih bermakna untuk setiap langkah masa dalam set data awal, iaitu, gabungan tingkah laku perdagangan dan tingkah laku harga yang dilihat sebelum ini untuk membuat set data yang lebih unik.

Apabila langkah masa selepas menetapkan semula persekitaran siri adalah 10, robot kami akan sentiasa berjalan dalam set data pada masa yang sama, dan terdapat tiga pilihan selepas setiap langkah masa: beli, jual atau tahan. Untuk setiap tiga pilihan, anda memerlukan pilihan lain: 10%, 20%,... atau 100% daripada jumlah pelaksanaan tertentu. Ini bermakna bahawa robot kami mungkin menghadapi salah satu daripada 10 keadaan mana-mana 103, sejumlah 1030 kes.

Sekarang kembali ke persekitaran slicing rawak kita. Apabila langkah masa adalah 10, robot kita boleh berada dalam apa-apa langkah waktu len(df) dalam bilangan bingkai data. Dengan mengandaikan bahawa pilihan yang sama dibuat selepas setiap langkah masa, ia bermakna bahawa robot boleh mengalami keadaan unik mana-mana len(df) kepada kuasa ke-30 dalam 10 langkah masa yang sama.

Walaupun ini boleh membawa bunyi bising yang besar kepada set data yang besar, saya percaya bahawa robot harus dibenarkan untuk belajar lebih banyak dari data yang terhad kami. Kami masih akan melintasi data ujian kami dengan cara siri untuk mendapatkan data yang paling segar dan nampaknya real-time , untuk mendapatkan pemahaman yang lebih tepat melalui keberkesanan algoritma.

Dilihat melalui mata robot

Melalui pemerhatian persekitaran visual yang berkesan, ia sering membantu untuk memahami jenis fungsi yang akan digunakan robot kita.

Pengamatan persekitaran visualisasi OpenCV

Setiap baris dalam imej mewakili baris dalam observation_space kami. Empat baris pertama garis merah dengan frekuensi yang sama mewakili data OHCL, dan titik oren dan kuning tepat di bawah mewakili jumlah dagangan. Bar biru turun naik di bawah mewakili nilai bersih robot, sementara bar yang lebih ringan di bawah mewakili transaksi robot.

Jika anda memerhatikan dengan teliti, anda juga boleh membuat peta lilin sendiri. Di bawah bar jumlah dagangan adalah antara muka kod Morse, memaparkan sejarah dagangan. Nampaknya robot kita harus dapat belajar dengan mencukupi dari data dalam observation_space kita, jadi mari kita teruskan. Di sini kita akan menentukan kaedah _next_observation, kita skala data yang diperhatikan dari 0 hingga 1.

  • Adalah penting untuk memperluaskan hanya data yang diperhatikan oleh robot setakat ini untuk mengelakkan penyimpangan utama.
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 menubuhkan ruang pemerhatian kami, dan kini tiba masanya untuk menulis fungsi tangga kami, dan kemudian mengambil tindakan yang dijadualkan robot. Setiap kali self.steps_left == 0 untuk sesi dagangan semasa kami, kami akan menjual BTC kami dan memanggil _reset_session(). Jika tidak, kami akan menetapkan ganjaran kepada nilai bersih semasa. Jika kami kehabisan dana, kami akan menetapkan selesai ke Benar.

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 semudah mendapatkan harga semasa, menentukan tindakan yang akan dilaksanakan dan kuantiti untuk membeli atau menjual.

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

Akhirnya, dengan kaedah yang sama, kita akan melampirkan urus niaga kepada perdagangan sendiri dan mengemas kini nilai bersih dan sejarah akaun kita.

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 boleh memulakan persekitaran baru sekarang, melengkapkan persekitaran secara beransur-ansur, dan mengambil tindakan yang memberi kesan kepada persekitaran.

Perhatikan perdagangan robot kita

Kaedah penyampaian kita boleh semudah memanggil print (self.net_word), tetapi ia tidak cukup menarik. Sebaliknya, kita akan melukis carta lilin mudah, yang mengandungi carta berasingan lajur jumlah dagangan dan kekayaan bersih kita.

Kita akan masukkan kodnya.StockTrackingGraph.pyAnda boleh mendapatkan kodnya dari Github saya.

Perubahan pertama yang perlu kita buat ialah mengemas kini self.df ['Tanggal '] kepada self.df [Timestamp] dan memadam semua panggilan ke date2num, kerana tarikh kita sudah dalam format timestamp unix. Seterusnya, dalam kaedah rendering kita, kita akan mengemas kini tag tarikh untuk mencetak tarikh yang boleh dibaca manusia dan bukannya nombor.

from datetime import datetime

Pertama, mengimport perpustakaan waktu tarikh, dan kemudian kita akan menggunakan utcfromtimestampmethod untuk mendapatkan rentetan UTC dari setiap stempel masa dan strftime supaya ia diformat sebagai rentetan: format Y-m-d H: M.

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

Akhirnya, kita akan menukar self. df['Volume '] kepada self. df[Volume_ (BTC) ] untuk sepadan dengan set data kita. Selepas menyelesaikan ini, kita sudah bersedia. Kembali ke BitcoinTradingEnv kita, kita boleh menulis kaedah rendering untuk memaparkan carta sekarang.

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)

Kita boleh menonton robot kita berdagang Bitcoin sekarang.

Bayangkan robot kami berdagang dengan Matplotlib

Label hantu hijau mewakili pembelian BTC, dan label hantu merah mewakili penjualan. Label putih di sudut kanan atas adalah nilai bersih robot semasa, dan label di sudut kanan bawah adalah harga Bitcoin semasa. Ia mudah dan elegan. Sekarang, sudah tiba masanya untuk melatih robot kita dan melihat berapa banyak wang yang kita boleh buat!

Masa latihan

Salah satu kritikan yang saya terima dalam artikel sebelumnya adalah kekurangan pengesahan silang dan kegagalan untuk membahagikan data ke dalam set latihan dan set ujian. Tujuan ini adalah untuk menguji ketepatan model akhir pada data baru yang tidak pernah dilihat sebelum ini. Walaupun ini bukan tumpuan artikel itu, ia sangat penting. Kerana kita menggunakan data siri masa, kita tidak mempunyai banyak pilihan dalam pengesahan silang.

Sebagai contoh, satu bentuk umum pengesahan silang dipanggil pengesahan k-ganda. Dalam pengesahan ini, anda membahagikan data ke dalam k kumpulan yang sama, satu demi satu, secara individu, sebagai kumpulan ujian dan menggunakan sisa data sebagai kumpulan latihan. Walau bagaimanapun, data siri masa sangat bergantung pada masa, yang bermaksud bahawa data berikutnya sangat bergantung pada data sebelumnya. Jadi k-ganda 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 nombor bingkai data lengkap sebagai set latihan dari nombor bingkai ke beberapa indeks sewenang-wenang, dan menggunakan sisa data sebagai set ujian.

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

Seterusnya, kerana persekitaran kita hanya ditubuhkan untuk mengendalikan satu bilangan bingkai data, kita 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)])

Sekarang, melatih model kita adalah semudah membuat robot menggunakan persekitaran kita dan memanggil model.learn.

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

Di sini, kita menggunakan plat tensor, jadi kita boleh memvisualisasikan carta aliran tensor dengan mudah dan melihat beberapa penunjuk kuantitatif mengenai robot kita.

img

Wow, nampaknya robot kami sangat menguntungkan! Robot terbaik kami boleh mencapai keseimbangan 1000x dalam 200,000 langkah, dan selebihnya akan meningkat sekurang-kurangnya 30 kali rata-rata!

Pada masa ini, saya sedar bahawa terdapat kesilapan dalam persekitaran... selepas memperbaiki pepijat, ini adalah carta ganjaran baru:

img

Seperti yang anda lihat, beberapa robot kami berjalan dengan baik, sementara yang lain akan muflis. Walau bagaimanapun, robot dengan prestasi yang baik boleh mencapai 10 kali atau bahkan 60 kali baki awal paling banyak. Saya mesti mengakui bahawa semua mesin yang menguntungkan dilatih dan diuji tanpa komisen, jadi tidak realistik bagi robot kami untuk membuat wang sebenar. Tetapi sekurang-kurangnya kami menemui jalan!

Mari kita uji robot kita dalam persekitaran ujian (menggunakan data baru yang tidak pernah mereka lihat sebelum ini) untuk melihat bagaimana mereka akan berkelakuan.

img

Robot kita yang terlatih dengan baik akan muflis apabila menukar data ujian baru.

Jelas sekali, kita masih mempunyai banyak kerja yang perlu dilakukan. Dengan hanya menukar model untuk menggunakan A2C dengan garis asas yang stabil dan bukannya robot PPO2 semasa, kita boleh meningkatkan prestasi kita pada set data ini dengan ketara. Akhirnya, menurut cadangan Sean OGorman, kita boleh mengemas kini fungsi ganjaran kita sedikit, supaya kita boleh menambah ganjaran kepada kekayaan bersih, daripada hanya merealisasikan kekayaan bersih yang tinggi dan tinggal di sana.

reward = self.net_worth - prev_net_worth

Dua perubahan ini sahaja dapat meningkatkan prestasi set data ujian dengan ketara, dan seperti yang anda lihat di bawah, kami akhirnya dapat memanfaatkan data baru yang tidak tersedia dalam set latihan.

img

Tetapi kita boleh berbuat lebih baik. Untuk meningkatkan hasil ini, kita perlu mengoptimumkan parameter super 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 merancang untuk berehat di sini.

Kesimpulan

Dalam artikel ini, kita mula menggunakan pembelajaran penguatan untuk membuat robot perdagangan Bitcoin yang menguntungkan dari awal.

  1. Buat persekitaran perdagangan Bitcoin dari awal menggunakan gim OpenAI.

  2. Gunakan Matplotlib untuk membina visualisasi persekitaran.

  3. Gunakan pengesahan silang yang mudah untuk melatih dan menguji robot kita.

  4. Sesuaikan robot kita sedikit untuk mencapai keuntungan.

Walaupun robot dagangan kami tidak menguntungkan seperti yang kami harapkan, kami sudah bergerak ke arah yang betul. Lain kali, kami akan memastikan bahawa robot kami dapat mengalahkan pasaran secara konsisten. Kami akan melihat bagaimana robot dagangan kami memproses data masa nyata. Sila terus mengikuti artikel seterusnya dan Viva Bitcoin!


Berkaitan

Lebih lanjut