请大家关注我,本文章粉丝可见,我会一直更新下去,完整代码进QQ群获取:323140750,大家一起进步、学习。
在下面的实例中,将从TuShare获取A股的股票数据,然后使用两种强化学习模型(DDPG和A2C)对股票交易策略进行训练。在训练期间,模型会学习如何最大化投资组合的价值。训练后的模型将用于执行股票交易策略,然后通过强化学习的方式来决定买入和卖出的时间点和股票。为了提高开发效率,本实例将使用库FinRL实现。FinRL(Financial Reinforcement Learning)是一个用于金融领域的强化学习库,它旨在帮助研究人员和开发者利用强化学习技术构建和优化量化金融交易策略。FinRL库提供了一系列强化学习算法,包括DQN、DDPG、PPO、SAC等,以及用于训练和评估这些算法的工具。
实例8-4:制作股票交易策略模型(源码路径:daima/8/China_A_share_market.py)
8.4.1 准备环境
(1)使用如下所示的pip命令安装库FinRL :
!pip install git+https://github.com/AI4Finance-Foundation/FinRL.git
(2)使用如下命令在线加载FinRL-Meta的GitHub存储库:
/
Cloning into 'FinRL-Meta'...
remote: Enumerating objects: 7862, done.
remote: Counting objects: 100% (101/101), done.
remote: Compressing objects: 100% (66/66), done.
remote: Total 7862 (delta 48), reused 67 (delta 35), pack-reused 7761
Receiving objects: 100% (7862/7862), 170.60 MiB | 20.49 MiB/s, done.
Resolving deltas: 100% (4565/4565), done.
Updating files: 100% (423/423), done.
/FinRL-Meta
(3)需要准备一些指定的目录,如果这些指定的目录不存在的话,使用如下代码创建这些目录。在注释部分向大家说明,可以使用名为check_and_make_directories()的函数来替代这些手动创建目录的步骤。
import os
'''
use check_and_make_directories() to replace the following
if not os.path.exists("./datasets"):
os.makedirs("./datasets")
if not os.path.exists("./trained_models"):
os.makedirs("./trained_models")
if not os.path.exists("./tensorboard_log"):
os.makedirs("./tensorboard_log")
if not os.path.exists("./results"):
os.makedirs("./results")
'''
check_and_make_directories([DATA_SAVE_DIR, TRAINED_MODEL_DIR, TENSORBOARD_LOG_DIR, RESULTS_DIR])
执行后会输出:
/
Cloning into 'FinRL-Meta'...
remote: Enumerating objects: 7862, done.
remote: Counting objects: 100% (101/101), done.
remote: Compressing objects: 100% (66/66), done.
remote: Total 7862 (delta 48), reused 67 (delta 35), pack-reused 7761
Receiving objects: 100% (7862/7862), 170.60 MiB | 20.49 MiB/s, done.
Resolving deltas: 100% (4565/4565), done.
Updating files: 100% (423/423), done.
/FinRL-Meta
8.4.2 准备数据
定义了一个股票代码列表 ticker_list,其中包含了多个A 股市场的股票代码,然后准备从TuShare获取这些股票的数据。具体实现代码如下所示。
ticker_list = ['600000.SH', '600009.SH', '600016.SH', '600028.SH', '600030.SH', '600031.SH', '600036.SH', '600050.SH', '600104.SH', '600196.SH', '600276.SH', '600309.SH', '600519.SH', '600547.SH', '600570.SH']
TRAIN_START_DATE = '2015-01-01'
TRAIN_END_DATE= '2019-08-01'
TRADE_START_DATE = '2019-08-01'
TRADE_END_DATE = '2020-01-03'
TIME_INTERVAL = "1d"
kwargs = {}
kwargs['token'] = ' '
p = DataProcessor(data_source='tushare', start_date=TRAIN_START_DATE, end_date=TRADE_END_DATE, time_interval=TIME_INTERVAL, **kwargs)
对上述代码的具体说明如下:
(1)设置训练和交易的时间范围:
- TRAIN_START_DATE:表示训练数据的起始日期,设置为 '2015-01-01'。
- TRAIN_END_DATE:表示训练数据的结束日期,设置为 '2019-08-01'。
- TRADE_START_DATE:表示交易数据的起始日期,设置为 '2019-08-01'。
- TRADE_END_DATE:表示交易数据的结束日期,设置为 '2020-01-03'。
(2)定义时间间隔 TIME_INTERVAL,设置为 "1d",表示每日数据。
(3)创建了一个空字典 kwargs,然后向 kwargs 字典中添加了一个键值对,键为 'token',值为 自己的token值。
(4)创建一个名为 p 的 DataProcessor 对象,用于数据处理和下载。在创建 DataProcessor 对象时,传入了以下参数:
- 将data_source 设置为 'tushare',设置使用 Tushare 数据源。
- start_date 设置为 TRAIN_START_DATE,表示数据下载的起始日期。
- end_date 设置为 TRADE_END_DATE,表示数据下载的结束日期。
- time_interval 设置为 TIME_INTERVAL,表示数据的时间间隔。
- **kwargs 表示将前面定义的 kwargs 字典作为额外的关键字参数传递给 DataProcessor。
上述代码的目的是为后续的数据下载和处理操作准备数据源和参数。
8.4.3 下载、清理和预处理股票数据
首先调用 p.download_data(ticker_list=ticker_list) 方法从 Tushare下载指定股票代码列表的股票数据,其中 ticker_list 参数传入了之前定义的股票代码列表;然后调用 p.clean_data() 方法对下载的数据进行清理和预处理,这个方法会处理数据中的缺失值、异常值等,以确保数据质量;最后调用 p.fillna() 方法填充数据中的缺失值,这一步骤通常是为了确保数据在后续的分析和建模过程中能够正常使用,因为很多机器学习算法不支持缺失值。具体实现代码如下所示。
p.download_data(ticker_list=ticker_list)
p.clean_data()
p.fillna()
执行后会输出:
100%|██████████| 15/15 [00:07<00:00, 1.94it/s]
Download complete! Dataset saved to ./data/dataset.csv.
Shape of DataFrame: (17960, 8)
Shape of DataFrame: (18315, 8)
8.4.4 添加技术指标
技术指标是根据股票价格、成交量等市场数据计算得出的衍生指标,用于分析股票的价格走势、波动性和可能的买卖信号。这些指标可以帮助交易员和投资者更好地理解市场趋势和价格动态,以制定投资决策。编写如下所示的代码,在前面已下载并清理的股票数据上计算并添加一组技术指标,并确保数据的完整性。
p.add_technical_indicator(config.INDICATORS)
p.fillna()
#print(f"p.dataframe: {p.dataframe}")
对上述代码的具体说明如下:
- 调用 p.add_technical_indicator(config.INDICATORS) 方法,其中 config.INDICATORS 是一个包含了一组技术指标的配置参数。这个方法的目的是根据配置参数计算并添加技术指标列到数据中。技术指标通常是根据股票价格和交易量等数据计算得出的衍生指标,用于分析股票的走势和价格动态。
- 调用 p.fillna() 方法,用于填充数据中的缺失值。这一步骤通常是为了确保数据在后续的分析和建模过程中能够正常使用,因为很多机器学习算法不支持缺失值。
执行后会输出:
tech_indicator_list: ['macd', 'boll_ub', 'boll_lb', 'rsi_30', 'cci_30', 'dx_30', 'close_30_sma', 'close_60_sma']
indicator: macd
indicator: boll_ub
indicator: boll_lb
indicator: rsi_30
indicator: cci_30
indicator: dx_30
indicator: close_30_sma
indicator: close_60_sma
Succesfully add technical indicators
Shape of DataFrame: (18270, 17)
8.4.5 拆分数据集
拆分训练数据集的目的是为了在模型的开发和评估过程中进行有效的监控和验证,这有助于确保模型不仅在训练数据上表现良好,还能够泛化到未见过的数据。通常,原始的训练数据集会被分成训练集、验证集和测试集,具体拆分比例可以根据具体的问题和数据集大小进行调整。
(1)将数据集 p.dataframe 按照给定的时间范围 TRAIN_START_DATE 和 TRAIN_END_DATE 进行划分,生成训练集 train。具体实现代码如下所示。
train = p.data_split(p.dataframe, TRAIN_START_DATE, TRAIN_END_DATE)
print(f"len(train.tic.unique()): {len(train.tic.unique())}")
对上述代码的具体说明如下:
- p.data_split(p.dataframe, TRAIN_START_DATE, TRAIN_END_DATE):这是一个数据处理的操作,它从整个数据集 p.dataframe 中选取了在指定时间范围 TRAIN_START_DATE 到 TRAIN_END_DATE 之间的数据。换句话说,它仅保留了在这个时间段内的数据点。
- len(train.tic.unique()):这行代码计算了训练集 train 中不同股票代码(tic)的数量,即计算了在训练集中有多少不同的股票。
上述代码的目的是检查训练集中涵盖了多少不同的股票,以便了解在训练模型时有多少不同的股票数据可用。这对于股票市场的预测建模任务非常重要,因为不同股票的行为可能会有所不同,了解覆盖的股票数量有助于评估模型的多样性和泛化能力。执行后会输出:
len(train.tic.unique()): 15
(2)打印输出训练集 train 中不同股票代码(tic)的列表,使用unique() 方法来获取训练集中唯一的股票代码,然后使用 print 函数将这些唯一的股票代码打印出来。具体实现代码如下所示。
print(f"train.tic.unique(): {train.tic.unique()}")
这个操作有助于了解在训练集中都包含了哪些股票,以及它们的数量。这对于后续的股票市场分析和建模非常重要,因为不同股票的行为和特征可能会有所不同,因此需要根据不同股票的数据进行个性化的建模和分析。执行后会输出会:
train.tic.unique(): ['600000.SH' '600009.SH' '600016.SH' '600028.SH' '600030.SH' '600031.SH'
'600036.SH' '600050.SH' '600104.SH' '600196.SH' '600276.SH' '600309.SH'
'600519.SH' '600547.SH' '600570.SH']
(3)使用 train.head() 方法来获取数据集的前几行,然后使用 print 函数将这些行的数据打印出来。具体实现代码如下所示。
print(f"train.head(): {train.head()}")
执行后会输出:
train.head(): tic time index open high low close adjusted_close \
0 600000.SH 2015-01-08 45 15.87 15.88 15.20 15.25 15.25
0 600009.SH 2015-01-08 46 20.18 20.18 19.73 20.00 20.00
0 600016.SH 2015-01-08 47 10.61 10.66 10.09 10.20 10.20
0 600028.SH 2015-01-08 48 7.09 7.41 6.83 6.85 6.85
0 600030.SH 2015-01-08 49 36.40 36.70 34.68 35.25 35.25
volume macd boll_ub boll_lb rsi_30 cci_30 \
0 3306271.72 -0.032571 16.617911 15.012089 6.058641 -125.593009
0 198117.45 -0.016008 20.663897 19.736103 12.828915 -90.842491
0 4851684.17 -0.018247 10.957604 9.997396 11.862558 -99.887006
0 8190902.35 -0.008227 7.342000 6.743000 27.409248 36.578171
0 6376268.69 0.032910 36.576444 33.808556 61.517448 47.947020
dx_30 close_30_sma close_60_sma
0 23.014040 15.8150 15.8150
0 100.000000 20.2000 20.2000
0 100.000000 10.4775 10.4775
0 64.934862 7.0425 7.0425
0 100.000000 35.1925 35.1925
(4)打印输出训练集 train 的形状(shape)。形状是一个元组,它包含了数据集的维度信息。具体实现代码如下所示。
print(f"train.shape: {train.shape}")
具体地说,train.shape 的输出是一个包含两个整数的元组,第一个整数表示数据集的行数(样本数量),第二个整数表示数据集的列数(特征数量)。执行后会输出:
train.shape: (16695, 17)
(5)计算股票维度(stock_dimension)和状态空间维度(state_space)的值,并将它们打印出来。具体实现代码如下所示。
stock_dimension = len(train.tic.unique())
state_space = stock_dimension * (len(config.INDICATORS) + 2) + 1
print(f"Stock Dimension: {stock_dimension}, State Space: {state_space}")
对上述代码的具体说明如下:
- stock_dimension 表示训练数据中不同股票的数量,它是通过计算训练集中唯一股票代码的数量来确定的。每个股票都被视为一个独立的维度,因为在股票交易问题中,每只股票都有其自己的价格和技术指标数据。
- state_space 表示强化学习环境的状态空间维度。在强化学习中,状态空间定义了智能体(在这种情况下是交易策略)可以观察到的所有可能状态的集合。在这里,状态空间的维度是根据股票维度和技术指标的数量计算的。len(config.INDICATORS) 给出了使用的技术指标的数量,而 + 2 表示还考虑了现金持有量和股票仓位作为状态。最后的 + 1 是为了包含一个额外的状态,通常用于表示时间步骤。
执行后会输出:
Stock Dimension: 15, State Space: 151
通过打印这些值,你可以了解在训练过程中将要处理的数据维度,这对于构建强化学习模型和环境非常重要。