Rangkaian Perdagangan Kuantitatif Rangkaian Rangkaian Neural dan Mata Wang Digital (2) - Pembelajaran dan Latihan Intensif Strategi Perdagangan Bitcoin

Penulis:Lydia, Dicipta: 2023-01-12 16:49:09, Dikemas kini: 2023-09-20 10:07:39

img

Rangkaian Perdagangan Kuantitatif Rangkaian Rangkaian Neural dan Mata Wang Digital (2) - Pembelajaran dan Latihan Intensif Strategi Perdagangan Bitcoin

1. Pengantar

Dalam artikel sebelumnya, kami memperkenalkan penggunaan rangkaian LSTM untuk meramalkan harga Bitcoin:https://www.fmz.com/bbs-topic/9879, seperti yang disebutkan dalam artikel, ini hanya projek latihan kecil untuk membiasakan diri dengan RNN dan pytorch. Artikel ini akan memperkenalkan penggunaan pembelajaran intensif untuk melatih strategi perdagangan secara langsung. Model pembelajaran intensif adalah OpenAI sumber terbuka PPO, dan persekitaran merujuk kepada gaya gym. Untuk memudahkan pemahaman dan pengujian, model PPO LSTM dan persekitaran gym untuk backtesting ditulis secara langsung tanpa menggunakan pakej siap pakai. PPO, atau Optimasi Dasar Proksimal, adalah peningkatan pengoptimuman Gradien Dasar. gym juga dikeluarkan oleh OpenAI. Ia boleh berinteraksi dengan rangkaian strategi dan maklum balas status dan ganjaran persekitaran semasa. Ia seperti amalan pembelajaran intensif. Ia menggunakan model PPO LSTM untuk membuat arahan, seperti membeli, menjual atau tidak beroperasi secara langsung mengikut maklumat pasaran Bitcoin. maklum balas diberikan oleh persekitaran backtest. Melalui latihan, model ini dioptimumkan secara berterusan untuk mencapai matlamat keuntungan strategik. Membaca artikel ini memerlukan asas pembelajaran intensif yang mendalam dalam Python, pytorch dan DRL. Tetapi tidak kira jika anda tidak boleh. Ia mudah dipelajari dan bermula dengan kod yang diberikan dalam artikel ini. Tutorial ini dihasilkan oleh platform FMZ Quant Trading (www.fmz.comSelamat datang untuk menyertai kumpulan QQ: 863946592 untuk komunikasi.

2. Data dan rujukan pembelajaran

Data harga Bitcoin berasal dari platform FMZ Quant Trading:https://www.quantinfo.com/Tools/View/4.html- Tidak. Satu artikel menggunakan DRL + gym untuk melatih strategi perdagangan:https://towardsdatascience.com/visualizing-stock-trading-agents-using-matplotlib-and-gym-584c992bc6d4. Beberapa contoh untuk memulakan dengan pytorch:https://github.com/yunjey/pytorch-tutorial. Artikel ini akan dilaksanakan oleh model LSTM-PPO secara langsung:https://github.com/seungeunrho/minimalRL/blob/master/ppo-lstm.py. Artikel mengenai PPO:https://zhuanlan.zhihu.com/p/38185553. Lebih banyak artikel mengenai DRL:https://www.zhihu.com/people/flood-sung/posts. Mengenai gimnasium, artikel ini tidak memerlukan pemasangan, tetapi ia sangat biasa dalam pembelajaran intensif:https://gym.openai.com/.

3. LSTM-PPO

Untuk penjelasan mendalam mengenai PPO, anda boleh belajar dari bahan rujukan sebelumnya. Berikut adalah pengenalan konsep yang mudah. Isu terakhir rangkaian LSTM hanya meramalkan harga. Cara membeli dan menjual berdasarkan harga yang diramalkan perlu direalisasikan secara berasingan. Adalah wajar untuk berfikir bahawa output langsung tindakan dagangan akan lebih langsung. Ini adalah kes dengan Gradien Dasar, yang dapat memberikan kebarangkalian pelbagai tindakan mengikut maklumat persekitaran input s. Kehilangan LSTM adalah perbezaan antara harga yang diramalkan dan harga sebenar, sementara kerugian PG adalah - log ยง * Q, di mana p adalah kebarangkalian tindakan output, dan Q adalah nilai tindakan (seperti skor ganjaran intuitif). Penjelasan adalah bahawa jika nilai tindakan lebih tinggi, rangkaian harus menjadi kunci untuk mengurangkan kerugian. Walaupun PPO lebih kompleks, prinsipnya lebih mirip dengan bagaimana nilai output setiap tindakan lebih baik untuk mengkaji dan mengemas kini.

Kod sumber LSTM-PPO diberikan di bawah, yang boleh difahami dalam kombinasi dengan data sebelumnya:


import time
import requests
import json
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.distributions import Categorical
from itertools import count

# Hyperparameters of the model
learning_rate = 0.0005
gamma         = 0.98
lmbda         = 0.95
eps_clip      = 0.1
K_epoch       = 3

device = torch.device('cpu') # It can also be changed to GPU version.

class PPO(nn.Module):
    def __init__(self, state_size, action_size):
        super(PPO, self).__init__()
        self.data = []
        self.fc1   = nn.Linear(state_size,10)
        self.lstm  = nn.LSTM(10,10)
        self.fc_pi = nn.Linear(10,action_size)
        self.fc_v  = nn.Linear(10,1)
        self.optimizer = optim.Adam(self.parameters(), lr=learning_rate)

    def pi(self, x, hidden):
        # Output the probability of each action. Since LSTM network also contains the information of hidden layer, please refer to the previous article.
        x = F.relu(self.fc1(x))
        x = x.view(-1, 1, 10)
        x, lstm_hidden = self.lstm(x, hidden)
        x = self.fc_pi(x)
        prob = F.softmax(x, dim=2)
        return prob, lstm_hidden
    
    def v(self, x, hidden):
        # Value function is used to evaluate the current situation, so there is only one output.
        x = F.relu(self.fc1(x))
        x = x.view(-1, 1, 10)
        x, lstm_hidden = self.lstm(x, hidden)
        v = self.fc_v(x)
        return v
      
    def put_data(self, transition):
        self.data.append(transition)
        
    def make_batch(self):
        # Prepare the training data.
        s_lst, a_lst, r_lst, s_prime_lst, prob_a_lst, hidden_lst, done_lst = [], [], [], [], [], [], []
        for transition in self.data:
            s, a, r, s_prime, prob_a, hidden, done = transition
            s_lst.append(s)
            a_lst.append([a])
            r_lst.append([r])
            s_prime_lst.append(s_prime)
            prob_a_lst.append([prob_a])
            hidden_lst.append(hidden)
            done_mask = 0 if done else 1
            done_lst.append([done_mask])
            
        s,a,r,s_prime,done_mask,prob_a = torch.tensor(s_lst, dtype=torch.float), torch.tensor(a_lst), \
                                         torch.tensor(r_lst), torch.tensor(s_prime_lst, dtype=torch.float), \
                                         torch.tensor(done_lst, dtype=torch.float), torch.tensor(prob_a_lst)
        self.data = []
        return s,a,r,s_prime, done_mask, prob_a, hidden_lst[0]
        
    def train_net(self):
        s,a,r,s_prime,done_mask, prob_a, (h1,h2) = self.make_batch()
        first_hidden = (h1.detach(), h2.detach())

        for i in range(K_epoch):
            v_prime = self.v(s_prime, first_hidden).squeeze(1)
            td_target = r + gamma * v_prime * done_mask
            v_s = self.v(s, first_hidden).squeeze(1)
            delta = td_target - v_s
            delta = delta.detach().numpy()
            
            advantage_lst = []
            advantage = 0.0
            for item in delta[::-1]:
                advantage = gamma * lmbda * advantage + item[0]
                advantage_lst.append([advantage])
            advantage_lst.reverse()
            advantage = torch.tensor(advantage_lst, dtype=torch.float)

            pi, _ = self.pi(s, first_hidden)
            pi_a = pi.squeeze(1).gather(1,a)
            ratio = torch.exp(torch.log(pi_a) - torch.log(prob_a))  # a/b == log(exp(a)-exp(b))

            surr1 = ratio * advantage
            surr2 = torch.clamp(ratio, 1-eps_clip, 1+eps_clip) * advantage
            loss = -torch.min(surr1, surr2) + F.smooth_l1_loss(v_s, td_target.detach()) # Trained both value and decision networks at the same time.

            self.optimizer.zero_grad()
            loss.mean().backward(retain_graph=True)
            self.optimizer.step()

4. persekitaran pengujian belakang Bitcoin

Langkah input tindakan, dan hasil yang dikembalikan adalah (status seterusnya, pendapatan tindakan, sama ada untuk menamatkan, maklumat tambahan). keseluruhan persekitaran backtest juga 60 baris. anda boleh mengubah suai versi yang lebih kompleks sendiri. kod tertentu adalah:

class BitcoinTradingEnv:
    def __init__(self, df, commission=0.00075,  initial_balance=10000, initial_stocks=1, all_data = False, sample_length= 500):
        self.initial_stocks = initial_stocks # Initial number of Bitcoins
        self.initial_balance = initial_balance # Initial assets
        self.current_time = 0 # Time position of the backtest
        self.commission = commission # Trading fees
        self.done = False # Is the backtest over?
        self.df = df
        self.norm_df = 100*(self.df/self.df.shift(1)-1).fillna(0) # Standardized approach, simple yield normalization.
        self.mode = all_data # Whether it is a sample backtest mode.
        self.sample_length = 500 # Sample length
        
    def reset(self):
        self.balance = self.initial_balance
        self.stocks = self.initial_stocks
        self.last_profit = 0
        
        if self.mode:
            self.start = 0
            self.end = self.df.shape[0]-1
        else:
            self.start = np.random.randint(0,self.df.shape[0]-self.sample_length)
            self.end = self.start + self.sample_length
            
        self.initial_value = self.initial_balance + self.initial_stocks*self.df.iloc[self.start,4]
        self.stocks_value = self.initial_stocks*self.df.iloc[self.start,4]
        self.stocks_pct = self.stocks_value/self.initial_value
        self.value = self.initial_value
        
        self.current_time = self.start
        return np.concatenate([self.norm_df[['o','h','l','c','v']].iloc[self.start].values , [self.balance/10000, self.stocks/1]])
    
    def step(self, action):
        # action is the action taken by the strategy, here the account will be updated and the reward will be calculated.
        done = False
        if action == 0: # Hold
            pass
        elif action == 1: # Buy
            buy_value = self.balance*0.5
            if buy_value > 1: # Insufficient balance, no account operation.
                self.balance -= buy_value
                self.stocks += (1-self.commission)*buy_value/self.df.iloc[self.current_time,4]
        elif action == 2: # Sell
            sell_amount = self.stocks*0.5
            if sell_amount > 0.0001:
                self.stocks -= sell_amount
                self.balance += (1-self.commission)*sell_amount*self.df.iloc[self.current_time,4]
                
        self.current_time += 1
        if self.current_time == self.end:
            done = True
        self.value = self.balance + self.stocks*self.df.iloc[self.current_time,4]
        self.stocks_value = self.stocks*self.df.iloc[self.current_time,4]
        self.stocks_pct = self.stocks_value/self.value
        if self.value < 0.1*self.initial_value:
            done = True
            
        profit = self.value - (self.initial_balance+self.initial_stocks*self.df.iloc[self.current_time,4])
        reward = profit - self.last_profit # The reward for each turn is the added revenue.
        self.last_profit = profit
        next_state = np.concatenate([self.norm_df[['o','h','l','c','v']].iloc[self.current_time].values , [self.balance/10000, self.stocks/1]])
        return (next_state, reward, done, profit)

5. Beberapa butiran yang patut diperhatikan

  • Kenapa akaun awal mempunyai mata wang?

Rumus untuk mengira pulangan persekitaran backtest adalah: pulangan semasa = nilai akaun semasa - nilai semasa akaun awal. Ini bermaksud bahawa jika harga Bitcoin menurun dan strategi membuat operasi penjualan syiling, walaupun nilai keseluruhan akaun menurun, strategi itu sebenarnya harus diberi ganjaran. Jika backtest mengambil masa yang lama, akaun awal mungkin mempunyai sedikit kesan, tetapi ia akan mempunyai kesan yang besar pada mulanya. Pengiraan pulangan relatif memastikan bahawa setiap operasi yang betul akan mendapat ganjaran positif.

  • Mengapa pasaran diambil sampel semasa latihan?

Jumlah data adalah lebih daripada 10,000 K-line. Jika anda menjalankan gelung penuh setiap kali, ia akan mengambil masa yang lama, dan strategi menghadapi situasi yang sama setiap kali, ia mungkin lebih mudah untuk overfit. Mengambil 500 bar pada satu masa sebagai backtest. Walaupun masih mungkin untuk overfit, strategi menghadapi lebih daripada 10,000 permulaan yang mungkin.

  • Bagaimana jika tiada mata wang atau wang?

Situasi ini tidak dipertimbangkan dalam persekitaran backtest. Jika mata wang telah terjual atau kuantiti perdagangan minimum tidak dapat dicapai, maka operasi penjualan bersamaan dengan tidak beroperasi sebenarnya. Jika harga menurun, menurut kaedah pengiraan pulangan relatif, ia masih berdasarkan pulangan positif strategik. Kesan situasi ini adalah apabila strategi menilai bahawa pasaran menurun dan mata wang baki akaun tidak dapat dijual, mustahil untuk membezakan tindakan penjualan dari tindakan tidak beroperasi, tetapi tidak memberi kesan kepada penilaian strategi itu sendiri di pasaran.

  • Mengapa saya harus mengembalikan maklumat akaun sebagai status?

Model PPO mempunyai rangkaian nilai untuk menilai nilai status semasa. Jelas, jika strategi menilai bahawa harga akan meningkat, keseluruhan status hanya akan mempunyai nilai positif apabila akaun semasa memegang Bitcoin, dan sebaliknya. Oleh itu, maklumat akaun adalah asas penting untuk penilaian rangkaian nilai. Perlu diperhatikan bahawa maklumat tindakan masa lalu tidak dikembalikan sebagai status. Saya menganggap tidak berguna untuk menilai nilai.

  • Bilakah ia akan kembali ke tidak beroperasi?

Apabila strategi menilai bahawa pulangan yang dibawa oleh urus niaga tidak dapat menampung yuran pengendalian, ia harus kembali ke tidak operasi. Walaupun penerangan sebelumnya menggunakan strategi berulang kali untuk menilai trend harga, ia hanya untuk kemudahan pemahaman. Sebenarnya, model PPO ini tidak meramalkan pasaran, tetapi hanya mengeluarkan kemungkinan tiga tindakan.

6. Pemerolehan dan latihan data

Seperti dalam artikel sebelumnya, kaedah dan format pemerolehan data adalah seperti berikut: K-line tempoh satu jam pasangan dagangan Bitfinex Exchange BTC_USD dari 7 Mei 2018 hingga 27 Jun 2019:

resp = requests.get('https://www.quantinfo.com/API/m/chart/history?symbol=BTC_USD_BITFINEX&resolution=60&from=1525622626&to=1561607596')
data = resp.json()
df = pd.DataFrame(data,columns = ['t','o','h','l','c','v'])
df.index = df['t']
df = df.dropna()
df = df.astype(np.float32)

Oleh kerana penggunaan rangkaian LSTM, masa latihan sangat lama. Saya beralih ke versi GPU, yang kira-kira tiga kali lebih cepat.

env = BitcoinTradingEnv(df)
model = PPO()

total_profit = 0 # Record total profit
profit_list = [] # Record the profits of each training session
for n_epi in range(10000):
    hidden = (torch.zeros([1, 1, 32], dtype=torch.float).to(device), torch.zeros([1, 1, 32], dtype=torch.float).to(device))
    s = env.reset()
    done = False
    buy_action = 0
    sell_action = 0
    while not done:
        h_input = hidden
        prob, hidden = model.pi(torch.from_numpy(s).float().to(device), h_input)
        prob = prob.view(-1)
        m = Categorical(prob)
        a = m.sample().item()
        if a==1:
            buy_action += 1
        if a==2:
            sell_action += 1
        s_prime, r, done, profit = env.step(a)

        model.put_data((s, a, r/10.0, s_prime, prob[a].item(), h_input, done))
        s = s_prime

    model.train_net()
    profit_list.append(profit)
    total_profit += profit
    if n_epi%10==0:
        print("# of episode :{:<5}, profit : {:<8.1f}, buy :{:<3}, sell :{:<3}, total profit: {:<20.1f}".format(n_epi, profit, buy_action, sell_action, total_profit))

7. Hasil latihan dan analisis

Selepas menunggu lama:

img

Pertama sekali, lihatlah pasaran data latihan. Secara amnya, separuh pertama adalah penurunan jangka panjang, dan separuh kedua adalah pemulihan yang kuat.

img

Terdapat banyak operasi pembelian pada peringkat awal latihan, dan pada dasarnya tidak ada pusingan yang menguntungkan. Pada pertengahan latihan, operasi pembelian telah beransur-ansur menurun, dan kebarangkalian keuntungan juga meningkat, tetapi masih ada peluang kerugian yang besar.

img

Lemparkan keuntungan setiap pusingan, dan hasilnya adalah seperti berikut:

img

Strategi ini dengan cepat menyingkirkan keadaan bahawa pulangan awal adalah negatif, tetapi turun naiknya besar. pulangan tidak berkembang pesat sehingga selepas 10,000 pusingan. Secara umum, latihan model sangat sukar.

Selepas latihan akhir, biarkan model menjalankan semua data lagi untuk melihat bagaimana ia berfungsi. Semasa tempoh itu, catat jumlah nilai pasaran akaun, jumlah Bitcoin yang dipegang, peratusan nilai Bitcoin, dan jumlah pulangan. Pertama adalah jumlah nilai pasaran, dan jumlah pulangan adalah sama dengan itu, mereka tidak akan dipaparkan:

img

Jumlah nilai pasaran meningkat perlahan dalam pasaran beruang awal, dan mengikuti peningkatan dalam pasaran lembu kemudian, tetapi masih ada kerugian berkala.

Akhirnya, lihatlah perkadaran kedudukan. paksi kiri carta adalah perkadaran kedudukan, dan paksi kanan adalah pasaran. Ia boleh dinilai secara awal bahawa model itu terlalu sesuai. Frekuensi kedudukan rendah di pasaran beruang awal, dan tinggi di bahagian bawah pasaran. Ia juga dapat dilihat bahawa model tidak belajar memegang kedudukan jangka panjang dan selalu menjual dengan cepat.

img

8. Analisis data ujian

Pasaran satu jam Bitcoin dari 27 Jun 2019 sehingga kini diperoleh dari data ujian. Ia dapat dilihat dari carta bahawa harga telah jatuh dari $ 13,000 kepada lebih daripada $ 9,000, yang merupakan ujian yang hebat untuk model ini.

img

Pertama sekali, pulangan relatif akhir dilakukan begitu-begitu, tetapi tidak ada kerugian.

img

Melihat situasi kedudukan, kita boleh meneka bahawa model cenderung membeli selepas kejatuhan tajam dan menjual selepas bangkit.

img

9. Ringkasan

Dalam kertas ini, robot perdagangan automatik Bitcoin dilatih dengan bantuan PPO, kaedah pembelajaran intensif yang mendalam, dan beberapa kesimpulan diperoleh. Oleh kerana masa yang terhad, masih ada beberapa aspek yang perlu diperbaiki dalam model. Selamat datang perbincangan. Pelajaran terbesar adalah bahawa untuk kaedah standardisasi data, jangan menggunakan skala dan kaedah lain, jika tidak, model akan dengan cepat mengingat hubungan antara harga dan pasaran, dan jatuh ke dalam overfit. Kadar perubahan standard adalah data relatif, yang menjadikannya sukar bagi model untuk mengingat hubungan dengan pasaran, dan terpaksa mencari hubungan antara kadar perubahan dan peningkatan dan penurunan.

Pengenalan kepada artikel sebelumnya: Strategi frekuensi tinggi yang saya mendedahkan yang pernah sangat menguntungkan:https://www.fmz.com/bbs-topic/9886.


Berkaitan

Lebih lanjut