iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0
AI/ ML & Data

自動交易程式探索系列 第 5

Day 5 - 透過FinRL入門 訓練RL模型 (2/3)

  • 分享至 

  • xImage
  •  

參考資料 Stock_NeurIPS2018_2_Train.ipynb

在這篇教學文章中,介紹了如何使用 FinRL 庫來訓練深度強化學習 (Deep Reinforcement Learning, DRL) 的代理人 (Agent) 進行股票交易。

強化學習 (Reinforcement Learning, RL) 基本概念

在強化學習中,主要有兩個核心組件:代理人 (Agent)環境 (Environment)。簡單來說,代理人在一個環境中行動,並根據環境的反饋來學習更好的行為策略。具體來說:

  1. 狀態 (State):代理人對環境的當前觀察。就像一個交易者會分析市場的各種信息一樣,代理人根據歷史數據、技術指標等來判斷當前市場的狀態。
  2. 動作 (Action):代理人在每個狀態下可以執行的操作。比如在股票交易中,動作可以是買入、賣出或持有股票。
  3. 獎勵 (Reward):代理人採取動作後得到的反饋,用來告訴代理人這個動作是好是壞。例如,獎勵可以是投資組合價值的變化,代理人通過最大化累積獎勵來學習更好的交易策略。

目標是通過不斷地互動,讓代理人學會在不同的市場狀況下,做出能最大化長期收益的動作。

建立股票市場環境StockTradingEnv (Market Environment)

這個步驟使用 OpenAI Gym 的風格來構建股票交易的環境,我們將歷史股票數據轉換成強化學習的環境。在這裡,代理人將會與這個環境進行互動。

stock_dimension = len(train.tic.unique())
state_space = 1 + 2 * stock_dimension + len(INDICATORS) * stock_dimension
print(f"Stock Dimension: {stock_dimension}, State Space: {state_space}")

buy_cost_list = sell_cost_list = [0.001] * stock_dimension
num_stock_shares = [0] * stock_dimension

env_kwargs = {
    "hmax": 100,
    "initial_amount": INIT_AMOUNT,
    "num_stock_shares": num_stock_shares,
    "buy_cost_pct": buy_cost_list,
    "sell_cost_pct": sell_cost_list,
    "state_space": state_space,
    "stock_dim": stock_dimension,
    "tech_indicator_list": INDICATORS,
    "action_space": stock_dimension,
    "reward_scaling": 1e-4
}

e_train_gym = StockTradingEnv(df=train, **env_kwargs)

這邊針對初始化 StockTradingEnv 的一些設定參數做一些解析:

1. hmax

含義:

  • hmax 表示每個時間步中,每支股票最大可交易的股票數量。它限制了在單個時間步中可買入或賣出的股票數量上限,防止一次性交易過多的股票。

在環境中的作用:

  • 限制交易量:通過設定 hmax,您可以控制在每個時間步中最大能交易的股票數量,模擬現實中交易量的限制或風險管理策略。
  • 平衡訓練過程:避免在訓練過程中因過大的交易量導致過度風險或異常的收益,確保訓練過程的穩定性。

代碼中的使用:

step 函數中,動作(actions)被處理如下:

  • 動作縮放:智能體輸出的動作最初在 [-1, 1] 範圍內。通過乘以 hmax,將動作映射到 [-hmax, hmax],表示每支股票可買入或賣出的最大股數。
    • 例如,如果 hmax = 100,那麼智能體在單個時間步中,最多可買入或賣出 100 股每支股票。
  • 整數化:由於無法購買或出售非整數股數,將動作轉換為整數。
def step(self, actions):
    # 將動作從 [-1, 1] 的範圍映射到 [-hmax, hmax]
    actions = actions * self.hmax  # actions initially is scaled between -1 to 1
    actions = actions.astype(int)  # 將動作轉換為整數,因為股票數量必須是整數
    # ...

範例:

  • 如果智能體輸出的動作為 [0.5, -0.3, 0.1],表示對三支股票的操作。
  • 乘以 hmax = 100 後,得到 [50, -30, 10],表示:
    • 買入第1支股票50股。
    • 賣出第2支股票30股。
    • 買入第3支股票10股。

2. initial_amount

初始時的現金總額。

3. num_stock_shares

num_stock_shares 是一個列表,表示在環境初始化時,智能體持有的每支股票的初始股數
它定義了智能體在交易開始前,對每支股票的初始持倉。

範例:

  • 假設有兩支股票,初始現金為 1,000,000,num_stock_shares = [10, 20],股票價格分別為 50 和 100。
  • 初始總資產為:
    • 現金:1,000,000
    • 股票價值:10 * 50 + 20 * 100 = 500 + 2000 = 2500
    • 總資產:1,000,000 + 2500 = 1,002,500

4. state_space

狀態空間state_space)指的是智能體在每個時間點下能夠觀察到的狀態數值有多少維度。

state_space = 1 + 2 * stock_dimension + len(INDICATORS) * stock_dimension

  • 現金餘額(1維):

    • 這是智能體當前持有的現金金額,通常存儲在 self.state[0]
  • 股票價格stock_dim 維):

    • 對於每支股票,智能體需要知道其當前的市場價格。
    • 共有 stock_dim 支股票,因此這部分狀態佔據 stock_dim 個維度,存儲在 self.state[1 : stock_dim + 1]
  • 持有的股票數量stock_dim 維):

    • 智能體需要知道自己在每支股票上的持倉數量。
    • 這部分狀態也佔據 stock_dim 個維度,存儲在 self.state[stock_dim + 1 : 2 * stock_dim + 1]
  • 技術指標len(INDICATORS) * stock_dim 維):

    • 對於每支股票,計算了一系列技術指標(如移動平均線、相對強弱指數等),用於幫助智能體判斷市場趨勢。
    • 如果您有 len(INDICATORS) 個技術指標,那麼對於每支股票,這部分狀態佔據 len(INDICATORS) 個維度。
    • 總共對於所有股票,這部分狀態佔據 len(INDICATORS) * stock_dim 個維度,存儲在 self.state[2 * stock_dim + 1 :]

5. action_space

action_space 定義了智能體在每個時間步可以執行的動作空間的維度。
在環境中,action_space 通常設定為 stock_dim,即股票的數量
這意味著智能體的動作是一個向量,每個元素對應一支股票的買賣操作。

在環境中的作用:

  • 決定動作的維度action_space 確定了智能體輸出的動作向量的長度,反映了智能體可以對多少支股票進行操作。
  • 限制動作範圍:通過定義動作空間的形狀和取值範圍,您可以控制智能體在每個時間步中對每支股票的交易行為。

範例:

  • 如果 stock_dim = 3,那麼 action_space = 3,智能體的動作向量長度為 3。
  • 動作 [0.7, -0.5, 0] 表示:
    • 第一支股票:買入 0.7 * hmax 股。
    • 第二支股票:賣出 0.5 * hmax 股。
    • 第三支股票:不進行交易。

附加说明

  • 技术指标列表 tech_indicator_list
    • 包含了您希望纳入状态的技术指标名称列表。
    • 每个技术指标为每只股票增加一个状态维度。
  • 交易成本 buy_cost_pctsell_cost_pct
    • 模拟实际交易中的买入和卖出成本。
    • 在买入和卖出操作中扣除相应的交易成本,影响智能体的净收益。

訓練強化學習代理人 (Training DRL Agent)

使用 Stable Baselines 3 庫中的 DRL 演算法來訓練代理人,這裡提供了多種演算法選擇,比如 A2C、PPO、SAC 等。

每個演算法的具體步驟包括:

  1. 初始化代理人。
  2. 設置演算法參數。
  3. 使用數據進行訓練。

下面是使用 A2C 演算法進行訓練的範例:

from finrl.agents.stablebaselines3.models import DRLAgent

# 設定 A2C 模型
agent = DRLAgent(env=env_train)
model_a2c = agent.get_model("a2c")

# 訓練模型
trained_a2c = agent.train_model(model=model_a2c, tb_log_name='a2c', total_timesteps=50000)

# 保存訓練好的模型
trained_a2c.save('trained_models/agent_a2c')

訓練部分的完整程式碼

import pandas as pd
from stable_baselines3.common.logger import configure

from finrl.agents.stablebaselines3.models import DRLAgent
from finrl.config import INDICATORS, TRAINED_MODEL_DIR, RESULTS_DIR
from finrl.main import check_and_make_directories
from finrl.meta.env_stock_trading.env_stocktrading import StockTradingEnv

check_and_make_directories([TRAINED_MODEL_DIR])
train = pd.read_csv('train_data.csv')

# If you are not using the data generated from part 1 of this tutorial, make sure 
# it has the columns and index in the form that could be make into the environment. 
# Then you can comment and skip the following two lines.
train = train.set_index(train.columns[0])
train.index.names = ['']

stock_dimension = len(train.tic.unique())
state_space = 1 + 2*stock_dimension + len(INDICATORS)*stock_dimension
print(f"Stock Dimension: {stock_dimension}, State Space: {state_space}")

buy_cost_list = sell_cost_list = [0.001] * stock_dimension
num_stock_shares = [0] * stock_dimension

env_kwargs = {
    "hmax": 100,
    "initial_amount": 1000000,
    "num_stock_shares": num_stock_shares,
    "buy_cost_pct": buy_cost_list,
    "sell_cost_pct": sell_cost_list,
    "state_space": state_space,
    "stock_dim": stock_dimension,
    "tech_indicator_list": INDICATORS,
    "action_space": stock_dimension,
    "reward_scaling": 1e-4
}

e_train_gym = StockTradingEnv(df = train, **env_kwargs)

env_train, _ = e_train_gym.get_sb_env()
print(type(env_train))

agent = DRLAgent(env = env_train)

# Set the corresponding values to 'True' for the algorithms that you want to use
if_using_a2c = True
if_using_ddpg = True
if_using_ppo = True
if_using_td3 = True
if_using_sac = True

##### Agent 1: A2C
agent = DRLAgent(env = env_train)
model_a2c = agent.get_model("a2c")

if if_using_a2c:
  # set up logger
  tmp_path = RESULTS_DIR + '/a2c'
  new_logger_a2c = configure(tmp_path, ["stdout", "csv", "tensorboard"])
  # Set new logger
  model_a2c.set_logger(new_logger_a2c)

trained_a2c = agent.train_model(model=model_a2c, 
                             tb_log_name='a2c',
                             total_timesteps=50000) if if_using_a2c else None

trained_a2c.save(TRAINED_MODEL_DIR + "/agent_a2c") if if_using_a2c else None

##### Agent 2: DDPG
agent = DRLAgent(env = env_train)
model_ddpg = agent.get_model("ddpg")

if if_using_ddpg:
  # set up logger
  tmp_path = RESULTS_DIR + '/ddpg'
  new_logger_ddpg = configure(tmp_path, ["stdout", "csv", "tensorboard"])
  # Set new logger
  model_ddpg.set_logger(new_logger_ddpg)

trained_ddpg = agent.train_model(model=model_ddpg, 
                             tb_log_name='ddpg',
                             total_timesteps=50000) if if_using_ddpg else None
trained_ddpg.save(TRAINED_MODEL_DIR + "/agent_ddpg") if if_using_ddpg else None

##### Agent 3: PPO
agent = DRLAgent(env = env_train)
PPO_PARAMS = {
    "n_steps": 2048,
    "ent_coef": 0.01,
    "learning_rate": 0.00025,
    "batch_size": 128,
}
model_ppo = agent.get_model("ppo",model_kwargs = PPO_PARAMS)

if if_using_ppo:
  # set up logger
  tmp_path = RESULTS_DIR + '/ppo'
  new_logger_ppo = configure(tmp_path, ["stdout", "csv", "tensorboard"])
  # Set new logger
  model_ppo.set_logger(new_logger_ppo)
trained_ppo = agent.train_model(model=model_ppo, 
                             tb_log_name='ppo',
                             total_timesteps=200000) if if_using_ppo else None
trained_ppo.save(TRAINED_MODEL_DIR + "/agent_ppo") if if_using_ppo else None

##### Agent 4: TD3
agent = DRLAgent(env = env_train)
TD3_PARAMS = {"batch_size": 100, 
              "buffer_size": 1000000, 
              "learning_rate": 0.001}

model_td3 = agent.get_model("td3",model_kwargs = TD3_PARAMS)

if if_using_td3:
  # set up logger
  tmp_path = RESULTS_DIR + '/td3'
  new_logger_td3 = configure(tmp_path, ["stdout", "csv", "tensorboard"])
  # Set new logger
  model_td3.set_logger(new_logger_td3)
trained_td3 = agent.train_model(model=model_td3, 
                             tb_log_name='td3',
                             total_timesteps=50000) if if_using_td3 else None
trained_td3.save(TRAINED_MODEL_DIR + "/agent_td3") if if_using_td3 else None

##### Agent 5: SAC
agent = DRLAgent(env = env_train)
SAC_PARAMS = {
    "batch_size": 128,
    "buffer_size": 100000,
    "learning_rate": 0.0001,
    "learning_starts": 100,
    "ent_coef": "auto_0.1",
}

model_sac = agent.get_model("sac",model_kwargs = SAC_PARAMS)

if if_using_sac:
  # set up logger
  tmp_path = RESULTS_DIR + '/sac'
  new_logger_sac = configure(tmp_path, ["stdout", "csv", "tensorboard"])
  # Set new logger
  model_sac.set_logger(new_logger_sac)
trained_sac = agent.train_model(model=model_sac, 
                             tb_log_name='sac',
                             total_timesteps=70000) if if_using_sac else None
trained_sac.save(TRAINED_MODEL_DIR + "/agent_sac") if if_using_sac else None

上一篇
Day 4 - 透過FinRL入門 Data (1/3)
下一篇
Day 6 - 使用MVO和DJIA驗證DRL的績效
系列文
自動交易程式探索30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言