Jaringan saraf dan transaksi kuantitatif mata uang digital seri ((2)) Pelatihan strategi perdagangan Bitcoin dengan pembelajaran intensif

Penulis:Rumput, Dibuat: 2019-07-31 11:13:15, Diperbarui: 2023-10-20 20:10:18

img

1.介绍

Artikel sebelumnya tentang penggunaan jaringan LSTM untuk memprediksi harga Bitcoinhttps://www.fmz.com/digest-topic/4035Seperti yang disebutkan dalam artikel ini, ini hanya sebuah proyek kecil bagi para praktisi untuk membiasakan diri dengan RNN dan pytorch. Artikel ini akan menjelaskan cara menggunakan pembelajaran reinforcement untuk melatih strategi perdagangan secara langsung. Model pembelajaran reinforcement adalah PPO open-source OpenAI, dan lingkungan mengacu pada gaya gym. Untuk memudahkan pemahaman dan pengujian, model PPO LSTM dan lingkungan gym retest langsung menulis paket yang tidak digunakan. PPO, yang dikenal sebagai Proximal Policy Optimization, adalah perbaikan optimasi pada Policy Graident, yaitu gradien strategi. Gym juga dirilis oleh OpenAI, dapat berinteraksi dengan jaringan strategi, memberikan umpan balik tentang keadaan dan imbalan lingkungan saat ini, seperti latihan pembelajaran penguatan yang menggunakan model PPO LSTM untuk membuat instruksi untuk membeli, menjual atau tidak beroperasi secara langsung berdasarkan informasi pasar Bitcoin, yang diberikan umpan balik oleh lingkungan retest, dan mencapai tujuan keuntungan strategis melalui pelatihan yang terus-menerus mengoptimalkan model. Membaca artikel ini membutuhkan dasar pembelajaran intensif Python, pytorch, DRL. Tetapi tidak ada bedanya, dengan kode yang diberikan dalam artikel ini, mudah dipelajari.www.fmz.comDi sini, saya akan berbagi beberapa tips yang bisa Anda manfaatkan untuk memulai sebuah bisnis.

2.数据和学习参考资料

Data harga Bitcoin berasal dari platform perdagangan kuantitatif FMZ:https://www.quantinfo.com/Tools/View/4.htmlArtikel yang menggunakan DRL+gym untuk melatih strategi trading:https://towardsdatascience.com/visualizing-stock-trading-agents-using-matplotlib-and-gym-584c992bc6d4Beberapa contoh di dalam pytorch:https://github.com/yunjey/pytorch-tutorialArtikel ini akan langsung menggunakan implementasi singkat dari model LSTM-PPO:https://github.com/seungeunrho/minimalRL/blob/master/ppo-lstm.pyArtikel tentang PPO:https://zhuanlan.zhihu.com/p/38185553Artikel lebih lanjut tentang DRL:https://www.zhihu.com/people/flood-sung/postsUntuk gym, artikel ini tidak memerlukan instalasi, tetapi belajar reinforcement sering digunakan:https://gym.openai.com/

3.LSTM-PPO

Untuk informasi lebih lanjut tentang PPO, kita dapat mempelajari referensi di atas, di sini hanya pengantar konsep sederhana. Pada tahap sebelumnya, jaringan LSTM hanya memprediksi satu harga, dan bagaimana transaksi jual beli dapat dilakukan secara berbeda berdasarkan harga yang diprediksi ini, tentu saja, tidakkah output langsung tindakan jual beli tidak lebih langsung?

Di bawah ini akan diberikan kode sumber LSTM-PPO, yang dapat dimengerti jika dikombinasikan dengan informasi 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

#模型的超参数
learning_rate = 0.0005
gamma         = 0.98
lmbda         = 0.95
eps_clip      = 0.1
K_epoch       = 3

device = torch.device('cpu') # 也可以改为GPU版本

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):
        #输出各个动作的概率,由于是LSTM网络还要包含hidden层的信息,可以参考上一期文章
        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):
        #价值函数,用于评价当前局面的好坏,所以只有一个输出
        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):
        #准备训练数据
        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()) #同时训练了价值网络和决策网络

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

4.比特币回测环境

Dimulai dari format gym, ada metode reset initialization, step input action, hasil return adalah ((keadaan berikutnya, gain action, apakah berakhir, informasi tambahan), seluruh retesting environment juga memiliki 60 baris, yang dapat dimodifikasi sendiri untuk versi yang lebih kompleks, kode spesifik:

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 #初始的比特币数量
        self.initial_balance = initial_balance #初始的资产
        self.current_time = 0 #回测的时间位置
        self.commission = commission #易手续费
        self.done = False #回测是否结束
        self.df = df
        self.norm_df = 100*(self.df/self.df.shift(1)-1).fillna(0) #标准化方法,简单的收益率标准化
        self.mode = all_data # 是否为抽样回测模式
        self.sample_length = 500 # 抽样长度
        
    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即策略采取的动作,这里将更新账户和计算reward
        done = False
        if action == 0: #持有
            pass
        elif action == 1: #买入
            buy_value = self.balance*0.5
            if buy_value > 1: #余钱不足,不操作账户
                self.balance -= buy_value
                self.stocks += (1-self.commission)*buy_value/self.df.iloc[self.current_time,4]
        elif action == 2: #卖出
            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 # 每回合的reward是新增收益
        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.几个值得注意的细节

Mengapa akun awal memiliki koin?

Rumus untuk menghitung keuntungan dari revitalisasi lingkungan adalah: keuntungan saat ini = nilai akun saat ini - nilai akun awal saat ini. Ini berarti bahwa jika harga Bitcoin turun, dan strategi melakukan transaksi jual, bahkan jika nilai akun keseluruhan berkurang, sebenarnya juga harus diberikan hadiah dengan strategi. Jika waktu revitalisasi panjang, akun awal mungkin tidak memiliki dampak besar, tetapi masih memiliki dampak besar di awal. Perhitungan keuntungan relatif menjamin bahwa setiap tindakan yang benar mendapatkan imbalan positif.

Mengapa Anda harus melakukan pencucian uang saat latihan?

Jumlah total data lebih dari 10.000 baris K, jika setiap kali jumlah penuh berjalan satu putaran, dibutuhkan waktu yang lama, dan strategi setiap kali menghadapi situasi yang sama, mungkin lebih mudah untuk overfitting. Setiap kali menarik 500 baris sebagai satu kali data ulangan, meskipun masih mungkin untuk overfitting, strategi menghadapi lebih dari 10.000 kemungkinan awal yang berbeda.

Apa yang harus dilakukan tanpa koin atau uang?

Dalam lingkungan retensi, hal ini tidak dipertimbangkan, jika mata uang telah terjual atau tidak mencapai volume minimum, maka melakukan operasi jual sebenarnya sama dengan tidak melakukan operasi, jika harga turun, menurut perhitungan keuntungan relatif, strategi masih didasarkan pada imbalan positif. Efek dari situasi ini adalah bahwa strategi menilai pasar menurun dan saldo mata uang tidak dapat dijual, sehingga tidak dapat membedakan antara tindakan jual dan tindakan tidak beroperasi, tetapi strategi itu sendiri tidak mempengaruhi penilaian pasar.

Mengapa harus mengembalikan informasi akun ke statusnya?

Model PPO memiliki jaringan nilai yang digunakan untuk menilai nilai dari keadaan saat ini, jelas jika strategi memutuskan harga akan naik, maka seluruh keadaan hanya memiliki nilai positif ketika akun saat ini memegang Bitcoin, dan sebaliknya. Jadi informasi akun adalah dasar penting dari penilaian jaringan nilai. Perhatikan bahwa tidak mengembalikan informasi tindakan masa lalu sebagai keadaan, yang dianggap tidak berguna oleh individu untuk menilai nilai.

Di mana mereka akan kembali tanpa operasi?

Ketika strategi menilai keuntungan dari pembelian dan penjualan tidak dapat menutupi biaya prosedur, maka harus kembali tidak beroperasi. Meskipun deskripsi sebelumnya berulang kali menggunakan strategi untuk menentukan tren harga, hanya untuk memudahkan pemahaman, sebenarnya model PPO ini tidak membuat prediksi pasar, hanya mengeluarkan probabilitas tiga tindakan.

6.数据的获取和训练

Seperti pada artikel sebelumnya, cara dan format untuk mendapatkan data adalah sebagai berikut:

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)

Karena menggunakan jaringan LSTM, waktu pelatihan yang lama, saya mengubah versi GPU yang lain, sekitar tiga kali lebih cepat.

env = BitcoinTradingEnv(df)
model = PPO()

total_profit = 0 #记录总收益
profit_list = [] #记录每次训练收益
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.训练结果和分析

Setelah menunggu lama:img

Pertama, lihatlah pasar data pelatihan, secara umum, paruh pertama adalah penurunan yang panjang, paruh kedua adalah rebound yang kuat.img

Operasi pembelian sebelum pelatihan banyak, dan pada dasarnya tidak ada putaran keuntungan. Pada pertengahan pelatihan, operasi pembelian secara bertahap berkurang, dan kemungkinan keuntungan juga semakin besar, tetapi tetap ada kemungkinan kerugian yang besar.img

Jika kita rata-rata mengoleskan keuntungan setiap putaran, hasilnya adalah sebagai berikut:img

Strategi ini dengan cepat keluar dari situasi yang sebelumnya menguntungkan negatif, tetapi turun naik besar, dan keuntungan tidak tumbuh dengan cepat sampai setelah 10.000 putaran, secara keseluruhan, model yang sulit untuk melatih.

Setelah pelatihan selesai, model akan menjalankan semua data sekali lagi untuk melihat bagaimana kinerja, mencatat total nilai pasar akun, jumlah bitcoin yang dipegang, persentase nilai bitcoin, total pendapatan selama periode tersebut. Pertama, nilai pasar total, total pendapatan dan sejenisnya, tidak termasuk:img
Nilai pasar total meningkat perlahan pada saat pasar beruang awal, dan juga meningkat pada saat pasar banteng akhir, tetapi masih mengalami kerugian bertahap.

Akhirnya, lihatlah rasio kepemilikan, sumbu kiri gambar adalah rasio kepemilikan, sumbu kanan adalah pasar, dapat dinilai awal bahwa model muncul terlalu cocok, frekuensi kepemilikan rendah pada awal pasar beruang, frekuensi kepemilikan tinggi pada dasar pasar.img

8.测试数据分析

Pada saat data uji diperoleh, harga Bitcoin dalam satu jam dari tanggal 27 Juni 2019 hingga saat ini. Pada gambar ini, Anda dapat melihat harga jatuh dari awal $ 13.000 menjadi lebih dari $ 9.000 saat ini, yang merupakan ujian besar bagi model tersebut.img

Pertama, pada akhirnya keuntungan relatif, kinerja buruk, tetapi tidak ada kerugian.img

Jika kita melihat kembali kondisi kepemilikan, kita dapat menebak bahwa model cenderung untuk membeli atau menjual setelah jatuh, karena volatilitas pasar bitcoin sangat kecil dan model ini selalu berada di posisi kosong.img

9.总结

Artikel ini menggunakan metode penguatan pembelajaran mendalam untuk melatih robot perdagangan otomatis Bitcoin, dan beberapa kesimpulan juga diperoleh. Karena waktu terbatas, model masih memiliki beberapa tempat untuk disempurnakan, silakan diskusikan. Pelajaran terbesar dari ini adalah metode standarisasi data, jangan menggunakan metode seperti skala, atau model akan dengan cepat mengingat hubungan harga dan pasar, jatuh ke dalam kesesuaian. Standarisasi tingkat perubahan adalah data relatif, membuat model sulit untuk mengingat hubungan dengan tren, dan dipaksa untuk mencari hubungan antara tingkat perubahan dan penurunan.

Artikel sebelumnya: Beberapa strategi terbuka yang dibagikan oleh para penemu FMZ di platform kuantifikasi:https://zhuanlan.zhihu.com/p/64961672Pelajaran Quantitative Digital Currency Trading di NetEase Cloud Classroom, hanya 20 dolar:https://study.163.com/course/courseMain.htm?courseId=1006074239&share=2&shareId=400000000602076Salah satu strategi frekuensi tinggi yang pernah saya publikasikan yang sangat menguntungkan adalah:https://www.fmz.com/bbs-topic/1211


Berkaitan

Lebih banyak

Lisa20231Mengapa Anda ingin membalikkan gambar hasil tes Anda? Mengapa pendapatan Anda selalu berkurang ketika harga dolar naik?

Jackmaprofit = self.value - (self.initial_balance+self.initial_stocks * self.df.iloc[self.current_time,4]) Ada bug Harusnya: profit = self.value - (self.initial_balance + self.initial_stocks * self.df.iloc[self.start,4])

Jackmaprofit = self.value - (self.initial_balance+self.initial_stocks*self.df.iloc[self.current_time,4]) Ada bug Harusnya: profit = self.value - (self.initial_balance+self.initial_stocks*self.df.iloc[self.start,4])

TimoshenkoIni lebih kuat dari versi pertama.

xw2021Aku tidak tahu apa yang terjadi.

Eddie."Satu-satunya yang bisa saya lakukan adalah menolong mereka".

RumputGPU `` device = torch.device (('cuda' if torch.cuda.is_available)) else 'cpu') class PPO ((nn.Module): def __init__ ((self): super ((PPO, self).__init__() Self.data = [] Apa yang terjadi? self.fc1 = nn.Linear ((8,64) self.lstm = nn.LSTM ((64,32) self.fc_pi = nn.Linear ((32,3) self.fc_v = nn.Linear ((32,1) self.optimizer = optim.Adam ((self.parameters ((), lr=learning_rate) def pi ((self, x, hidden): x = F.relu ((self.fc1 ((x)) x = x.view ((-1, 1, 64) x, lstm_hidden = self.lstm ((x, hidden)) x = self. fc_pi ((x) prob = F.softmax ((x, dim = 2) return prob, lstm_hidden Apa yang terjadi? def v ((self, x, hidden): x = F.relu ((self.fc1 ((x)) x = x.view ((-1, 1, 64) x, lstm_hidden = self. lstm ((x, hidden)) v = self.fc_v ((x) kembali v Apa yang terjadi? def put_data ((self, transition): self.data.append (transisi) Apa yang terjadi? def make_batch ((self): s_lst, a_lst, r_lst, s_prime_lst, prob_a_lst, hidden_lst, done_lst = [], [], [], [], [], [], [] Untuk transisi di self.data: s, a, r, s_prime, prob_a, hidden, done = transisi Apa yang terjadi? 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 (disembunyikan) done_mask = 0 jika done else 1 done_lst.append ([done_mask]) Apa yang terjadi? s,a,r,s_prime,done_mask,prob_a = torch.tensor ((s_lst,dtype=torch.float).to ((device),torch.tensor ((a_lst).to ((device).to ((device), \ torch.tensor ((r_lst).to ((device), torch.tensor ((s_prime_lst, dtype=torch.float).to ((device), \ torch.tensor ((done_lst, dtype=torch.float).to ((device), torch.tensor ((prob_a_lst).to ((device) Self.data = [] return s, a, r, s_prime, done_mask, prob_a, hidden_lst[0] Apa yang terjadi? def train_net ((self): s,a,r,s_prime,done_mask, prob_a, (h1,h2) = self.make_batch ()) first_hidden = (h1.to ((device).detach ((), h2.to ((device).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.cpu (().detach (().numpy (()) Keuntungan = [] keuntungan = 0.0 for item in delta [::-1]: advantage = gamma * lmbda * advantage + item[0] advantage_lst.append (([advantage]) advantage_lst.reverse (dalam bahasa Inggris) advantage = torch.tensor ((advantage_lst, dtype=torch.float).to ((device)) 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 = rasio * keuntungan 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))) Self.optimizer.zero_grad (dalam bahasa Inggris). loss.mean (().backward ((retain_graph=True)) Self.optimizer.step (dalam bahasa Inggris) ``