Rangkaian Perdagangan Kuantitatif Rangkaian Neural dan Mata Wang Digital ()) 2) Latihan Memperkuat Pembelajaran mendalam Strategi Perdagangan Bitcoin

Penulis:Rumput, Dicipta: 2019-07-31 11:13:15, Dikemas kini: 2023-10-20 20:10:18

img

1.介绍

Artikel sebelumnya mengenai penggunaan rangkaian LSTM untuk meramalkan harga Bitcoinhttps://www.fmz.com/digest-topic/4035Seperti yang dinyatakan dalam artikel ini, ini adalah projek kecil untuk para pengamal yang digunakan untuk membiasakan diri dengan RNN dan pytorch. Artikel ini akan menerangkan kaedah menggunakan pembelajaran penguat, melatih strategi perdagangan secara langsung. Model pembelajaran penguat adalah PPO sumber terbuka OpenAI, dan persekitaran merujuk kepada gaya gim. Untuk kemudahan pemahaman dan ujian, model PPO LSTM dan persekitaran gim yang diuji semula secara langsung menulis pakej siap pakai yang tidak digunakan. PPO, yang dikenali sebagai Proximal Policy Optimization, adalah peningkatan pengoptimuman kepada Policy Graident, iaitu gradien dasar. Gym juga dikeluarkan oleh OpenAI, boleh berinteraksi dengan rangkaian dasar, memberi maklum balas mengenai keadaan dan ganjaran persekitaran semasa, seperti latihan pembelajaran penguat menggunakan model PPO menggunakan LSTM untuk membuat arahan membeli, menjual atau tidak beroperasi secara langsung berdasarkan maklumat pasaran Bitcoin, yang diberi maklum balas oleh persekitaran retest, untuk mencapai tujuan keuntungan strategi melalui latihan yang terus mengoptimumkan model. Membaca artikel ini memerlukan asas pembelajaran penguatan mendalam Python, pytorch, DRL. Tetapi tidak penting, dengan kod yang diberikan dalam artikel ini, mudah dipelajari.www.fmz.com), selamat datang ke kumpulan QQ: 863946592.

2.数据和学习参考资料

Data harga Bitcoin berasal dari platform dagangan kuantitatif FMZ:https://www.quantinfo.com/Tools/View/4.htmlSatu artikel menggunakan DRL+gym untuk melatih strategi dagangan:https://towardsdatascience.com/visualizing-stock-trading-agents-using-matplotlib-and-gym-584c992bc6d4Contoh-contoh perbincangan mengenai pytorch:https://github.com/yunjey/pytorch-tutorialArtikel ini akan menggunakan pelaksanaan ringkas model LSTM-PPO ini secara langsung:https://github.com/seungeunrho/minimalRL/blob/master/ppo-lstm.pyArtikel mengenai PPO:https://zhuanlan.zhihu.com/p/38185553Lebih banyak artikel mengenai DRL:https://www.zhihu.com/people/flood-sung/postsMengenai gim, artikel ini tidak memerlukan pemasangan, tetapi pembelajaran penguat adalah biasa:https://gym.openai.com/

3.LSTM-PPO

Untuk penjelasan mendalam mengenai PPO, anda boleh mempelajari rujukan di atas, di sini adalah pengenalan konsep yang sederhana. Pada peringkat sebelumnya, rangkaian LSTM hanya meramalkan satu harga, dan bagaimana transaksi beli dan jual boleh dilaksanakan dengan cara lain berdasarkan harga yang diramalkan ini, secara semula jadi, anda boleh berfikir, bukankah output langsung tindakan beli dan jual lebih langsung?

Berikut adalah kod sumber LSTM-PPO, yang boleh difahami dengan maklumat di atas:


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.比特币回测环境

Mengikut format gim, terdapat kaedah inisialisasi reset, langkah input tindakan, hasil yang dikembalikan adalah ((keadaan seterusnya, keuntungan tindakan, apakah tamat, maklumat tambahan), keseluruhan persekitaran reset juga dalam 60 baris, boleh diubahsuai sendiri untuk versi yang lebih rumit, kod tertentu:

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 akaun awal mempunyai wang?

Rumus untuk mengira pendapatan dalam keadaan ulangan adalah: pendapatan semasa = nilai akaun semasa - nilai akaun awal. Ini bermaksud bahawa jika harga Bitcoin turun, dan strategi melakukan operasi jual mata wang, walaupun nilai akaun keseluruhan berkurangan, sebenarnya harus memberi ganjaran kepada strategi. Jika masa ulangan panjang, akaun awal mungkin tidak memberi kesan yang besar, tetapi masih memberi kesan yang besar pada mulanya. Pengiraan pendapatan relatif memastikan bahawa setiap tindakan yang betul mendapat ganjaran positif.

Mengapa anda mengambil sampel semasa latihan?

Jumlah keseluruhan data adalah lebih daripada 10,000 baris K, jika setiap kali jumlah keseluruhan berjalan satu putaran, masa yang diperlukan adalah lama, dan strategi mungkin lebih mudah disesuaikan. Memetik 500 baris setiap kali sebagai data yang diulang sekali, walaupun masih mungkin disesuaikan, strategi menghadapi lebih daripada 10,000 kemungkinan permulaan yang berbeza.

Apa yang perlu dilakukan jika tiada wang atau tiada duit?

Keadaan ini tidak dipertimbangkan dalam persekitaran retargeting, jika mata wang telah dijual atau tidak mencapai jumlah dagangan minimum, pada masa ini menjalankan operasi jual sebenarnya sama dengan tidak menjalankan operasi, jika harga turun, berdasarkan perhitungan keuntungan relatif, strategi masih berdasarkan pahala positif. Kesan keadaan ini adalah apabila strategi menilai keadaan pasaran menurun dan baki akaun mata wang tidak dapat dijual, tidak dapat membezakan tindakan jual dan tidak bertindak, tetapi tidak mempengaruhi penilaian strategi itu sendiri terhadap keadaan pasaran.

Mengapa perlu mengembalikan maklumat akaun ke status?

Model PPO mempunyai rangkaian nilai yang digunakan untuk menilai nilai keadaan semasa, jelas jika strategi memutuskan bahawa harga akan naik, keseluruhan keadaan hanya mempunyai nilai positif apabila akaun semasa memegang Bitcoin, dan sebaliknya. Oleh itu, maklumat akaun adalah asas penting untuk penilaian rangkaian nilai. Perhatikan bahawa tidak mengembalikan maklumat tindakan masa lalu sebagai keadaan, individu menganggap ini tidak berguna untuk menilai nilai.

Dalam apa keadaan ia akan kembali tidak berfungsi?

Apabila keuntungan yang dibawa oleh keputusan strategi tidak dapat menampung kos urus niaga, ia harus dikembalikan tanpa tindakan. Walaupun perihalan di atas berulang kali menggunakan strategi untuk menentukan trend harga, hanya untuk kemudahan pemahaman, sebenarnya model PPO ini tidak membuat ramalan mengenai pasaran, hanya mengeluarkan kemungkinan tiga tindakan.

6.数据的获取和训练

Seperti artikel sebelumnya, cara dan format data diperoleh adalah sebagai berikut, pertukaran Bitfinex BTC_USD berdagang pada K-Line untuk tempoh satu jam dari 2018/5/7 hingga 2019/6/27:

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 menggunakan rangkaian LSTM, latihan mengambil masa yang lama, saya menukar versi GPU yang lain, kira-kira 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.训练结果和分析

Selepas menunggu lama:img

Pertama, lihatlah pasaran data latihan, secara keseluruhan, separuh pertama adalah penurunan yang panjang, separuh kedua adalah kemerosotan yang kuat.img

Operasi pembelian sebelum latihan banyak, pada dasarnya tidak ada pusingan yang menguntungkan. Untuk latihan pertengahan, operasi pembelian secara beransur-ansur berkurangan, kemungkinan keuntungan juga semakin besar, tetapi masih ada kemungkinan kerugian yang besar.img

Jika kita ratakan pendapatan setiap pusingan, hasilnya adalah seperti ini:img

Strategi ini dengan cepat keluar dari keadaan pendapatan negatif awal, tetapi turun naik yang besar, sehingga selepas 10,000 putaran, pendapatan tumbuh dengan cepat, secara keseluruhan, latihan model sukar.

Selepas latihan akhir, model akan menjalankan semua data sekali lagi untuk melihat bagaimana ia berfungsi, dan mencatat nilai pasaran keseluruhan akaun, jumlah bitcoin yang dipegang, peratusan nilai bitcoin, pendapatan keseluruhan. Pertama, nilai pasaran keseluruhan, pendapatan keseluruhan dan sebagainya, tidak termasuk:img
Nilai pasaran keseluruhan meningkat dengan perlahan semasa pasaran beruang awal, dan juga meningkat semasa pasaran lembu akhir, tetapi masih mengalami kerugian berkala.

Akhirnya, lihatlah perbandingan pegangan saham, poros kiri gambar adalah perbandingan pegangan saham, poros kanan adalah pasaran, yang dapat menilai secara awal bahawa model muncul terlalu sesuai, frekuensi pegangan saham rendah pada masa pasaran beruang awal, frekuensi pegangan saham tinggi pada masa pasaran bawah.img

8.测试数据分析

Pada masa data ujian diperoleh, harga Bitcoin dalam satu jam dari 27 Jun 2019 hingga kini. Gambar ini menunjukkan harga jatuh dari $ 13,000 pada awal kepada lebih dari $ 9,000 pada hari ini, yang merupakan ujian besar untuk model.img

Pertama, pada akhirnya keuntungan relatif, menunjukkan prestasi yang buruk, tetapi tidak ada kerugian.img

Jika kita melihat lagi keadaan pegangan, kita boleh meneka bahawa model cenderung untuk membeli dan menjual semula selepas jatuh, dan pergerakan pasaran Bitcoin dalam beberapa masa kebelakangan ini sangat kecil, dan model itu berada dalam keadaan kosong.img

9.总结

Artikel ini menggunakan kaedah pembelajaran mendalam yang diperkuat oleh PPO untuk melatih bot dagangan automatik Bitcoin, dan beberapa kesimpulan juga diperoleh. Oleh kerana masa yang terhad, model masih mempunyai beberapa tempat untuk diperbaiki, selamat datang untuk dibincangkan. Pelajaran terbesar adalah kaedah standardisasi data, jangan menggunakan kaedah seperti skala, jika tidak, model akan cepat mengingat hubungan harga dan pasaran, jatuh ke dalam kesesuaian.

Artikel sebelum ini: Beberapa strategi terbuka yang dikongsi oleh pencipta FMZ di platform kuantiti:https://zhuanlan.zhihu.com/p/64961672Kursus transaksi kuantitatif mata wang digital di kelas awan mudah di NetEase hanya 20 yuan:https://study.163.com/course/courseMain.htm?courseId=1006074239&share=2&shareId=400000000602076Saya telah mendedahkan satu strategi frekuensi tinggi yang pernah mendapat banyak wang:https://www.fmz.com/bbs-topic/1211


Berkaitan

Lebih lanjut

Lisa20231Saya ingin bertanya kepada anda, mengapa anda membalikkan gambar hasil ujian? Mengapa pendapatan anda selalu berkurangan apabila harga dolar naik?

Jackmaprofit = self.value - (self.initial_balance+self.initial_stocks * self.df.iloc[self.current_time,4]) Terdapat bug Ia sepatutnya: 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]) Terdapat bug Ia sepatutnya: profit = self.value - (self.initial_balance+self.initial_stocks*self.df.iloc[self.start,4])

TimoshenkoLebih kuat daripada versi pertama.

xw2021Berani!

Eddie.Tanah-tanah yang tidak boleh ditanam.

RumputGPU versi `` device = torch.device (('cuda' if torch.cuda.is_available)) else 'cpu') class PPO ((nn.Module): def __init__(self): super ((PPO, self).__init__() self.data = [] Pergilah. 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 Pergilah. 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) return v Pergilah. def put_data ((self, transition): self.data.append ((transition)) Pergilah. def make_batch ((self): s_lst, a_lst, r_lst, s_prime_lst, prob_a_lst, hidden_lst, done_lst = [], [], [], [], [], [], [] Untuk transisi dalam self.data: s, a, r, s_prime, prob_a, hidden, done = transition Pergilah. 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 (dalam bahasa Inggeris) done_mask = 0 if done else 1 done_lst.append (([done_mask]) Pergilah. 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] Pergilah. 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 (()) Advantage_lst = [] kelebihan = 0.0 for item in delta [::-1]: Advantage = gamma * lmbda * Advantage + item[0] advantage_lst.append (([advantage]) Advantage_lst.reverse (dalam bahasa Inggeris) 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 = nisbah * kelebihan surr2 = torch.clamp ((ratio, 1-eps_clip, 1+eps_clip) * kelebihan loss = -torch.min ((surr1, surr2) + F.smooth_l1_loss ((v_s, td_target.detach))) Self.optimizer.zero_grad (dalam bahasa Inggeris) loss.mean (().backward ((retain_graph=True)) self.optimizer.step (dalam bahasa Inggeris) ``