0
fokus pada
78
Pengikut

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Dibuat di: 2019-06-27 10:58:40, diperbarui pada: 2024-12-24 20:16:45
comments   0
hits   5916

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Mari kita gunakan pembelajaran penguatan dalam kecerdasan buatan untuk membuat robot perdagangan mata uang kripto

Dalam artikel ini, kita akan membuat dan menerapkan kerangka pembelajaran penguatan untuk mempelajari cara membuat bot perdagangan Bitcoin. Dalam tutorial ini, kita akan menggunakan gym OpenAI dan robot PPO dari pustaka stable-baselines, yang merupakan percabangan dari pustaka OpenAI baselines.

Terima kasih banyak kepada OpenAI dan DeepMind karena telah menyediakan perangkat lunak sumber terbuka bagi para peneliti pembelajaran mendalam selama beberapa tahun terakhir. Jika Anda belum melihat pencapaian luar biasa yang telah mereka buat dengan teknologi seperti AlphaGo, OpenAI Five, dan AlphaStar, Anda mungkin telah hidup dalam isolasi selama setahun terakhir, tetapi Anda harus memeriksanya.

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Pelatihan AlphaStar https://deepmind.com/blog/alphastar-menguasai-permainan-strategi-waktu-nyata-starcraft-ii/

Meskipun kami tidak akan menciptakan sesuatu yang mengesankan, perdagangan robot Bitcoin masih bukan tugas yang mudah dalam perdagangan sehari-hari. Namun, seperti yang pernah dikatakan Teddy Roosevelt,

Hal-hal yang datang dengan mudah tidak ada nilainya.

Jadi, jangan hanya belajar trading untuk diri sendiri…tetapi biarkan juga robot trading untuk kita.

rencana

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

  1. Buat lingkungan gym agar robot kita dapat melakukan pembelajaran mesin

  2. Render lingkungan visualisasi yang sederhana dan elegan

  3. Melatih robot kita untuk mempelajari strategi perdagangan yang menguntungkan

Jika Anda belum familier dengan cara membuat lingkungan pusat kebugaran dari awal, atau cara sekadar membuat visualisasi lingkungan tersebut. Jangan ragu untuk mencari artikel seperti ini di Google sebelum melanjutkan. Kedua tindakan ini tidak akan sulit bahkan bagi programmer paling pemula.

Memulai

Dalam tutorial ini, kita akan menggunakan dataset Kaggle yang dihasilkan oleh Zielak. Jika Anda ingin mengunduh kode sumbernya, kode tersebut tersedia di repositori Github saya, bersama dengan file data .csv. Oke, mari kita mulai.

Pertama, mari impor semua pustaka yang diperlukan. Pastikan untuk menginstal pustaka yang hilang menggunakan pip.

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

Selanjutnya, mari kita buat kelas untuk lingkungan. Kita perlu memasukkan kerangka data pandas, dan juga initial_balance (opsional) dan lookback_window_size (ukuran jendela belakang) yang akan menentukan berapa banyak langkah waktu lampau yang akan diamati robot di setiap langkah. Kami menetapkan komisi per perdagangan secara default sebesar 0,075%, nilai tukar saat ini di Bitmex, dan menetapkan parameter serial secara default ke false, yang berarti kerangka data kami akan dilintasi dalam potongan acak secara default.

Kami juga memanggil dropna() dan reset_index() pada data, pertama untuk menghapus baris dengan nilai NaN dan kemudian untuk mengatur ulang indeks untuk nomor bingkai karena kami telah membuang 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)

Ruang tindakan kita di sini direpresentasikan sebagai satu set 3 pilihan (beli, jual, atau tahan) dan satu set 10 jumlah lainnya (110, 210, 310, dst.). Saat memilih tindakan beli, kita akan membeli sejumlah BTC * saldo kita sendiri. Untuk penjualan, kami akan menjual sejumlah * self.btc_held senilai BTC. Tentu saja, tindakan menahan mengabaikan jumlah dan tidak melakukan apa pun.

Ruang observasi kita didefinisikan sebagai sekumpulan float kontinu antara 0 dan 1, dengan bentuk (10, lookback_window_size + 1). + 1 digunakan untuk menghitung langkah waktu saat ini. Untuk setiap langkah waktu dalam jendela, kita akan mengamati nilai OHCLV. Kekayaan bersih kita sama dengan jumlah BTC yang dibeli atau dijual, dan jumlah total USD yang kita belanjakan atau terima dari BTC tersebut.

Berikutnya, 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, kami belum mendefinisikannya. Mari kita definisikan terlebih dahulu.

Sesi Perdagangan

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Bagian penting dari lingkungan kami adalah konsep sesi perdagangan. Jika kami menerapkan bot ini di luar pasar, kami mungkin tidak akan pernah menjalankannya lebih dari beberapa bulan saja. Karena alasan ini, kami akan membatasi jumlah frame berurutan dalam self.df, yaitu jumlah frame yang dapat dilihat robot kami dalam satu waktu.

Dalam metode _reset_session, pertama-tama kita mereset current_step ke 0. Berikutnya, kita akan menetapkan steps_left ke angka acak antara 1 dan MAX_TRADING_SESSION, yang akan kita definisikan di bagian atas program.

MAX_TRADING_SESSION = 100000 # ~2个月

Selanjutnya, jika kita ingin mengulang frame secara terus-menerus, kita harus mengaturnya untuk mengulang seluruh frame, jika tidak, kita mengatur frame_start ke titik acak di self.df dan membuat frame data baru yang disebut active_df yang hanya self. Sebuah irisan dari df 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]

Efek samping penting dari pengulangan sejumlah bingkai data dalam pemotongan acak adalah bahwa robot kita akan memiliki lebih banyak data unik untuk digunakan saat berlatih dalam jangka waktu lama. Misalnya, jika kita sekadar mengulang sejumlah kerangka data secara serial (yaitu dari 0 hingga len(df)), maka kita hanya akan memiliki banyak titik data unik sebanyak jumlah kerangka datanya. Ruang pengamatan kita bahkan hanya dapat mengadopsi sejumlah keadaan diskret pada setiap langkah waktu.

Akan tetapi, dengan melakukan iterasi acak pada potongan-potongan himpunan data, kita dapat membuat serangkaian hasil perdagangan yang lebih berarti untuk setiap langkah waktu dalam himpunan data awal, yaitu, kombinasi tindakan perdagangan dan tindakan harga yang terlihat sebelumnya untuk membuat himpunan data yang lebih unik. Mari saya jelaskan dengan sebuah contoh.

Pada langkah waktu ke-10 setelah mengatur ulang lingkungan serial, robot kita akan selalu berjalan secara bersamaan dalam kumpulan data dan akan memiliki 3 pilihan setelah setiap langkah waktu: Beli, Jual, atau Tahan. Untuk masing-masing ketiga opsi ini, ada opsi lain: 10%, 20%, … atau 100% dari jumlah implementasi spesifik. Artinya robot kita dapat menghadapi salah satu dari 103 pangkat 10, sehingga totalnya ada 1030 situasi.

Sekarang kembali ke lingkungan pemotongan acak kita. Pada langkah waktu 10, robot kita mungkin berada pada langkah waktu len(df) apa pun dalam jumlah bingkai data. Dengan asumsi pilihan yang sama dibuat setelah setiap langkah waktu, ini berarti robot dapat melalui status unik len(df)30 dalam 10 langkah waktu yang sama.

Meskipun hal ini mungkin menimbulkan banyak gangguan pada kumpulan data besar, saya yakin hal ini akan memungkinkan robot untuk belajar lebih banyak dari jumlah data terbatas yang kita miliki. Kami akan tetap mengulang data pengujian kami secara serial untuk mendapatkan data terbaru yang seakan-akan ‘data waktu nyata’ agar memperoleh pemahaman yang lebih akurat tentang efektivitas algoritma.

Melalui mata robot

Sering kali sangat membantu untuk memiliki gambaran visual yang baik tentang lingkungan guna memahami jenis fungsi yang akan digunakan robot kita. Misalnya, berikut adalah visualisasi ruang yang dapat diamati yang dirender menggunakan OpenCV.

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Pengamatan lingkungan visualisasi OpenCV

Tiap baris pada gambar mewakili baris di ruang observasi kita. 4 baris pertama dari garis merah frekuensi serupa mewakili data OHCL, dan titik oranye dan kuning tepat di bawahnya mewakili volume. Batang biru yang berfluktuasi di bawah adalah ekuitas bot, sedangkan batang yang lebih terang di bawahnya mewakili perdagangan bot.

Jika Anda perhatikan dengan seksama, Anda bahkan dapat membuat grafik lilin Anda sendiri. Di bawah bilah volume terdapat antarmuka seperti kode Morse yang menampilkan riwayat perdagangan. Sepertinya bot kita seharusnya dapat belajar dengan baik dari data di ruang observasi kita, jadi mari kita lanjutkan. Di sini kita akan mendefinisikan metode _next_observation di mana kita akan menskalakan data yang diamati dari 0 hingga 1.

  • Penting untuk hanya memperluas data yang telah diamati robot sejauh ini untuk mencegah bias lookahead.
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

Ambil Tindakan

Setelah kita menyiapkan ruang observasi, sekarang waktunya menulis fungsi langkah dan mengambil tindakan yang ingin dilakukan robot. Setiap kali self.steps_left == 0 untuk sesi perdagangan kita saat ini, kita akan menjual kepemilikan BTC kita dan memanggil reset session(). Kalau tidak, kami tetapkan imbalan pada ekuitas saat ini, atau tetapkan selesai ke Benar jika kami kehabisan dana.

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 perdagangan semudah mendapatkan current_price, menentukan tindakan yang perlu dilakukan, dan jumlah yang akan dibeli atau dijual. Mari kita tulis _take_action dengan cepat 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

Terakhir, dengan metode yang sama, kita akan menambahkan perdagangan ke self.trades dan memperbarui ekuitas dan riwayat akun 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 sekarang dapat meluncurkan lingkungan baru, melangkah melewati lingkungan tersebut, dan mengambil tindakan yang memengaruhi lingkungan tersebut. Sekarang saatnya menyaksikan mereka berdagang.

Saksikan robot kami berdagang

Metode render kita bisa sesederhana memanggil print(self.net_worth), tetapi itu tidak akan cukup menarik. Sebagai gantinya, kita akan menggambar grafik kandil sederhana dengan batang volume dan grafik terpisah untuk ekuitas kita.

Kami akan mengambil kode di StockTradingGraph.py dari artikel saya sebelumnya dan mengerjakan ulang agar sesuai dengan lingkungan Bitcoin. Anda bisa mendapatkan kodenya dari Github saya.

Perubahan pertama yang akan kita buat adalah mengubah self.df[ [Tanggal] Perbarui ke self.df[‘Timestamp’] dan menghapus semua panggilan ke date2num karena tanggal kita sudah dalam format stempel waktu unix. Berikutnya, dalam metode render kita, kita akan memperbarui label tanggal untuk mencetak tanggal yang dapat dibaca manusia, bukan angka.

from datetime import datetime

Pertama, kita akan mengimpor pustaka datetime, lalu kita akan menggunakan utcfromtimestampmethod untuk mendapatkan string UTC dari setiap stempel waktu dan strftime untuk membuatnya menjadi string dalam 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]])

Terakhir, kita akan menggunakan self.df[[‘Volume’] diubah menjadi self.df[‘Volume_(BTC)’] agar cocok dengan kumpulan data kita, dan setelah itu, kita siap untuk memulai. Kembali ke BitcoinTradingEnv, sekarang kita dapat menulis metode render 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)

Lihat! Sekarang kita dapat melihat robot kita memperdagangkan Bitcoin.

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Memvisualisasikan perdagangan robot kami dengan Matplotlib

Label hantu hijau melambangkan pembelian BTC, dan label hantu merah melambangkan penjualan. Label putih di sudut kanan atas adalah kekayaan bersih robot saat ini, dan label di sudut kanan bawah adalah harga Bitcoin saat ini. Sederhana dan elegan. Sekarang saatnya melatih bot kita dan melihat berapa banyak uang yang bisa kita hasilkan!

Waktu pelatihan

Salah satu kritik yang saya terima dalam artikel saya sebelumnya adalah kurangnya validasi silang dan tidak membagi data ke dalam set pelatihan dan pengujian. Tujuannya adalah untuk menguji keakuratan model akhir pada data baru yang belum pernah dilihat sebelumnya. Meskipun ini bukan fokus artikel itu, namun hal ini tentu saja penting. Karena kita bekerja dengan data deret waktu, kita tidak punya banyak pilihan dalam hal validasi silang.

Misalnya, bentuk umum validasi silang disebut validasi k-fold, di mana Anda membagi data menjadi k kelompok yang sama, memisahkan satu kelompok sebagai kelompok uji, dan menggunakan sisa data sebagai kelompok pelatihan. . Namun, data deret waktu sangat bergantung pada waktu, artinya data selanjutnya sangat bergantung pada data sebelumnya. Jadi k-fold tidak akan berfungsi karena robot kita akan belajar dari data masa depan sebelum berdagang, yang merupakan keuntungan yang tidak adil.

Kelemahan yang sama berlaku pada sebagian besar strategi validasi silang lainnya ketika diterapkan pada data deret waktu. Oleh karena itu, kita hanya perlu menggunakan sebagian kecil dari keseluruhan jumlah bingkai data sebagai set pelatihan, dimulai dari awal nomor bingkai hingga indeks tertentu yang berubah-ubah, lalu menggunakan sisa data sebagai set pengujian.

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

Berikutnya, karena lingkungan kita hanya disiapkan untuk menangani satu bingkai data, kita 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)])

Sekarang, melatih model kita semudah membuat robot dengan lingkungan kita dan memanggil model.learn.

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

Di sini, kami menggunakan tensorboard sehingga kami dapat dengan mudah memvisualisasikan grafik tensorflow kami dan melihat beberapa metrik kuantitatif tentang robot kami. Misalnya, berikut ini adalah diagram hadiah diskon untuk banyak robot dengan langkah waktu lebih dari 200.000:

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Wah, tampaknya bot kita cukup menguntungkan! Robot terbaik kami bahkan mampu mencapai keseimbangan 1000x lebih baik dalam 200.000 langkah, dan sisanya rata-rata peningkatan setidaknya 30x!

Pada titik inilah saya menyadari adanya bug di lingkungan tersebut… Setelah memperbaikinya, berikut peta hadiah baru:

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Seperti yang Anda lihat, beberapa robot kami melakukan pekerjaan dengan baik, dan sisanya bangkrut dengan sendirinya. Namun, bot yang berkinerja baik dapat mencapai hingga 10x atau bahkan 60x saldo awal. Saya harus mengakui bahwa semua bot yang menguntungkan dilatih dan diuji tanpa komisi, jadi tidak realistis bagi bot kami untuk menghasilkan uang sungguhan. Tetapi setidaknya kita menemukan arahnya!

Mari kita uji bot kita di lingkungan pengujian (dengan data baru yang belum pernah mereka lihat sebelumnya) dan lihat bagaimana kinerjanya.

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Bot terlatih kami bangkrut saat memperdagangkan data uji baru

Jelas, kita masih punya banyak pekerjaan yang harus dilakukan. Dengan hanya mengganti model untuk menggunakan A2C dasar yang stabil, alih-alih robot PPO2 saat ini, kami dapat meningkatkan performa kami pada kumpulan data ini secara signifikan. Akhirnya, mengikuti saran Sean O’Gorman, kita dapat sedikit memperbarui fungsi penghargaan kita sehingga kita menambahkan penghargaan pada kekayaan bersih alih-alih sekadar mencapai kekayaan bersih tinggi dan membiarkannya begitu saja.

reward = self.net_worth - prev_net_worth

Kedua perubahan ini sendiri meningkatkan performa pada set data uji secara signifikan, dan seperti yang Anda lihat di bawah, kami akhirnya mampu meraih profitabilitas pada data baru yang tidak ada dalam set pelatihan.

Buat Bot Perdagangan Bitcoin yang Tidak Akan Pernah Kehilangan Uang

Namun kita bisa melakukan yang lebih baik. Agar kami dapat meningkatkan hasil ini, kami perlu mengoptimalkan hiperparameter kami dan melatih bot kami lebih lama. Saatnya membuat GPU Anda bekerja dan bekerja dengan maksimal!

Tulisan ini sudah agak panjang, dan masih banyak detail yang perlu dipertimbangkan, jadi kami akan istirahat dulu di sini. Pada postingan berikutnya, kita akan menggunakan Optimasi Bayesian untuk mempartisi hiperparameter terbaik untuk ruang masalah kita dan mempersiapkan pelatihan/pengujian pada GPU menggunakan CUDA.

sebagai kesimpulan

Dalam artikel ini, kami berupaya membuat bot perdagangan Bitcoin yang menguntungkan dari awal menggunakan pembelajaran penguatan. Kami dapat menyelesaikan tugas-tugas berikut:

  1. Ciptakan lingkungan perdagangan Bitcoin dari awal menggunakan gym OpenAI.

  2. Gunakan Matplotlib untuk membangun visualisasi lingkungan.

  3. Latih dan uji bot kita menggunakan validasi silang sederhana.

  4. Sedikit mengubah robot kita untuk mencapai profitabilitas

Meskipun robot perdagangan kami tidak menguntungkan seperti yang kami harapkan, kami menuju ke arah yang benar. Di lain waktu, kami akan memastikan bot kami dapat mengalahkan pasar secara konsisten dan kami akan melihat bagaimana kinerja bot perdagangan kami pada data langsung. Nantikan artikel saya berikutnya, dan Hidup Bitcoin!