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.
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/
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()
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)
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.
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))
Selepas menunggu lama:
Pertama, lihatlah pasaran data latihan, secara keseluruhan, separuh pertama adalah penurunan yang panjang, separuh kedua adalah kemerosotan yang kuat.
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.
Jika kita ratakan pendapatan setiap pusingan, hasilnya adalah seperti ini:
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:
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.
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.
Pertama, pada akhirnya keuntungan relatif, menunjukkan prestasi yang buruk, tetapi tidak ada kerugian.
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.
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
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) ``