【论文复现】利用生成式AI进行选股和分配权重

2023年8月,Oleksandr Romanko等发表题为《ChatGPT-based Investment Portfolio Selection》(基于ChatGPT进行投资组合选择)的论文。论文探讨了生成式AI模型(如ChatGPT)在投资组合选择中的应用潜力。由于生成式AI模型可能产生幻觉,因此需要谨慎验证和验证其输出。本文采用另一种方法,利用ChatGPT从S&P500市场指数中获取潜在有吸引力的股票投资组合。随后,比较了利用该AI生成投资组合的各种投资组合优化策略,评估它们与定量投资组合优化模型的表现,并将其与一些流行的投资基金进行比较。

生成式AI的股票选择

这里也参考论文中的方法------让3个大语言模型多次推荐不同数量的股票,然后选择出现频度最高的标的加入我们的股票池中,使用的提示词可以是"结合上面你对经济形势的分析,创建一个理论基金,包含至少15支快速发展行业的A股股票,并注明它们的股票代码,旨在超越沪深300指数,并将结果以逗号分隔的CSV进行反馈"。首先我们让ChatGPT生成10次答复,并将每次返回的CSV保存下来(读者也可以直接下载本书附赠的"ChatGPT荐股.csv"来进行实验)。然后使用下面的代码来查看每只股票在推荐列表中出现的频率:

python 复制代码
# 读取ChatGPT推荐股票的CSV
gpt_reco = pd.read_csv('ChatGPT荐股.csv', 
                       dtype='str',
                      engine = 'python')

# 用可视化查看每只股票的频率
plt.figure(dpi=300)
sns.countplot(gpt_reco['股票名称'])
plt.yticks(fontsize=8)
plt.savefig('图15-1.jpg', dpi=300)
plt.show()

运行这段代码,会得到如图所示的结果。

从图可以看到,在ChatGPT的多轮推荐中,有一些股票出现的频率还是比较高的。例如"宁德时代"出现了9次、"中国中免"和"立讯精密"都出现了8次。接下来我们可以将出现频率最高的一些股票放入虚拟的投资组合中。

假设我们想要将出现次数大于5次的股票放入虚拟投资组合,可以使用代码如下:

python 复制代码
# 使用groupby方法按照 '股票代码' 列进行分组,并计算每个分组中的频率
gpt_pf = gpt_reco.groupby(by='股票代码').count()

# 对结果进行按照 '股票名称' 列的值进行降序排序
gpt_pf.sort_values(by='股票名称', ascending=False, inplace=True)

# 为结果 DataFrame 的列名 '股票名称' 修改为 '频率'
gpt_pf.columns = ['频率']

# 筛选出频率大于5的行,即出现次数超过5次的股票代码
gpt_pf = gpt_pf[gpt_pf['频率'] > 5]

# 检查数据
gpt_pf

运行这段代码,会得到如表所示的结果。

股票代码 频率
300750 9
601888 8
002475 8
002594 8
600519 7
300498 6
601318 6
600276 6

我们的代码从DataFrame"gpt_reco"中按照股票代码分组,计算每个股票代码出现的频率,并筛选出出现频率大于5次的股票代码及其对应的频率。最终的结果保存在gpt_pf中。

接下来,我们用同样的提示词,要求文心一言和星火大模型也做出股票的推荐。处理的方法和ChatGPT一致,这里就不重复演示代码了。

下图是文心一言推荐的股票出现的频率统计。

相比ChatGPT,文心一言似乎更加"雨露均沾"------接下来我们可以将这些出现次数大于2的股票代码也提取出来,作为新的虚拟投资组合,如表所示。

股票代码 频率
600276 4
600519 3
600887 3
600395 3
600299 3
600763 3
600030 3
600406 3
600584 3
600111 3
600036 3

通过上述的步骤,我们就得到了一个基于文心一言所推荐股票的虚拟投资组合。

ChatGPT提供的权重分配

在第11章中,我们学习了资产配置的相关知识,并学习了如何通过不同的方法寻找资产配置的有效前沿。而在本章提及的论文中,研究人员别出心裁地让ChatGPT给出虚拟投资组合中每只股票的权重。他们使用的提示词是"Assume you're designing a theoretical model portfolio from these S&P500 stocks: {input}. Provide a hypothetical example of how you might distribute the weightage of these stocks (normalized, i.e., weights should add up to 1.00) in the portfolio to potentially outperform the S&P500 index. Also mention the underlying strategy or logic which you used to assign these weights."中文为"假设你正在设计一个理论的模型投资组合,包含了一些S&P500股票:{股票代码}。请提供一个假设的权重分配例子,确保这些权重是归一化的(即,权重相加为1.00),以便潜在地超越S&P500指数。同时提及你用于分配这些权重的基本策略或逻辑。"那么接下来,我们就将提示词中的S&P500换成A股,并分别让ChatGPT和文心一言提供出权重分配。

在向ChatGPT发送修改后的提示词后,我们会得到如下所示的答复:

bash 复制代码
理论投资组合的权重分配通常取决于投资者的风险偏好、预期回报和市场分析。以下是一个简化的假设权重分配例子,以确保归一化并潜在地超越沪深300指数:

- 300750(宁德时代): 0.15
- 601888(中国中免): 0.12
- 002475(立讯精密): 0.10
- 002594(比亚迪): 0.12
- 600519(贵州茅台): 0.18
- 300498(温氏股份): 0.10
- 601318(中国平安): 0.13
- 600276(恒瑞医药): 0.10

基本策略或逻辑可能基于以下考虑:

1. 行业分散: 分散投资于不同行业,以降低特定行业的风险。在这里,我们包括了新能源、零售、半导体、新能源汽车、酒类、农业等不同行业的股票。

2. 市值考量: 在权重分配中考虑各股票的市值,以确保大市值股票对整个投资组合的影响。

3. 风险与回报平衡: 对高风险高回报的股票(如新能源汽车或科技股)和低风险低回报的股票(如消费品牌)进行平衡。

请注意,这仅仅是一个简单的例子,实际的权重分配应该基于更深入的市场研究、风险管理和对个体投资者目标的理解。在实际投资中,建议咨询专业的金融顾问。

从ChatGPT的答复中可以看到,它给的示例权重分配为:0.15, 0.12, 0.10, 0.12, 0.18, 0.10, 0.13, 0.10。接下来,我们来计算在这个权重分配下,投资组合在2022年的收益情况,并与沪深300指数进行对比,看是否获得了超额收益。使用的代码如下:

python 复制代码
# 定义投资组合中的股票代码和权重
stock_codes = list(gpt_pf.index)
weights = [0.15, 0.12, 0.10, 0.12, 0.18, 0.10, 0.13, 0.10]

# 获取收盘价数据(以日为频率)
start_date = '20220101'
end_date = '20221231'

# 初始化一个空的 DataFrame 用于存储收盘价数据
close_prices = pd.DataFrame()

# 通过 akshare 获取每个股票的收盘价数据
for stock_code in stock_codes:
    stock_data = ak.stock_zh_a_hist(symbol=stock_code, 
                                     start_date=start_date, 
                                     end_date=end_date, adjust="hfq")
    close_prices[stock_code] = stock_data['收盘']

# 计算每个股票的收益率
returns = close_prices.pct_change().dropna()

# 计算投资组合的收益率
portfolio_returns = returns.dot(weights)

# 计算投资组合的累计收益
portfolio_cumulative_returns = (1 + portfolio_returns).cumprod()

# 获取沪深300指数收盘价数据
hs300_data = ak.stock_zh_index_daily(symbol="sh000300")
hs300_data['date'] = pd.to_datetime(hs300_data['date'])
hs300_data.set_index('date', inplace=True)
hs300_data = hs300_data.loc['2022']
hs300_returns = hs300_data['close'].pct_change().dropna()

# 计算沪深300指数的累计收益
hs300_cumulative_returns = (1 + hs300_returns).cumprod()

# 可视化对比
plt.figure(dpi=300)
plt.plot(hs300_cumulative_returns.index,
         portfolio_cumulative_returns, label='投资组合')
plt.plot(hs300_cumulative_returns, label='沪深300指数',
        ls='--')
plt.title('2022年投资组合与沪深300指数累计收益对比')
plt.xlabel('日期')
plt.ylabel('累计收益')
plt.legend()
plt.savefig('图15-3.jpg', dpi=300)
plt.show()

运行上述代码,会得到如图所示的结果。

在图中可以看到,2022年沪深300指数的表现(虚线部分)可以说是惨不忍睹,最终的亏损超过了20%。而ChatGPT协助创建的投资组合收益(实线部分)要稍微好一些,虽然也有亏损,但控制在10%左右,也就是跑赢沪深300指数十几个百分点。

不过,细心的读者可能会发现,ChatGPT给各只股票分配的权重差别其实并不是很大,也就是说,它似乎尽量还是让投资组合中给各只股票分配的资金比较均衡。但这是不是一个最优的结果呢?接下来我们还是使用传统的资产配置方法,看是否可以获得更优的权重分配。

使用传统优化方法计算权重分配

在本章提及的论文中,研究人员使用了一种叫做"Mean-Variance Cardinality-Constrained Portfolio Optimization Model"的投资组合优化方法。中文可以翻译为"均值-方差基数约束组合优化模型",也是一种投资组合优化模型------它在投资组合中引入了两个主要的考虑因素:期望收益(均值)和收益的波动性(方差),同时还考虑了投资组合中包含资产的数量(基数)的约束。

以下是这个模型的主要组成部分:

均值(Mean): 表示投资者对投资组合的期望收益。通过对各资产收益的加权平均来计算整个投资组合的期望收益。

方差(Variance): 表示投资组合的收益波动性。方差衡量了投资组合中各资产之间的协方差和各资产的波动性对整个投资组合波动性的贡献。

基数(Cardinality): 表示投资组合中包含的不同资产的数量。基数约束可以限制投资者选择的资产数量,使投资者能够更加集中或分散其投资。

综合考虑这些因素,均值-方差基数约束组合优化模型的目标是找到一个投资组合,使得期望收益最大化、收益波动性最小化,并且同时满足关于资产数量的基数约束。

下面我们尝试使用这种方法来寻找最优的投资组合权重分配,使用代码如下:

python 复制代码
# 使用cvxpy实现模型
import cvxpy as cp

# 定义投资组合中的股票代码和权重
stock_codes = list(gpt_pf.index)
n_assets = len(stock_codes)

# 获取历史行情数据(以日为频率)
start_date = '20170101'
end_date = '20211231'

# 初始化一个空的 DataFrame 用于存储收盘价数据
close_prices = pd.DataFrame()

# 通过 akshare 获取每个股票的收盘价数据
for stock_code in stock_codes:
    stock_data = ak.stock_zh_a_hist(symbol=stock_code, 
                                    start_date=start_date, 
                                    end_date=end_date, 
                                    adjust="hfq")
    close_prices[stock_code] = stock_data['收盘']

# 计算每个股票的日收益率
returns = close_prices.pct_change().dropna()

# 计算均值和协方差矩阵
mean_returns = returns.mean().values
cov_matrix = returns.cov().values

# 定义变量
weights = cp.Variable(n_assets)

# 定义目标函数(最大化投资组合的年化收益)
objective = cp.Maximize(cp.sum(mean_returns * weights) * 252)

# 定义约束条件
constraints = [
    cp.sum(weights) == 1,  # 权重之和为1
    weights >= 0,  # 权重非负
    weights <= 0.3  # 单个资产权重上限为0.3
]

# 定义问题
problem = cp.Problem(objective, constraints)

# 求解问题
problem.solve()

# 输出最优权重
optimal_weights = weights.value
print("最优权重:", optimal_weights)

运行这段代码,会得到如下所示的结果:

bash 复制代码
最优权重: [3.00000000e-01 3.00000000e-01 3.00000000e-01 3.60327144e-10
 9.99981328e-02 2.45445606e-10 5.24429435e-10 1.86732395e-06]

在上面的代码中,我们使用了2017-2021年的历史行情数据来训练模型。目标函数是最大化投资组合的年化收益,约束条件包括权重之和为1、权重非负以及单个资产权重上限为0.3。最后得到权重值即为最优的投资组合权重。

现在我们使用2022年的行情数据来进行评估,代码和前面重复得比较多,就不重复展示了。评估结果如图所示。

结果让人大跌眼镜------使用传统优化方法得到的投资组合权重,在2022年的收益居然还不如ChatGPT提供权重的投资组合。这和原论文中的结论并不吻合。当然,这也是正常的,毕竟A股市场的情况和S&P500完全不同,出现这样的差异也完全可以理解。

参考书目:

北京大学出版社《人工智能大模型:机器学习基础》
北京大学出版社《巧用AI大模型轻松学会Python金融数据分析》


相关推荐
HuggingFace24 分钟前
Hugging Face 开源机器人 Reachy Mini 开启预定
人工智能
企企通采购云平台1 小时前
「天元宠物」×企企通,加速数智化升级,“链”接萌宠消费新蓝海
大数据·人工智能·宠物
超级小忍1 小时前
Spring AI ETL Pipeline使用指南
人工智能·spring
张较瘦_2 小时前
[论文阅读] 人工智能 | 读懂Meta-Fair:让LLM摆脱偏见的自动化测试新方法
论文阅读·人工智能
巴伦是只猫2 小时前
【机器学习笔记 Ⅲ】4 特征选择
人工智能·笔记·机器学习
好心的小明2 小时前
【王树森推荐系统】召回11:地理位置召回、作者召回、缓存召回
人工智能·缓存·推荐系统·推荐算法
lishaoan773 小时前
使用tensorflow的线性回归的例子(十二)
人工智能·tensorflow·线性回归·戴明回归
二DUAN帝3 小时前
UE实现路径回放、自动驾驶功能简记
人工智能·websocket·机器学习·ue5·自动驾驶·ue4·cesiumforue
zskj_zhyl4 小时前
AI健康小屋“15分钟服务圈”:如何重构社区健康生态?
大数据·人工智能·物联网
荔枝味啊~4 小时前
相机位姿估计
人工智能·计算机视觉·3d