監督&強化學習模型在金融市場的應用

本文是關於我在Github上維護的項目:

Ceruleanacg/Personae?

github.com圖標

的介紹。

目前,在本項目中:

  • 實現了4個強化學習模型。
  • 實現了3個監督學習模型。
  • 實現了1個簡單的交易所,提供基本的買入、持有、賣出操作(賣空仍在開發中),標的物可以是股票組合或者期貨合約組合。

對於監督學習模型的數據集:

我們採用2008年1月1日到2018年1月1日這個區間內,

  • 招商銀行(600036)
  • 交通銀行(601328)
  • 中信銀行(601998)
  • 工商銀行(601389)

這四隻銀行股在第T天的,

  • 開盤價(Open)
  • 收盤價(Close)
  • 最高價(High)
  • 最低價(Low)
  • 交易量(Volume)

作為輸入數據,第T+1天的,

  • 收盤價(Close)

作為輸出數據,進行訓練,其中,這個區間前70%的數據作為訓練數據,後30%作為測試數據,目前沒有設置驗證集數據。

下圖是目前的實驗結果,就目前的實驗結果來看,監督學習的表現要好於強化學習。

圖例 :藍色的折線是測試數據集,其他顏色的折線是三種不同的監督學習模型在測試集上的預測。

接下來,我們將會依次對這3個監督學習模型與4個強化學習模型做一個簡短的介紹。

1. Naive-LSTM (LSTM)

該模型是基於LSTM和Dense(全連接)的基本模型,輸入是序列長度為5,即第T到第T+4天的OCHLV數據,輸出是一個實數,代表了第T+5的預測收盤價格。

arXiv:1506.02078: Visualizing and Understanding Recurrent Network

模型計算圖:

Naive-LSTM

以下是構建模型的核心代碼:

def _init_nn(self): self.rnn = self.add_rnn(1, self.hidden_size) self.rnn_output, _ = tf.nn.dynamic_rnn(self.rnn, self.x, dtype=tf.float32) self.rnn_output = self.rnn_output[:, -1] self.rnn_output_dense = self.add_fc(self.rnn_output, 16) self.y = self.add_fc(self.rnn_output_dense, self.y_space)

可以看出,第一行代碼調用了項目中封裝的用於構建LSTM層的API,第二行代碼用於計算該層輸出和狀態序列,第四行和第五行構造了一個全連接層並計算最終的輸出。

2. TreNet (HNN)

IJCAI 2017. Hybrid Neural Networks for Learning the Trend in Time Series

上述引用的論文提出了一種混合神經網路的結構,同時用RNN與CNN提取序列特徵,然後將輸出拼接作為全連接層的輸入,最後輸出最終的預測結果。

模型計算圖:

Tre-Net

以下是構建模型的核心代碼:

def _init_nn(self): self.rnn = self.add_rnn(1, self.hidden_size) self.rnn_output, _ = tf.nn.dynamic_rnn(self.rnn, self.rnn_x, dtype=tf.float32) self.rnn_output = self.rnn_output[:, -1] # self.cnn_x_input is a [-1, 5, 20, 1] tensor, after cnn, the shape will be [-1, 5, 20, 5]. self.cnn = self.add_cnn(self.cnn_x, filters=2, kernel_size=[2, 2], pooling_size=[2, 2]) self.cnn_output = tf.reshape(self.cnn, [-1, self.seq_length * self.x_space * 2]) self.y_concat = tf.concat([self.rnn_output, self.cnn_output], axis=1) self.y_dense = self.add_fc(self.y_concat, 16) self.y = self.add_fc(self.y_dense, self.y_space)

可以看出,第一到第三行構造了LSTM層並計算結果,第四到第五行用項目封裝的構造CNN的API構造了CNN層並計算了結果。最後拼接了RNN和CNN的結果,作為全連接層的輸入,然後得到最終的計算結果。

3. DA-RNN (DualAttnRNN)

arXiv:1704.02971: A Dual-Stage Attention-Based Recurrent Neural Network for Time Series Prediction

上述引用的論文提出了一種基於注意力機制(Attention Based Model)的與Seq-to-Seq模型的網路結構,其創新點在於該模型連續兩次使用注意力機制,在對原始序列使用注意力機制求權重後再次使用注意力機制對編碼後的序列求權重,然後經解碼與全連接層後輸出結果。

模型計算圖:

Dual-Stage RNN

以下是構建模型的核心代碼:

def _init_nn(self): # First Attn with tf.variable_scope("1st_encoder"): self.f_encoder_rnn = self.add_rnn(1, self.hidden_size) self.f_encoder_outputs, _ = tf.nn.dynamic_rnn(self.f_encoder_rnn, self.x, dtype=tf.float32) self.f_attn_inputs = self.add_fc(self.f_encoder_outputs, self.hidden_size, tf.tanh) self.f_attn_outputs = tf.nn.softmax(self.f_attn_inputs) with tf.variable_scope("1st_decoder"): self.f_decoder_input = tf.multiply(self.f_encoder_outputs, self.f_attn_outputs) self.f_decoder_rnn = self.add_rnn(1, self.hidden_size) self.f_decoder_outputs, _ = tf.nn.dynamic_rnn(self.f_decoder_rnn, self.f_decoder_input, dtype=tf.float32) # Second Attn with tf.variable_scope("2nd_encoder"): self.s_attn_input = self.add_fc(self.f_decoder_outputs, self.hidden_size, tf.tanh) self.s_attn_outputs = tf.nn.softmax(self.s_attn_input) with tf.variable_scope("2nd_decoder"): self.s_decoder_input = tf.multiply(self.f_decoder_outputs, self.s_attn_outputs) self.s_decoder_rnn = self.add_rnn(2, self.hidden_size) self.f_decoder_outputs, _ = tf.nn.dynamic_rnn(self.s_decoder_rnn, self.s_decoder_input, dtype=tf.float32) self.f_decoder_outputs_dense = self.add_fc(self.f_decoder_outputs[:, -1], 16) self.y = self.add_fc(self.f_decoder_outputs_dense, self.y_space)

可以看出,分別對應於四個變數命名空間,具有2組4個編解碼層,在每一個編解碼層都運用了一次注意力機制,求出當前序列的權重,然後與序列相乘後進行下一步的編解碼工作,這是一種Seq-to-Seq的機制,更廣泛地用於自然語言處理。最終解碼的輸出結果作為全連接層的輸入,然後計算最終的結果。

以上是關於項目中監督學習模型的簡短介紹,其中,所有模型的具體實現可以在項目鏈接中看到。

接下來是關於3個強化學習模型的介紹,但是在介紹強化學習模型前,我們首先對強化學習的數據和環境一個簡短的概述。

Financial Market

這個文件實現了三個核心類,分別是:

  • Market
  • Trader
  • Position

他們分別代表了市場、交易員、持倉信息,最終Market類作為Agent(強化學習模型)的Environment(環境),接受Agent的Action(動作),同時給出Next State(下一狀態)和Reward(獎勵),並進行迭代。

對於強化學習使用的數據,

我們使用這四隻銀行股在第T天的,

  • 開盤價(Open)
  • 收盤價(Close)
  • 最高價(High)
  • 最低價(Low)
  • 交易量(Volume)

和交易員在第T天的,

  • 現金(Cash)
  • 持倉價值(Holding Value)
  • 各持倉量(Holding Amount)

作為State(狀態),使用交易指令,

  • 買入(Buy)
  • 賣出(Sell)
  • 持有(Hold)

作為Agent(智能體)的Action(動作),其中,Reward Func(獎勵函數)有2個版本,的計算代碼如下:

def _update_reward(self, action_code, action_status, position): if action_code == ActionCode.Buy: if action_status == ActionStatus.Success: if position.pro_value > position.cur_value: self.reward += 70 else: self.reward -= 50 else: self.reward -= 100 elif action_code == ActionCode.Sell: if action_statu @staticmethod def _calculate_reward_v1(action_code, action_status, position): if action_status == ActionStatus.Failed: reward = -100 else: if position.pro_value >= position.cur_value: if action_code == ActionCode.Hold: reward = 50 else: reward = 100 else: reward = - 50 return reward @staticmethod def _calculate_reward_v2(_, action_status, position): if action_status == ActionStatus.Success: reward = position.pro_value - position.cur_value else: reward = -200 return rewards == ActionStatus.Success: if position.pro_value > position.cur_value: self.reward -= 70 else: self.reward += 50 else: self.reward -= 100 else: if action_status == ActionStatus.Success: if position.pro_value > position.cur_value: self.reward += 70 else: self.reward -= 50 else: self.reward -= 100

接下來是關於實驗結果與強化學習模型的介紹:

圖例 - 橫坐標是時間,縱坐標是利潤,其中藍色折線是基準線,其他顏色的折線是強化學習模型表現

可以看出,除了Policy Gradient可以跑贏基準收益外,其他強化學習模型的收益甚至不如基準,這裡非常值得討論,目前筆者也在嘗試從參數、輸入特徵、輸出特徵、獎勵函數等多個角度考慮解決該問題。

接下來是關於強化學習模型的介紹:

1. Policy Gradient

NIPS. Vol. 99. 1999: Policy gradient methods for reinforcement learning with function approximation

模型計算圖:

Policy Gradient

Basic Policy Gradient的思想很樸素,重複及可能多的採樣,對於一次採樣的所有動作中,根據獎勵函數值的正負決定梯度下降的方向,從而提高或者降低這些動作出現的概率。

Policy Gradient Algorithm

以下是構建模型的核心代碼:

def _init_nn(self): # Initialize predict actor and critic. w_init, b_init = tf.random_normal_initializer(.0, .3), tf.constant_initializer(0.1) with tf.variable_scope(nn): first_dense = tf.layers.dense(self.s, 50, tf.nn.relu, kernel_initializer=w_init, bias_initializer=b_init) second_dense = tf.layers.dense(first_dense, 50, tf.nn.relu, kernel_initializer=w_init, bias_initializer=b_init) action_prob = tf.layers.dense(second_dense, self.a_space, tf.nn.tanh, kernel_initializer=w_init, bias_initializer=b_init) self.a_prob = action_prob self.a_s_prob = tf.nn.softmax(action_prob)def _init_op(self): with tf.variable_scope(loss): # a_one_hot = tf.one_hot(self.a, self.a_space) # negative_cross_entropy = -tf.reduce_sum(tf.log(self.a_prob) * a_one_hot) negative_cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=self.a_prob, labels=self.a) self.loss_fn = tf.reduce_mean(negative_cross_entropy * self.r) with tf.variable_scope(train): self.train_op = tf.train.RMSPropOptimizer(self.learning_rate * 2).minimize(self.loss_fn) self.session.run(tf.global_variables_initializer())

本實現簡單地採用兩次全連接後輸出Softmax後各個動作的概率,最後期望最小化採樣動作的概率與真實概率乘以獎勵函數的值的交叉熵。

2. Double DQN

arXiv:1509.06461: Deep Reinforcement Learning with Double Q-learning

模型計算圖:

Double-DQN

Double-DQN採用評估網路與目標網路相互制約,期望避免傳統DQN中容易出現的過度估計問題。首先使用評估網路預測下一個狀態的狀態-動作函數值,然後選取取得最大值的動作,計做 a_{max} ,接著用目標網路預測下一狀態與採用 a_{max} 的狀態值計算標籤,然後期望最小化標籤與評估網路對當前狀態的狀態-動作函數和當前動作的Q的均方差。

Double-DQN

以下是構建模型的核心代碼:

def _init_op(self): self.loss = tf.reduce_mean(tf.squared_difference(self.q_next, self.q_eval)) self.train_op = tf.train.RMSPropOptimizer(self.learning_rate).minimize(self.loss) self.e_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=q_eval) self.t_params = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope=q_target) self.update_q_target_op = [tf.assign(t, e) for t, e in zip(self.t_params, self.e_params)] self.session.run(tf.global_variables_initializer())def train(self): # 1. If buffer length is less than buffer size, return. if self.buffer_length < self.buffer_size: return # 2. Update Q-Target if need. if self.total_step % self.update_q_target_step == 0: self.session.run(self.update_q_target_op) # 3. Get transition batch. s, a, r, s_next = self.get_transition_batch() # 4. Calculate q_eval_next. q_eval_next = self.session.run(self.q_eval, {self.s: s_next}) # 5. Get action indices and make batch indices. a_indices = np.argmax(q_eval_next, axis=1) b_indices = np.arange(self.batch_size, dtype=np.int) # 6. Calculate q_target_next selected by actions. q_target_next = self.session.run(self.q_target, {self.s_next: s_next}) q_target_next_with_a = q_target_next[b_indices, a_indices] # 7. Calculate labels. q_eval = self.session.run(self.q_eval, {self.s: s}) q_next = q_eval.copy() q_next[b_indices, a.astype(np.int)] = r + self.gamma * q_target_next_with_a # 8. Calculate loss. _, self.critic_loss = self.session.run([self.train_op, self.loss], {self.s: s, self.q_next: q_next}) # 9. Increase total step. self.total_step += 1

Double-DQN的實現代碼中,注釋已經非常詳盡,在這裡就不再過多贅述。

3. Deep Deterministic Policy Gradient (DDPG)

arXiv:1509.02971: Continuous control with deep reinforcement learning

模型計算圖:

DDPG

DDPG用於連續動作空間,在本問題中,對於四隻股票的買賣持有的動作被映射到區間[0, 11],其中,DDPG使用Actor-Critic Model,引入評估Actor,目標Actor模型與評估Critic,目標Critic模型兩組四個網路,其中Actor模型用於預測動作,Critic模型用於評估當前狀態與動作的分數(狀態-動作值函數),該方法期望最小化:評估Critic與評估Actor對當前狀態-動作函數值與目標Critic和目標Actor對下一狀態-動作函數值的均方差(如演算法圖所示),依次迭代改進目標Critic和目標Actor。

DDPG

以下是構建模型的核心代碼:

def _init_nn(self): # Initialize predict actor and critic. self.a_predict = self.__build_actor_nn(self.s, "predict/actor", trainable=True) self.q_predict = self.__build_critic(self.s, self.a_predict, "predict/critic", trainable=True) # Initialize target actor and critic. self.a_next = self.__build_actor_nn(self.s_next, "target/actor", trainable=False) self.q_next = self.__build_critic(self.s_next, self.a_next, "target/critic", trainable=False) # Save scopes self.scopes = ["predict/actor", "target/actor", "predict/critic", "target/critic"]def _init_op(self): # Get actor and critic parameters. params = [tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES, scope) for scope in self.scopes] zipped_a_params, zipped_c_params = zip(params[0], params[1]), zip(params[2], params[3]) # Initialize update actor and critic op. self.update_a = [tf.assign(t_a, (1 - self.tau) * t_a + self.tau * p_a) for p_a, t_a in zipped_a_params] self.update_c = [tf.assign(t_c, (1 - self.tau) * t_c + self.tau * p_c) for p_c, t_c in zipped_c_params] # Initialize actor loss and train op. self.a_loss = -tf.reduce_mean(self.q_predict) self.a_train_op = tf.train.RMSPropOptimizer(self.learning_rate).minimize(self.a_loss, var_list=params[0]) # Initialize critic loss and train op. self.q_target = self.r + self.gamma * self.q_next self.c_loss = tf.losses.mean_squared_error(self.q_target, self.q_predict) self.c_train_op = tf.train.RMSPropOptimizer(self.learning_rate * 2).minimize(self.c_loss, var_list=params[2]) # Initialize variables. self.session.run(tf.global_variables_initializer())

代碼首先初始化兩組四個網路,分別是評估Actor、目標Actor,評估Critic,目標Critic,然後根據DDPG的演算法最小化評估Critic與評估Actor對當前狀態-動作函數值與目標Critic和目標Actor對下一狀態-動作函數值的均方差,依次迭代更新Actor與Critic直至收斂。

4. Dueling-DQN

arXiv:1511.06581: Dueling Network Architectures for Deep Reinforcement Learning

模型計算圖:

Dueling-DQN

演算法:

Dueling-DQN

相對於DQN直接輸出狀態-動作函數值,Dueling-DQN的狀態-動作函數值由上式決定,從網路結構上可以看出,在輸出狀態-動作函數值前,Dueling-DQN的結構拆分了原DQN網路結構的最後一層,這樣的思想很像Actor-Critic模型中的Baseline,因為並不是每個狀態都是十分重要的,有些時候對於這些狀態,採取那個動作都不會有很大的影響。

以下是構建模型的核心代碼:

def __build_critic_nn(self, s, scope): w_init, b_init = tf.random_normal_initializer(.0, .3), tf.constant_initializer(.1) with tf.variable_scope(scope): s_first_dense = tf.layers.dense(s, 16, activation=tf.nn.relu, kernel_initializer=w_init, bias_initializer=b_init) s_second_dense = tf.layers.dense(s_first_dense, 16, tf.nn.relu, kernel_initializer=w_init, bias_initializer=b_init) value = tf.layers.dense(s_second_dense, 1, kernel_initializer=w_init, bias_initializer=b_init) advantage = tf.layers.dense(s_second_dense, self.a_space, kernel_initializer=w_init, bias_initializer=b_init) q = value + (advantage - tf.reduce_mean(advantage, axis=1, keep_dims=True)) return q

即最終對於某個狀態-動作函數值而言,Advantage的在不同動作維度上的值一定意義上描述了這個動作對於這個狀態的重要性,最後加上Q值,避免了過度估計。

以上是最近關於強化學習和監督學習在金融市場中的一些應用和相關論文方法的實現。

Ceruleanacg/Personae?

github.com圖標

目前仍有以下問題亟待探討與解決:

  • 強化學習模型獎勵函數的設計
  • 強化學習中基於值迭代的演算法難以收斂
  • 監督學習的特徵維度如何擴展

同時,項目中可能有Bug,歡迎各種Issue提出以及歡迎貢獻各種代碼 : )

推薦閱讀:

TAG:強化學習ReinforcementLearning | 深度學習DeepLearning | 股票 |