iT邦幫忙

2024 iThome 鐵人賽

DAY 18
0
AI/ ML & Data

打開就會 AI 與數據分析的投資理財術系列 第 18

Day17:強化學習在交易中的應用--基礎打底

  • 分享至 

  • xImage
  •  

在本節中,我們將探索強化學習(Reinforcement Learning,RL)的基本概念,以及如何將其應用於金融交易,以開發自適應的交易策略。我們將從理論出發,了解強化學習的核心組成部分,深入介紹深度Q網絡(Deep Q-Network, DQN)的原理和實現,並通過 from-scratch 實踐示例,展示如何使用DQN算法來優化交易決策,包括其損失函數的詳細解釋,明日則會使用廣用的套件來展示。今日 Colab


一、引言

1. 強化學習的背景

  • 人工智慧的三大領域:強化學習是機器學習的三大分支之一,與監督學習和非監督學習並列。

  • 應用範圍廣泛:強化學習在遊戲、機器人控制、自動駕駛等領域取得了顯著的成果。

2. 為什麼將強化學習應用於交易

  • 動態決策:金融市場是一個複雜的動態環境,需要連續的決策過程。

  • 自適應策略:強化學習可以通過與環境的交互,不斷學習和優化策略,適應市場的變化。

  • 最大化長期收益:強化學習的目標是通過累積獎勵,最大化長期回報,這與投資的目標一致。

  • 讓AI自己找尋最佳策略:透過 Reward 的設計讓AI可以找到適合處理複雜的邏輯。

https://ithelp.ithome.com.tw/upload/images/20241001/20120549T6OPKR24vW.jpg


二、強化學習的基本概念

1. 強化學習的核心組成

  • 代理(Agent):學習並執動作作的主體,在交易中可以視為交易策略或機器人。

  • 環境(Environment):代理所處的外部系統,在交易中即為金融市場。

  • 狀態(State, S:環境在某一時刻的描述,如價格、技術指標等。

  • 動作(Action, A:代理可以採取的動作,如買入、賣出、持有。

  • 獎勵(Reward, R:代理採取動作後獲得的反饋,用於評估動作的好壞。
    https://ithelp.ithome.com.tw/upload/images/20241001/20120549yndPWT2j3t.png

2. 強化學習的學習目標

  • 策略(Policy, \pi:代理從狀態到動作的映射,即在某一狀態下應該採取的動作。

  • 值函數(Value Function, V(s):在狀態 s 下,預期能夠獲得的累積獎勵。

  • 目標:學習最優策略 \pi^*,使得累積獎勵最大化。

3. 強化學習的類型

  • 模型驅動(Model-based):代理對環境有模型,可預測未來狀態。

  • 無模型(Model-free):代理不知道環境的模型,通過試錯進行學習。

  • 值函數方法:如Q學習,學習狀態-動作對(state-action pair)的值。

  • 策略梯度方法:直接優化策略函數。


三、強化學習在交易中的應用

1. 定義交易環境

  • 狀態空間:包括價格、技術指標、成交量等市場資訊。

  • 動作空間:買入、賣出、持有。

  • 獎勵機制:通常與交易收益相關,如實現的利潤或資產增值。

2. 設計交易代理

  • 策略表示:可以使用深度神經網絡來表示策略函數或值函數。

  • 決策過程:代理根據當前的狀態,選擇最優動作。

3. 強化學習算法的選擇

  • Q學習(Q-Learning):一種經典的無模型強化學習算法。

  • 深度Q網絡(Deep Q-Network, DQN):結合深度學習的Q學習,適合處理高維度的狀態空間。

  • 策略梯度方法:如A2C、PPO等,直接優化策略函數,效果更佳。


四、深度Q網絡(DQN)的詳細介紹

1. DQN的基本原理

  • Q學習回顧

    • Q函數(動作價值函數)Q(s, a),表示在狀態 s 下採取動作 a 所能獲得的期望累積獎勵。

    • Bellman方程

      https://ithelp.ithome.com.tw/upload/images/20241001/20120549R5SC40IsfD.png

      • r:當前獎勵
      • \gamma:折扣因子,介於 [0, 1]
      • s':下一狀態
      • a':下一狀態下的可能動作
  • DQN的改進

    • 使用神經網絡近似Q函數:由於狀態空間可能非常大,無法用表格表示Q函數,DQN使用深度神經網絡作為函數逼近器。

    • 經驗回放(Experience Replay):將代理的經驗存儲在記憶庫中,隨機抽取小批量樣本進行訓練,打破數據的相關性,提高樣本效率。

    • 固定目標網絡(Fixed Target Network):使用一個拷貝的目標網絡來計算目標Q值,定期更新,穩定訓練過程。

2. DQN的損失函數

  • 目標Q值的計算

    • 目標Q值(Target Q Value)

      https://ithelp.ithome.com.tw/upload/images/20241001/20120549Bc9HWTp66w.png

      • Q_{\text{target}}:目標網絡的Q函數
  • 損失函數

    • 均方誤差損失(Mean Squared Error Loss)

      https://ithelp.ithome.com.tw/upload/images/20241001/20120549fBbblicsMH.png

      • \theta:當前網絡的參數
      • Q_{\theta}(s, a):當前網絡的Q值
  • 優化目標

    • 通過最小化損失函數 L(\theta),調整網絡參數 \theta,使得當前網絡的Q值逼近目標Q值。

3. DQN的訓練流程

  1. 初始化:建立當前Q網絡 Q_{\theta} 和目標Q網絡 Q_{\theta^-},並將參數 \theta^- 設置為 \theta

  2. 經驗回放記憶庫:建立一個固定容量的記憶庫,用於存儲代理的經驗 (s, a, r, s', done)

  3. 重複以下步驟

    • 狀態觀測:從環境獲取當前狀態 s

    • 選擇動作:使用 \epsilon-貪婪策略,選擇動作 a

      • 以機率 \epsilon 隨機選擇動作(探索)。
      • 以機率 1 - \epsilon 選擇最大Q值的動作(利用):
        https://ithelp.ithome.com.tw/upload/images/20241001/20120549BPEdAU3ZGs.png
    • 執行動作:在環境中執行動作 a,獲得獎勵 r 和下一狀態 s'

    • 存儲經驗:將 (s, a, r, s', done) 存入記憶庫。

    • 隨機採樣:從記憶庫中隨機抽取一個小批量經驗。

    • 計算目標Q值
      https://ithelp.ithome.com.tw/upload/images/20241001/20120549ZaDYGVFrnn.png

    • 計算損失

      https://ithelp.ithome.com.tw/upload/images/20241001/201205490O9rCCFlPA.png

    • 梯度下降更新參數:使用優化器(如Adam)最小化損失 L(\theta)

    • 更新目標網絡:每隔 C 步,將 \theta^- 更新為 \theta

  4. 調整探索率 \epsilon:隨著訓練進行,逐漸減小 \epsilon,從探索轉向利用。


五、使用深度Q網絡(DQN)進行交易策略開發

1. 準備工作

  • 安裝必要的庫

    !pip install torch torchvision
    !pip install gym
    !pip install pandas numpy matplotlib
    
    #如果在本機上
    #pip install torch torchvision
    #pip install gym
    #pip install pandas numpy matplotlib
    
    
  • 導入庫

    import gym
    import torch
    import torch.nn as nn
    import torch.optim as optim
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    

2. 構建交易環境

在應用強化學習於交易時,我們需要建立一個環境,讓代理(Agent)可以與之互動。這個環境需要模擬真實的交易情況,包括價格變化、持倉狀態、資金狀況等。

2.1 為什麼需要自定義環境

  • Gym庫的限制:OpenAI 的 Gym 庫提供了許多現成的環境(如經典控制、遊戲等),但並沒有針對股票交易的預設環境。

  • 特定需求:金融交易環境具有特殊的需求,如多維度的狀態信息、複雜的交易規則、手續費和滑點等。

2.2 環境的組成

  • 狀態(Observation)

    • 包含市場資訊和代理自身的狀態。

    • 在本例中,狀態包括:

      • 開盤價(Open)

      • 最高價(High)

      • 最低價(Low)

      • 收盤價(Close)

      • 資金餘額(Balance)

      • 持有的股票數量(Shares Held)

  • 動作(Action)

    • 定義代理可以採取的動作。

    • 在本例中,動作空間為離散的三個動作:

      • 0:持有(Hold)

      • 1:買入(Buy)

      • 2:賣出(Sell)

  • 獎勵(Reward)

    • 代理在執行動作後獲得的反饋,用於引導學習。

    • 在本例中,獎勵定義為資產淨值的增減量。

2.3 環境的實現

以下是環境的詳細實現,並附有詳細的解釋:

class StockTradingEnv(gym.Env):
    def __init__(self, data):
        super(StockTradingEnv, self).__init__()
        self.data = data
        self.current_step = 0
        self.balance = 10000  # 初始資金
        self.shares_held = 0
        self.net_worth = self.balance
        self.max_net_worth = self.balance
        self.done = False
        
        # 定義動作空間:0-持有,1-買入,2-賣出
        self.action_space = gym.spaces.Discrete(3)
        
        # 定義狀態空間
        self.observation_space = gym.spaces.Box(low=0, high=np.inf, shape=(6,), dtype=np.float32)
    
    def reset(self):
        self.balance = 10000
        self.shares_held = 0
        self.net_worth = self.balance
        self.max_net_worth = self.balance
        self.current_step = 0
        self.done = False
        return self._next_observation()
    
    def _next_observation(self):
        obs = np.array([
            self.data.iloc[self.current_step]['Open'],
            self.data.iloc[self.current_step]['High'],
            self.data.iloc[self.current_step]['Low'],
            self.data.iloc[self.current_step]['Close'],
            self.balance,
            self.shares_held
        ])
        return obs
    
    def step(self, action):
        current_price = self.data.iloc[self.current_step]['Close']
        self.current_step += 1
        
        if action == 1:  # 買入
            max_shares = int(self.balance / current_price)
            shares_bought = max_shares
            self.balance -= shares_bought * current_price
            self.shares_held += shares_bought
            
        elif action == 2:  # 賣出
            self.balance += self.shares_held * current_price
            self.shares_held = 0
        
        self.net_worth = self.balance + self.shares_held * current_price
        self.max_net_worth = max(self.max_net_worth, self.net_worth)
        
        reward = self.net_worth - self.max_net_worth
        
        if self.current_step >= len(self.data) - 1:
            self.done = True
        
        obs = self._next_observation()
        return obs, reward, self.done, {}

2.4 程式解釋

  • __init__ 方法

    • 初始化環境的各種參數和變數

    • self.data:存儲市場數據,用於模擬價格變化。

    • self.current_step:當前的時間步,用於遍歷數據集中的每一個時間點。

    • self.balance:代理的資金餘額,初始設定為 10,000 美元。

    • self.shares_held:代理目前持有的股票數量,初始為 0。

    • self.net_worth:代理的資產淨值,等於資金餘額加上持有股票的市值。

    • self.max_net_worth:歷史上資產淨值的最大值,用於計算獎勵。

    • self.done:環境是否終止的標誌。

    • self.action_space:定義代理可以採取的動作,共有三個動作(持有、買入、賣出)。

    • self.observation_space:定義環境的狀態空間,由六個連續的數值組成,代表市場和代理的狀態。

  • reset 方法

    • 重置環境到初始狀態,以便開始新的訓練或測試。

    • 重置所有變數,包括資金、持倉、資產淨值和時間步。

    • 調用 _next_observation() 方法獲取初始狀態。

  • _next_observation 方法

    • 獲取當前時間步的觀測值(狀態)

    • self.data 中提取當前時間步的市場數據(Open、High、Low、Close)。

    • 包含代理的資金餘額和持有的股票數量。

    • 返回一個包含六個元素的 NumPy 陣列,作為代理的觀測。

  • step 方法

    • 執行代理的動作,並更新環境的狀態

    • 動作執行

      • 買入(action == 1)

        • 計算代理可以購買的最大股票數量(max_shares),確保不超過資金餘額。

        • 更新資金餘額(self.balance)和持有的股票數量(self.shares_held)。

      • 賣出(action == 2)

        • 將持有的所有股票賣出。

        • 更新資金餘額,持有的股票數量設置為 0。

      • 持有(action == 0)

        • 不進行任何操作,持續保持當前的資金和持倉狀態。
    • 狀態更新

      • 更新資產淨值(self.net_worth),計算當前的總資產。

      • 更新歷史最高資產淨值(self.max_net_worth),用於評估代理的表現。

    • 獎勵計算

      • 獎勵 定義為資產淨值與歷史最高資產淨值之間的差值。

      • 如果資產淨值超過了之前的最高值,獎勵為正;否則,獎勵為負。

      • 這種獎勵機制鼓勵代理持續提高資產淨值,避免資產的回撤。

    • 終止條件

      • 如果時間步超過了數據的長度,則設置 self.done = True,表示環境終止。
    • 返回值

      • 新的狀態(觀測值 obs)。

      • 獎勵(reward)。

      • 是否終止(self.done)。

      • 其他信息(此處為空字典 {},可用於擴展)。

2.5 環境設計的注意事項

  • 手續費和滑點

    • 手續費:在實際交易中,買賣股票會產生手續費,可以在 step 方法中扣除一定比例的費用。

    • 滑點:價格可能因市場流動性等因素而發生偏移,可以在執行買賣時調整成交價格。

  • 資金和持倉限制

    • 保證金交易:如果允許槓桿交易,需要考慮保證金和槓桿倍數。

    • 持倉限制:確保代理不能賣出超過持有的股票數量,避免出現負持倉。

  • 市場規則

    • 漲跌停限制:在某些市場中,價格波動有一定的限制,需在價格更新時考慮。

    • 交易時間:模擬交易時間,如開盤和收盤時間,非交易時間不允許下單。

  • 狀態歸一化

    • 為了提高模型的穩定性和收斂速度,建議對狀態進行歸一化處理,如標準化或最小最大縮放。
  • 隨機性引入

    • 可以在環境中引入隨機性,如隨機抽樣初始時間步,增加模型的泛化能力。
  • 安全性檢查

    • 確保在任何情況下,代理的資金、持倉和資產淨值不會出現非法值(如負值)。

3. 準備數據

  • 獲取數據

    import yfinance as yf
    
    data = yf.download('AAPL', start='2015-01-01', end='2021-01-01')
    data.reset_index(inplace=True)
    data = data[['Open', 'High', 'Low', 'Close', 'Volume']]
    
  • 創建環境實例

    env = StockTradingEnv(data)
    

4. 定義DQN代理

  • 建立神經網絡### Agent 類的定義
    接下來這一段會比較冗,最下面有富完整程式或者上 Colab看完整 Agent 程式
#使用 3 層 FCN 來當作 DQN 
class DQN(nn.Module):
      def __init__(self, input_dim, action_dim):
          super(DQN, self).__init__()
          self.fc1 = nn.Linear(input_dim, 64)
          self.fc2 = nn.Linear(64, 64)
          self.out = nn.Linear(64, action_dim)

      def forward(self, x):
          x = torch.relu(self.fc1(x))
          x = torch.relu(self.fc2(x))
          return self.out(x)

class Agent:
    def __init__(self, state_size, action_size):
        self.state_size = state_size  # 狀態空間的維度
        self.action_size = action_size  # 行動空間的維度
        self.memory = []  # 經驗回放記憶庫
        self.gamma = 0.99  # 折扣因子,用於計算未來獎勵的現值
        self.epsilon = 1.0  # 探索率(初始值),控制隨機行動的概率
        self.epsilon_min = 0.01  # 探索率的最小值
        self.epsilon_decay = 0.995  # 探索率的衰減率
        self.model = DQN(state_size, action_size)  # 當前 Q 網絡
        self.target_model = DQN(state_size, action_size)  # 目標 Q 網絡
        self.update_target_model()  # 初始化目標網絡的權重
        self.optimizer = optim.Adam(self.model.parameters(), lr=0.001)  # 優化器
        self.criterion = nn.MSELoss()  # 損失函數(均方誤差)
  • 解釋:
    • state_size:狀態向量的維度,即環境觀測的特徵數量。
    • action_size:行動的種類數量。在我們的交易環境中,有三個行動:持有、買入、賣出。
    • memory:經驗回放記憶庫,用於存儲代理在環境中收集的經驗樣本 (state, action, reward, next_state, done)
    • gamma:折扣因子,介於 0 和 1 之間,用於平衡即時獎勵和未來獎勵的重要性。值越接近 1,表示代理更重視長期獎勵。
    • epsilon:探索率,初始為 1.0,表示代理在訓練初期完全隨機選擇行動,以充分探索環境。
    • epsilon_min:探索率的下限,防止探索率衰減至 0,保證代理始終有一定概率進行探索。
    • epsilon_decay:每次更新後,探索率乘以該衰減率,逐漸降低隨機行動的概率。
    • self.modelself.target_model:兩個深度 Q 網絡,使用相同的結構,但權重不同。self.model 用於選擇行動和更新權重,self.target_model 用於計算目標 Q 值,定期從 self.model 更新權重。
    • self.optimizer:使用 Adam 優化器,學習率為 0.001,用於更新 self.model 的權重。
    • self.criterion:損失函數,採用均方誤差(MSE),用於衡量預測 Q 值與目標 Q 值之間的差距。

  • 更新目標網絡的方法

    def update_target_model(self):
        self.target_model.load_state_dict(self.model.state_dict())
    
    • 解釋:

      • 目的:將當前 Q 網絡的權重複製到目標 Q 網絡,以保持目標網絡的穩定性。
      • load_state_dict:PyTorch 的方法,用於加載模型的權重參數。

  • 選擇動作的方法
def act(self, state):
    if np.random.rand() <= self.epsilon:
        return np.random.choice(self.action_size)
    state = torch.FloatTensor(state).unsqueeze(0)
    act_values = self.model(state)
    return torch.argmax(act_values, dim=1).item()
  • 解釋:

    • np.random.rand() <= self.epsilon:產生一個介於 0 和 1 之間的隨機數,如果小於等於探索率 epsilon,則進行隨機探索。
    • 隨機行動:使用 np.random.choice(self.action_size) 從可用的行動中隨機選擇一個。
    • 狀態轉換:將輸入的狀態 state 轉換為 PyTorch 的張量,並增加一個維度以匹配模型輸入的形狀。
    • 模型預測:使用當前 Q 網絡 self.model 對當前狀態進行前向傳播,得到對各個行動的 Q 值估計。
    • 選擇最優行動:使用 torch.argmax 獲取 Q 值最大的行動,並返回對應的行動索引。

  • 記憶存儲的方法
def remember(self, state, action, reward, next_state, done):
    self.memory.append((state, action, reward, next_state, done))
  • 解釋:

    • 目的:將代理在環境中經歷的每個步驟的經驗存入記憶庫,以供後續的訓練使用。
    • 參數
      • state:當前狀態。
      • action:採取的行動。
      • reward:收到的獎勵。
      • next_state:執行行動後的新狀態。
      • done:是否達到終止狀態(回合結束)。

  • 訓練(回放)的方法
def replay(self, batch_size):
    if len(self.memory) < batch_size:
        return
    minibatch = random.sample(self.memory, batch_size)
    for state, action, reward, next_state, done in minibatch:
        target = reward
        if not done:
            next_state_tensor = torch.FloatTensor(next_state).unsqueeze(0)
            target = reward + self.gamma * torch.max(self.target_model(next_state_tensor)).item()
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        target_f = self.model(state_tensor)
        target_f = target_f.clone().detach()
        target_f[0][action] = target
        self.optimizer.zero_grad()
        output = self.model(state_tensor)
        loss = self.criterion(output, target_f)
        loss.backward()
        self.optimizer.step()
    if self.epsilon > self.epsilon_min:
        self.epsilon *= self.epsilon_decay
  • 解釋:

    • 檢查記憶庫大小:如果記憶庫中的經驗數量少於批次大小 batch_size,則不進行訓練,等待收集更多經驗。
    • 隨機抽樣:使用 random.sample 從記憶庫中隨機選取 batch_size 條經驗,組成小批次(minibatch)。
    • 對每個樣本進行訓練
      • 目標 Q 值計算
        • 如果當前樣本未達到終止狀態 done
          • 計算下一狀態的最大 Q 值:
            next_state_tensor = torch.FloatTensor(next_state).unsqueeze(0)
            max_future_q = torch.max(self.target_model(next_state_tensor)).item()
            
          • 計算目標 Q 值:
            target = reward + self.gamma * max_future_q
            
        • 如果已達到終止狀態:
          • 目標 Q 值等於當前的獎勵:
            target = reward
            
      • 當前 Q 值計算
        • 將當前狀態轉換為張量:
          state_tensor = torch.FloatTensor(state).unsqueeze(0)
          
        • 通過當前 Q 網絡計算 Q 值預測:
          target_f = self.model(state_tensor)
          
        • 使用 clone().detach() 方法斷開計算圖,避免梯度傳播:
          target_f = target_f.clone().detach()
          
        • 將目標 Q 值替換到對應的行動位置:
          target_f[0][action] = target
          
      • 計算損失並更新網絡參數
        • 將梯度緩存清零:

          self.optimizer.zero_grad()
          
        • 通過模型計算輸出:

          output = self.model(state_tensor)
          
        • 使用均方誤差損失函數
          https://ithelp.ithome.com.tw/upload/images/20241001/201205496yxT5jwEVs.png

          • output 是模型當前的預測,target_f 是目標Q值。
          loss = self.criterion(output, target_f)
          
        • 反向傳播計算梯度:

          loss.backward()
          
        • 更新模型參數:

          self.optimizer.step()
          
    • 調整探索率 epsilon
      • 如果當前探索率大於最小值,則按照衰減率進行衰減,逐漸降低隨機行動的概率,增加對學習策略的利用:
        if self.epsilon > self.epsilon_min:
            self.epsilon *= self.epsilon_decay
        

  • 模型保存和加載的方法

    def load(self, name):
        self.model.load_state_dict(torch.load(name))
    
    def save(self, name):
        torch.save(self.model.state_dict(), name)
    
    • 解釋:

      • load 方法
        • 用於加載已保存的模型權重,方便在不同的訓練階段或在測試時復現結果。
        • torch.load(name):從指定的文件中加載模型權重。
        • self.model.load_state_dict():將加載的權重應用到當前模型。
      • save 方法
        • 用於保存當前模型的權重,以便後續加載或部署。
        • torch.save():將模型的權重保存到指定的文件中。

  • 完整的 Agent

    class Agent:
        def __init__(self, state_size, action_size):
            self.state_size = state_size  # 狀態空間的維度
            self.action_size = action_size  # 行動空間的維度
            self.memory = []  # 經驗回放記憶庫
            self.gamma = 0.99  # 折扣因子
            self.epsilon = 1.0  # 初始探索率
            self.epsilon_min = 0.01  # 最小探索率
            self.epsilon_decay = 0.995  # 探索率衰減率
            self.model = DQN(state_size, action_size)  # 當前 Q 網絡
            self.target_model = DQN(state_size, action_size)  # 目標 Q 網絡
            self.update_target_model()  # 初始化目標網絡
            self.optimizer = optim.Adam(self.model.parameters(), lr=0.001)  # 優化器
            self.criterion = nn.MSELoss()  # 損失函數
    
        def update_target_model(self):
            # 將當前模型的權重複製到目標模型
            self.target_model.load_state_dict(self.model.state_dict())
    
        def act(self, state):
            # 根據當前狀態選擇行動
            if np.random.rand() <= self.epsilon:
                # 隨機選擇行動(探索)
                return np.random.choice(self.action_size)
            # 使用模型預測行動(利用)
            state = torch.FloatTensor(state).unsqueeze(0)
            act_values = self.model(state)
            return torch.argmax(act_values, dim=1).item()
    
        def remember(self, state, action, reward, next_state, done):
            # 將經驗存入記憶庫
            self.memory.append((state, action, reward, next_state, done))
    
        def replay(self, batch_size):
            # 從記憶庫中取樣進行訓練
            if len(self.memory) < batch_size:
                return
            minibatch = random.sample(self.memory, batch_size)
            for state, action, reward, next_state, done in minibatch:
                target = reward
                if not done:
                    next_state_tensor = torch.FloatTensor(next_state).unsqueeze(0)
                    target = reward + self.gamma * torch.max(self.target_model(next_state_tensor)).item()
                state_tensor = torch.FloatTensor(state).unsqueeze(0)
                target_f = self.model(state_tensor)
                target_f = target_f.clone().detach()
                target_f[0][action] = target
                self.optimizer.zero_grad()
                output = self.model(state_tensor)
                loss = self.criterion(output, target_f)
                loss.backward()
                self.optimizer.step()
            if self.epsilon > self.epsilon_min:
                self.epsilon *= self.epsilon_decay
    
        def load(self, name):
            # 加載模型權重
            self.model.load_state_dict(torch.load(name))
    
        def save(self, name):
            # 保存模型權重
            torch.save(self.model.state_dict(), name)
    
  • 損失函數的詳細解釋

    • 計算目標Q值

      target = reward
      if not done:
          next_state = torch.FloatTensor(next_state).unsqueeze(0)
          target = (reward + self.gamma * torch.max(self.target_model(next_state)).item())
      
      • 如果未達到終止狀態,目標Q值為當前獎勵加上折扣後的最大未來Q值。
    • 計算當前Q值

      state = torch.FloatTensor(state).unsqueeze(0)
      target_f = self.model(state)
      target_f = target_f.clone().detach()
      target_f[0][action] = target
      
      • target_f 是模型對當前狀態的Q值預測,使用 detach() 避免梯度計算。

      • 將目標Q值替換到對應的動作位置。

    • 計算損失

      output = self.model(state)
      loss = self.criterion(output, target_f)
      
      • 使用均方誤差損失函數 https://ithelp.ithome.com.tw/upload/images/20241002/20120549GD6mfpdkHr.png

      • output 是模型當前的預測,target_f 是目標Q值。

    • 更新模型參數

      loss.backward()
      self.optimizer.step()
      
      • 反向傳播計算梯度,使用優化器更新模型參數。
    • 更新目標網絡

      def update_target_model(self):
          self.target_model.load_state_dict(self.model.state_dict())
      
      • 定期將當前模型的參數複製到目標網絡,保持穩定的目標Q值計算。

5. 訓練代理

  • 設置訓練參數

    n_episodes = 50
    batch_size = 32
    state_size = env.observation_space.shape[0]
    action_size = env.action_space.n
    agent = Agent(state_size, action_size)
    
  • 訓練循環

    import random
    
    for e in range(n_episodes):
        state = env.reset()
        total_reward = 0
        for time in range(len(data) - 1):
            action = agent.act(state)
            next_state, reward, done, _ = env.step(action)
            agent.remember(state, action, reward, next_state, done)
            state = next_state
            total_reward += reward
            if done:
                agent.update_target_model()
                print(f"Episode: {e+1}/{n_episodes}, Total Reward: {total_reward:.2f}, Epsilon: {agent.epsilon:.2f}")
                break
            agent.replay(batch_size)
    
    • 更新目標網絡:在每個episode結束後,更新目標網絡。

    • 打印訓練資訊:輸出當前episode的總獎勵和探索率。

    可得以下圖形:
    https://ithelp.ithome.com.tw/upload/images/20241001/20120549ILYQcK1RVW.png
    RL 本身容易因為超參數而敏感因此有興趣的人可以嘗試調整看看超參數看有沒有更好的訓練結果
    https://ithelp.ithome.com.tw/upload/images/20241001/20120549L9wOB3gGaV.jpg

6. 評估代理

  • 測試代理

    state = env.reset()
    total_reward = 0
    net_worths = []
    for time in range(len(data) - 1):
        action = agent.act(state)
        next_state, reward, done, _ = env.step(action)
        state = next_state
        total_reward += reward
        net_worths.append(env.net_worth)
        if done:
            print(f"Total Test Reward: {total_reward:.2f}")
            break
    
  • 繪製資產淨值變化

    plt.plot(net_worths)
    plt.xlabel('Time Step')
    plt.ylabel('Net Worth')
    plt.title('Agent Net Worth Over Time')
    plt.show()
    

    可以看到淨值有上升
    https://ithelp.ithome.com.tw/upload/images/20241001/20120549Fmi94rEtsc.png


六、強化學習應用於交易的挑戰和注意事項

1. 挑戰

  • 市場的不確定性:金融市場具有高度的隨機性和不確定性,難以預測。

  • 樣本效率:強化學習通常需要大量的數據進行訓練,這在金融領域可能導致過擬合。

  • 風險控制:單純追求收益可能導致高風險,需要引入風險管理機制。

2. 注意事項

  • 環境設計:需要謹慎設計環境和獎勵機制,確保代理學習到正確的策略。

  • 過擬合問題:採用交叉驗證、早停等方法,防止模型過擬合。

  • 評估方式:使用回測等方法對策略進行全面評估,考慮交易成本、滑點等實際因素。


七、改進和擴展

1. 使用先進的強化學習算法

  • 策略梯度方法:如A2C、PPO,可以直接優化策略,收斂速度更快。

  • 分層強化學習:將決策過程分解為多個層級,提高策略的靈活性。

2. 增加狀態和動作空間的維度

  • 多資產交易:擴展到多個資產的組合交易。

  • 更多特徵:引入技術指標、基本面數據、新聞情緒等作為狀態特徵。

3. 風險控制

  • 引入風險約束:如最大回撤、夏普比率等,調整獎勵函數。

  • 風險敏感的策略:採用CVaR等風險測度,優化策略的風險收益比。


八、總結

在本節中,我們:

  • 深入了解了深度Q網絡(DQN)的原理和實現,包括其損失函數的詳細解釋。

  • 了解了強化學習的基本概念,包括代理、環境、狀態、動作和獎勵等核心組成部分。

  • 探討了如何將強化學習應用於交易,設計了交易環境和代理,並使用DQN進行交易策略的開發。

  • 認識到強化學習應用於交易的挑戰和注意事項,包括市場不確定性、樣本效率、風險控制等。

  • 討論了改進和擴展的方向,如使用更先進的算法、增加狀態和動作空間的維度、引入風險控制等。

通過這次實踐,應該對強化學習在交易中的應用有了深入的了解,並掌握了DQN的核心原理和實現方法。強化學習是一個強大但複雜的工具,需要深入的研究和謹慎的應用。


作業:

  1. 嘗試使用其他強化學習算法:如A2C、PPO等,替換DQN,觀察策略的表現差異。

  2. 增加狀態特徵:將技術指標、基本面數據等加入狀態空間,提升代理的決策能力。

  3. 優化獎勵函數:引入風險控制指標,調整獎勵函數,使代理學習到更穩健的策略。

  4. 多資產交易:擴展環境,讓代理可以在多個資產之間進行交易。

透過這些練習,您將能夠更深入地理解強化學習在交易中的應用,並提高模型的實際效果。


提示:

  • 數據預處理:確保數據的質量,處理缺失值和異常值,對數據進行歸一化。

  • 調參技巧:強化學習模型的效果對參數敏感,嘗試不同的學習率、折扣因子等參數。

  • 日誌和監控:在訓練過程中記錄代理的表現,使用可視化工具觀察訓練進展。


注意:

  • 風險提示:金融市場具有高度的不確定性,強化學習模型的預測結果僅供參考,不應作為實際投資決策的唯一依據。

  • 合規要求:在應用交易策略時,需遵守相關的法律法規和市場規則。

  • 倫理考慮:確保模型的設計和應用符合道德和倫理標準,不得利用市場漏洞或進行操縱行為。


上一篇
Day16:時間序列預測--Transformer
下一篇
Day18:強化學習在交易中的應用--使用 Stable Baselines 和 FinRL
系列文
打開就會 AI 與數據分析的投資理財術30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言